aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.build.yml21
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml31
-rw-r--r--CMake/FindA2X.cmake75
-rw-r--r--CMake/FindCairo.cmake42
-rw-r--r--CMake/FindDBus.cmake59
-rw-r--r--CMake/FindEpollShim.cmake17
-rw-r--r--CMake/FindGdkPixbuf.cmake43
-rw-r--r--CMake/FindJsonC.cmake29
-rw-r--r--CMake/FindLibInput.cmake66
-rw-r--r--CMake/FindLibcap.cmake56
-rw-r--r--CMake/FindPAM.cmake245
-rw-r--r--CMake/FindPCRE.cmake37
-rw-r--r--CMake/FindPango.cmake42
-rw-r--r--CMake/FindWLC.cmake20
-rw-r--r--CMake/FindWayland.cmake67
-rw-r--r--CMake/FindXKBCommon.cmake19
-rw-r--r--CMake/Manpage.cmake33
-rw-r--r--CMake/Wayland.cmake77
-rw-r--r--CMakeLists.txt161
-rw-r--r--HACKING.md11
-rw-r--r--README.de.md14
-rw-r--r--README.el.md14
-rw-r--r--README.fr.md14
-rw-r--r--README.it.md14
-rw-r--r--README.ja.md14
-rw-r--r--README.md16
-rw-r--r--README.pt.md14
-rw-r--r--README.ru.md14
-rw-r--r--README.uk.md14
-rw-r--r--client/meson.build16
-rw-r--r--client/pool-buffer.c (renamed from wayland/buffers.c)58
-rw-r--r--common/CMakeLists.txt15
-rw-r--r--common/background-image.c119
-rw-r--r--common/cairo.c (renamed from wayland/cairo.c)45
-rw-r--r--common/ipc-client.c34
-rw-r--r--common/log.c151
-rw-r--r--common/meson.build23
-rw-r--r--common/pango.c (renamed from wayland/pango.c)33
-rw-r--r--common/readline.c4
-rw-r--r--common/unicode.c101
-rw-r--r--common/util.c24
-rw-r--r--completions/zsh/CMakeLists.txt4
-rw-r--r--config.in20
-rw-r--r--include/background-image.h20
-rw-r--r--include/cairo.h (renamed from include/client/cairo.h)10
-rw-r--r--include/client/buffer.h8
-rw-r--r--include/client/pango.h16
-rw-r--r--include/client/registry.h75
-rw-r--r--include/client/window.h67
-rw-r--r--include/ipc.h1
-rw-r--r--include/log.h33
-rw-r--r--include/meson.build1
-rw-r--r--include/pango.h16
-rw-r--r--include/pool-buffer.h22
-rw-r--r--include/sway.h6
-rw-r--r--include/sway/border.h28
-rw-r--r--include/sway/commands.h45
-rw-r--r--include/sway/config.h159
-rw-r--r--include/sway/container.h357
-rw-r--r--include/sway/criteria.h8
-rw-r--r--include/sway/debug.h7
-rw-r--r--include/sway/desktop.h4
-rw-r--r--include/sway/extensions.h56
-rw-r--r--include/sway/focus.h45
-rw-r--r--include/sway/handlers.h11
-rw-r--r--include/sway/input.h23
-rw-r--r--include/sway/input/cursor.h36
-rw-r--r--include/sway/input/input-manager.h64
-rw-r--r--include/sway/input/keyboard.h30
-rw-r--r--include/sway/input/seat.h111
-rw-r--r--include/sway/input_state.h102
-rw-r--r--include/sway/ipc-json.h13
-rw-r--r--include/sway/ipc-server.h38
-rw-r--r--include/sway/layers.h25
-rw-r--r--include/sway/layout.h85
-rw-r--r--include/sway/output.h53
-rw-r--r--include/sway/server.h57
-rw-r--r--include/sway/tree/container.h192
-rw-r--r--include/sway/tree/layout.h78
-rw-r--r--include/sway/tree/output.h0
-rw-r--r--include/sway/tree/view.h186
-rw-r--r--include/sway/tree/workspace.h26
-rw-r--r--include/sway/workspace.h22
-rw-r--r--include/swaybar/bar.h110
-rw-r--r--include/swaybar/config.h59
-rw-r--r--include/swaybar/event_loop.h4
-rw-r--r--include/swaybar/ipc.h26
-rw-r--r--include/swaybar/render.h22
-rw-r--r--include/swaybar/status_line.h78
-rw-r--r--include/swaylock/seat.h38
-rw-r--r--include/swaylock/swaylock.h98
-rw-r--r--include/unicode.h33
-rw-r--r--meson.build171
-rw-r--r--meson_options.txt3
-rw-r--r--protocols/CMakeLists.txt46
-rw-r--r--protocols/desktop-shell.xml138
-rw-r--r--protocols/gamma-control.xml57
-rw-r--r--protocols/meson.build73
-rw-r--r--protocols/server-decoration.xml94
-rw-r--r--protocols/swaylock.xml18
-rw-r--r--protocols/wlr-input-inhibitor-unstable-v1.xml67
-rw-r--r--protocols/wlr-layer-shell-unstable-v1.xml281
-rw-r--r--protocols/xdg-shell.xml430
-rw-r--r--security.d/00-defaults.in18
-rw-r--r--sway/CMakeLists.txt107
-rw-r--r--sway/border.c510
-rw-r--r--sway/commands.c480
-rw-r--r--sway/commands/assign.c57
-rw-r--r--sway/commands/bar.c12
-rw-r--r--sway/commands/bar/activate_button.c22
-rw-r--r--sway/commands/bar/binding_mode_indicator.c20
-rw-r--r--sway/commands/bar/bindsym.c39
-rw-r--r--sway/commands/bar/colors.c51
-rw-r--r--sway/commands/bar/context_button.c22
-rw-r--r--sway/commands/bar/font.c13
-rw-r--r--sway/commands/bar/height.c5
-rw-r--r--sway/commands/bar/hidden_state.c24
-rw-r--r--sway/commands/bar/icon_theme.c21
-rw-r--r--sway/commands/bar/id.c7
-rw-r--r--sway/commands/bar/mode.c15
-rw-r--r--sway/commands/bar/modifier.c8
-rw-r--r--sway/commands/bar/output.c15
-rw-r--r--sway/commands/bar/pango_markup.c11
-rw-r--r--sway/commands/bar/position.c29
-rw-r--r--sway/commands/bar/secondary_button.c22
-rw-r--r--sway/commands/bar/separator_symbol.c9
-rw-r--r--sway/commands/bar/status_command.c9
-rw-r--r--sway/commands/bar/strip_workspace_numbers.c18
-rw-r--r--sway/commands/bar/swaybar_command.c9
-rw-r--r--sway/commands/bar/tray_output.c25
-rw-r--r--sway/commands/bar/tray_padding.c29
-rw-r--r--sway/commands/bar/workspace_buttons.c15
-rw-r--r--sway/commands/bar/wrap_scroll.c12
-rw-r--r--sway/commands/bind.c139
-rw-r--r--sway/commands/border.c65
-rw-r--r--sway/commands/client.c72
-rw-r--r--sway/commands/clipboard.c38
-rw-r--r--sway/commands/commands.c26
-rw-r--r--sway/commands/debuglog.c27
-rw-r--r--sway/commands/default_border.c44
-rw-r--r--sway/commands/default_floating_border.c45
-rw-r--r--sway/commands/default_orientation.c (renamed from sway/commands/orientation.c)8
-rw-r--r--sway/commands/exec.c4
-rw-r--r--sway/commands/exec_always.c22
-rw-r--r--sway/commands/exit.c9
-rw-r--r--sway/commands/floating.c81
-rw-r--r--sway/commands/floating_maximum_size.c38
-rw-r--r--sway/commands/floating_minimum_size.c38
-rw-r--r--sway/commands/floating_mod.c42
-rw-r--r--sway/commands/floating_scroll.c46
-rw-r--r--sway/commands/focus.c134
-rw-r--r--sway/commands/focus_follows_mouse.c1
-rw-r--r--sway/commands/font.c27
-rw-r--r--sway/commands/for_window.c41
-rw-r--r--sway/commands/force_focus_wrapping.c13
-rw-r--r--sway/commands/fullscreen.c60
-rw-r--r--sway/commands/gaps.c166
-rw-r--r--sway/commands/hide_edge_borders.c27
-rw-r--r--sway/commands/input.c100
-rw-r--r--sway/commands/input/accel_profile.c13
-rw-r--r--sway/commands/input/click_method.c17
-rw-r--r--sway/commands/input/drag_lock.c16
-rw-r--r--sway/commands/input/dwt.c13
-rw-r--r--sway/commands/input/events.c22
-rw-r--r--sway/commands/input/left_handed.c16
-rw-r--r--sway/commands/input/map_to_output.c27
-rw-r--r--sway/commands/input/middle_emulation.c19
-rw-r--r--sway/commands/input/natural_scroll.c16
-rw-r--r--sway/commands/input/pointer_accel.c16
-rw-r--r--sway/commands/input/scroll_method.c16
-rw-r--r--sway/commands/input/tap.c17
-rw-r--r--sway/commands/input/xkb_layout.c26
-rw-r--r--sway/commands/input/xkb_model.c26
-rw-r--r--sway/commands/input/xkb_options.c26
-rw-r--r--sway/commands/input/xkb_rules.c26
-rw-r--r--sway/commands/input/xkb_variant.c26
-rw-r--r--sway/commands/ipc.c162
-rw-r--r--sway/commands/kill.c16
-rw-r--r--sway/commands/layout.c173
-rw-r--r--sway/commands/log_colors.c22
-rw-r--r--sway/commands/mark.c87
-rw-r--r--sway/commands/mode.c38
-rw-r--r--sway/commands/mouse_warping.c4
-rw-r--r--sway/commands/move.c328
-rw-r--r--sway/commands/new_float.c8
-rw-r--r--sway/commands/new_window.c8
-rw-r--r--sway/commands/no_focus.c41
-rw-r--r--sway/commands/opacity.c36
-rw-r--r--sway/commands/output.c429
-rw-r--r--sway/commands/permit.c108
-rw-r--r--sway/commands/reload.c4
-rw-r--r--sway/commands/resize.c515
-rw-r--r--sway/commands/scratchpad.c72
-rw-r--r--sway/commands/seamless_mouse.c13
-rw-r--r--sway/commands/seat.c61
-rw-r--r--sway/commands/seat/attach.c28
-rw-r--r--sway/commands/seat/cursor.c89
-rw-r--r--sway/commands/seat/fallback.c32
-rw-r--r--sway/commands/set.c13
-rw-r--r--sway/commands/show_marks.c13
-rw-r--r--sway/commands/smart_gaps.c20
-rw-r--r--sway/commands/split.c103
-rw-r--r--sway/commands/sticky.c25
-rw-r--r--sway/commands/swaybg_command.c20
-rw-r--r--sway/commands/unmark.c31
-rw-r--r--sway/commands/workspace.c48
-rw-r--r--sway/commands/workspace_layout.c40
-rw-r--r--sway/commands/ws_auto_back_and_forth.c (renamed from sway/commands/workspace_auto_back_and_forth.c)8
-rw-r--r--sway/config.c1027
-rw-r--r--sway/config/bar.c240
-rw-r--r--sway/config/input.c119
-rw-r--r--sway/config/output.c213
-rw-r--r--sway/config/seat.c146
-rw-r--r--sway/container.c1016
-rw-r--r--sway/criteria.c156
-rw-r--r--sway/debug-tree.c119
-rw-r--r--sway/debug_log.c103
-rw-r--r--sway/desktop/desktop.c14
-rw-r--r--sway/desktop/layer_shell.c347
-rw-r--r--sway/desktop/output.c579
-rw-r--r--sway/desktop/wl_shell.c131
-rw-r--r--sway/desktop/xdg_shell_v6.c249
-rw-r--r--sway/desktop/xwayland.c310
-rw-r--r--sway/extensions.c407
-rw-r--r--sway/focus.c277
-rw-r--r--sway/handlers.c1143
-rw-r--r--sway/input.c69
-rw-r--r--sway/input/cursor.c384
-rw-r--r--sway/input/input-manager.c445
-rw-r--r--sway/input/keyboard.c504
-rw-r--r--sway/input/seat.c675
-rw-r--r--sway/input_state.c490
-rw-r--r--sway/ipc-json.c655
-rw-r--r--sway/ipc-server.c983
-rw-r--r--sway/layout.c1770
-rw-r--r--sway/main.c254
-rw-r--r--sway/meson.build131
-rw-r--r--sway/output.c277
-rw-r--r--sway/security.c212
-rw-r--r--sway/server.c141
-rw-r--r--sway/sway-input.5.txt74
-rw-r--r--sway/sway.1.txt23
-rw-r--r--sway/sway.5.txt75
-rw-r--r--sway/tree/container.c534
-rw-r--r--sway/tree/layout.c1030
-rw-r--r--sway/tree/output.c73
-rw-r--r--sway/tree/view.c329
-rw-r--r--sway/tree/workspace.c401
-rw-r--r--sway/workspace.c374
-rw-r--r--swaybar/CMakeLists.txt51
-rw-r--r--swaybar/bar.c644
-rw-r--r--swaybar/config.c53
-rw-r--r--swaybar/event_loop.c10
-rw-r--r--swaybar/i3bar.c215
-rw-r--r--swaybar/ipc.c445
-rw-r--r--swaybar/main.c33
-rw-r--r--swaybar/meson.build28
-rw-r--r--swaybar/render.c638
-rw-r--r--swaybar/status_line.c594
-rw-r--r--swaybar/tray/dbus.c197
-rw-r--r--swaybar/tray/icon.c400
-rw-r--r--swaybar/tray/sni.c481
-rw-r--r--swaybar/tray/sni_watcher.c497
-rw-r--r--swaybar/tray/tray.c398
-rw-r--r--swaybg/CMakeLists.txt37
-rw-r--r--swaybg/main.c377
-rw-r--r--swaybg/meson.build18
-rw-r--r--swaygrab/CMakeLists.txt28
-rw-r--r--swaylock/CMakeLists.txt51
-rw-r--r--swaylock/main.c918
-rw-r--r--swaylock/meson.build23
-rw-r--r--swaylock/password.c126
-rw-r--r--swaylock/render.c150
-rw-r--r--swaylock/seat.c190
-rw-r--r--swaymsg/CMakeLists.txt23
-rw-r--r--swaymsg/main.c125
-rw-r--r--swaymsg/meson.build8
-rw-r--r--wayland/CMakeLists.txt32
-rw-r--r--wayland/registry.c293
-rw-r--r--wayland/window.c177
281 files changed, 14666 insertions, 20887 deletions
diff --git a/.build.yml b/.build.yml
index 9c9e8adb..3cfb7483 100644
--- a/.build.yml
+++ b/.build.yml
@@ -1,24 +1,29 @@
1# vim: ft=yaml ts=2 sw=2 et : 1# vim: ft=yaml ts=2 sw=2 et :
2image: archlinux 2image: archlinux
3packages: 3packages:
4 - cmake 4 - meson
5 - wlc-git
6 - xorg-server-xwayland 5 - xorg-server-xwayland
7 - xcb-util-image 6 - xcb-util-image
8 - json-c 7 - json-c
9 - pango 8 - pango
10 - cairo 9 - cairo
11 - wayland 10 - wayland
11 - wayland-protocols
12 - gdk-pixbuf2 12 - gdk-pixbuf2
13 - libinput
14 - libxkbcommon
13sources: 15sources:
14 - https://git.sr.ht/~sircmpwn/sway 16 - https://github.com/swaywm/sway
17 - https://github.com/swaywm/wlroots
15tasks: 18tasks:
19 - wlroots: |
20 cd wlroots
21 meson --prefix=/usr build
22 ninja -C build
23 sudo ninja -C build install
16 - setup: | 24 - setup: |
17 cd sway 25 cd sway
18 mkdir build 26 meson build
19 cd build
20 cmake ..
21 - build: | 27 - build: |
22 cd sway 28 cd sway
23 cd build 29 ninja -C build
24 make
diff --git a/.gitignore b/.gitignore
index 48f8a0cb..1352970b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,3 @@
1CMakeCache.txt
2CMakeFiles
3Makefile
4cmake_install.cmake
5install_manifest.txt 1install_manifest.txt
6*.swp 2*.swp
7*.o 3*.o
@@ -12,3 +8,5 @@ build/
12.lvimrc 8.lvimrc
13config-debug 9config-debug
14wayland-*-protocol.* 10wayland-*-protocol.*
11/subprojects/wlroots
12subprojects
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 3eeb1682..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
1sudo: required
2
3language: c
4
5compiler:
6 - gcc
7 - clang
8
9env:
10 - BUILD_TYPE=Release
11 - BUILD_TYPE=Debug
12 - BUILD_TYPE=ASAN
13
14arch:
15 packages:
16 - cmake
17 - xorg-server-xwayland
18 - json-c
19 - wayland
20 - xcb-util-image
21 - pango
22 - cairo
23 - gdk-pixbuf2
24 - wlc-git
25 - libcap
26 script:
27 - "cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ."
28 - "make"
29
30script:
31 - "curl -s https://raw.githubusercontent.com/mikkeloscar/arch-travis/master/arch-travis.sh | bash"
diff --git a/CMake/FindA2X.cmake b/CMake/FindA2X.cmake
deleted file mode 100644
index b38f5086..00000000
--- a/CMake/FindA2X.cmake
+++ /dev/null
@@ -1,75 +0,0 @@
1#
2# (c)2015 KiCad Developers
3# (c)2015 Brian Sidebotham <brian.sidebotham@gmail.com>
4#
5# CMake module to find a2x (part of the asciidoc toolchain).
6#
7# Variables generated:
8#
9# A2X_FOUND true when A2X_COMMAND is valid
10# A2X_COMMAND The command to run a2x (may be a list including an interpreter)
11# A2X_VERSION The a2x version that has been found
12#
13
14# Have a go at finding a a2x executable
15find_program( A2X_PROGRAM a2x )
16
17# Found something, attempt to try and use it...
18if( A2X_PROGRAM )
19 execute_process(
20 COMMAND ${A2X_PROGRAM} --version
21 OUTPUT_VARIABLE _OUT
22 ERROR_VARIABLE _ERR
23 RESULT_VARIABLE _RES
24 OUTPUT_STRIP_TRAILING_WHITESPACE )
25
26 # If it worked, set the A2X_COMMAND
27 if( _RES MATCHES 0 )
28 set( A2X_COMMAND "${A2X_PROGRAM}" )
29 endif()
30endif()
31
32# If nothing could be found, test to see if we can just find the script file,
33# that we'll then run with the python interpreter
34if( NOT A2X_COMMAND )
35 find_file( A2X_SCRIPT a2x.py )
36
37 if( A2X_SCRIPT )
38 # Find the python interpreter quietly
39 if( NOT PYTHONINTERP_FOUND )
40 find_package( PYTHONINTERP QUIET )
41 endif()
42
43 if( NOT PYTHONINTERP_FOUND )
44 # Python's not available so can't find a2x...
45 set( A2X_COMMAND "" )
46 else()
47 # Build the python based command
48 set( A2X_COMMAND "${PYTHON_EXECUTABLE}" "${A2X_SCRIPT}" )
49
50 execute_process(
51 COMMAND ${A2X_COMMAND} --version
52 OUTPUT_VARIABLE _OUT
53 ERROR_VARIABLE _ERR
54 RESULT_VARIABLE _RES
55 OUTPUT_STRIP_TRAILING_WHITESPACE )
56
57 # If it still can't be run, then give up
58 if( NOT _RES MATCHES 0 )
59 set( A2X_COMMAND "" )
60 endif()
61 endif()
62 endif()
63endif()
64
65# If we've found a command that works, check the version
66if( A2X_COMMAND )
67 string(REGEX REPLACE ".*a2x[^0-9.]*\([0-9.]+\).*" "\\1" A2X_VERSION "${_OUT}")
68endif()
69
70# Generate the *_FOUND as necessary, etc.
71include( FindPackageHandleStandardArgs )
72find_package_handle_standard_args(
73 A2X
74 REQUIRED_VARS A2X_COMMAND
75 VERSION_VAR A2X_VERSION )
diff --git a/CMake/FindCairo.cmake b/CMake/FindCairo.cmake
deleted file mode 100644
index 10ab8fb0..00000000
--- a/CMake/FindCairo.cmake
+++ /dev/null
@@ -1,42 +0,0 @@
1# - Try to find the cairo library
2# Once done this will define
3#
4# CAIRO_FOUND - system has cairo
5# CAIRO_INCLUDE_DIRS - the cairo include directory
6# CAIRO_LIBRARIES - Link these to use cairo
7#
8# Define CAIRO_MIN_VERSION for which version desired.
9#
10
11find_package(PkgConfig)
12
13if(Cairo_FIND_REQUIRED)
14 set(_pkgconfig_REQUIRED "REQUIRED")
15else(Cairo_FIND_REQUIRED)
16 set(_pkgconfig_REQUIRED "")
17endif(Cairo_FIND_REQUIRED)
18
19if(CAIRO_MIN_VERSION)
20 pkg_check_modules(CAIRO ${_pkgconfig_REQUIRED} cairo>=${CAIRO_MIN_VERSION})
21else(CAIRO_MIN_VERSION)
22 pkg_check_modules(CAIRO ${_pkgconfig_REQUIRED} cairo)
23endif(CAIRO_MIN_VERSION)
24
25if(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND)
26 find_path(CAIRO_INCLUDE_DIRS cairo.h)
27 find_library(CAIRO_LIBRARIES cairo)
28else(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND)
29 # Make paths absolute https://stackoverflow.com/a/35476270
30 # Important on FreeBSD because /usr/local/lib is not on /usr/bin/ld's default path
31 set(CAIRO_LIBS_ABSOLUTE)
32 foreach(lib ${CAIRO_LIBRARIES})
33 set(var_name CAIRO_${lib}_ABS)
34 find_library(${var_name} ${lib} ${CAIRO_LIBRARY_DIRS})
35 list(APPEND CAIRO_LIBS_ABSOLUTE ${${var_name}})
36 endforeach()
37 set(CAIRO_LIBRARIES ${CAIRO_LIBS_ABSOLUTE})
38endif(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND)
39
40include(FindPackageHandleStandardArgs)
41find_package_handle_standard_args(CAIRO DEFAULT_MSG CAIRO_LIBRARIES CAIRO_INCLUDE_DIRS)
42mark_as_advanced(CAIRO_LIBRARIES CAIRO_INCLUDE_DIRS)
diff --git a/CMake/FindDBus.cmake b/CMake/FindDBus.cmake
deleted file mode 100644
index 4a1a1805..00000000
--- a/CMake/FindDBus.cmake
+++ /dev/null
@@ -1,59 +0,0 @@
1# - Try to find DBus
2# Once done, this will define
3#
4# DBUS_FOUND - system has DBus
5# DBUS_INCLUDE_DIRS - the DBus include directories
6# DBUS_LIBRARIES - link these to use DBus
7#
8# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
9#
10# Redistribution and use in source and binary forms, with or without
11# modification, are permitted provided that the following conditions
12# are met:
13# 1. Redistributions of source code must retain the above copyright
14# notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16# notice, this list of conditions and the following disclaimer in the
17# documentation and/or other materials provided with the distribution.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
20# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
23# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31FIND_PACKAGE(PkgConfig)
32PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1)
33
34FIND_LIBRARY(DBUS_LIBRARIES
35 NAMES dbus-1
36 HINTS ${PC_DBUS_LIBDIR}
37 ${PC_DBUS_LIBRARY_DIRS}
38)
39
40FIND_PATH(DBUS_INCLUDE_DIR
41 NAMES dbus/dbus.h
42 HINTS ${PC_DBUS_INCLUDEDIR}
43 ${PC_DBUS_INCLUDE_DIRS}
44)
45
46GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH)
47FIND_PATH(DBUS_ARCH_INCLUDE_DIR
48 NAMES dbus/dbus-arch-deps.h
49 HINTS ${PC_DBUS_INCLUDEDIR}
50 ${PC_DBUS_INCLUDE_DIRS}
51 ${_DBUS_LIBRARY_DIR}
52 ${DBUS_INCLUDE_DIR}
53 PATH_SUFFIXES include
54)
55
56SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
57
58INCLUDE(FindPackageHandleStandardArgs)
59FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES)
diff --git a/CMake/FindEpollShim.cmake b/CMake/FindEpollShim.cmake
deleted file mode 100644
index b6bed142..00000000
--- a/CMake/FindEpollShim.cmake
+++ /dev/null
@@ -1,17 +0,0 @@
1# - Find EpollShim
2# Once done, this will define
3#
4# EPOLLSHIM_FOUND - System has EpollShim
5# EPOLLSHIM_INCLUDE_DIRS - The EpollShim include directories
6# EPOLLSHIM_LIBRARIES - The libraries needed to use EpollShim
7
8find_path(EPOLLSHIM_INCLUDE_DIRS NAMES sys/epoll.h sys/timerfd.h HINTS /usr/local/include/libepoll-shim)
9find_library(EPOLLSHIM_LIBRARIES NAMES epoll-shim libepoll-shim HINTS /usr/local/lib)
10
11if (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES)
12 set(EPOLLSHIM_FOUND TRUE)
13endif (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES)
14
15include(FindPackageHandleStandardArgs)
16find_package_handle_standard_args(EPOLLSHIM DEFAULT_MSG EPOLLSHIM_LIBRARIES EPOLLSHIM_INCLUDE_DIRS)
17mark_as_advanced(EPOLLSHIM_INCLUDE_DIRS EPOLLSHIM_LIBRARIES)
diff --git a/CMake/FindGdkPixbuf.cmake b/CMake/FindGdkPixbuf.cmake
deleted file mode 100644
index 9cad4f92..00000000
--- a/CMake/FindGdkPixbuf.cmake
+++ /dev/null
@@ -1,43 +0,0 @@
1# - Try to find the gdk-pixbuf-2.0 library
2# Once done this will define
3#
4# GDK_PIXBUF_FOUND - system has gdk-pixbuf-2.0
5# GDK_PIXBUF_INCLUDE_DIRS - the gdk-pixbuf-2.0 include directory
6# GDK_PIXBUF_LIBRARIES - Link these to use gdk-pixbuf-2.0
7#
8# Define GDK_PIXBUF_MIN_VERSION for which version desired.
9#
10
11find_package(PkgConfig)
12
13if(GdkPixbuf_FIND_REQUIRED)
14 set(_pkgconfig_REQUIRED "REQUIRED")
15else(GdkPixbuf_FIND_REQUIRED)
16 set(_pkgconfig_REQUIRED "")
17endif(GdkPixbuf_FIND_REQUIRED)
18
19if(GDK_PIXBUF_MIN_VERSION)
20 pkg_check_modules(GDK_PIXBUF ${_pkgconfig_REQUIRED} "gdk-pixbuf-2.0>=${GDK_PIXBUF_MIN_VERSION}")
21else(GDK_PIXBUF_MIN_VERSION)
22 pkg_check_modules(GDK_PIXBUF ${_pkgconfig_REQUIRED} "gdk-pixbuf-2.0")
23endif(GDK_PIXBUF_MIN_VERSION)
24
25if(NOT GDK_PIXBUF_FOUND AND NOT PKG_CONFIG_FOUND)
26 find_path(GDK_PIXBUF_INCLUDE_DIRS gdk-pixbuf/gdk-pixbuf.h)
27 find_library(GDK_PIXBUF_LIBRARIES gdk_pixbuf-2.0)
28else(NOT GDK_PIXBUF_FOUND AND NOT PKG_CONFIG_FOUND)
29 SET(GdkPixbuf_FOUND 1)
30 # Make paths absolute https://stackoverflow.com/a/35476270
31 # Important on FreeBSD because /usr/local/lib is not on /usr/bin/ld's default path
32 set(GDK_PIXBUF_LIBS_ABSOLUTE)
33 foreach(lib ${GDK_PIXBUF_LIBRARIES})
34 set(var_name GDK_PIXBUF_${lib}_ABS)
35 find_library(${var_name} ${lib} ${GDK_PIXBUF_LIBRARY_DIRS})
36 list(APPEND GDK_PIXBUF_LIBS_ABSOLUTE ${${var_name}})
37 endforeach()
38 set(GDK_PIXBUF_LIBRARIES ${GDK_PIXBUF_LIBS_ABSOLUTE})
39endif(NOT GDK_PIXBUF_FOUND AND NOT PKG_CONFIG_FOUND)
40
41include(FindPackageHandleStandardArgs)
42find_package_handle_standard_args(GDK_PIXBUF DEFAULT_MSG GDK_PIXBUF_LIBRARIES GDK_PIXBUF_INCLUDE_DIRS)
43mark_as_advanced(GDK_PIXBUF_LIBRARIES GDK_PIXBUF_INCLUDE_DIRS)
diff --git a/CMake/FindJsonC.cmake b/CMake/FindJsonC.cmake
deleted file mode 100644
index bbf6930c..00000000
--- a/CMake/FindJsonC.cmake
+++ /dev/null
@@ -1,29 +0,0 @@
1# - Find json-c
2# Find the json-c libraries
3#
4# This module defines the following variables:
5# JSONC_FOUND - True if JSONC is found
6# JSONC_LIBRARIES - JSONC libraries
7# JSONC_INCLUDE_DIRS - JSONC include directories
8#
9
10find_package(PkgConfig)
11
12if (JsonC_FIND_REQUIRED)
13 set(_pkgconfig_REQUIRED "REQUIRED")
14else()
15 set(_pkgconfig_REQUIRED "")
16endif()
17
18if(JsonC_FIND_VERSION)
19 pkg_check_modules(PC_JSONC ${_pkgconfig_REQUIRED} json-c=${JsonC_FIND_VERSION})
20else()
21 pkg_check_modules(PC_JSONC ${_pkgconfig_REQUIRED} json-c)
22endif()
23
24find_path(JSONC_INCLUDE_DIRS NAMES json-c/json.h HINTS ${PC_JSONC_INCLUDE_DIRS})
25find_library(JSONC_LIBRARIES NAMES json-c HINTS ${PC_JSONC_LIBRARY_DIRS})
26include(FindPackageHandleStandardArgs)
27
28find_package_handle_standard_args(JSONC DEFAULT_MSG JSONC_LIBRARIES JSONC_INCLUDE_DIRS)
29mark_as_advanced(JSONC_LIBRARIES JSONC_INCLUDE_DIRS)
diff --git a/CMake/FindLibInput.cmake b/CMake/FindLibInput.cmake
deleted file mode 100644
index 87721998..00000000
--- a/CMake/FindLibInput.cmake
+++ /dev/null
@@ -1,66 +0,0 @@
1#.rst:
2# FindLibInput
3# -------
4#
5# Find LibInput library
6#
7# Try to find LibInpu library. The following values are defined
8#
9# ::
10#
11# LIBINPUT_FOUND - True if libinput is available
12# LIBINPUT_INCLUDE_DIRS - Include directories for libinput
13# LIBINPUT_LIBRARIES - List of libraries for libinput
14# LIBINPUT_DEFINITIONS - List of definitions for libinput
15#
16# and also the following more fine grained variables
17#
18# ::
19#
20# LIBINPUT_VERSION
21# LIBINPUT_VERSION_MAJOR
22# LIBINPUT_VERSION_MINOR
23# LIBINPUT_VERSION_MICRO
24#
25#=============================================================================
26# Copyright (c) 2015 Jari Vetoniemi
27#
28# Distributed under the OSI-approved BSD License (the "License");
29#
30# This software is distributed WITHOUT ANY WARRANTY; without even the
31# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
32# See the License for more information.
33#=============================================================================
34
35include(FeatureSummary)
36set_package_properties(LibInput PROPERTIES
37 URL "http://freedesktop.org/wiki/Software/libinput/"
38 DESCRIPTION "Library to handle input devices")
39
40find_package(PkgConfig)
41pkg_check_modules(PC_INPUT QUIET libinput)
42find_library(LIBINPUT_LIBRARIES NAMES input HINTS ${PC_INPUT_LIBRARY_DIRS})
43find_path(LIBINPUT_INCLUDE_DIRS libinput.h HINTS ${PC_INPUT_INCLUDE_DIRS})
44
45set(LIBINPUT_VERSION ${PC_INPUT_VERSION})
46string(REPLACE "." ";" VERSION_LIST "${PC_INPUT_VERSION}")
47
48LIST(LENGTH VERSION_LIST n)
49if (n EQUAL 3)
50 list(GET VERSION_LIST 0 LIBINPUT_VERSION_MAJOR)
51 list(GET VERSION_LIST 1 LIBINPUT_VERSION_MINOR)
52 list(GET VERSION_LIST 2 LIBINPUT_VERSION_MICRO)
53endif ()
54
55# This is compatible with libinput-version.h that exists in upstream
56# but isn't in distribution (probably forgotten)
57set(LIBINPUT_DEFINITIONS ${PC_INPUT_CFLAGS_OTHER}
58 -DLIBINPUT_VERSION=\"${LIBINPUT_VERSION}\"
59 -DLIBINPUT_VERSION_MAJOR=${LIBINPUT_VERSION_MAJOR}
60 -DLIBINPUT_VERSION_MINOR=${LIBINPUT_VERSION_MINOR}
61 -DLIBINPUT_VERSION_MICRO=${LIBINPUT_VERSION_MICRO})
62
63include(FindPackageHandleStandardArgs)
64find_package_handle_standard_args(LIBINPUT DEFAULT_MSG LIBINPUT_INCLUDE_DIRS LIBINPUT_LIBRARIES)
65mark_as_advanced(LIBINPUT_INCLUDE_DIRS LIBINPUT_LIBRARIES LIBINPUT_DEFINITIONS
66 LIBINPUT_VERSION LIBINPUT_VERSION_MAJOR LIBINPUT_VERSION_MICRO LIBINPUT_VERSION_MINOR)
diff --git a/CMake/FindLibcap.cmake b/CMake/FindLibcap.cmake
deleted file mode 100644
index b34e5e37..00000000
--- a/CMake/FindLibcap.cmake
+++ /dev/null
@@ -1,56 +0,0 @@
1#.rst:
2# FindLibcap
3# -------
4#
5# Find Libcap library
6#
7# Try to find Libcap library. The following values are defined
8#
9# ::
10#
11# Libcap_FOUND - True if Libcap is available
12# Libcap_INCLUDE_DIRS - Include directories for Libcap
13# Libcap_LIBRARIES - List of libraries for Libcap
14# Libcap_DEFINITIONS - List of definitions for Libcap
15#
16# and also the following more fine grained variables
17#
18# ::
19#
20# Libcap_VERSION
21# Libcap_VERSION_MAJOR
22# Libcap_VERSION_MINOR
23#
24#=============================================================================
25# Copyright (c) 2017 Jerzi Kaminsky
26#
27# Distributed under the OSI-approved BSD License (the "License");
28#
29# This software is distributed WITHOUT ANY WARRANTY; without even the
30# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
31# See the License for more information.
32#=============================================================================
33
34include(FeatureSummary)
35set_package_properties(Libcap PROPERTIES
36 URL "https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2"
37 DESCRIPTION "Library for getting and setting POSIX.1e capabilities")
38
39find_package(PkgConfig)
40pkg_check_modules(PC_CAP QUIET Libcap)
41find_library(Libcap_LIBRARIES NAMES cap HINTS ${PC_CAP_LIBRARY_DIRS})
42find_path(Libcap_INCLUDE_DIRS sys/capability.h HINTS ${PC_CAP_INCLUDE_DIRS})
43
44set(Libcap_VERSION ${PC_CAP_VERSION})
45string(REPLACE "." ";" VERSION_LIST "${PC_CAP_VERSION}")
46
47LIST(LENGTH VERSION_LIST n)
48if (n EQUAL 2)
49 list(GET VERSION_LIST 0 Libcap_VERSION_MAJOR)
50 list(GET VERSION_LIST 1 Libcap_VERSION_MINOR)
51endif ()
52
53include(FindPackageHandleStandardArgs)
54find_package_handle_standard_args(Libcap DEFAULT_MSG Libcap_INCLUDE_DIRS Libcap_LIBRARIES)
55mark_as_advanced(Libcap_INCLUDE_DIRS Libcap_LIBRARIES Libcap_DEFINITIONS
56 Libcap_VERSION Libcap_VERSION_MAJOR Libcap_VERSION_MICRO Libcap_VERSION_MINOR)
diff --git a/CMake/FindPAM.cmake b/CMake/FindPAM.cmake
deleted file mode 100644
index 7e6e333a..00000000
--- a/CMake/FindPAM.cmake
+++ /dev/null
@@ -1,245 +0,0 @@
1# - Try to find the PAM libraries
2# Once done this will define
3#
4# PAM_FOUND - system has pam
5# PAM_INCLUDE_DIR - the pam include directory
6# PAM_LIBRARIES - libpam library
7
8if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
9 set(PAM_FIND_QUIETLY TRUE)
10endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
11
12find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h)
13find_library(PAM_LIBRARY pam)
14find_library(DL_LIBRARY dl)
15
16if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
17 set(PAM_FOUND TRUE)
18 if (DL_LIBRARY)
19 set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY})
20 else (DL_LIBRARY)
21 set(PAM_LIBRARIES ${PAM_LIBRARY})
22 endif (DL_LIBRARY)
23
24 if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
25 set(HAVE_PAM_PAM_APPL_H 1)
26 endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
27endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
28
29if (PAM_FOUND)
30 if (NOT PAM_FIND_QUIETLY)
31 message(STATUS "Found PAM: ${PAM_LIBRARIES}")
32 endif (NOT PAM_FIND_QUIETLY)
33else (PAM_FOUND)
34 if (PAM_FIND_REQUIRED)
35 message(FATAL_ERROR "PAM was not found")
36 endif(PAM_FIND_REQUIRED)
37endif (PAM_FOUND)
38
39mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY)
40
41# This file taken from https://github.com/FreeRDP/FreeRDP
42#
43#
44#
45# Apache License
46# Version 2.0, January 2004
47# http://www.apache.org/licenses/
48#
49# TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
50#
51# 1. Definitions.
52#
53# "License" shall mean the terms and conditions for use, reproduction,
54# and distribution as defined by Sections 1 through 9 of this document.
55#
56# "Licensor" shall mean the copyright owner or entity authorized by
57# the copyright owner that is granting the License.
58#
59# "Legal Entity" shall mean the union of the acting entity and all
60# other entities that control, are controlled by, or are under common
61# control with that entity. For the purposes of this definition,
62# "control" means (i) the power, direct or indirect, to cause the
63# direction or management of such entity, whether by contract or
64# otherwise, or (ii) ownership of fifty percent (50%) or more of the
65# outstanding shares, or (iii) beneficial ownership of such entity.
66#
67# "You" (or "Your") shall mean an individual or Legal Entity
68# exercising permissions granted by this License.
69#
70# "Source" form shall mean the preferred form for making modifications,
71# including but not limited to software source code, documentation
72# source, and configuration files.
73#
74# "Object" form shall mean any form resulting from mechanical
75# transformation or translation of a Source form, including but
76# not limited to compiled object code, generated documentation,
77# and conversions to other media types.
78#
79# "Work" shall mean the work of authorship, whether in Source or
80# Object form, made available under the License, as indicated by a
81# copyright notice that is included in or attached to the work
82# (an example is provided in the Appendix below).
83#
84# "Derivative Works" shall mean any work, whether in Source or Object
85# form, that is based on (or derived from) the Work and for which the
86# editorial revisions, annotations, elaborations, or other modifications
87# represent, as a whole, an original work of authorship. For the purposes
88# of this License, Derivative Works shall not include works that remain
89# separable from, or merely link (or bind by name) to the interfaces of,
90# the Work and Derivative Works thereof.
91#
92# "Contribution" shall mean any work of authorship, including
93# the original version of the Work and any modifications or additions
94# to that Work or Derivative Works thereof, that is intentionally
95# submitted to Licensor for inclusion in the Work by the copyright owner
96# or by an individual or Legal Entity authorized to submit on behalf of
97# the copyright owner. For the purposes of this definition, "submitted"
98# means any form of electronic, verbal, or written communication sent
99# to the Licensor or its representatives, including but not limited to
100# communication on electronic mailing lists, source code control systems,
101# and issue tracking systems that are managed by, or on behalf of, the
102# Licensor for the purpose of discussing and improving the Work, but
103# excluding communication that is conspicuously marked or otherwise
104# designated in writing by the copyright owner as "Not a Contribution."
105#
106# "Contributor" shall mean Licensor and any individual or Legal Entity
107# on behalf of whom a Contribution has been received by Licensor and
108# subsequently incorporated within the Work.
109#
110# 2. Grant of Copyright License. Subject to the terms and conditions of
111# this License, each Contributor hereby grants to You a perpetual,
112# worldwide, non-exclusive, no-charge, royalty-free, irrevocable
113# copyright license to reproduce, prepare Derivative Works of,
114# publicly display, publicly perform, sublicense, and distribute the
115# Work and such Derivative Works in Source or Object form.
116#
117# 3. Grant of Patent License. Subject to the terms and conditions of
118# this License, each Contributor hereby grants to You a perpetual,
119# worldwide, non-exclusive, no-charge, royalty-free, irrevocable
120# (except as stated in this section) patent license to make, have made,
121# use, offer to sell, sell, import, and otherwise transfer the Work,
122# where such license applies only to those patent claims licensable
123# by such Contributor that are necessarily infringed by their
124# Contribution(s) alone or by combination of their Contribution(s)
125# with the Work to which such Contribution(s) was submitted. If You
126# institute patent litigation against any entity (including a
127# cross-claim or counterclaim in a lawsuit) alleging that the Work
128# or a Contribution incorporated within the Work constitutes direct
129# or contributory patent infringement, then any patent licenses
130# granted to You under this License for that Work shall terminate
131# as of the date such litigation is filed.
132#
133# 4. Redistribution. You may reproduce and distribute copies of the
134# Work or Derivative Works thereof in any medium, with or without
135# modifications, and in Source or Object form, provided that You
136# meet the following conditions:
137#
138# (a) You must give any other recipients of the Work or
139# Derivative Works a copy of this License; and
140#
141# (b) You must cause any modified files to carry prominent notices
142# stating that You changed the files; and
143#
144# (c) You must retain, in the Source form of any Derivative Works
145# that You distribute, all copyright, patent, trademark, and
146# attribution notices from the Source form of the Work,
147# excluding those notices that do not pertain to any part of
148# the Derivative Works; and
149#
150# (d) If the Work includes a "NOTICE" text file as part of its
151# distribution, then any Derivative Works that You distribute must
152# include a readable copy of the attribution notices contained
153# within such NOTICE file, excluding those notices that do not
154# pertain to any part of the Derivative Works, in at least one
155# of the following places: within a NOTICE text file distributed
156# as part of the Derivative Works; within the Source form or
157# documentation, if provided along with the Derivative Works; or,
158# within a display generated by the Derivative Works, if and
159# wherever such third-party notices normally appear. The contents
160# of the NOTICE file are for informational purposes only and
161# do not modify the License. You may add Your own attribution
162# notices within Derivative Works that You distribute, alongside
163# or as an addendum to the NOTICE text from the Work, provided
164# that such additional attribution notices cannot be construed
165# as modifying the License.
166#
167# You may add Your own copyright statement to Your modifications and
168# may provide additional or different license terms and conditions
169# for use, reproduction, or distribution of Your modifications, or
170# for any such Derivative Works as a whole, provided Your use,
171# reproduction, and distribution of the Work otherwise complies with
172# the conditions stated in this License.
173#
174# 5. Submission of Contributions. Unless You explicitly state otherwise,
175# any Contribution intentionally submitted for inclusion in the Work
176# by You to the Licensor shall be under the terms and conditions of
177# this License, without any additional terms or conditions.
178# Notwithstanding the above, nothing herein shall supersede or modify
179# the terms of any separate license agreement you may have executed
180# with Licensor regarding such Contributions.
181#
182# 6. Trademarks. This License does not grant permission to use the trade
183# names, trademarks, service marks, or product names of the Licensor,
184# except as required for reasonable and customary use in describing the
185# origin of the Work and reproducing the content of the NOTICE file.
186#
187# 7. Disclaimer of Warranty. Unless required by applicable law or
188# agreed to in writing, Licensor provides the Work (and each
189# Contributor provides its Contributions) on an "AS IS" BASIS,
190# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
191# implied, including, without limitation, any warranties or conditions
192# of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
193# PARTICULAR PURPOSE. You are solely responsible for determining the
194# appropriateness of using or redistributing the Work and assume any
195# risks associated with Your exercise of permissions under this License.
196#
197# 8. Limitation of Liability. In no event and under no legal theory,
198# whether in tort (including negligence), contract, or otherwise,
199# unless required by applicable law (such as deliberate and grossly
200# negligent acts) or agreed to in writing, shall any Contributor be
201# liable to You for damages, including any direct, indirect, special,
202# incidental, or consequential damages of any character arising as a
203# result of this License or out of the use or inability to use the
204# Work (including but not limited to damages for loss of goodwill,
205# work stoppage, computer failure or malfunction, or any and all
206# other commercial damages or losses), even if such Contributor
207# has been advised of the possibility of such damages.
208#
209# 9. Accepting Warranty or Additional Liability. While redistributing
210# the Work or Derivative Works thereof, You may choose to offer,
211# and charge a fee for, acceptance of support, warranty, indemnity,
212# or other liability obligations and/or rights consistent with this
213# License. However, in accepting such obligations, You may act only
214# on Your own behalf and on Your sole responsibility, not on behalf
215# of any other Contributor, and only if You agree to indemnify,
216# defend, and hold each Contributor harmless for any liability
217# incurred by, or claims asserted against, such Contributor by reason
218# of your accepting any such warranty or additional liability.
219#
220# END OF TERMS AND CONDITIONS
221#
222# APPENDIX: How to apply the Apache License to your work.
223#
224# To apply the Apache License to your work, attach the following
225# boilerplate notice, with the fields enclosed by brackets "[]"
226# replaced with your own identifying information. (Don't include
227# the brackets!) The text should be enclosed in the appropriate
228# comment syntax for the file format. We also recommend that a
229# file or class name and description of purpose be included on the
230# same "printed page" as the copyright notice for easier
231# identification within third-party archives.
232#
233# Copyright [yyyy] [name of copyright owner]
234#
235# Licensed under the Apache License, Version 2.0 (the "License");
236# you may not use this file except in compliance with the License.
237# You may obtain a copy of the License at
238#
239# http://www.apache.org/licenses/LICENSE-2.0
240#
241# Unless required by applicable law or agreed to in writing, software
242# distributed under the License is distributed on an "AS IS" BASIS,
243# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
244# See the License for the specific language governing permissions and
245# limitations under the License.
diff --git a/CMake/FindPCRE.cmake b/CMake/FindPCRE.cmake
deleted file mode 100644
index dbbd60ad..00000000
--- a/CMake/FindPCRE.cmake
+++ /dev/null
@@ -1,37 +0,0 @@
1# Copyright (C) 2007-2009 LuaDist.
2# Created by Peter Kapec <kapecp@gmail.com>
3# Redistribution and use of this file is allowed according to the terms of the MIT license.
4# For details see the COPYRIGHT file distributed with LuaDist.
5# Note:
6# Searching headers and libraries is very simple and is NOT as powerful as scripts
7# distributed with CMake, because LuaDist defines directories to search for.
8# Everyone is encouraged to contact the author with improvements. Maybe this file
9# becomes part of CMake distribution sometimes.
10
11# - Find pcre
12# Find the native PCRE headers and libraries.
13#
14# PCRE_INCLUDE_DIRS - where to find pcre.h, etc.
15# PCRE_LIBRARIES - List of libraries when using pcre.
16# PCRE_FOUND - True if pcre found.
17
18# Look for the header file.
19FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h)
20
21# Look for the library.
22FIND_LIBRARY(PCRE_LIBRARY NAMES pcre)
23
24# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
25INCLUDE(FindPackageHandleStandardArgs)
26FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
27
28# Copy the results to the output variables.
29IF(PCRE_FOUND)
30 SET(PCRE_LIBRARIES ${PCRE_LIBRARY})
31 SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
32ELSE(PCRE_FOUND)
33 SET(PCRE_LIBRARIES)
34 SET(PCRE_INCLUDE_DIRS)
35ENDIF(PCRE_FOUND)
36
37MARK_AS_ADVANCED(PCRE_INCLUDE_DIRS PCRE_LIBRARIES)
diff --git a/CMake/FindPango.cmake b/CMake/FindPango.cmake
deleted file mode 100644
index eb296d75..00000000
--- a/CMake/FindPango.cmake
+++ /dev/null
@@ -1,42 +0,0 @@
1# - Try to find the pango library
2# Once done this will define
3#
4# PANGO_FOUND - system has pango
5# PANGO_INCLUDE_DIRS - the pango include directory
6# PANGO_LIBRARIES - Link these to use pango
7#
8# Define PANGO_MIN_VERSION for which version desired.
9#
10
11find_package(PkgConfig)
12
13if(Pango_FIND_REQUIRED)
14 set(_pkgconfig_REQUIRED "REQUIRED")
15else(Pango_FIND_REQUIRED)
16 set(_pkgconfig_REQUIRED "")
17endif(Pango_FIND_REQUIRED)
18
19if(PANGO_MIN_VERSION)
20 pkg_check_modules(PANGO ${_pkgconfig_REQUIRED} "pango>=${PANGO_MIN_VERSION}" "pangocairo>=${PANGO_MIN_VERSION}")
21else(PANGO_MIN_VERSION)
22 pkg_check_modules(PANGO ${_pkgconfig_REQUIRED} pango pangocairo)
23endif(PANGO_MIN_VERSION)
24
25if(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND)
26 find_path(PANGO_INCLUDE_DIRS pango.h)
27 find_library(PANGO_LIBRARIES pango pangocairo)
28else(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND)
29 # Make paths absolute https://stackoverflow.com/a/35476270
30 # Important on FreeBSD because /usr/local/lib is not on /usr/bin/ld's default path
31 set(PANGO_LIBS_ABSOLUTE)
32 foreach(lib ${PANGO_LIBRARIES})
33 set(var_name PANGO_${lib}_ABS)
34 find_library(${var_name} ${lib} ${PANGO_LIBRARY_DIRS})
35 list(APPEND PANGO_LIBS_ABSOLUTE ${${var_name}})
36 endforeach()
37 set(PANGO_LIBRARIES ${PANGO_LIBS_ABSOLUTE})
38endif(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND)
39
40include(FindPackageHandleStandardArgs)
41find_package_handle_standard_args(PANGO DEFAULT_MSG PANGO_LIBRARIES PANGO_INCLUDE_DIRS)
42mark_as_advanced(PANGO_LIBRARIES PANGO_INCLUDE_DIRS)
diff --git a/CMake/FindWLC.cmake b/CMake/FindWLC.cmake
deleted file mode 100644
index 15b26ce7..00000000
--- a/CMake/FindWLC.cmake
+++ /dev/null
@@ -1,20 +0,0 @@
1# - Find wlc
2# Find the wlc libraries
3#
4# This module defines the following variables:
5# WLC_FOUND - True if wlc is found
6# WLC_LIBRARIES - wlc libraries
7# WLC_INCLUDE_DIRS - wlc include directories
8# WLC_DEFINITIONS - Compiler switches required for using wlc
9#
10
11find_package(PkgConfig)
12pkg_check_modules(PC_WLC QUIET wlc)
13find_path(WLC_INCLUDE_DIRS NAMES wlc/wlc.h HINTS ${PC_WLC_INCLUDE_DIRS})
14find_library(WLC_LIBRARIES NAMES wlc HINTS ${PC_WLC_LIBRARY_DIRS})
15
16set(WLC_DEFINITIONS ${PC_WLC_CFLAGS_OTHER})
17
18include(FindPackageHandleStandardArgs)
19find_package_handle_standard_args(wlc DEFAULT_MSG WLC_LIBRARIES WLC_INCLUDE_DIRS)
20mark_as_advanced(WLC_LIBRARIES WLC_INCLUDE_DIRS)
diff --git a/CMake/FindWayland.cmake b/CMake/FindWayland.cmake
deleted file mode 100644
index 69130fe0..00000000
--- a/CMake/FindWayland.cmake
+++ /dev/null
@@ -1,67 +0,0 @@
1# Try to find Wayland on a Unix system
2#
3# This will define:
4#
5# WAYLAND_FOUND - True if Wayland is found
6# WAYLAND_LIBRARIES - Link these to use Wayland
7# WAYLAND_INCLUDE_DIR - Include directory for Wayland
8# WAYLAND_DEFINITIONS - Compiler flags for using Wayland
9#
10# In addition the following more fine grained variables will be defined:
11#
12# WAYLAND_CLIENT_FOUND WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
13# WAYLAND_SERVER_FOUND WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
14# WAYLAND_EGL_FOUND WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
15# WAYLAND_CURSOR_FOUND WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES
16#
17# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
18#
19# Redistribution and use is allowed according to the terms of the BSD license.
20# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
21
22IF (NOT WIN32)
23 IF (WAYLAND_INCLUDE_DIR AND WAYLAND_LIBRARIES)
24 # In the cache already
25 SET(WAYLAND_FIND_QUIETLY TRUE)
26 ENDIF ()
27
28 # Use pkg-config to get the directories and then use these values
29 # in the FIND_PATH() and FIND_LIBRARY() calls
30 FIND_PACKAGE(PkgConfig)
31 PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor)
32
33 SET(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS})
34
35 FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
36 FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
37 FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
38 FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
39
40 FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
41 FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
42 FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
43 FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
44
45 set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR} ${WAYLAND_CURSOR_INCLUDE_DIR})
46
47 set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES})
48
49 list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIR)
50
51 include(FindPackageHandleStandardArgs)
52
53 FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT DEFAULT_MSG WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR)
54 FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER DEFAULT_MSG WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR)
55 FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL DEFAULT_MSG WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR)
56 FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR DEFAULT_MSG WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR)
57 FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIR)
58
59 MARK_AS_ADVANCED(
60 WAYLAND_INCLUDE_DIR WAYLAND_LIBRARIES
61 WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
62 WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
63 WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
64 WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES
65 )
66
67ENDIF ()
diff --git a/CMake/FindXKBCommon.cmake b/CMake/FindXKBCommon.cmake
deleted file mode 100644
index 30ac503a..00000000
--- a/CMake/FindXKBCommon.cmake
+++ /dev/null
@@ -1,19 +0,0 @@
1# - Find XKBCommon
2# Once done, this will define
3#
4# XKBCOMMON_FOUND - System has XKBCommon
5# XKBCOMMON_INCLUDE_DIRS - The XKBCommon include directories
6# XKBCOMMON_LIBRARIES - The libraries needed to use XKBCommon
7# XKBCOMMON_DEFINITIONS - Compiler switches required for using XKBCommon
8
9find_package(PkgConfig)
10pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon)
11find_path(XKBCOMMON_INCLUDE_DIRS NAMES xkbcommon/xkbcommon.h HINTS ${PC_XKBCOMMON_INCLUDE_DIRS})
12find_library(XKBCOMMON_LIBRARIES NAMES xkbcommon HINTS ${PC_XKBCOMMON_LIBRARY_DIRS})
13
14set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER})
15
16include(FindPackageHandleStandardArgs)
17find_package_handle_standard_args(XKBCOMMON DEFAULT_MSG XKBCOMMON_LIBRARIES XKBCOMMON_INCLUDE_DIRS)
18mark_as_advanced(XKBCOMMON_LIBRARIES XKBCOMMON_INCLUDE_DIRS)
19
diff --git a/CMake/Manpage.cmake b/CMake/Manpage.cmake
deleted file mode 100644
index bbd6f2dc..00000000
--- a/CMake/Manpage.cmake
+++ /dev/null
@@ -1,33 +0,0 @@
1find_package(A2X)
2
3if (A2X_FOUND)
4 add_custom_target(man ALL)
5
6 function(add_manpage name section)
7 add_custom_command(
8 OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}.${section}
9 COMMAND ${A2X_COMMAND}
10 --no-xmllint
11 --doctype manpage
12 --format manpage
13 -D ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
14 ${CMAKE_CURRENT_SOURCE_DIR}/${name}.${section}.txt
15 DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.${section}.txt
16 COMMENT Generating manpage for ${name}.${section}
17 )
18
19 add_custom_target(man-${name}.${section}
20 DEPENDS
21 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}.${section}
22 )
23 add_dependencies(man
24 man-${name}.${section}
25 )
26
27 install(
28 FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}.${section}
29 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man${section}
30 COMPONENT documentation
31 )
32 endfunction()
33endif()
diff --git a/CMake/Wayland.cmake b/CMake/Wayland.cmake
deleted file mode 100644
index f9349667..00000000
--- a/CMake/Wayland.cmake
+++ /dev/null
@@ -1,77 +0,0 @@
1#=============================================================================
2# Copyright (C) 2012-2013 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# * Neither the name of Pier Luigi Fiorini nor the names of his
17# contributors may be used to endorse or promote products derived
18# from this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#=============================================================================
32
33find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
34
35# wayland_add_protocol_client(outfiles inputfile basename)
36function(WAYLAND_ADD_PROTOCOL_CLIENT _sources _protocol _basename)
37 if(NOT WAYLAND_SCANNER_EXECUTABLE)
38 message(FATAL "The wayland-scanner executable has not been found on your system. You must install it.")
39 endif()
40
41 get_filename_component(_infile ${_protocol} ABSOLUTE)
42 set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h")
43 set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.c")
44
45 add_custom_command(OUTPUT "${_client_header}"
46 COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header < ${_infile} > ${_client_header}
47 DEPENDS ${_infile} VERBATIM)
48
49 add_custom_command(OUTPUT "${_code}"
50 COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code}
51 DEPENDS ${_infile} VERBATIM)
52
53 list(APPEND ${_sources} "${_client_header}" "${_code}")
54 set(${_sources} ${${_sources}} PARENT_SCOPE)
55endfunction()
56
57# wayland_add_protocol_server(outfiles inputfile basename)
58function(WAYLAND_ADD_PROTOCOL_SERVER _sources _protocol _basename)
59 if(NOT WAYLAND_SCANNER_EXECUTABLE)
60 message(FATAL "The wayland-scanner executable has not been found on your system. You must install it.")
61 endif()
62
63 get_filename_component(_infile ${_protocol} ABSOLUTE)
64 set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-server-protocol.h")
65 set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-server-protocol.c")
66
67 add_custom_command(OUTPUT "${_server_header}"
68 COMMAND ${WAYLAND_SCANNER_EXECUTABLE} server-header < ${_infile} > ${_server_header}
69 DEPENDS ${_infile} VERBATIM)
70
71 add_custom_command(OUTPUT "${_code}"
72 COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code}
73 DEPENDS ${_infile} VERBATIM)
74
75 list(APPEND ${_sources} "${_server_header}" "${_code}")
76 set(${_sources} ${${_sources}} PARENT_SCOPE)
77endfunction()
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index dc521570..00000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,161 +0,0 @@
1cmake_minimum_required(VERSION 3.1.0)
2
3project(sway C)
4
5add_compile_options(-g)
6set(CMAKE_C_STANDARD 99)
7set(CMAKE_C_EXTENSIONS OFF)
8set(CMAKE_POSITION_INDEPENDENT_CODE ON)
9set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
10add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-result -Werror)
11
12# Add Address Sanitiezed build type
13set(CMAKE_C_FLAGS_ASAN
14 "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer"
15 CACHE STRING "Flags used by the C compiler during address sanitizer builds."
16 FORCE )
17mark_as_advanced(
18 CMAKE_C_FLAGS_ASAN
19 CMAKE_EXE_LINKER_FLAGS_DEBUG
20 CMAKE_SHARED_LINKER_FLAGS_DEBUG
21 )
22
23list(INSERT CMAKE_MODULE_PATH 0
24 ${CMAKE_CURRENT_SOURCE_DIR}/CMake
25 )
26
27if (VERSION)
28 add_definitions(-DSWAY_VERSION=\"${VERSION}\")
29else()
30 execute_process(
31 COMMAND git describe --always --tags
32 OUTPUT_VARIABLE GIT_COMMIT_HASH
33 OUTPUT_STRIP_TRAILING_WHITESPACE
34 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
35 )
36 execute_process(
37 COMMAND git rev-parse --abbrev-ref HEAD
38 OUTPUT_VARIABLE GIT_BRANCH
39 OUTPUT_STRIP_TRAILING_WHITESPACE
40 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
41 )
42
43 string(TIMESTAMP CURRENT_DATE "%Y-%m-%d" UTC)
44 add_definitions("-DSWAY_VERSION=\"${GIT_COMMIT_HASH} (${CURRENT_DATE}, branch \\\"${GIT_BRANCH}\\\")\"")
45endif()
46
47option(enable-swaylock "Enables the swaylock utility" YES)
48option(enable-swaybg "Enables the wallpaper utility" YES)
49option(enable-swaybar "Enables the swaybar utility" YES)
50option(enable-swaygrab "Enables the swaygrab utility" YES)
51option(enable-swaymsg "Enables the swaymsg utility" YES)
52option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" YES)
53option(enable-tray "Enables the swaybar tray" YES)
54option(zsh-completions "Zsh shell completions" NO)
55option(default-wallpaper "Installs the default wallpaper" YES)
56option(LD_LIBRARY_PATH "Configure sway's default LD_LIBRARY_PATH")
57
58if (LD_LIBRARY_PATH)
59 add_definitions(-D_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}")
60endif()
61
62find_package(JsonC 0.12.1 REQUIRED)
63find_package(PCRE REQUIRED)
64find_package(WLC REQUIRED)
65find_package(Wayland REQUIRED)
66find_package(XKBCommon REQUIRED)
67find_package(Cairo REQUIRED)
68find_package(Pango REQUIRED)
69find_package(GdkPixbuf)
70find_package(PAM)
71find_package(DBus 1.10)
72
73find_package(LibInput REQUIRED)
74
75if (CMAKE_SYSTEM_NAME STREQUAL Linux)
76 find_package(Libcap REQUIRED)
77endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
78
79if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
80 find_package(EpollShim REQUIRED)
81endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
82
83include(FeatureSummary)
84include(Manpage)
85include(GNUInstallDirs)
86
87if (enable-gdk-pixbuf)
88 if (GDK_PIXBUF_FOUND)
89 set(WITH_GDK_PIXBUF YES)
90 add_definitions(-DWITH_GDK_PIXBUF)
91 else()
92 message(WARNING "gdk-pixbuf required but not found, only png images supported.")
93 endif()
94else()
95 message(STATUS "Building without gdk-pixbuf, only png images supported.")
96endif()
97
98if (enable-tray)
99 if (DBUS_FOUND)
100 set(ENABLE_TRAY TRUE)
101 add_definitions(-DENABLE_TRAY)
102 else()
103 message(WARNING "Tray required but DBus was not found. Tray will not be included")
104 endif()
105else()
106 message(STATUS "Building without the tray.")
107endif()
108
109include_directories(include)
110
111add_subdirectory(protocols)
112add_subdirectory(common)
113add_subdirectory(wayland)
114
115add_subdirectory(sway)
116if(enable-swaybg)
117 if(CAIRO_FOUND AND PANGO_FOUND)
118 add_subdirectory(swaybg)
119 else()
120 message(WARNING "Not building swaybg - cairo, and pango are required.")
121 endif()
122endif()
123if(enable-swaymsg)
124 add_subdirectory(swaymsg)
125endif()
126if(enable-swaygrab)
127 add_subdirectory(swaygrab)
128endif()
129if(enable-swaybar)
130 if(CAIRO_FOUND AND PANGO_FOUND)
131 add_subdirectory(swaybar)
132 else()
133 message(WARNING "Not building swaybar - cairo, and pango are required.")
134 endif()
135endif()
136if(enable-swaylock)
137 if(CAIRO_FOUND AND PANGO_FOUND AND PAM_FOUND)
138 add_subdirectory(swaylock)
139 else()
140 message(WARNING "Not building swaylock - cairo, pango, and PAM are required.")
141 endif()
142endif()
143if(zsh-completions)
144 add_subdirectory(completions/zsh)
145endif()
146install(
147 FILES ${CMAKE_CURRENT_SOURCE_DIR}/sway.desktop
148 DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/wayland-sessions
149 COMPONENT data
150 )
151
152if(default-wallpaper)
153 install(
154 DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/assets/
155 DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/backgrounds/sway
156 COMPONENT data
157 FILES_MATCHING PATTERN "*Wallpaper*"
158 )
159endif()
160
161feature_summary(WHAT ALL)
diff --git a/HACKING.md b/HACKING.md
index cf18dbb0..3600db24 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -22,14 +22,3 @@ assertion fails.
22 22
23Outside of the compositor (swaymsg, swaybar, etc), using `assert.h` is 23Outside of the compositor (swaymsg, swaybar, etc), using `assert.h` is
24permitted. 24permitted.
25
26## Building against a local wlc
27
281. Build wlc as described [here](https://github.com/Cloudef/wlc#building)
292. Inside your sway source folder, tell `cmake` to use your local version of wlc:
30
31```bash
32cmake \
33 -DWLC_LIBRARIES=path/to/wlc/target/src/libwlc.so \
34 -DWLC_INCLUDE_DIRS=path/to/wlc/include .
35```
diff --git a/README.de.md b/README.de.md
index 701cc34f..530d8dd7 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Mit fosspay spenden](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3Der Fortschritt dieser Ãœbersetzung kann [hier](https://github.com/swaywm/sway/issues/1318) 3Der Fortschritt dieser Ãœbersetzung kann [hier](https://github.com/swaywm/sway/issues/1318)
4eingesehen werden. 4eingesehen werden.
@@ -52,7 +52,7 @@ schau im IRC-Channel vorbei oder schreibe eine e-Mail an sir@cmpwn.com (nur engl
52 52
53Abhängigkeiten: 53Abhängigkeiten:
54 54
55* cmake 55* meson
56* [wlc](https://github.com/Cloudef/wlc) 56* [wlc](https://github.com/Cloudef/wlc)
57* wayland 57* wayland
58* xwayland 58* xwayland
@@ -60,7 +60,7 @@ Abhängigkeiten:
60* libcap 60* libcap
61* asciidoc 61* asciidoc
62* pcre 62* pcre
63* json-c <= 0.12.1 63* json-c >= 0.13
64* pango 64* pango
65* cairo 65* cairo
66* gdk-pixbuf2 * 66* gdk-pixbuf2 *
@@ -74,11 +74,9 @@ _\*\*Nur erforderlich für swaylock_
74 74
75Führe diese Befehle aus: 75Führe diese Befehle aus:
76 76
77 mkdir build 77 meson build
78 cd build 78 ninja -C build
79 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 79 sudo ninja -C build install
80 make
81 sudo make install
82 80
83In Systemen mit logind musst du `sway` einige Capabilities geben: 81In Systemen mit logind musst du `sway` einige Capabilities geben:
84 82
diff --git a/README.el.md b/README.el.md
index df39364f..682dbc84 100644
--- a/README.el.md
+++ b/README.el.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Donate with fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3"Ο Sway (**S**irCmpwn's **Way**land) είναι ένας **υπό ανάπτυξη** [Wayland](http://wayland.freedesktop.org/) διαχειÏιστής παÏαθÏÏων συμβατός με τον αντίστοιχο διαχειÏιστή παÏαθÏÏων i3 για τον X11. 3"Ο Sway (**S**irCmpwn's **Way**land) είναι ένας **υπό ανάπτυξη** [Wayland](http://wayland.freedesktop.org/) διαχειÏιστής παÏαθÏÏων συμβατός με τον αντίστοιχο διαχειÏιστή παÏαθÏÏων i3 για τον X11.
4Διαβάστε τις [Συνήθεις ΕÏωτήσεις](https://github.com/swaywm/sway/wiki). Συνδεθείτε στο [κανάλι μας στο IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway στο 4Διαβάστε τις [Συνήθεις ΕÏωτήσεις](https://github.com/swaywm/sway/wiki). Συνδεθείτε στο [κανάλι μας στο IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway στο
@@ -45,7 +45,7 @@ To username μου στο Freenode είναι kon14 και θα με βÏείτÎ
45 45
46Εγκατάσταση εξαÏτήσεων: 46Εγκατάσταση εξαÏτήσεων:
47 47
48* cmake 48* meson
49* [wlc](https://github.com/Cloudef/wlc) 49* [wlc](https://github.com/Cloudef/wlc)
50* wayland 50* wayland
51* xwayland 51* xwayland
@@ -53,7 +53,7 @@ To username μου στο Freenode είναι kon14 και θα με βÏείτÎ
53* libcap 53* libcap
54* asciidoc 54* asciidoc
55* pcre 55* pcre
56* json-c <= 0.12.1 56* json-c >= 0.13
57* pango 57* pango
58* cairo 58* cairo
59* gdk-pixbuf2 * 59* gdk-pixbuf2 *
@@ -67,11 +67,9 @@ _\*\*Απαιτείται μόνο για swaylock_
67 67
68Εκτελέστε αυτές τις εντολές: 68Εκτελέστε αυτές τις εντολές:
69 69
70 mkdir build 70 meson build
71 cd build 71 ninja -C build
72 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 72 sudo ninja -C build install
73 make
74 sudo make install
75 73
76Σε συστήματα με logind, χÏειάζεται να οÏίσετε μεÏικά δικαιώματα caps στο εκτελέσιμο αÏχείο: 74Σε συστήματα με logind, χÏειάζεται να οÏίσετε μεÏικά δικαιώματα caps στο εκτελέσιμο αÏχείο:
77 75
diff --git a/README.fr.md b/README.fr.md
index 47b6c410..360fe618 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Donate with fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" est un compositeur [Wayland](http://wayland.freedesktop.org/) 3"**S**irCmpwn's **Way**land compositor" est un compositeur [Wayland](http://wayland.freedesktop.org/)
4compatible avec i3, **en cours de développement**. 4compatible avec i3, **en cours de développement**.
@@ -47,7 +47,7 @@ IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des consei
47 47
48Installez les dépendances : 48Installez les dépendances :
49 49
50* cmake 50* meson
51* [wlc](https://github.com/Cloudef/wlc) 51* [wlc](https://github.com/Cloudef/wlc)
52* wayland 52* wayland
53* xwayland 53* xwayland
@@ -55,7 +55,7 @@ Installez les dépendances :
55* libcap 55* libcap
56* asciidoc 56* asciidoc
57* pcre 57* pcre
58* json-c <= 0.12.1 58* json-c >= 0.13
59* pango 59* pango
60* cairo 60* cairo
61* gdk-pixbuf2 * 61* gdk-pixbuf2 *
@@ -69,11 +69,9 @@ _\*\*Uniquement requis pour swaylock_
69 69
70Exécutez ces commandes : 70Exécutez ces commandes :
71 71
72 mkdir build 72 meson build
73 cd build 73 ninja -C build
74 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 74 sudo ninja -C build install
75 make
76 sudo make install
77 75
78Sur les systèmes avec logind, vous devez définir quelques caps sur le binaire : 76Sur les systèmes avec logind, vous devez définir quelques caps sur le binaire :
79 77
diff --git a/README.it.md b/README.it.md
index f5e0d8e2..94dc96c6 100644
--- a/README.it.md
+++ b/README.it.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Donate with fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" è un compositor 3"**S**irCmpwn's **Way**land compositor" è un compositor
4[Wayland](http://wayland.freedesktop.org/) **in via di sviluppo** 4[Wayland](http://wayland.freedesktop.org/) **in via di sviluppo**
@@ -48,7 +48,7 @@ a sir@cmpwn.com.
48 48
49Installa queste dipendenze: 49Installa queste dipendenze:
50 50
51* cmake 51* meson
52* [wlc](https://github.com/Cloudef/wlc) 52* [wlc](https://github.com/Cloudef/wlc)
53* wayland 53* wayland
54* xwayland 54* xwayland
@@ -56,7 +56,7 @@ Installa queste dipendenze:
56* libcap 56* libcap
57* asciidoc 57* asciidoc
58* pcre 58* pcre
59* json-c <= 0.12.1 59* json-c >= 0.13
60* pango 60* pango
61* cairo 61* cairo
62* gdk-pixbuf2 * 62* gdk-pixbuf2 *
@@ -70,11 +70,9 @@ _\*\*Richiesto solo per swaylock_
70 70
71Esegui questi comandi: 71Esegui questi comandi:
72 72
73 mkdir build 73 meson build
74 cd build 74 ninja -C build
75 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 75 sudo ninja -C build install
76 make
77 sudo make install
78 76
79Per i sistemi con logind, devi impostare un paio di caps sull'eseguibile: 77Per i sistemi con logind, devi impostare un paio di caps sull'eseguibile:
80 78
diff --git a/README.ja.md b/README.ja.md
index 2e8f9bfb..42d31b86 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Donate with fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor"ã¯**開発中**ã® 3"**S**irCmpwn's **Way**land compositor"ã¯**開発中**ã®
4i3互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ 4i3互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚
@@ -38,7 +38,7 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚"
38 38
39次ã®ä¾å­˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„: 39次ã®ä¾å­˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„:
40 40
41* cmake 41* meson
42* [wlc](https://github.com/Cloudef/wlc) 42* [wlc](https://github.com/Cloudef/wlc)
43* wayland 43* wayland
44* xwayland 44* xwayland
@@ -46,7 +46,7 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚"
46* libcap 46* libcap
47* asciidoc 47* asciidoc
48* pcre 48* pcre
49* json-c <= 0.12.1 49* json-c >= 0.13
50* pango 50* pango
51* cairo 51* cairo
52* gdk-pixbuf2 * 52* gdk-pixbuf2 *
@@ -60,11 +60,9 @@ _\*\*swaylockã§ã®ã¿å¿…è¦ã§ã™_
60 60
61次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„: 61次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„:
62 62
63 mkdir build 63 meson build
64 cd build 64 ninja -C build
65 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 65 sudo ninja -C build install
66 make
67 sudo make install
68 66
69logindを使用ã—ã¦ã„るシステムã§ã¯ã€ãƒã‚¤ãƒŠãƒªã«ã„ãã¤ã‹ã®ã‚±ãƒ¼ãƒ‘ビリティを設定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™: 67logindを使用ã—ã¦ã„るシステムã§ã¯ã€ãƒã‚¤ãƒŠãƒªã«ã„ãã¤ã‹ã®ã‚±ãƒ¼ãƒ‘ビリティを設定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™:
70 68
diff --git a/README.md b/README.md
index 0e92f78e..848827ca 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Donate with fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [УкраїнÑька](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - 3[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [УкраїнÑька](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) -
4[РуÑÑкий](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) 4[РуÑÑкий](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--)
@@ -53,15 +53,15 @@ channel or shoot an email to sir@cmpwn.com for advice.
53 53
54Install dependencies: 54Install dependencies:
55 55
56* cmake 56* meson
57* [wlc](https://github.com/Cloudef/wlc) 57* [wlroots](https://github.com/swaywm/wlroots)
58* wayland 58* wayland
59* xwayland 59* xwayland
60* libinput >= 1.6.0 60* libinput >= 1.6.0
61* libcap 61* libcap
62* asciidoc 62* asciidoc
63* pcre 63* pcre
64* json-c <= 0.12.1 64* json-c >= 0.13
65* pango 65* pango
66* cairo 66* cairo
67* gdk-pixbuf2 * 67* gdk-pixbuf2 *
@@ -78,11 +78,9 @@ _\*\*\*Only required for tray support_
78 78
79Run these commands: 79Run these commands:
80 80
81 mkdir build 81 meson build
82 cd build 82 ninja -C build
83 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 83 sudo ninja -C build install
84 make
85 sudo make install
86 84
87On systems with logind, you need to set a few caps on the binary: 85On systems with logind, you need to set a few caps on the binary:
88 86
diff --git a/README.pt.md b/README.pt.md
index b5ca132f..98ad72e3 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Doe através do fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" é um compositor [Wayland](http://wayland.freedesktop.org/) 3"**S**irCmpwn's **Way**land compositor" é um compositor [Wayland](http://wayland.freedesktop.org/)
4compatível com o i3. Leia o [FAQ](https://github.com/swaywm/sway/wiki). Participe do 4compatível com o i3. Leia o [FAQ](https://github.com/swaywm/sway/wiki). Participe do
@@ -54,7 +54,7 @@ visite o canal no IRC ou mande um email para sir@cmpwn.com (*em inglês*).
54 54
55Antes de iniciar a compilação, instale as dependências: 55Antes de iniciar a compilação, instale as dependências:
56 56
57* cmake 57* meson
58* [wlc](https://github.com/Cloudef/wlc) 58* [wlc](https://github.com/Cloudef/wlc)
59* wayland 59* wayland
60* xwayland 60* xwayland
@@ -62,7 +62,7 @@ Antes de iniciar a compilação, instale as dependências:
62* libcap 62* libcap
63* asciidoc 63* asciidoc
64* pcre 64* pcre
65* json-c <= 0.12.1 65* json-c >= 0.13
66* pango 66* pango
67* cairo 67* cairo
68* gdk-pixbuf2 * 68* gdk-pixbuf2 *
@@ -76,11 +76,9 @@ _\*\*Dependência apenas de swaylock_
76 76
77Para compilar, execute estes comandos: 77Para compilar, execute estes comandos:
78 78
79 mkdir build 79 meson build
80 cd build 80 ninja -C build
81 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 81 sudo ninja -C build install
82 make
83 sudo make install
84 82
85Em sistemas com logind, configure as seguintes capacidades para o arquivo binário: 83Em sistemas com logind, configure as seguintes capacidades para o arquivo binário:
86 84
diff --git a/README.ru.md b/README.ru.md
index ae2eb697..7329303e 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Пожертвовать через fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3"**S**irCmpwn's **Way**land compositor" на данный момент **(в разработке)** 3"**S**irCmpwn's **Way**land compositor" на данный момент **(в разработке)**
4i3-ÑовмеÑтимый [Wayland](http://wayland.freedesktop.org/) композитор. 4i3-ÑовмеÑтимый [Wayland](http://wayland.freedesktop.org/) композитор.
@@ -49,7 +49,7 @@ Sway доÑтупен во многих диÑтрибутивах и наход
49 49
50УÑтановите Ñледующие пакеты: 50УÑтановите Ñледующие пакеты:
51 51
52* cmake 52* meson
53* [wlc](https://github.com/Cloudef/wlc) 53* [wlc](https://github.com/Cloudef/wlc)
54* wayland 54* wayland
55* xwayland 55* xwayland
@@ -57,7 +57,7 @@ Sway доÑтупен во многих диÑтрибутивах и наход
57* libcap 57* libcap
58* asciidoc 58* asciidoc
59* pcre 59* pcre
60* json-c <= 0.12.1 60* json-c >= 0.13
61* pango 61* pango
62* cairo 62* cairo
63* gdk-pixbuf2 * 63* gdk-pixbuf2 *
@@ -74,11 +74,9 @@ _\*\*\*ТребуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ tray_
74 74
75Выполните Ñледующие команды: 75Выполните Ñледующие команды:
76 76
77 mkdir build 77 meson build
78 cd build 78 ninja -C build
79 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 79 sudo ninja -C build install
80 make
81 sudo make install
82 80
83ЕÑли у Ð²Ð°Ñ logind: 81ЕÑли у Ð²Ð°Ñ logind:
84 82
diff --git a/README.uk.md b/README.uk.md
index 2e107afc..81bc70fa 100644
--- a/README.uk.md
+++ b/README.uk.md
@@ -1,4 +1,4 @@
1# sway [![](https://api.travis-ci.org/swaywm/sway.svg)](https://travis-ci.org/swaywm/sway) [![Donate with fosspay](https://drewdevault.com/donate/static/donate-with-fosspay.png)](https://drewdevault.com/donate?project=4) 1# sway
2 2
3**Sway** ("**S**irCmpwn's **Way**land compositor") це ÑуміÑний з i3 композитор 3**Sway** ("**S**irCmpwn's **Way**land compositor") це ÑуміÑний з i3 композитор
4[Wayland](http://wayland.freedesktop.org/) (**у Ñтані розробки**). 4[Wayland](http://wayland.freedesktop.org/) (**у Ñтані розробки**).
@@ -54,7 +54,7 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а
54 54
55Ð’Ñтановіть залежноÑÑ‚Ñ–: 55Ð’Ñтановіть залежноÑÑ‚Ñ–:
56 56
57* cmake 57* meson
58* [wlc](https://github.com/Cloudef/wlc) 58* [wlc](https://github.com/Cloudef/wlc)
59* wayland 59* wayland
60* xwayland 60* xwayland
@@ -62,7 +62,7 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а
62* libcap 62* libcap
63* asciidoc 63* asciidoc
64* pcre 64* pcre
65* json-c <= 0.12.1 65* json-c >= 0.13
66* pango 66* pango
67* cairo 67* cairo
68* gdk-pixbuf2 * 68* gdk-pixbuf2 *
@@ -76,11 +76,9 @@ _\*\*Лише Ð´Ð»Ñ swaylock_
76 76
77Виконайте ці команди: 77Виконайте ці команди:
78 78
79 mkdir build 79 meson build
80 cd build 80 ninja -C build
81 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc .. 81 sudo ninja -C build install
82 make
83 sudo make install
84 82
85Ðа ÑиÑтемах **з** logind, варто вÑтановити декілька можливоÑтей (caps) 83Ðа ÑиÑтемах **з** logind, варто вÑтановити декілька можливоÑтей (caps)
86на виконуваний файл sway: 84на виконуваний файл sway:
diff --git a/client/meson.build b/client/meson.build
new file mode 100644
index 00000000..2bdda457
--- /dev/null
+++ b/client/meson.build
@@ -0,0 +1,16 @@
1lib_sway_client = static_library(
2 'sway-client',
3 files(
4 'pool-buffer.c',
5 ),
6 dependencies: [
7 cairo,
8 gdk_pixbuf,
9 pango,
10 pangocairo,
11 wlroots,
12 wayland_client,
13 ],
14 link_with: [lib_sway_common],
15 include_directories: sway_inc
16)
diff --git a/wayland/buffers.c b/client/pool-buffer.c
index e9780997..1f54a77c 100644
--- a/wayland/buffers.c
+++ b/client/pool-buffer.c
@@ -1,21 +1,20 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <wayland-client.h> 2#include <assert.h>
3#include <cairo/cairo.h> 3#include <cairo/cairo.h>
4#include <pango/pangocairo.h> 4#include <stdio.h>
5#include <stdlib.h> 5#include <stdlib.h>
6#include <string.h> 6#include <string.h>
7#include <stdio.h>
8#include <unistd.h>
9#include <errno.h>
10#include <sys/mman.h> 7#include <sys/mman.h>
11#include "client/buffer.h" 8#include <pango/pangocairo.h>
12#include "list.h" 9#include <unistd.h>
13#include "log.h" 10#include <wayland-client.h>
11#include "config.h"
12#include "pool-buffer.h"
14 13
15static int create_pool_file(size_t size, char **name) { 14static int create_pool_file(size_t size, char **name) {
16 static const char template[] = "sway-client-XXXXXX"; 15 static const char template[] = "sway-client-XXXXXX";
17 const char *path = getenv("XDG_RUNTIME_DIR"); 16 const char *path = getenv("XDG_RUNTIME_DIR");
18 if (!path) { 17 if (!path) {
19 return -1; 18 return -1;
20 } 19 }
21 20
@@ -42,7 +41,7 @@ static int create_pool_file(size_t size, char **name) {
42} 41}
43 42
44static void buffer_release(void *data, struct wl_buffer *wl_buffer) { 43static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
45 struct buffer *buffer = data; 44 struct pool_buffer *buffer = data;
46 buffer->busy = false; 45 buffer->busy = false;
47} 46}
48 47
@@ -50,22 +49,17 @@ static const struct wl_buffer_listener buffer_listener = {
50 .release = buffer_release 49 .release = buffer_release
51}; 50};
52 51
53static struct buffer *create_buffer(struct window *window, struct buffer *buf, 52static struct pool_buffer *create_buffer(struct wl_shm *shm,
54 int32_t width, int32_t height, int32_t scale, uint32_t format) { 53 struct pool_buffer *buf, int32_t width, int32_t height,
55 54 uint32_t format) {
56 width *= scale;
57 height *= scale;
58 uint32_t stride = width * 4; 55 uint32_t stride = width * 4;
59 uint32_t size = stride * height; 56 uint32_t size = stride * height;
60 57
61 char *name; 58 char *name;
62 int fd = create_pool_file(size, &name); 59 int fd = create_pool_file(size, &name);
63 if (fd == -1) { 60 assert(fd != -1);
64 sway_abort("Unable to allocate buffer");
65 return NULL; // never reached
66 }
67 void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 61 void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
68 struct wl_shm_pool *pool = wl_shm_create_pool(window->registry->shm, fd, size); 62 struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
69 buf->buffer = wl_shm_pool_create_buffer(pool, 0, 63 buf->buffer = wl_shm_pool_create_buffer(pool, 0,
70 width, height, stride, format); 64 width, height, stride, format);
71 wl_shm_pool_destroy(pool); 65 wl_shm_pool_destroy(pool);
@@ -85,7 +79,7 @@ static struct buffer *create_buffer(struct window *window, struct buffer *buf,
85 return buf; 79 return buf;
86} 80}
87 81
88static void destroy_buffer(struct buffer *buffer) { 82void destroy_buffer(struct pool_buffer *buffer) {
89 if (buffer->buffer) { 83 if (buffer->buffer) {
90 wl_buffer_destroy(buffer->buffer); 84 wl_buffer_destroy(buffer->buffer);
91 } 85 }
@@ -98,37 +92,33 @@ static void destroy_buffer(struct buffer *buffer) {
98 if (buffer->pango) { 92 if (buffer->pango) {
99 g_object_unref(buffer->pango); 93 g_object_unref(buffer->pango);
100 } 94 }
101 memset(buffer, 0, sizeof(struct buffer)); 95 memset(buffer, 0, sizeof(struct pool_buffer));
102} 96}
103 97
104struct buffer *get_next_buffer(struct window *window) { 98struct pool_buffer *get_next_buffer(struct wl_shm *shm,
105 struct buffer *buffer = NULL; 99 struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
100 struct pool_buffer *buffer = NULL;
106 101
107 int i; 102 for (size_t i = 0; i < 2; ++i) {
108 for (i = 0; i < 2; ++i) { 103 if (pool[i].busy) {
109 if (window->buffers[i].busy) {
110 continue; 104 continue;
111 } 105 }
112 buffer = &window->buffers[i]; 106 buffer = &pool[i];
113 } 107 }
114 108
115 if (!buffer) { 109 if (!buffer) {
116 return NULL; 110 return NULL;
117 } 111 }
118 112
119 if (buffer->width != window->width || buffer->height != window->height) { 113 if (buffer->width != width || buffer->height != height) {
120 destroy_buffer(buffer); 114 destroy_buffer(buffer);
121 } 115 }
122 116
123 if (!buffer->buffer) { 117 if (!buffer->buffer) {
124 if (!create_buffer(window, buffer, 118 if (!create_buffer(shm, buffer, width, height,
125 window->width, window->height, window->scale,
126 WL_SHM_FORMAT_ARGB8888)) { 119 WL_SHM_FORMAT_ARGB8888)) {
127 return NULL; 120 return NULL;
128 } 121 }
129 } 122 }
130
131 window->cairo = buffer->cairo;
132 window->buffer = buffer;
133 return buffer; 123 return buffer;
134} 124}
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
deleted file mode 100644
index 4fa71f3a..00000000
--- a/common/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
1include_directories(
2 ${WLC_INCLUDE_DIRS}
3 ${XKBCOMMON_INCLUDE_DIRS}
4)
5
6add_library(sway-common STATIC
7 ipc-client.c
8 list.c
9 log.c
10 util.c
11 readline.c
12 stringop.c
13)
14
15target_link_libraries(sway-common m)
diff --git a/common/background-image.c b/common/background-image.c
new file mode 100644
index 00000000..e5fb4433
--- /dev/null
+++ b/common/background-image.c
@@ -0,0 +1,119 @@
1#include <assert.h>
2#include <stdbool.h>
3#include <wlr/util/log.h>
4#include "background-image.h"
5#include "cairo.h"
6
7enum background_mode parse_background_mode(const char *mode) {
8 if (strcmp(mode, "stretch") == 0) {
9 return BACKGROUND_MODE_STRETCH;
10 } else if (strcmp(mode, "fill") == 0) {
11 return BACKGROUND_MODE_FILL;
12 } else if (strcmp(mode, "fit") == 0) {
13 return BACKGROUND_MODE_FIT;
14 } else if (strcmp(mode, "center") == 0) {
15 return BACKGROUND_MODE_CENTER;
16 } else if (strcmp(mode, "tile") == 0) {
17 return BACKGROUND_MODE_TILE;
18 } else if (strcmp(mode, "solid_color") == 0) {
19 return BACKGROUND_MODE_SOLID_COLOR;
20 }
21 wlr_log(L_ERROR, "Unsupported background mode: %s", mode);
22 return BACKGROUND_MODE_INVALID;
23}
24
25cairo_surface_t *load_background_image(const char *path) {
26 cairo_surface_t *image;
27#ifdef HAVE_GDK_PIXBUF
28 GError *err = NULL;
29 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
30 if (!pixbuf) {
31 wlr_log(L_ERROR, "Failed to load background image (%s).",
32 err->message);
33 return false;
34 }
35 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
36 g_object_unref(pixbuf);
37#else
38 image = cairo_image_surface_create_from_png(path);
39#endif //HAVE_GDK_PIXBUF
40 if (!image) {
41 wlr_log(L_ERROR, "Failed to read background image.");
42 return NULL;
43 }
44 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
45 wlr_log(L_ERROR, "Failed to read background image: %s."
46#ifndef HAVE_GDK_PIXBUF
47 "\nSway was compiled without gdk_pixbuf support, so only"
48 "\nPNG images can be loaded. This is the likely cause."
49#endif //HAVE_GDK_PIXBUF
50 , cairo_status_to_string(cairo_surface_status(image)));
51 return NULL;
52 }
53 return image;
54}
55
56void render_background_image(cairo_t *cairo, cairo_surface_t *image,
57 enum background_mode mode, int buffer_width, int buffer_height) {
58 double width = cairo_image_surface_get_width(image);
59 double height = cairo_image_surface_get_height(image);
60
61 switch (mode) {
62 case BACKGROUND_MODE_STRETCH:
63 cairo_scale(cairo,
64 (double)buffer_width / width,
65 (double)buffer_height / height);
66 cairo_set_source_surface(cairo, image, 0, 0);
67 break;
68 case BACKGROUND_MODE_FILL: {
69 double window_ratio = (double)buffer_width / buffer_height;
70 double bg_ratio = width / height;
71
72 if (window_ratio > bg_ratio) {
73 double scale = (double)buffer_width / width;
74 cairo_scale(cairo, scale, scale);
75 cairo_set_source_surface(cairo, image,
76 0, (double)buffer_height / 2 / scale - height / 2);
77 } else {
78 double scale = (double)buffer_height / height;
79 cairo_scale(cairo, scale, scale);
80 cairo_set_source_surface(cairo, image,
81 (double)buffer_width / 2 / scale - width / 2, 0);
82 }
83 break;
84 }
85 case BACKGROUND_MODE_FIT: {
86 double window_ratio = (double)buffer_width / buffer_height;
87 double bg_ratio = width / height;
88
89 if (window_ratio > bg_ratio) {
90 double scale = (double)buffer_height / height;
91 cairo_scale(cairo, scale, scale);
92 cairo_set_source_surface(cairo, image,
93 (double)buffer_width / 2 / scale - width / 2, 0);
94 } else {
95 double scale = (double)buffer_width / width;
96 cairo_scale(cairo, scale, scale);
97 cairo_set_source_surface(cairo, image,
98 0, (double)buffer_height / 2 / scale - height / 2);
99 }
100 break;
101 }
102 case BACKGROUND_MODE_CENTER:
103 cairo_set_source_surface(cairo, image,
104 (double)buffer_width / 2 - width / 2,
105 (double)buffer_height / 2 - height / 2);
106 break;
107 case BACKGROUND_MODE_TILE: {
108 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
109 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
110 cairo_set_source(cairo, pattern);
111 break;
112 }
113 case BACKGROUND_MODE_SOLID_COLOR:
114 case BACKGROUND_MODE_INVALID:
115 assert(0);
116 break;
117 }
118 cairo_paint(cairo);
119}
diff --git a/wayland/cairo.c b/common/cairo.c
index 193205b1..c267c77c 100644
--- a/wayland/cairo.c
+++ b/common/cairo.c
@@ -1,4 +1,9 @@
1#include "client/cairo.h" 1#include <stdint.h>
2#include <cairo/cairo.h>
3#include "cairo.h"
4#ifdef HAVE_GDK_PIXBUF
5#include <gdk-pixbuf/gdk-pixbuf.h>
6#endif
2 7
3void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { 8void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
4 cairo_set_source_rgba(cairo, 9 cairo_set_source_rgba(cairo,
@@ -8,45 +13,31 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
8 (color >> (0*8) & 0xFF) / 255.0); 13 (color >> (0*8) & 0xFF) / 255.0);
9} 14}
10 15
11cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) { 16cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
17 int width, int height) {
12 int image_width = cairo_image_surface_get_width(image); 18 int image_width = cairo_image_surface_get_width(image);
13 int image_height = cairo_image_surface_get_height(image); 19 int image_height = cairo_image_surface_get_height(image);
14 20
15 cairo_surface_t *new = 21 cairo_surface_t *new =
16 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); 22 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
17
18 cairo_t *cairo = cairo_create(new); 23 cairo_t *cairo = cairo_create(new);
19 24 cairo_scale(cairo, (double)width / image_width,
20 cairo_scale(cairo, (double) width / image_width, (double) height / image_height); 25 (double)height / image_height);
21
22 cairo_set_source_surface(cairo, image, 0, 0); 26 cairo_set_source_surface(cairo, image, 0, 0);
23 cairo_paint(cairo);
24 27
28 cairo_paint(cairo);
25 cairo_destroy(cairo); 29 cairo_destroy(cairo);
26
27 return new; 30 return new;
28} 31}
29 32
30#ifdef WITH_GDK_PIXBUF 33#ifdef HAVE_GDK_PIXBUF
31#include <gdk-pixbuf/gdk-pixbuf.h>
32
33#ifndef GDK_PIXBUF_CHECK_VERSION
34#define GDK_PIXBUF_CHECK_VERSION(major,minor,micro) \
35 (GDK_PIXBUF_MAJOR > (major) || \
36 (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR > (minor)) || \
37 (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR == (minor) && \
38 GDK_PIXBUF_MICRO >= (micro)))
39#endif
40
41cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { 34cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) {
42 int chan = gdk_pixbuf_get_n_channels(gdkbuf); 35 int chan = gdk_pixbuf_get_n_channels(gdkbuf);
43 if (chan < 3) return NULL; 36 if (chan < 3) {
37 return NULL;
38 }
44 39
45#if GDK_PIXBUF_CHECK_VERSION(2,32,0)
46 const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); 40 const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);
47#else
48 const guint8* gdkpix = gdk_pixbuf_get_pixels(gdkbuf);
49#endif
50 if (!gdkpix) { 41 if (!gdkpix) {
51 return NULL; 42 return NULL;
52 } 43 }
@@ -101,7 +92,9 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
101 * ------ 92 * ------
102 * tested as equal to lround(z/255.0) for uint z in [0..0xfe02] 93 * tested as equal to lround(z/255.0) for uint z in [0..0xfe02]
103 */ 94 */
104#define PREMUL_ALPHA(x,a,b,z) G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } G_STMT_END 95#define PREMUL_ALPHA(x,a,b,z) \
96 G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \
97 G_STMT_END
105 int i; 98 int i;
106 for (i = h; i; --i) { 99 for (i = h; i; --i) {
107 const guint8 *gp = gdkpix; 100 const guint8 *gp = gdkpix;
@@ -131,4 +124,4 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
131 cairo_surface_mark_dirty(cs); 124 cairo_surface_mark_dirty(cs);
132 return cs; 125 return cs;
133} 126}
134#endif //WITH_GDK_PIXBUF 127#endif //HAVE_GDK_PIXBUF
diff --git a/common/ipc-client.c b/common/ipc-client.c
index 1ab6627b..117e9910 100644
--- a/common/ipc-client.c
+++ b/common/ipc-client.c
@@ -1,4 +1,4 @@
1#define _POSIX_C_SOURCE 2 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <stdint.h> 3#include <stdint.h>
4#include <stdlib.h> 4#include <stdlib.h>
@@ -14,13 +14,31 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
14static const size_t ipc_header_size = sizeof(ipc_magic)+8; 14static const size_t ipc_header_size = sizeof(ipc_magic)+8;
15 15
16char *get_socketpath(void) { 16char *get_socketpath(void) {
17 FILE *fp = popen("sway --get-socketpath", "r"); 17 const char *swaysock = getenv("SWAYSOCK");
18 if (!fp) { 18 if (swaysock) {
19 return NULL; 19 return strdup(swaysock);
20 } 20 }
21 char *line = read_line(fp); 21 FILE *fp = popen("sway --get-socketpath 2>/dev/null", "r");
22 pclose(fp); 22 if (fp) {
23 return line; 23 char *line = read_line(fp);
24 pclose(fp);
25 if (line && *line) {
26 return line;
27 }
28 }
29 const char *i3sock = getenv("I3SOCK");
30 if (i3sock) {
31 return strdup(i3sock);
32 }
33 fp = popen("i3 --get-socketpath 2>/dev/null", "r");
34 if (fp) {
35 char *line = read_line(fp);
36 pclose(fp);
37 if (line && *line) {
38 return line;
39 }
40 }
41 return NULL;
24} 42}
25 43
26int ipc_open_socket(const char *socket_path) { 44int ipc_open_socket(const char *socket_path) {
@@ -79,7 +97,7 @@ struct ipc_response *ipc_recv_response(int socketfd) {
79error_2: 97error_2:
80 free(response); 98 free(response);
81error_1: 99error_1:
82 sway_log(L_ERROR, "Unable to allocate memory for IPC response"); 100 wlr_log(L_ERROR, "Unable to allocate memory for IPC response");
83 return NULL; 101 return NULL;
84} 102}
85 103
diff --git a/common/log.c b/common/log.c
index 6dc9d743..2cc7289c 100644
--- a/common/log.c
+++ b/common/log.c
@@ -1,167 +1,26 @@
1#define _POSIX_C_SOURCE 199506L
2#include <errno.h>
3#include <libgen.h>
4#include <signal.h> 1#include <signal.h>
5#include <stdarg.h> 2#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h> 3#include <stdlib.h>
8#include <unistd.h>
9#include <string.h>
10#include <time.h>
11#include "log.h" 4#include "log.h"
12#include "sway.h"
13#include "readline.h"
14 5
15static int colored = 1; 6void sway_terminate(int code);
16static log_importance_t loglevel_default = L_ERROR;
17static log_importance_t v = L_SILENT;
18 7
19static const char *verbosity_colors[] = { 8void _sway_abort(const char *format, ...) {
20 [L_SILENT] = "",
21 [L_ERROR ] = "\x1B[1;31m",
22 [L_INFO ] = "\x1B[1;34m",
23 [L_DEBUG ] = "\x1B[1;30m",
24};
25static const char verbosity_chars[] = {
26 [L_SILENT] = '\0',
27 [L_ERROR ] = 'E',
28 [L_INFO ] = 'I',
29 [L_DEBUG ] = 'D',
30};
31
32void init_log(log_importance_t verbosity) {
33 if (verbosity != L_DEBUG) {
34 // command "debuglog" needs to know the user specified log level when
35 // turning off debug logging.
36 loglevel_default = verbosity;
37 }
38 v = verbosity;
39}
40
41void set_log_level(log_importance_t verbosity) {
42 v = verbosity;
43}
44
45log_importance_t get_log_level(void) {
46 return v;
47}
48
49void reset_log_level(void) {
50 v = loglevel_default;
51}
52
53bool toggle_debug_logging(void) {
54 v = (v == L_DEBUG) ? loglevel_default : L_DEBUG;
55 return (v == L_DEBUG);
56}
57
58void sway_log_colors(int mode) {
59 colored = (mode == 1) ? 1 : 0;
60}
61
62void _sway_vlog(const char *filename, int line, log_importance_t verbosity,
63 const char *format, va_list args) {
64 if (verbosity <= v) {
65 // prefix the time to the log message
66 static struct tm result;
67 static time_t t;
68 static struct tm *tm_info;
69 char buffer[26];
70
71 unsigned int c = verbosity;
72 if (c > sizeof(verbosity_colors) / sizeof(char *) - 1) {
73 c = sizeof(verbosity_colors) / sizeof(char *) - 1;
74 }
75
76 // First, if not printing color, show the log level
77 if (!(colored && isatty(STDERR_FILENO)) && c != L_SILENT) {
78 fprintf(stderr, "%c: ", verbosity_chars[c]);
79 }
80
81 // get current time
82 t = time(NULL);
83 // convert time to local time (determined by the locale)
84 tm_info = localtime_r(&t, &result);
85 // generate time prefix
86 strftime(buffer, sizeof(buffer), "%x %X - ", tm_info);
87 fprintf(stderr, "%s", buffer);
88
89 if (colored && isatty(STDERR_FILENO)) {
90 fprintf(stderr, "%s", verbosity_colors[c]);
91 }
92
93 if (filename && line) {
94 const char *file = filename + strlen(filename);
95 while (file != filename && *file != '/') {
96 --file;
97 }
98 if (*file == '/') {
99 ++file;
100 }
101 fprintf(stderr, "[%s:%d] ", file, line);
102 }
103
104 vfprintf(stderr, format, args);
105
106 if (colored && isatty(STDERR_FILENO)) {
107 fprintf(stderr, "\x1B[0m");
108 }
109 fprintf(stderr, "\n");
110 }
111}
112
113void _sway_log(const char *filename, int line, log_importance_t verbosity, const char* format, ...) {
114 va_list args;
115 va_start(args, format);
116 _sway_vlog(filename, line, verbosity, format, args);
117 va_end(args);
118}
119
120
121void _sway_abort(const char *filename, int line, const char* format, ...) {
122 va_list args; 9 va_list args;
123 va_start(args, format); 10 va_start(args, format);
124 _sway_vlog(filename, line, L_ERROR, format, args); 11 _wlr_vlog(L_ERROR, format, args);
125 va_end(args); 12 va_end(args);
126 sway_terminate(EXIT_FAILURE); 13 sway_terminate(EXIT_FAILURE);
127} 14}
128 15
129void sway_log_errno(log_importance_t verbosity, char* format, ...) { 16bool _sway_assert(bool condition, const char *format, ...) {
130 if (verbosity <= v) {
131 unsigned int c = verbosity;
132 if (c > sizeof(verbosity_colors) / sizeof(char *) - 1) {
133 c = sizeof(verbosity_colors) / sizeof(char *) - 1;
134 }
135
136 if (colored && isatty(STDERR_FILENO)) {
137 fprintf(stderr, "%s", verbosity_colors[c]);
138 } else if (c != L_SILENT) {
139 fprintf(stderr, "%c: ", verbosity_chars[c]);
140 }
141
142 va_list args;
143 va_start(args, format);
144 vfprintf(stderr, format, args);
145 va_end(args);
146
147 fprintf(stderr, ": ");
148 fprintf(stderr, "%s", strerror(errno));
149
150 if (colored && isatty(STDERR_FILENO)) {
151 fprintf(stderr, "\x1B[0m");
152 }
153 fprintf(stderr, "\n");
154 }
155}
156
157bool _sway_assert(bool condition, const char *filename, int line, const char* format, ...) {
158 if (condition) { 17 if (condition) {
159 return true; 18 return true;
160 } 19 }
161 20
162 va_list args; 21 va_list args;
163 va_start(args, format); 22 va_start(args, format);
164 _sway_vlog(filename, line, L_ERROR, format, args); 23 _wlr_vlog(L_ERROR, format, args);
165 va_end(args); 24 va_end(args);
166 25
167#ifndef NDEBUG 26#ifndef NDEBUG
diff --git a/common/meson.build b/common/meson.build
new file mode 100644
index 00000000..44a29508
--- /dev/null
+++ b/common/meson.build
@@ -0,0 +1,23 @@
1lib_sway_common = static_library(
2 'sway-common',
3 files(
4 'background-image.c',
5 'cairo.c',
6 'ipc-client.c',
7 'log.c',
8 'list.c',
9 'pango.c',
10 'readline.c',
11 'stringop.c',
12 'unicode.c',
13 'util.c'
14 ),
15 dependencies: [
16 cairo,
17 gdk_pixbuf,
18 pango,
19 pangocairo,
20 wlroots
21 ],
22 include_directories: sway_inc
23)
diff --git a/wayland/pango.c b/common/pango.c
index f9eec98c..658d2876 100644
--- a/wayland/pango.c
+++ b/common/pango.c
@@ -1,20 +1,26 @@
1#include <cairo/cairo.h> 1#include <cairo/cairo.h>
2#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
3#include <stdarg.h> 3#include <stdarg.h>
4#include <stdlib.h>
5#include <string.h>
6#include <stdio.h>
7#include <stdbool.h> 4#include <stdbool.h>
8#include <stdint.h> 5#include <stdint.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include "log.h" 9#include "log.h"
10 10
11PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, 11PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
12 int32_t scale, bool markup) { 12 const char *text, int32_t scale, bool markup) {
13 PangoLayout *layout = pango_cairo_create_layout(cairo); 13 PangoLayout *layout = pango_cairo_create_layout(cairo);
14 PangoAttrList *attrs; 14 PangoAttrList *attrs;
15 if (markup) { 15 if (markup) {
16 char *buf; 16 char *buf;
17 pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL); 17 GError *error = NULL;
18 if (!sway_assert(pango_parse_markup(
19 text, -1, 0, &attrs, &buf, NULL, &error),
20 "pango_parse_markup '%s' -> error %s", text,
21 error ? error->message : NULL)) {
22 return NULL;
23 }
18 pango_layout_set_markup(layout, buf, -1); 24 pango_layout_set_markup(layout, buf, -1);
19 free(buf); 25 free(buf);
20 } else { 26 } else {
@@ -33,7 +39,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text
33 39
34void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 40void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
35 int32_t scale, bool markup, const char *fmt, ...) { 41 int32_t scale, bool markup, const char *fmt, ...) {
36 char *buf = malloc(2048); 42 static char buf[2048];
37 43
38 va_list args; 44 va_list args;
39 va_start(args, fmt); 45 va_start(args, fmt);
@@ -44,16 +50,13 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
44 50
45 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 51 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
46 pango_cairo_update_layout(cairo, layout); 52 pango_cairo_update_layout(cairo, layout);
47
48 pango_layout_get_pixel_size(layout, width, height); 53 pango_layout_get_pixel_size(layout, width, height);
49
50 g_object_unref(layout); 54 g_object_unref(layout);
51
52 free(buf);
53} 55}
54 56
55void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...) { 57void pango_printf(cairo_t *cairo, const char *font,
56 char *buf = malloc(2048); 58 int32_t scale, bool markup, const char *fmt, ...) {
59 static char buf[2048];
57 60
58 va_list args; 61 va_list args;
59 va_start(args, fmt); 62 va_start(args, fmt);
@@ -64,10 +67,6 @@ void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup,
64 67
65 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 68 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
66 pango_cairo_update_layout(cairo, layout); 69 pango_cairo_update_layout(cairo, layout);
67
68 pango_cairo_show_layout(cairo, layout); 70 pango_cairo_show_layout(cairo, layout);
69
70 g_object_unref(layout); 71 g_object_unref(layout);
71
72 free(buf);
73} 72}
diff --git a/common/readline.c b/common/readline.c
index cc40a2cc..ed5801de 100644
--- a/common/readline.c
+++ b/common/readline.c
@@ -8,7 +8,7 @@ char *read_line(FILE *file) {
8 char *string = malloc(size); 8 char *string = malloc(size);
9 char lastChar = '\0'; 9 char lastChar = '\0';
10 if (!string) { 10 if (!string) {
11 sway_log(L_ERROR, "Unable to allocate memory for read_line"); 11 wlr_log(L_ERROR, "Unable to allocate memory for read_line");
12 return NULL; 12 return NULL;
13 } 13 }
14 while (1) { 14 while (1) {
@@ -29,7 +29,7 @@ char *read_line(FILE *file) {
29 char *new_string = realloc(string, size *= 2); 29 char *new_string = realloc(string, size *= 2);
30 if (!new_string) { 30 if (!new_string) {
31 free(string); 31 free(string);
32 sway_log(L_ERROR, "Unable to allocate memory for read_line"); 32 wlr_log(L_ERROR, "Unable to allocate memory for read_line");
33 return NULL; 33 return NULL;
34 } 34 }
35 string = new_string; 35 string = new_string;
diff --git a/common/unicode.c b/common/unicode.c
new file mode 100644
index 00000000..38a9b48e
--- /dev/null
+++ b/common/unicode.c
@@ -0,0 +1,101 @@
1#include <stdint.h>
2#include <stddef.h>
3#include "unicode.h"
4
5size_t utf8_chsize(uint32_t ch) {
6 if (ch < 0x80) {
7 return 1;
8 } else if (ch < 0x800) {
9 return 2;
10 } else if (ch < 0x10000) {
11 return 3;
12 }
13 return 4;
14}
15
16static const uint8_t masks[] = {
17 0x7F,
18 0x1F,
19 0x0F,
20 0x07,
21 0x03,
22 0x01
23};
24
25uint32_t utf8_decode(const char **char_str) {
26 uint8_t **s = (uint8_t **)char_str;
27
28 uint32_t cp = 0;
29 if (**s < 128) {
30 // shortcut
31 cp = **s;
32 ++*s;
33 return cp;
34 }
35 int size = utf8_size((char *)*s);
36 if (size == -1) {
37 ++*s;
38 return UTF8_INVALID;
39 }
40 uint8_t mask = masks[size - 1];
41 cp = **s & mask;
42 ++*s;
43 while (--size) {
44 cp <<= 6;
45 cp |= **s & 0x3f;
46 ++*s;
47 }
48 return cp;
49}
50
51size_t utf8_encode(char *str, uint32_t ch) {
52 size_t len = 0;
53 uint8_t first;
54
55 if (ch < 0x80) {
56 first = 0;
57 len = 1;
58 } else if (ch < 0x800) {
59 first = 0xc0;
60 len = 2;
61 } else if (ch < 0x10000) {
62 first = 0xe0;
63 len = 3;
64 } else {
65 first = 0xf0;
66 len = 4;
67 }
68
69 for (size_t i = len - 1; i > 0; --i) {
70 str[i] = (ch & 0x3f) | 0x80;
71 ch >>= 6;
72 }
73
74 str[0] = ch | first;
75 return len;
76}
77
78
79static const struct {
80 uint8_t mask;
81 uint8_t result;
82 int octets;
83} sizes[] = {
84 { 0x80, 0x00, 1 },
85 { 0xE0, 0xC0, 2 },
86 { 0xF0, 0xE0, 3 },
87 { 0xF8, 0xF0, 4 },
88 { 0xFC, 0xF8, 5 },
89 { 0xFE, 0xF8, 6 },
90 { 0x80, 0x80, -1 },
91};
92
93int utf8_size(const char *s) {
94 uint8_t c = (uint8_t)*s;
95 for (size_t i = 0; i < sizeof(sizes) / 2; ++i) {
96 if ((c & sizes[i].mask) == sizes[i].result) {
97 return sizes[i].octets;
98 }
99 }
100 return -1;
101}
diff --git a/common/util.c b/common/util.c
index d6369853..fb7f9454 100644
--- a/common/util.c
+++ b/common/util.c
@@ -8,8 +8,8 @@
8#include <stdlib.h> 8#include <stdlib.h>
9#include <string.h> 9#include <string.h>
10#include <strings.h> 10#include <strings.h>
11#include <wlc/wlc.h>
12#include <xkbcommon/xkbcommon-names.h> 11#include <xkbcommon/xkbcommon-names.h>
12#include <wlr/types/wlr_keyboard.h>
13#include "log.h" 13#include "log.h"
14#include "readline.h" 14#include "readline.h"
15#include "util.h" 15#include "util.h"
@@ -29,16 +29,16 @@ static struct modifier_key {
29 char *name; 29 char *name;
30 uint32_t mod; 30 uint32_t mod;
31} modifiers[] = { 31} modifiers[] = {
32 { XKB_MOD_NAME_SHIFT, WLC_BIT_MOD_SHIFT }, 32 { XKB_MOD_NAME_SHIFT, WLR_MODIFIER_SHIFT },
33 { XKB_MOD_NAME_CAPS, WLC_BIT_MOD_CAPS }, 33 { XKB_MOD_NAME_CAPS, WLR_MODIFIER_CAPS },
34 { XKB_MOD_NAME_CTRL, WLC_BIT_MOD_CTRL }, 34 { XKB_MOD_NAME_CTRL, WLR_MODIFIER_CTRL },
35 { "Ctrl", WLC_BIT_MOD_CTRL }, 35 { "Ctrl", WLR_MODIFIER_CTRL },
36 { XKB_MOD_NAME_ALT, WLC_BIT_MOD_ALT }, 36 { XKB_MOD_NAME_ALT, WLR_MODIFIER_ALT },
37 { "Alt", WLC_BIT_MOD_ALT }, 37 { "Alt", WLR_MODIFIER_ALT },
38 { XKB_MOD_NAME_NUM, WLC_BIT_MOD_MOD2 }, 38 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
39 { "Mod3", WLC_BIT_MOD_MOD3 }, 39 { "Mod3", WLR_MODIFIER_MOD3 },
40 { XKB_MOD_NAME_LOGO, WLC_BIT_MOD_LOGO }, 40 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
41 { "Mod5", WLC_BIT_MOD_MOD5 }, 41 { "Mod5", WLR_MODIFIER_MOD5 },
42}; 42};
43 43
44uint32_t get_modifier_mask_by_name(const char *name) { 44uint32_t get_modifier_mask_by_name(const char *name) {
@@ -113,7 +113,7 @@ uint32_t parse_color(const char *color) {
113 113
114 int len = strlen(color); 114 int len = strlen(color);
115 if (len != 6 && len != 8) { 115 if (len != 6 && len != 8) {
116 sway_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color); 116 wlr_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);
117 return 0xFFFFFFFF; 117 return 0xFFFFFFFF;
118 } 118 }
119 uint32_t res = (uint32_t)strtoul(color, NULL, 16); 119 uint32_t res = (uint32_t)strtoul(color, NULL, 16);
diff --git a/completions/zsh/CMakeLists.txt b/completions/zsh/CMakeLists.txt
deleted file mode 100644
index 62c85090..00000000
--- a/completions/zsh/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
1install(
2 FILES _sway _swaymsg _swaygrab _swaylock
3 DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/zsh/site-functions/
4)
diff --git a/config.in b/config.in
index b39a39aa..54817f2a 100644
--- a/config.in
+++ b/config.in
@@ -21,7 +21,7 @@ set $menu dmenu_run
21### Output configuration 21### Output configuration
22# 22#
23# Default wallpaper (more resolutions are available in __DATADIR__/backgrounds/sway/) 23# Default wallpaper (more resolutions are available in __DATADIR__/backgrounds/sway/)
24output * bg __DATADIR__/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill 24output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
25# 25#
26# Example configuration: 26# Example configuration:
27# 27#
@@ -164,16 +164,16 @@ mode "resize" {
164 # right will grow the containers width 164 # right will grow the containers width
165 # up will shrink the containers height 165 # up will shrink the containers height
166 # down will grow the containers height 166 # down will grow the containers height
167 bindsym $left resize shrink width 10 px or 10 ppt 167 bindsym $left resize shrink width 10px
168 bindsym $down resize grow height 10 px or 10 ppt 168 bindsym $down resize grow height 10px
169 bindsym $up resize shrink height 10 px or 10 ppt 169 bindsym $up resize shrink height 10px
170 bindsym $right resize grow width 10 px or 10 ppt 170 bindsym $right resize grow width 10px
171 171
172 # ditto, with arrow keys 172 # ditto, with arrow keys
173 bindsym Left resize shrink width 10 px or 10 ppt 173 bindsym Left resize shrink width 10px
174 bindsym Down resize grow height 10 px or 10 ppt 174 bindsym Down resize grow height 10px
175 bindsym Up resize shrink height 10 px or 10 ppt 175 bindsym Up resize shrink height 10px
176 bindsym Right resize grow width 10 px or 10 ppt 176 bindsym Right resize grow width 10px
177 177
178 # return to default mode 178 # return to default mode
179 bindsym Return mode "default" 179 bindsym Return mode "default"
@@ -194,4 +194,4 @@ bar {
194 } 194 }
195} 195}
196 196
197include __SYSCONFDIR__/sway/config.d/* 197include @sysconfdir@/sway/config.d/*
diff --git a/include/background-image.h b/include/background-image.h
new file mode 100644
index 00000000..15935ffd
--- /dev/null
+++ b/include/background-image.h
@@ -0,0 +1,20 @@
1#ifndef _SWAY_BACKGROUND_IMAGE_H
2#define _SWAY_BACKGROUND_IMAGE_H
3#include "cairo.h"
4
5enum background_mode {
6 BACKGROUND_MODE_STRETCH,
7 BACKGROUND_MODE_FILL,
8 BACKGROUND_MODE_FIT,
9 BACKGROUND_MODE_CENTER,
10 BACKGROUND_MODE_TILE,
11 BACKGROUND_MODE_SOLID_COLOR,
12 BACKGROUND_MODE_INVALID,
13};
14
15enum background_mode parse_background_mode(const char *mode);
16cairo_surface_t *load_background_image(const char *path);
17void render_background_image(cairo_t *cairo, cairo_surface_t *image,
18 enum background_mode mode, int buffer_width, int buffer_height);
19
20#endif
diff --git a/include/client/cairo.h b/include/cairo.h
index e7ef7c7e..31672705 100644
--- a/include/client/cairo.h
+++ b/include/cairo.h
@@ -1,17 +1,19 @@
1#ifndef _SWAY_CAIRO_H 1#ifndef _SWAY_CAIRO_H
2#define _SWAY_CAIRO_H 2#define _SWAY_CAIRO_H
3
4#include <stdint.h> 3#include <stdint.h>
5#include <cairo/cairo.h> 4#include <cairo/cairo.h>
6 5
7void cairo_set_source_u32(cairo_t *cairo, uint32_t color); 6void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
8 7
9cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); 8cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
9 int width, int height);
10 10
11#ifdef WITH_GDK_PIXBUF 11#include "config.h"
12#ifdef HAVE_GDK_PIXBUF
12#include <gdk-pixbuf/gdk-pixbuf.h> 13#include <gdk-pixbuf/gdk-pixbuf.h>
13 14
14cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf); 15cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
16 const GdkPixbuf *gdkbuf);
15#endif //WITH_GDK_PIXBUF 17#endif //WITH_GDK_PIXBUF
16 18
17#endif 19#endif
diff --git a/include/client/buffer.h b/include/client/buffer.h
deleted file mode 100644
index eb9973ed..00000000
--- a/include/client/buffer.h
+++ /dev/null
@@ -1,8 +0,0 @@
1#ifndef _BUFFER_H
2#define _BUFFER_H
3
4#include "client/window.h"
5
6struct buffer *get_next_buffer(struct window *state);
7
8#endif
diff --git a/include/client/pango.h b/include/client/pango.h
deleted file mode 100644
index dd2f53c3..00000000
--- a/include/client/pango.h
+++ /dev/null
@@ -1,16 +0,0 @@
1#ifndef _SWAY_CLIENT_PANGO_H
2#define _SWAY_CLIENT_PANGO_H
3
4#include <cairo/cairo.h>
5#include <pango/pangocairo.h>
6#include <stdarg.h>
7#include <stdbool.h>
8#include <stdint.h>
9
10PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
11 int32_t scale, bool markup);
12void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
13 int32_t scale, bool markup, const char *fmt, ...);
14void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...);
15
16#endif
diff --git a/include/client/registry.h b/include/client/registry.h
deleted file mode 100644
index 9dfbd835..00000000
--- a/include/client/registry.h
+++ /dev/null
@@ -1,75 +0,0 @@
1#ifndef _SWAY_CLIENT_REGISTRY_H
2#define _SWAY_CLIENT_REGISTRY_H
3
4#include <wayland-client.h>
5#include <xkbcommon/xkbcommon.h>
6#include "wayland-desktop-shell-client-protocol.h"
7#include "wayland-swaylock-client-protocol.h"
8#include "list.h"
9
10enum mod_bit {
11 MOD_SHIFT = 1<<0,
12 MOD_CAPS = 1<<1,
13 MOD_CTRL = 1<<2,
14 MOD_ALT = 1<<3,
15 MOD_MOD2 = 1<<4,
16 MOD_MOD3 = 1<<5,
17 MOD_LOGO = 1<<6,
18 MOD_MOD5 = 1<<7,
19};
20
21enum mask {
22 MASK_SHIFT,
23 MASK_CAPS,
24 MASK_CTRL,
25 MASK_ALT,
26 MASK_MOD2,
27 MASK_MOD3,
28 MASK_LOGO,
29 MASK_MOD5,
30 MASK_LAST
31};
32
33struct output_state {
34 struct wl_output *output;
35 uint32_t flags;
36 uint32_t width, height;
37 uint32_t scale;
38};
39
40struct xkb {
41 struct xkb_state *state;
42 struct xkb_context *context;
43 struct xkb_keymap *keymap;
44 xkb_mod_mask_t masks[MASK_LAST];
45};
46
47struct input {
48 struct xkb xkb;
49
50 xkb_keysym_t sym;
51 uint32_t code;
52 uint32_t last_code;
53 uint32_t modifiers;
54
55 void (*notify)(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint);
56};
57
58struct registry {
59 struct wl_compositor *compositor;
60 struct wl_display *display;
61 struct wl_pointer *pointer;
62 struct wl_keyboard *keyboard;
63 struct wl_seat *seat;
64 struct wl_shell *shell;
65 struct wl_shm *shm;
66 struct desktop_shell *desktop_shell;
67 struct lock *swaylock;
68 struct input *input;
69 list_t *outputs;
70};
71
72struct registry *registry_poll(void);
73void registry_teardown(struct registry *registry);
74
75#endif
diff --git a/include/client/window.h b/include/client/window.h
deleted file mode 100644
index 8af8225c..00000000
--- a/include/client/window.h
+++ /dev/null
@@ -1,67 +0,0 @@
1#ifndef _CLIENT_H
2#define _CLIENT_H
3
4#include <wayland-client.h>
5#include "wayland-desktop-shell-client-protocol.h"
6#include <cairo/cairo.h>
7#include <pango/pangocairo.h>
8#include <stdbool.h>
9#include "list.h"
10#include "client/registry.h"
11
12struct window;
13
14struct buffer {
15 struct wl_buffer *buffer;
16 cairo_surface_t *surface;
17 cairo_t *cairo;
18 PangoContext *pango;
19 uint32_t width, height;
20 bool busy;
21};
22
23struct cursor {
24 struct wl_surface *surface;
25 struct wl_cursor_theme *cursor_theme;
26 struct wl_cursor *cursor;
27 struct wl_pointer *pointer;
28};
29
30enum scroll_direction {
31 SCROLL_UP,
32 SCROLL_DOWN,
33 SCROLL_LEFT,
34 SCROLL_RIGHT,
35};
36
37struct pointer_input {
38 int last_x;
39 int last_y;
40
41 void (*notify_button)(struct window *window, int x, int y, uint32_t button, uint32_t state_w);
42 void (*notify_scroll)(struct window *window, enum scroll_direction direction);
43};
44
45struct window {
46 struct registry *registry;
47 struct buffer buffers[2];
48 struct buffer *buffer;
49 struct wl_surface *surface;
50 struct wl_shell_surface *shell_surface;
51 struct wl_callback *frame_cb;
52 struct cursor cursor;
53 uint32_t width, height;
54 int32_t scale;
55 char *font;
56 cairo_t *cairo;
57 struct pointer_input pointer_input;
58};
59
60struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
61 int32_t scale, bool shell_surface);
62void window_teardown(struct window *state);
63int window_prerender(struct window *state);
64int window_render(struct window *state);
65void window_make_shell(struct window *window);
66
67#endif
diff --git a/include/ipc.h b/include/ipc.h
index 2b16dc50..9665a88d 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -23,7 +23,6 @@ enum ipc_command_type {
23 IPC_EVENT_BINDING = ((1<<31) | 5), 23 IPC_EVENT_BINDING = ((1<<31) | 5),
24 IPC_EVENT_MODIFIER = ((1<<31) | 6), 24 IPC_EVENT_MODIFIER = ((1<<31) | 6),
25 IPC_EVENT_INPUT = ((1<<31) | 7), 25 IPC_EVENT_INPUT = ((1<<31) | 7),
26 IPC_SWAY_GET_PIXELS = 0x81
27}; 26};
28 27
29#endif 28#endif
diff --git a/include/log.h b/include/log.h
index a1e33fa2..a9748127 100644
--- a/include/log.h
+++ b/include/log.h
@@ -1,38 +1,15 @@
1#ifndef _SWAY_LOG_H 1#ifndef _SWAY_LOG_H
2#define _SWAY_LOG_H 2#define _SWAY_LOG_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wlr/util/log.h>
4 5
5typedef enum { 6void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);
6 L_SILENT = 0,
7 L_ERROR = 1,
8 L_INFO = 2,
9 L_DEBUG = 3,
10} log_importance_t;
11
12void init_log(log_importance_t verbosity);
13void set_log_level(log_importance_t verbosity);
14log_importance_t get_log_level(void);
15void reset_log_level(void);
16// returns whether debug logging is on after switching.
17bool toggle_debug_logging(void);
18void sway_log_colors(int mode);
19void sway_log_errno(log_importance_t verbosity, char* format, ...) __attribute__((format(printf,2,3)));
20
21void _sway_abort(const char *filename, int line, const char* format, ...) __attribute__((format(printf,3,4)));
22#define sway_abort(FMT, ...) \ 7#define sway_abort(FMT, ...) \
23 _sway_abort(__FILE__, __LINE__, FMT, ##__VA_ARGS__) 8 _sway_abort("[%s:%d] " FMT, wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__)
24 9
25bool _sway_assert(bool condition, const char *filename, int line, const char* format, ...) __attribute__((format(printf,4,5))); 10bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);
26#define sway_assert(COND, FMT, ...) \ 11#define sway_assert(COND, FMT, ...) \
27 _sway_assert(COND, __FILE__, __LINE__, "%s:" FMT, __PRETTY_FUNCTION__, ##__VA_ARGS__) 12 _sway_assert(COND, "[%s:%d] %s:" FMT, wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
28
29void _sway_log(const char *filename, int line, log_importance_t verbosity, const char* format, ...) __attribute__((format(printf,4,5)));
30
31#define sway_log(VERBOSITY, FMT, ...) \
32 _sway_log(__FILE__, __LINE__, VERBOSITY, FMT, ##__VA_ARGS__)
33
34#define sway_vlog(VERBOSITY, FMT, VA_ARGS) \
35 _sway_vlog(__FILE__, __LINE__, VERBOSITY, FMT, VA_ARGS)
36 13
37void error_handler(int sig); 14void error_handler(int sig);
38 15
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 00000000..65ed027a
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1 @@
configure_file(output: 'config.h', configuration: conf_data)
diff --git a/include/pango.h b/include/pango.h
new file mode 100644
index 00000000..f6325f28
--- /dev/null
+++ b/include/pango.h
@@ -0,0 +1,16 @@
1#ifndef _SWAY_PANGO_H
2#define _SWAY_PANGO_H
3#include <stdarg.h>
4#include <stdbool.h>
5#include <stdint.h>
6#include <cairo/cairo.h>
7#include <pango/pangocairo.h>
8
9PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
10 const char *text, int32_t scale, bool markup);
11void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
12 int32_t scale, bool markup, const char *fmt, ...);
13void pango_printf(cairo_t *cairo, const char *font,
14 int32_t scale, bool markup, const char *fmt, ...);
15
16#endif
diff --git a/include/pool-buffer.h b/include/pool-buffer.h
new file mode 100644
index 00000000..856f7c8c
--- /dev/null
+++ b/include/pool-buffer.h
@@ -0,0 +1,22 @@
1#ifndef _SWAY_BUFFERS_H
2#define _SWAY_BUFFERS_H
3#include <cairo/cairo.h>
4#include <pango/pangocairo.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <wayland-client.h>
8
9struct pool_buffer {
10 struct wl_buffer *buffer;
11 cairo_surface_t *surface;
12 cairo_t *cairo;
13 PangoContext *pango;
14 uint32_t width, height;
15 bool busy;
16};
17
18struct pool_buffer *get_next_buffer(struct wl_shm *shm,
19 struct pool_buffer pool[static 2], uint32_t width, uint32_t height);
20void destroy_buffer(struct pool_buffer *buffer);
21
22#endif
diff --git a/include/sway.h b/include/sway.h
deleted file mode 100644
index b5cfb668..00000000
--- a/include/sway.h
+++ /dev/null
@@ -1,6 +0,0 @@
1#ifndef _SWAY_SWAY_H
2#define _SWAY_SWAY_H
3
4void sway_terminate(int exit_code);
5
6#endif
diff --git a/include/sway/border.h b/include/sway/border.h
deleted file mode 100644
index c30c9da3..00000000
--- a/include/sway/border.h
+++ /dev/null
@@ -1,28 +0,0 @@
1#ifndef _SWAY_BORDER_H
2#define _SWAY_BORDER_H
3#include <wlc/wlc.h>
4#include "container.h"
5
6/**
7 * Border pixel buffer and corresponding geometry.
8 */
9struct border {
10 unsigned char *buffer;
11 struct wlc_geometry geometry;
12};
13
14/**
15 * Clear border buffer.
16 */
17void border_clear(struct border *border);
18
19/**
20 * Recursively update all of the borders within a container.
21 */
22void update_container_border(swayc_t *container);
23
24void render_view_borders(wlc_handle view);
25int get_font_text_height(const char *font);
26bool should_hide_top_border(swayc_t *con, double y);
27
28#endif
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 660da2c2..dbebaa49 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -1,12 +1,7 @@
1#ifndef _SWAY_COMMANDS_H 1#ifndef _SWAY_COMMANDS_H
2#define _SWAY_COMMANDS_H 2#define _SWAY_COMMANDS_H
3#include <stdbool.h>
4#include <json-c/json.h>
5#include <wlc/wlc.h>
6#include "config.h"
7 3
8// Container that a called command should act upon. Only valid in command functions. 4#include "config.h"
9extern swayc_t *current_container;
10 5
11/** 6/**
12 * Indicates the result of a command's execution. 7 * Indicates the result of a command's execution.
@@ -22,6 +17,7 @@ enum cmd_status {
22 CMD_BLOCK_BAR, 17 CMD_BLOCK_BAR,
23 CMD_BLOCK_BAR_COLORS, 18 CMD_BLOCK_BAR_COLORS,
24 CMD_BLOCK_INPUT, 19 CMD_BLOCK_INPUT,
20 CMD_BLOCK_SEAT,
25 CMD_BLOCK_COMMANDS, 21 CMD_BLOCK_COMMANDS,
26 CMD_BLOCK_IPC, 22 CMD_BLOCK_IPC,
27 CMD_BLOCK_IPC_EVENTS, 23 CMD_BLOCK_IPC_EVENTS,
@@ -46,18 +42,13 @@ enum expected_args {
46 EXPECTED_EQUAL_TO 42 EXPECTED_EQUAL_TO
47}; 43};
48 44
49struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val); 45struct cmd_results *checkarg(int argc, const char *name,
50struct cmd_results *add_color(const char*, char*, const char*); 46 enum expected_args type, int val);
51void input_cmd_apply(struct input_config *input);
52void hide_view_in_scratchpad(swayc_t *sp_view);
53
54swayc_t *sp_view;
55int sp_index;
56 47
57/** 48/**
58 * Parse and handles a command. 49 * Parse and executes a command.
59 */ 50 */
60struct cmd_results *handle_command(char *command, enum command_context context); 51struct cmd_results *execute_command(char *command, struct sway_seat *seat);
61/** 52/**
62 * Parse and handles a command during config file loading. 53 * Parse and handles a command during config file loading.
63 * 54 *
@@ -68,7 +59,6 @@ struct cmd_results *config_command(char *command, enum cmd_status block);
68 * Parses a command policy rule. 59 * Parses a command policy rule.
69 */ 60 */
70struct cmd_results *config_commands_command(char *exec); 61struct cmd_results *config_commands_command(char *exec);
71
72/** 62/**
73 * Allocates a cmd_results object. 63 * Allocates a cmd_results object.
74 */ 64 */
@@ -84,11 +74,8 @@ void free_cmd_results(struct cmd_results *results);
84 */ 74 */
85const char *cmd_results_to_json(struct cmd_results *results); 75const char *cmd_results_to_json(struct cmd_results *results);
86 76
87void remove_view_from_scratchpad(swayc_t *); 77struct cmd_results *add_color(const char *name,
88 78 char *buffer, const char *color);
89/**
90 * Actual command function signatures for individual .c files in commands/ directory.
91 */
92 79
93typedef struct cmd_results *sway_cmd(int argc, char **argv); 80typedef struct cmd_results *sway_cmd(int argc, char **argv);
94 81
@@ -108,6 +95,7 @@ sway_cmd cmd_commands;
108sway_cmd cmd_debuglog; 95sway_cmd cmd_debuglog;
109sway_cmd cmd_default_border; 96sway_cmd cmd_default_border;
110sway_cmd cmd_default_floating_border; 97sway_cmd cmd_default_floating_border;
98sway_cmd cmd_default_orientation;
111sway_cmd cmd_exec; 99sway_cmd cmd_exec;
112sway_cmd cmd_exec_always; 100sway_cmd cmd_exec_always;
113sway_cmd cmd_exit; 101sway_cmd cmd_exit;
@@ -126,6 +114,7 @@ sway_cmd cmd_gaps;
126sway_cmd cmd_hide_edge_borders; 114sway_cmd cmd_hide_edge_borders;
127sway_cmd cmd_include; 115sway_cmd cmd_include;
128sway_cmd cmd_input; 116sway_cmd cmd_input;
117sway_cmd cmd_seat;
129sway_cmd cmd_ipc; 118sway_cmd cmd_ipc;
130sway_cmd cmd_kill; 119sway_cmd cmd_kill;
131sway_cmd cmd_layout; 120sway_cmd cmd_layout;
@@ -134,10 +123,10 @@ sway_cmd cmd_mark;
134sway_cmd cmd_mode; 123sway_cmd cmd_mode;
135sway_cmd cmd_mouse_warping; 124sway_cmd cmd_mouse_warping;
136sway_cmd cmd_move; 125sway_cmd cmd_move;
126sway_cmd cmd_opacity;
137sway_cmd cmd_new_float; 127sway_cmd cmd_new_float;
138sway_cmd cmd_new_window; 128sway_cmd cmd_new_window;
139sway_cmd cmd_no_focus; 129sway_cmd cmd_no_focus;
140sway_cmd cmd_orientation;
141sway_cmd cmd_output; 130sway_cmd cmd_output;
142sway_cmd cmd_permit; 131sway_cmd cmd_permit;
143sway_cmd cmd_reject; 132sway_cmd cmd_reject;
@@ -153,6 +142,7 @@ sway_cmd cmd_splith;
153sway_cmd cmd_splitt; 142sway_cmd cmd_splitt;
154sway_cmd cmd_splitv; 143sway_cmd cmd_splitv;
155sway_cmd cmd_sticky; 144sway_cmd cmd_sticky;
145sway_cmd cmd_swaybg_command;
156sway_cmd cmd_unmark; 146sway_cmd cmd_unmark;
157sway_cmd cmd_workspace; 147sway_cmd cmd_workspace;
158sway_cmd cmd_ws_auto_back_and_forth; 148sway_cmd cmd_ws_auto_back_and_forth;
@@ -195,17 +185,28 @@ sway_cmd bar_colors_cmd_statusline;
195sway_cmd bar_colors_cmd_focused_statusline; 185sway_cmd bar_colors_cmd_focused_statusline;
196sway_cmd bar_colors_cmd_urgent_workspace; 186sway_cmd bar_colors_cmd_urgent_workspace;
197 187
188sway_cmd input_cmd_seat;
198sway_cmd input_cmd_accel_profile; 189sway_cmd input_cmd_accel_profile;
199sway_cmd input_cmd_click_method; 190sway_cmd input_cmd_click_method;
200sway_cmd input_cmd_drag_lock; 191sway_cmd input_cmd_drag_lock;
201sway_cmd input_cmd_dwt; 192sway_cmd input_cmd_dwt;
202sway_cmd input_cmd_events; 193sway_cmd input_cmd_events;
203sway_cmd input_cmd_left_handed; 194sway_cmd input_cmd_left_handed;
195sway_cmd input_cmd_map_to_output;
204sway_cmd input_cmd_middle_emulation; 196sway_cmd input_cmd_middle_emulation;
205sway_cmd input_cmd_natural_scroll; 197sway_cmd input_cmd_natural_scroll;
206sway_cmd input_cmd_pointer_accel; 198sway_cmd input_cmd_pointer_accel;
207sway_cmd input_cmd_scroll_method; 199sway_cmd input_cmd_scroll_method;
208sway_cmd input_cmd_tap; 200sway_cmd input_cmd_tap;
201sway_cmd input_cmd_xkb_layout;
202sway_cmd input_cmd_xkb_model;
203sway_cmd input_cmd_xkb_options;
204sway_cmd input_cmd_xkb_rules;
205sway_cmd input_cmd_xkb_variant;
206
207sway_cmd seat_cmd_attach;
208sway_cmd seat_cmd_fallback;
209sway_cmd seat_cmd_cursor;
209 210
210sway_cmd cmd_ipc_cmd; 211sway_cmd cmd_ipc_cmd;
211sway_cmd cmd_ipc_events; 212sway_cmd cmd_ipc_events;
diff --git a/include/sway/config.h b/include/sway/config.h
index a05d5ede..ed49fbbd 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -1,18 +1,18 @@
1#ifndef _SWAY_CONFIG_H 1#ifndef _SWAY_CONFIG_H
2#define _SWAY_CONFIG_H 2#define _SWAY_CONFIG_H
3
4#define PID_WORKSPACE_TIMEOUT 60 3#define PID_WORKSPACE_TIMEOUT 60
5
6#include <libinput.h> 4#include <libinput.h>
7#include <stdint.h> 5#include <stdint.h>
8#include <wlc/geometry.h> 6#include <string.h>
9#include <wlc/wlc.h>
10#include <xkbcommon/xkbcommon.h>
11#include <time.h> 7#include <time.h>
12#include "wayland-desktop-shell-server-protocol.h" 8#include <wlr/types/wlr_box.h>
9#include <xkbcommon/xkbcommon.h>
13#include "list.h" 10#include "list.h"
14#include "layout.h" 11#include "tree/layout.h"
15#include "container.h" 12#include "tree/container.h"
13#include "wlr-layer-shell-unstable-v1-protocol.h"
14
15// TODO: Refactor this shit
16 16
17/** 17/**
18 * Describes a variable created via the `set` command. 18 * Describes a variable created via the `set` command.
@@ -47,11 +47,12 @@ struct sway_mouse_binding {
47 */ 47 */
48struct sway_mode { 48struct sway_mode {
49 char *name; 49 char *name;
50 list_t *bindings; 50 list_t *keysym_bindings;
51 list_t *keycode_bindings;
51}; 52};
52 53
53/** 54/**
54 * libinput options for input devices 55 * options for input devices
55 */ 56 */
56struct input_config { 57struct input_config {
57 char *identifier; 58 char *identifier;
@@ -68,8 +69,33 @@ struct input_config {
68 int send_events; 69 int send_events;
69 int tap; 70 int tap;
70 71
72 char *xkb_layout;
73 char *xkb_model;
74 char *xkb_options;
75 char *xkb_rules;
76 char *xkb_variant;
77
78 char *mapped_output;
79
71 bool capturable; 80 bool capturable;
72 struct wlc_geometry region; 81 struct wlr_box region;
82};
83
84/**
85 * Options for misc device configurations that happen in the seat block
86 */
87struct seat_attachment_config {
88 char *identifier;
89 // TODO other things are configured here for some reason
90};
91
92/**
93 * Options for multiseat and other misc device configurations
94 */
95struct seat_config {
96 char *name;
97 int fallback; // -1 means not set
98 list_t *attachments; // list of seat_attachment configs
73}; 99};
74 100
75/** 101/**
@@ -81,8 +107,11 @@ struct output_config {
81 char *name; 107 char *name;
82 int enabled; 108 int enabled;
83 int width, height; 109 int width, height;
110 float refresh_rate;
84 int x, y; 111 int x, y;
85 int scale; 112 float scale;
113 int32_t transform;
114
86 char *background; 115 char *background;
87 char *background_option; 116 char *background_option;
88}; 117};
@@ -126,24 +155,13 @@ struct bar_config {
126 char *id; 155 char *id;
127 uint32_t modifier; 156 uint32_t modifier;
128 list_t *outputs; 157 list_t *outputs;
129 enum desktop_shell_panel_position position; 158 char *position;
130 list_t *bindings; 159 list_t *bindings;
131 char *status_command; 160 char *status_command;
132 bool pango_markup; 161 bool pango_markup;
133 char *swaybar_command; 162 char *swaybar_command;
134 char *font; 163 char *font;
135 int height; // -1 not defined 164 int height; // -1 not defined
136
137#ifdef ENABLE_TRAY
138 // Tray
139 char *tray_output;
140 char *icon_theme;
141 uint32_t tray_padding;
142 uint32_t activate_button;
143 uint32_t context_button;
144 uint32_t secondary_button;
145#endif
146
147 bool workspace_buttons; 165 bool workspace_buttons;
148 bool wrap_scroll; 166 bool wrap_scroll;
149 char *separator_symbol; 167 char *separator_symbol;
@@ -260,11 +278,13 @@ struct sway_config {
260 list_t *pid_workspaces; 278 list_t *pid_workspaces;
261 list_t *output_configs; 279 list_t *output_configs;
262 list_t *input_configs; 280 list_t *input_configs;
281 list_t *seat_configs;
263 list_t *criteria; 282 list_t *criteria;
264 list_t *no_focus; 283 list_t *no_focus;
265 list_t *active_bar_modifiers; 284 list_t *active_bar_modifiers;
266 struct sway_mode *current_mode; 285 struct sway_mode *current_mode;
267 struct bar_config *current_bar; 286 struct bar_config *current_bar;
287 char *swaybg_command;
268 uint32_t floating_mod; 288 uint32_t floating_mod;
269 uint32_t dragging_key; 289 uint32_t dragging_key;
270 uint32_t resizing_key; 290 uint32_t resizing_key;
@@ -272,8 +292,8 @@ struct sway_config {
272 char *floating_scroll_down_cmd; 292 char *floating_scroll_down_cmd;
273 char *floating_scroll_left_cmd; 293 char *floating_scroll_left_cmd;
274 char *floating_scroll_right_cmd; 294 char *floating_scroll_right_cmd;
275 enum swayc_layouts default_orientation; 295 enum sway_container_layout default_orientation;
276 enum swayc_layouts default_layout; 296 enum sway_container_layout default_layout;
277 char *font; 297 char *font;
278 int font_height; 298 int font_height;
279 299
@@ -286,7 +306,6 @@ struct sway_config {
286 bool reloading; 306 bool reloading;
287 bool reading; 307 bool reading;
288 bool auto_back_and_forth; 308 bool auto_back_and_forth;
289 bool seamless_mouse;
290 bool show_marks; 309 bool show_marks;
291 310
292 bool edge_gaps; 311 bool edge_gaps;
@@ -297,8 +316,8 @@ struct sway_config {
297 list_t *config_chain; 316 list_t *config_chain;
298 const char *current_config; 317 const char *current_config;
299 318
300 enum swayc_border_types border; 319 enum sway_container_border border;
301 enum swayc_border_types floating_border; 320 enum sway_container_border floating_border;
302 int border_thickness; 321 int border_thickness;
303 int floating_border_thickness; 322 int floating_border_thickness;
304 enum edge_border_types hide_edge_borders; 323 enum edge_border_types hide_edge_borders;
@@ -323,6 +342,14 @@ struct sway_config {
323 list_t *command_policies; 342 list_t *command_policies;
324 list_t *feature_policies; 343 list_t *feature_policies;
325 list_t *ipc_policies; 344 list_t *ipc_policies;
345
346 // Context for command handlers
347 struct {
348 struct input_config *input_config;
349 struct seat_config *seat_config;
350 struct sway_seat *seat;
351 struct sway_container *current_container;
352 } handler_context;
326}; 353};
327 354
328void pid_workspace_add(struct pid_workspace *pw); 355void pid_workspace_add(struct pid_workspace *pw);
@@ -348,6 +375,11 @@ bool read_config(FILE *file, struct sway_config *config);
348 * Free config struct 375 * Free config struct
349 */ 376 */
350void free_config(struct sway_config *config); 377void free_config(struct sway_config *config);
378
379void config_clear_handler_context(struct sway_config *config);
380
381void free_sway_variable(struct sway_variable *var);
382
351/** 383/**
352 * Does variable replacement for a string based on the config's currently loaded variables. 384 * Does variable replacement for a string based on the config's currently loaded variables.
353 */ 385 */
@@ -356,51 +388,74 @@ char *do_var_replacement(char *str);
356struct cmd_results *check_security_config(); 388struct cmd_results *check_security_config();
357 389
358int input_identifier_cmp(const void *item, const void *data); 390int input_identifier_cmp(const void *item, const void *data);
391
392struct input_config *new_input_config(const char* identifier);
393
359void merge_input_config(struct input_config *dst, struct input_config *src); 394void merge_input_config(struct input_config *dst, struct input_config *src);
360void apply_input_config(struct input_config *ic, struct libinput_device *dev); 395
396struct input_config *copy_input_config(struct input_config *ic);
397
361void free_input_config(struct input_config *ic); 398void free_input_config(struct input_config *ic);
362 399
400void apply_input_config(struct input_config *input);
401
402int seat_name_cmp(const void *item, const void *data);
403
404struct seat_config *new_seat_config(const char* name);
405
406void merge_seat_config(struct seat_config *dst, struct seat_config *src);
407
408struct seat_config *copy_seat_config(struct seat_config *seat);
409
410void free_seat_config(struct seat_config *ic);
411
412struct seat_attachment_config *seat_attachment_config_new();
413
414struct seat_attachment_config *seat_config_get_attachment(
415 struct seat_config *seat_config, char *identifier);
416
417void apply_seat_config(struct seat_config *seat);
418
363int output_name_cmp(const void *item, const void *data); 419int output_name_cmp(const void *item, const void *data);
420
421void output_get_identifier(char *identifier, size_t len,
422 struct sway_output *output);
423
424struct output_config *new_output_config(const char *name);
425
364void merge_output_config(struct output_config *dst, struct output_config *src); 426void merge_output_config(struct output_config *dst, struct output_config *src);
365/** Sets up a WLC output handle based on a given output_config.
366 */
367void apply_output_config(struct output_config *oc, swayc_t *output);
368void free_output_config(struct output_config *oc);
369 427
370/** 428void apply_output_config(struct output_config *oc,
371 * Updates the list of active bar modifiers 429 struct sway_container *output);
372 */ 430
373void update_active_bar_modifiers(void); 431void free_output_config(struct output_config *oc);
374 432
375int workspace_output_cmp_workspace(const void *a, const void *b); 433int workspace_output_cmp_workspace(const void *a, const void *b);
376 434
377int sway_binding_cmp(const void *a, const void *b); 435int sway_binding_cmp(const void *a, const void *b);
436
378int sway_binding_cmp_qsort(const void *a, const void *b); 437int sway_binding_cmp_qsort(const void *a, const void *b);
438
379int sway_binding_cmp_keys(const void *a, const void *b); 439int sway_binding_cmp_keys(const void *a, const void *b);
440
380void free_sway_binding(struct sway_binding *sb); 441void free_sway_binding(struct sway_binding *sb);
381struct sway_binding *sway_binding_dup(struct sway_binding *sb);
382 442
383int sway_mouse_binding_cmp(const void *a, const void *b); 443struct sway_binding *sway_binding_dup(struct sway_binding *sb);
384int sway_mouse_binding_cmp_qsort(const void *a, const void *b);
385int sway_mouse_binding_cmp_buttons(const void *a, const void *b);
386void free_sway_mouse_binding(struct sway_mouse_binding *smb);
387 444
388void load_swaybars(); 445void load_swaybars();
446
447void invoke_swaybar(struct bar_config *bar);
448
389void terminate_swaybg(pid_t pid); 449void terminate_swaybg(pid_t pid);
390 450
391/**
392 * Allocate and initialize default bar configuration.
393 */
394struct bar_config *default_bar_config(void); 451struct bar_config *default_bar_config(void);
395 452
396/** 453void free_bar_config(struct bar_config *bar);
397 * Global config singleton. 454
398 */ 455/* Global config singleton. */
399extern struct sway_config *config; 456extern struct sway_config *config;
400 457
401/** 458/* Config file currently being read */
402 * Config file currently being read.
403 */
404extern const char *current_config_path; 459extern const char *current_config_path;
405 460
406#endif 461#endif
diff --git a/include/sway/container.h b/include/sway/container.h
deleted file mode 100644
index 37192ce3..00000000
--- a/include/sway/container.h
+++ /dev/null
@@ -1,357 +0,0 @@
1#ifndef _SWAY_CONTAINER_H
2#define _SWAY_CONTAINER_H
3#include <sys/types.h>
4#include <wlc/wlc.h>
5#include <stdint.h>
6
7#include "list.h"
8
9typedef struct sway_container swayc_t;
10
11extern swayc_t root_container;
12extern swayc_t *current_focus;
13
14/**
15 * Different kinds of containers.
16 *
17 * This enum is in order. A container will never be inside of a container below
18 * it on this list.
19 */
20enum swayc_types {
21 C_ROOT, /**< The root container. Only one of these ever exists. */
22 C_OUTPUT, /**< An output (aka monitor, head, etc). */
23 C_WORKSPACE, /**< A workspace. */
24 C_CONTAINER, /**< A manually created container. */
25 C_VIEW, /**< A view (aka window). */
26 // Keep last
27 C_TYPES,
28};
29
30/**
31 * Different ways to arrange a container.
32 */
33enum swayc_layouts {
34 L_NONE, /**< Used for containers that have no layout (views, root) */
35 L_HORIZ,
36 L_VERT,
37 L_STACKED,
38 L_TABBED,
39 L_FLOATING, /**< A psuedo-container, removed from the tree, to hold floating windows */
40
41 /* Awesome/Monad style auto layouts */
42 L_AUTO_LEFT,
43 L_AUTO_RIGHT,
44 L_AUTO_TOP,
45 L_AUTO_BOTTOM,
46
47 L_AUTO_FIRST = L_AUTO_LEFT,
48 L_AUTO_LAST = L_AUTO_BOTTOM,
49
50 // Keep last
51 L_LAYOUTS,
52};
53
54enum swayc_border_types {
55 B_NONE, /**< No border */
56 B_PIXEL, /**< 1px border */
57 B_NORMAL /**< Normal border with title bar */
58};
59
60/**
61 * Stores information about a container.
62 *
63 * The tree is made of these. Views are containers that cannot have children.
64 */
65struct sway_container {
66 /**
67 * If this container maps to a WLC object, this is set to that object's
68 * handle. Otherwise, NULL.
69 */
70 wlc_handle handle;
71
72 /**
73 * A unique ID to identify this container. Primarily used in the
74 * get_tree JSON output.
75 */
76 size_t id;
77
78 enum swayc_types type;
79 enum swayc_layouts layout;
80 enum swayc_layouts prev_layout;
81 enum swayc_layouts workspace_layout;
82
83 /**
84 * Width and height of this container, without borders or gaps.
85 */
86 double width, height;
87
88 /**
89 * Views may request geometry, which is stored in this and ignored until
90 * the views are floated.
91 */
92 int desired_width, desired_height;
93
94 /**
95 * The coordinates that this view appear at, relative to the output they
96 * are located on (output containers have absolute coordinates).
97 */
98 double x, y;
99
100 /**
101 * Cached geometry used to store view/container geometry when switching
102 * between tabbed/stacked and horizontal/vertical layouts.
103 */
104 struct wlc_geometry cached_geometry;
105
106 /**
107 * False if this view is invisible. It could be in the scratchpad or on a
108 * workspace that is not shown.
109 */
110 bool visible;
111 bool is_floating;
112 bool is_focused;
113 bool sticky; // floating view always visible on its output
114
115 // Attributes that mostly views have.
116 char *name;
117 char *class;
118 char *instance;
119 char *app_id;
120
121 // Used by output containers to keep track of swaybg child processes.
122 pid_t bg_pid;
123
124 int gaps;
125
126 list_t *children;
127 /**
128 * Children of this container that are floated.
129 */
130 list_t *floating;
131 /**
132 * Unmanaged view handles in this container.
133 */
134 list_t *unmanaged;
135
136 /**
137 * The parent of this container. NULL for the root container.
138 */
139 struct sway_container *parent;
140 /**
141 * Which of this container's children has focus.
142 */
143 struct sway_container *focused;
144 /**
145 * If this container's children include a fullscreen view, this is that view.
146 */
147 struct sway_container *fullscreen;
148 /**
149 * If this container is a view, this may be set to the window's decoration
150 * buffer (or NULL).
151 */
152 struct border *border;
153 enum swayc_border_types border_type;
154 struct wlc_geometry border_geometry;
155 struct wlc_geometry title_bar_geometry;
156 struct wlc_geometry actual_geometry;
157 int border_thickness;
158
159 /**
160 * Number of master views in auto layouts.
161 */
162 size_t nb_master;
163
164 /**
165 * Number of slave groups (e.g. columns) in auto layouts.
166 */
167 size_t nb_slave_groups;
168
169 /**
170 * Marks applied to the container, list_t of char*.
171 */
172 list_t *marks;
173};
174
175enum visibility_mask {
176 VISIBLE = true
177} visible;
178
179/**
180 * Allocates a new output container.
181 */
182swayc_t *new_output(wlc_handle handle);
183/**
184 * Allocates a new workspace container.
185 */
186swayc_t *new_workspace(swayc_t *output, const char *name);
187/**
188 * Allocates a new container and places a child into it.
189 *
190 * This is used from the split command, which creates a new container with the
191 * requested layout and replaces the focused container in the tree with the new
192 * one. Then the removed container is added as a child of the new container.
193 */
194swayc_t *new_container(swayc_t *child, enum swayc_layouts layout);
195/**
196 * Allocates a new view container.
197 *
198 * Pass in a sibling view, or a workspace to become this container's parent.
199 */
200swayc_t *new_view(swayc_t *sibling, wlc_handle handle);
201/**
202 * Allocates a new floating view in the active workspace.
203 */
204swayc_t *new_floating_view(wlc_handle handle);
205
206void floating_view_sane_size(swayc_t *view);
207
208/**
209 * Frees an output's container.
210 */
211swayc_t *destroy_output(swayc_t *output);
212/**
213 * Destroys a workspace container and returns the parent pointer, or NULL.
214 */
215swayc_t *destroy_workspace(swayc_t *workspace);
216/**
217 * Destroys a container and all empty parents. Returns the topmost non-empty
218 * parent container, or NULL.
219 */
220swayc_t *destroy_container(swayc_t *container);
221/**
222 * Destroys a view container and all empty parents. Returns the topmost
223 * non-empty parent container, or NULL.
224 */
225swayc_t *destroy_view(swayc_t *view);
226
227/**
228 * Finds a container based on test criteria. Returns the first container that
229 * passes the test.
230 */
231swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data);
232/**
233 * Finds a parent container with the given swayc_type.
234 */
235swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types);
236/**
237 * Finds a parent with the given swayc_layout.
238 */
239swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts);
240/**
241 * Finds the bottom-most focused container of a type.
242 */
243swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types);
244/**
245 * Finds the bottom-most focused container of a layout.
246 */
247swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts);
248
249/**
250 * Gets the swayc_t associated with a wlc_handle.
251 */
252swayc_t *swayc_by_handle(wlc_handle handle);
253/**
254 * Gets the named swayc_t.
255 */
256swayc_t *swayc_by_name(const char *name);
257/**
258 * Gets the active output's container.
259 */
260swayc_t *swayc_active_output(void);
261/**
262 * Gets the active workspace's container.
263 */
264swayc_t *swayc_active_workspace(void);
265/**
266 * Gets the workspace for the given view container.
267 */
268swayc_t *swayc_active_workspace_for(swayc_t *view);
269/**
270 * Finds the container currently underneath the pointer.
271 */
272swayc_t *container_under_pointer(void);
273/**
274 * Finds the first container following a callback.
275 */
276swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data);
277
278/**
279 * Returns true if a container is fullscreen.
280 */
281bool swayc_is_fullscreen(swayc_t *view);
282/**
283 * Returns true if this view is focused.
284 */
285bool swayc_is_active(swayc_t *view);
286/**
287 * Returns true if the parent is an ancestor of the child.
288 */
289bool swayc_is_parent_of(swayc_t *parent, swayc_t *child);
290/**
291 * Returns true if the child is a desecendant of the parent.
292 */
293bool swayc_is_child_of(swayc_t *child, swayc_t *parent);
294
295/**
296 * Returns true if this container is an empty workspace.
297 */
298bool swayc_is_empty_workspace(swayc_t *container);
299
300/**
301 * Returns the top most tabbed or stacked parent container. Returns NULL if
302 * view is not in a tabbed/stacked layout.
303 */
304swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view);
305
306/**
307 * Returns the immediate tabbed or stacked parent container. Returns NULL if
308 * view is not directly in a tabbed/stacked layout.
309 */
310swayc_t *swayc_tabbed_stacked_parent(swayc_t *view);
311
312/**
313 * Returns the gap (padding) of the container.
314 *
315 * This returns the inner gaps for a view, the outer gaps for a workspace, and
316 * 0 otherwise.
317 */
318int swayc_gap(swayc_t *container);
319
320/**
321 * Maps a container's children over a function.
322 */
323void container_map(swayc_t *, void (*f)(swayc_t *, void *), void *);
324
325/**
326 * Set a view as visible or invisible.
327 *
328 * This will perform the required wlc calls as well; it is not sufficient to
329 * simply toggle the boolean in swayc_t.
330 */
331void set_view_visibility(swayc_t *view, void *data);
332/**
333 * Set the gaps value for a view.
334 */
335void set_gaps(swayc_t *view, void *amount);
336/**
337 * Add to the gaps value for a view.
338 */
339void add_gaps(swayc_t *view, void *amount);
340
341/**
342 * Issue wlc calls to make the visibility of a container consistent.
343 */
344void update_visibility(swayc_t *container);
345
346/**
347 * Close all child views of container
348 */
349void close_views(swayc_t *container);
350
351/**
352 * Assign layout to a container. Needed due to workspace container specifics.
353 * Workspace should always have either L_VERT or L_HORIZ layout.
354 */
355swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout);
356
357#endif
diff --git a/include/sway/criteria.h b/include/sway/criteria.h
index c5ed9857..ec256ddb 100644
--- a/include/sway/criteria.h
+++ b/include/sway/criteria.h
@@ -1,7 +1,7 @@
1#ifndef _SWAY_CRITERIA_H 1#ifndef _SWAY_CRITERIA_H
2#define _SWAY_CRITERIA_H 2#define _SWAY_CRITERIA_H
3 3
4#include "container.h" 4#include "tree/container.h"
5#include "list.h" 5#include "list.h"
6 6
7/** 7/**
@@ -31,12 +31,12 @@ char *extract_crit_tokens(list_t *tokens, const char *criteria);
31 31
32// Returns list of criteria that match given container. These criteria have 32// Returns list of criteria that match given container. These criteria have
33// been set with `for_window` commands and have an associated cmdlist. 33// been set with `for_window` commands and have an associated cmdlist.
34list_t *criteria_for(swayc_t *cont); 34list_t *criteria_for(struct sway_container *cont);
35 35
36// Returns a list of all containers that match the given list of tokens. 36// Returns a list of all containers that match the given list of tokens.
37list_t *container_for(list_t *tokens); 37list_t *container_for_crit_tokens(list_t *tokens);
38 38
39// Returns true if any criteria in the given list matches this container 39// Returns true if any criteria in the given list matches this container
40bool criteria_any(swayc_t *cont, list_t *criteria); 40bool criteria_any(struct sway_container *cont, list_t *criteria);
41 41
42#endif 42#endif
diff --git a/include/sway/debug.h b/include/sway/debug.h
new file mode 100644
index 00000000..2430d319
--- /dev/null
+++ b/include/sway/debug.h
@@ -0,0 +1,7 @@
1#ifndef SWAY_DEBUG_H
2#define SWAY_DEBUG_H
3
4extern bool enable_debug_tree;
5void update_debug_tree();
6
7#endif
diff --git a/include/sway/desktop.h b/include/sway/desktop.h
new file mode 100644
index 00000000..f1ad759a
--- /dev/null
+++ b/include/sway/desktop.h
@@ -0,0 +1,4 @@
1#include <wlr/types/wlr_surface.h>
2
3void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
4 bool whole);
diff --git a/include/sway/extensions.h b/include/sway/extensions.h
deleted file mode 100644
index 5212eb3a..00000000
--- a/include/sway/extensions.h
+++ /dev/null
@@ -1,56 +0,0 @@
1#ifndef _SWAY_EXTENSIONS_H
2#define _SWAY_EXTENSIONS_H
3
4#include <wayland-server.h>
5#include <wlc/wlc-wayland.h>
6#include "wayland-desktop-shell-server-protocol.h"
7#include "list.h"
8
9struct background_config {
10 wlc_handle output;
11 wlc_resource surface;
12 // we need the wl_resource of the surface in the destructor
13 struct wl_resource *wl_surface_res;
14 struct wl_client *client;
15 wlc_handle handle;
16};
17
18struct panel_config {
19 // wayland resource used in callbacks, is used to track this panel
20 struct wl_resource *wl_resource;
21 wlc_handle output;
22 wlc_resource surface;
23 // we need the wl_resource of the surface in the destructor
24 struct wl_resource *wl_surface_res;
25 enum desktop_shell_panel_position panel_position;
26 // used to determine if client is a panel
27 struct wl_client *client;
28 // wlc handle for this panel's surface, not set until panel is created
29 wlc_handle handle;
30};
31
32struct desktop_shell_state {
33 list_t *backgrounds;
34 list_t *panels;
35 list_t *lock_surfaces;
36 bool is_locked;
37};
38
39struct swaylock_state {
40 bool active;
41 wlc_handle output;
42 wlc_resource surface;
43};
44
45struct decoration_state {
46 list_t *csd_resources;
47};
48
49extern struct desktop_shell_state desktop_shell;
50extern struct decoration_state decoration_state;
51
52void register_extensions(void);
53
54void server_decoration_enable_csd(wlc_handle handle);
55
56#endif
diff --git a/include/sway/focus.h b/include/sway/focus.h
deleted file mode 100644
index 652cdccc..00000000
--- a/include/sway/focus.h
+++ /dev/null
@@ -1,45 +0,0 @@
1#ifndef _SWAY_FOCUS_H
2#define _SWAY_FOCUS_H
3enum movement_direction {
4 MOVE_LEFT,
5 MOVE_RIGHT,
6 MOVE_UP,
7 MOVE_DOWN,
8 MOVE_PARENT,
9 MOVE_CHILD,
10 MOVE_NEXT,
11 MOVE_PREV,
12 MOVE_FIRST
13};
14
15#include "container.h"
16
17// focused_container - the container found by following the `focused` pointer
18// from a given container to a container with `is_focused` boolean set
19// ---
20// focused_view - the container found by following the `focused` pointer from a
21// given container to a view.
22// ---
23
24swayc_t *get_focused_container(swayc_t *parent);
25swayc_t *get_focused_view(swayc_t *parent);
26swayc_t *get_focused_float(swayc_t *ws);
27
28// a special-case function to get the focused view, regardless
29// of whether it's tiled or floating
30swayc_t *get_focused_view_include_floating(swayc_t *parent);
31
32bool set_focused_container(swayc_t *container);
33bool set_focused_container_for(swayc_t *ancestor, swayc_t *container);
34
35// lock focused container/view. locked by windows with OVERRIDE attribute
36// and unlocked when they are destroyed
37
38extern bool locked_container_focus;
39
40// Prevents wss from being destroyed on focus switch
41extern bool suspend_workspace_cleanup;
42
43bool move_focus(enum movement_direction direction);
44
45#endif
diff --git a/include/sway/handlers.h b/include/sway/handlers.h
deleted file mode 100644
index 956b98f4..00000000
--- a/include/sway/handlers.h
+++ /dev/null
@@ -1,11 +0,0 @@
1#ifndef _SWAY_HANDLERS_H
2#define _SWAY_HANDLERS_H
3#include "container.h"
4#include <stdbool.h>
5#include <wlc/wlc.h>
6
7void register_wlc_handlers();
8
9extern uint32_t keys_pressed[32];
10
11#endif
diff --git a/include/sway/input.h b/include/sway/input.h
deleted file mode 100644
index 4ed9bffe..00000000
--- a/include/sway/input.h
+++ /dev/null
@@ -1,23 +0,0 @@
1#ifndef _SWAY_INPUT_H
2#define _SWAY_INPUT_H
3
4#include <libinput.h>
5#include "config.h"
6#include "list.h"
7
8struct input_config *new_input_config(const char* identifier);
9
10char* libinput_dev_unique_id(struct libinput_device *dev);
11
12/**
13 * Global input device list.
14 */
15extern list_t *input_devices;
16
17/**
18 * Pointer used when reading input blocked.
19 * Shared so that it can be cleared from commands.c when closing the block
20 */
21extern struct input_config *current_input_config;
22
23#endif
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
new file mode 100644
index 00000000..fcd94437
--- /dev/null
+++ b/include/sway/input/cursor.h
@@ -0,0 +1,36 @@
1#ifndef _SWAY_INPUT_CURSOR_H
2#define _SWAY_INPUT_CURSOR_H
3#include <stdint.h>
4#include "sway/input/seat.h"
5
6struct sway_cursor {
7 struct sway_seat *seat;
8 struct wlr_cursor *cursor;
9 struct wlr_xcursor_manager *xcursor_manager;
10
11 struct wl_client *image_client;
12
13 struct wl_listener motion;
14 struct wl_listener motion_absolute;
15 struct wl_listener button;
16 struct wl_listener axis;
17
18 struct wl_listener touch_down;
19 struct wl_listener touch_up;
20 struct wl_listener touch_motion;
21
22 struct wl_listener tool_axis;
23 struct wl_listener tool_tip;
24 struct wl_listener tool_button;
25 uint32_t tool_buttons;
26
27 struct wl_listener request_set_cursor;
28};
29
30void sway_cursor_destroy(struct sway_cursor *cursor);
31struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
32void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time);
33void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec,
34 uint32_t button, enum wlr_button_state state);
35
36#endif
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
new file mode 100644
index 00000000..89a3ac71
--- /dev/null
+++ b/include/sway/input/input-manager.h
@@ -0,0 +1,64 @@
1#ifndef _SWAY_INPUT_INPUT_MANAGER_H
2#define _SWAY_INPUT_INPUT_MANAGER_H
3#include <libinput.h>
4#include <wlr/types/wlr_input_inhibitor.h>
5#include "sway/server.h"
6#include "sway/config.h"
7#include "list.h"
8
9/**
10 * The global singleton input manager
11 * TODO: make me not a global
12 */
13extern struct sway_input_manager *input_manager;
14
15struct sway_input_device {
16 char *identifier;
17 struct wlr_input_device *wlr_device;
18 struct wl_list link;
19 struct wl_listener device_destroy;
20};
21
22struct sway_input_manager {
23 struct sway_server *server;
24 struct wl_list devices;
25 struct wl_list seats;
26
27 struct wlr_input_inhibit_manager *inhibit;
28
29 struct wl_listener new_input;
30 struct wl_listener inhibit_activate;
31 struct wl_listener inhibit_deactivate;
32};
33
34struct sway_input_manager *input_manager_create(struct sway_server *server);
35
36bool input_manager_has_focus(struct sway_input_manager *input,
37 struct sway_container *container);
38
39void input_manager_set_focus(struct sway_input_manager *input,
40 struct sway_container *container);
41
42void input_manager_configure_xcursor(struct sway_input_manager *input);
43
44void input_manager_apply_input_config(struct sway_input_manager *input,
45 struct input_config *input_config);
46
47void input_manager_apply_seat_config(struct sway_input_manager *input,
48 struct seat_config *seat_config);
49
50struct sway_seat *input_manager_get_default_seat(
51 struct sway_input_manager *input);
52
53struct sway_seat *input_manager_get_seat(struct sway_input_manager *input,
54 const char *seat_name);
55
56/**
57 * Gets the last seat the user interacted with
58 */
59struct sway_seat *input_manager_current_seat(struct sway_input_manager *input);
60
61struct input_config *input_device_get_config(struct sway_input_device *device);
62
63
64#endif
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
new file mode 100644
index 00000000..8ec3eb35
--- /dev/null
+++ b/include/sway/input/keyboard.h
@@ -0,0 +1,30 @@
1#ifndef _SWAY_INPUT_KEYBOARD_H
2#define _SWAY_INPUT_KEYBOARD_H
3
4#include "sway/input/seat.h"
5
6#define SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP 32
7
8struct sway_keyboard {
9 struct sway_seat_device *seat_device;
10
11 struct xkb_keymap *keymap;
12
13 struct wl_listener keyboard_key;
14 struct wl_listener keyboard_modifiers;
15
16 xkb_keysym_t pressed_keysyms_translated[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP];
17 uint32_t modifiers_translated;
18
19 xkb_keysym_t pressed_keysyms_raw[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP];
20 uint32_t modifiers_raw;
21};
22
23struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
24 struct sway_seat_device *device);
25
26void sway_keyboard_configure(struct sway_keyboard *keyboard);
27
28void sway_keyboard_destroy(struct sway_keyboard *keyboard);
29
30#endif
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
new file mode 100644
index 00000000..ff76841e
--- /dev/null
+++ b/include/sway/input/seat.h
@@ -0,0 +1,111 @@
1#ifndef _SWAY_INPUT_SEAT_H
2#define _SWAY_INPUT_SEAT_H
3
4#include <wlr/types/wlr_layer_shell.h>
5#include <wlr/types/wlr_seat.h>
6#include "sway/input/input-manager.h"
7
8struct sway_seat_device {
9 struct sway_seat *sway_seat;
10 struct sway_input_device *input_device;
11 struct sway_keyboard *keyboard;
12 struct wl_list link; // sway_seat::devices
13};
14
15struct sway_seat_container {
16 struct sway_seat *seat;
17 struct sway_container *container;
18
19 struct wl_list link; // sway_seat::focus_stack
20
21 struct wl_listener destroy;
22};
23
24struct sway_seat {
25 struct wlr_seat *wlr_seat;
26 struct sway_cursor *cursor;
27 struct sway_input_manager *input;
28
29 bool has_focus;
30 struct wl_list focus_stack; // list of containers in focus order
31
32 // If the focused layer is set, views cannot receive keyboard focus
33 struct wlr_layer_surface *focused_layer;
34
35 // If exclusive_client is set, no other clients will receive input events
36 struct wl_client *exclusive_client;
37
38 struct wl_listener focus_destroy;
39 struct wl_listener new_container;
40
41 struct wl_list devices; // sway_seat_device::link
42
43 struct wl_list link; // input_manager::seats
44};
45
46struct sway_seat *seat_create(struct sway_input_manager *input,
47 const char *seat_name);
48
49void seat_destroy(struct sway_seat *seat);
50
51void seat_add_device(struct sway_seat *seat,
52 struct sway_input_device *device);
53
54void seat_configure_device(struct sway_seat *seat,
55 struct sway_input_device *device);
56
57void seat_remove_device(struct sway_seat *seat,
58 struct sway_input_device *device);
59
60void seat_configure_xcursor(struct sway_seat *seat);
61
62void seat_set_focus(struct sway_seat *seat, struct sway_container *container);
63
64void seat_set_focus_warp(struct sway_seat *seat,
65 struct sway_container *container, bool warp);
66
67void seat_set_focus_surface(struct sway_seat *seat,
68 struct wlr_surface *surface);
69
70void seat_set_focus_layer(struct sway_seat *seat,
71 struct wlr_layer_surface *layer);
72
73void seat_set_exclusive_client(struct sway_seat *seat,
74 struct wl_client *client);
75
76struct sway_container *seat_get_focus(struct sway_seat *seat);
77
78/**
79 * Return the last container to be focused for the seat (or the most recently
80 * opened if no container has received focused) that is a child of the given
81 * container. The focus-inactive container of the root window is the focused
82 * container for the seat (if the seat does have focus). This function can be
83 * used to determine what container gets focused next if the focused container
84 * is destroyed, or focus moves to a container with children and we need to
85 * descend into the next leaf in focus order.
86 */
87struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
88 struct sway_container *container);
89
90/**
91 * Descend into the focus stack to find the focus-inactive view. Useful for
92 * container placement when they change position in the tree.
93 */
94struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
95 struct sway_container *container);
96
97/**
98 * Iterate over the focus-inactive children of the container calling the
99 * function on each.
100 */
101void seat_focus_inactive_children_for_each(struct sway_seat *seat,
102 struct sway_container *container,
103 void (*f)(struct sway_container *container, void *data), void *data);
104
105void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);
106
107struct seat_config *seat_get_config(struct sway_seat *seat);
108
109bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
110
111#endif
diff --git a/include/sway/input_state.h b/include/sway/input_state.h
deleted file mode 100644
index fd5a3a25..00000000
--- a/include/sway/input_state.h
+++ /dev/null
@@ -1,102 +0,0 @@
1#ifndef _SWAY_KEY_STATE_H
2#define _SWAY_KEY_STATE_H
3#include <stdbool.h>
4#include <stdint.h>
5#include "container.h"
6
7/* Keyboard state */
8
9// returns true if key has been pressed, otherwise false
10bool check_key(uint32_t key_sym, uint32_t key_code);
11
12// returns true if key_sym matches latest released key.
13bool check_released_key(uint32_t key_sym);
14
15// sets a key as pressed
16void press_key(uint32_t key_sym, uint32_t key_code);
17
18// unsets a key as pressed
19void release_key(uint32_t key_sym, uint32_t key_code);
20
21
22/* Pointer state */
23
24enum pointer_values {
25 M_LEFT_CLICK = 272,
26 M_RIGHT_CLICK = 273,
27 M_SCROLL_CLICK = 274,
28 M_SCROLL_UP = 275,
29 M_SCROLL_DOWN = 276,
30};
31
32enum pointer_mode {
33 // Target
34 M_FLOATING = 1,
35 M_TILING = 2,
36 // Action
37 M_DRAGGING = 4,
38 M_RESIZING = 8,
39};
40
41struct pointer_button_state {
42 bool held;
43 // state at the point it was pressed
44 int x, y;
45 swayc_t *view;
46};
47
48extern struct pointer_state {
49 // mouse clicks
50 struct pointer_button_state left;
51 struct pointer_button_state right;
52 struct pointer_button_state scroll;
53
54 // change in pointer position
55 struct {
56 int x, y;
57 } delta;
58
59 // view pointer is currently over
60 swayc_t *view;
61
62 // Pointer mode
63 int mode;
64} pointer_state;
65
66enum modifier_state {
67 MOD_STATE_UNCHANGED = 0,
68 MOD_STATE_PRESSED = 1,
69 MOD_STATE_RELEASED = 2
70};
71
72void pointer_position_set(double new_x, double new_y, bool force_focus);
73void center_pointer_on(swayc_t *view);
74
75// on button release unset mode depending on the button.
76// on button press set mode conditionally depending on the button
77void pointer_mode_set(uint32_t button, bool condition);
78
79// Update mode in mouse motion
80void pointer_mode_update(void);
81
82// Reset mode on any keypress;
83void pointer_mode_reset(void);
84
85void input_init(void);
86
87/**
88 * Check if state of mod changed from current state to new_state.
89 *
90 * Returns MOD_STATE_UNCHANGED if the state didn't change, MOD_STATE_PRESSED if
91 * the state changed to pressed and MOD_STATE_RELEASED if the state changed to
92 * released.
93 */
94uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod);
95
96/**
97 * Update the current modifiers state to new_state.
98 */
99void modifiers_state_update(uint32_t new_state);
100
101#endif
102
diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h
index 3a5af0f5..7d87d377 100644
--- a/include/sway/ipc-json.h
+++ b/include/sway/ipc-json.h
@@ -1,15 +1,14 @@
1#ifndef _SWAY_IPC_JSON_H 1#ifndef _SWAY_IPC_JSON_H
2#define _SWAY_IPC_JSON_H 2#define _SWAY_IPC_JSON_H
3
4#include <json-c/json.h> 3#include <json-c/json.h>
5#include "config.h" 4#include "sway/tree/container.h"
6#include "container.h" 5#include "sway/input/input-manager.h"
7 6
8json_object *ipc_json_get_version(); 7json_object *ipc_json_get_version();
8
9json_object *ipc_json_describe_container(struct sway_container *c);
10json_object *ipc_json_describe_container_recursive(struct sway_container *c);
11json_object *ipc_json_describe_input(struct sway_input_device *device);
9json_object *ipc_json_describe_bar_config(struct bar_config *bar); 12json_object *ipc_json_describe_bar_config(struct bar_config *bar);
10json_object *ipc_json_describe_container(swayc_t *c);
11json_object *ipc_json_describe_container_recursive(swayc_t *c);
12json_object *ipc_json_describe_window(swayc_t *c);
13json_object *ipc_json_describe_input(struct libinput_device *device);
14 13
15#endif 14#endif
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h
index 1d199134..c3389fe8 100644
--- a/include/sway/ipc-server.h
+++ b/include/sway/ipc-server.h
@@ -1,41 +1,21 @@
1#ifndef _SWAY_IPC_SERVER_H 1#ifndef _SWAY_IPC_SERVER_H
2#define _SWAY_IPC_SERVER_H 2#define _SWAY_IPC_SERVER_H
3#include <sys/socket.h>
4#include "sway/tree/container.h"
5#include "ipc.h"
3 6
4#include <wlc/wlc.h> 7struct sway_server;
5 8
6#include "container.h" 9void ipc_init(struct sway_server *server);
7#include "config.h"
8#include "ipc.h"
9 10
10void ipc_init(void);
11void ipc_terminate(void); 11void ipc_terminate(void);
12
12struct sockaddr_un *ipc_user_sockaddr(void); 13struct sockaddr_un *ipc_user_sockaddr(void);
13 14
14void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change); 15void ipc_event_workspace(struct sway_container *old,
16 struct sway_container *new, const char *change);
17void ipc_event_window(struct sway_container *window, const char *change);
15void ipc_event_barconfig_update(struct bar_config *bar); 18void ipc_event_barconfig_update(struct bar_config *bar);
16/**
17 * Send IPC mode event to all listening clients
18 */
19void ipc_event_mode(const char *mode); 19void ipc_event_mode(const char *mode);
20/**
21 * Send IPC window change event
22 */
23void ipc_event_window(swayc_t *window, const char *change);
24/**
25 * Sends an IPC modifier event to all listening clients. The modifier event
26 * includes a key 'change' with the value of state and a key 'modifier' with
27 * the name of that modifier.
28 */
29void ipc_event_modifier(uint32_t modifier, const char *state);
30/**
31 * Send IPC keyboard binding event.
32 */
33void ipc_event_binding_keyboard(struct sway_binding *sb);
34const char *swayc_type_string(enum swayc_types type);
35
36/**
37 * Send pixel data to registered clients.
38 */
39void ipc_get_pixels(wlc_handle output);
40 20
41#endif 21#endif
diff --git a/include/sway/layers.h b/include/sway/layers.h
new file mode 100644
index 00000000..ee47c5ad
--- /dev/null
+++ b/include/sway/layers.h
@@ -0,0 +1,25 @@
1#ifndef _SWAY_LAYERS_H
2#define _SWAY_LAYERS_H
3#include <stdbool.h>
4#include <wlr/types/wlr_box.h>
5#include <wlr/types/wlr_surface.h>
6#include <wlr/types/wlr_layer_shell.h>
7
8struct sway_layer_surface {
9 struct wlr_layer_surface *layer_surface;
10 struct wl_list link;
11
12 struct wl_listener destroy;
13 struct wl_listener map;
14 struct wl_listener unmap;
15 struct wl_listener surface_commit;
16 struct wl_listener output_destroy;
17
18 bool configured;
19 struct wlr_box geo;
20};
21
22struct sway_output;
23void arrange_layers(struct sway_output *output);
24
25#endif
diff --git a/include/sway/layout.h b/include/sway/layout.h
deleted file mode 100644
index f0791588..00000000
--- a/include/sway/layout.h
+++ /dev/null
@@ -1,85 +0,0 @@
1#ifndef _SWAY_LAYOUT_H
2#define _SWAY_LAYOUT_H
3
4#include <wlc/wlc.h>
5#include "log.h"
6#include "list.h"
7#include "container.h"
8#include "focus.h"
9
10extern list_t *scratchpad;
11
12extern int min_sane_w;
13extern int min_sane_h;
14
15// Set initial values for root_container
16void init_layout(void);
17
18// Returns the index of child for its parent
19int index_child(const swayc_t *child);
20
21// Adds child to parent, if parent has no focus, it is set to child
22// parent must be of type C_WORKSPACE or C_CONTAINER
23void add_child(swayc_t *parent, swayc_t *child);
24
25// Adds child to parent at index, if parent has no focus, it is set to child
26// parent must be of type C_WORKSPACE or C_CONTAINER
27void insert_child(swayc_t *parent, swayc_t *child, int index);
28
29// Adds child as floating window to ws, if there is no focus it is set to child.
30// ws must be of type C_WORKSPACE
31void add_floating(swayc_t *ws, swayc_t *child);
32
33// insert child after sibling in parents children.
34swayc_t *add_sibling(swayc_t *sibling, swayc_t *child);
35
36// Replace child with new_child in parents children
37// new_child will inherit childs geometry, childs geometry will be reset
38// if parents focus is on child, it will be changed to new_child
39swayc_t *replace_child(swayc_t *child, swayc_t *new_child);
40
41// Remove child from its parent, if focus is on child, focus will be changed to
42// a sibling, or to a floating window, or NULL
43swayc_t *remove_child(swayc_t *child);
44
45// 2 containers are swapped, they inherit eachothers focus
46void swap_container(swayc_t *a, swayc_t *b);
47
48// 2 Containers geometry are swapped, used with `swap_container`
49void swap_geometry(swayc_t *a, swayc_t *b);
50
51void move_container(swayc_t* container, enum movement_direction direction, int move_amt);
52void move_container_to(swayc_t* container, swayc_t* destination);
53void move_workspace_to(swayc_t* workspace, swayc_t* destination);
54
55// Layout
56/**
57 * Update child container geometries when switching between layouts.
58 */
59void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout);
60void update_geometry(swayc_t *view);
61void arrange_windows(swayc_t *container, double width, double height);
62void arrange_backgrounds(void);
63
64swayc_t *get_focused_container(swayc_t *parent);
65swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir);
66swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit);
67
68void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge);
69
70void layout_log(const swayc_t *c, int depth);
71void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) __attribute__((format(printf,3,4)));
72
73/**
74 * Get default layout.
75 */
76enum swayc_layouts default_layout(swayc_t *output);
77
78bool is_auto_layout(enum swayc_layouts layout);
79int auto_group_start_index(const swayc_t *container, int index);
80int auto_group_end_index(const swayc_t *container, int index);
81size_t auto_group_count(const swayc_t *container);
82size_t auto_group_index(const swayc_t *container, int index);
83bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end);
84
85#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index e1bdd3f0..56571548 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -1,25 +1,48 @@
1#ifndef _SWAY_OUTPUT_H 1#ifndef _SWAY_OUTPUT_H
2#define _SWAY_OUTPUT_H 2#define _SWAY_OUTPUT_H
3#include <time.h>
4#include <unistd.h>
5#include <wayland-server.h>
6#include <wlr/types/wlr_box.h>
7#include <wlr/types/wlr_output.h>
8#include "sway/tree/view.h"
3 9
4#include "container.h" 10struct sway_server;
5#include "focus.h" 11struct sway_container;
6 12
7// Position is absolute coordinates on the edge where the adjacent output 13struct sway_output {
8// should be searched for. 14 struct wlr_output *wlr_output;
9swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos); 15 struct sway_container *swayc;
10swayc_t *swayc_opposite_output(enum movement_direction dir, const struct wlc_point *abs_pos); 16 struct sway_server *server;
11swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir, const struct wlc_point *abs_pos, bool pick_closest);
12 17
13// Place absolute coordinates for given container into given wlc_point. 18 struct wl_list layers[4]; // sway_layer_surface::link
14void get_absolute_position(swayc_t *container, struct wlc_point *point); 19 struct wlr_box usable_area;
15 20
16// Place absolute coordinates for the center point of given container into 21 struct timespec last_frame;
17// given wlc_point. 22 struct wlr_output_damage *damage;
18void get_absolute_center_position(swayc_t *container, struct wlc_point *point);
19 23
20// stable sort workspaces on this output 24 struct wl_listener destroy;
21void sort_workspaces(swayc_t *output); 25 struct wl_listener mode;
26 struct wl_listener transform;
27 struct wl_listener scale;
22 28
23void output_get_scaled_size(wlc_handle handle, struct wlc_size *size); 29 struct wl_listener damage_destroy;
30 struct wl_listener damage_frame;
31
32 pid_t bg_pid;
33};
34
35void output_damage_whole(struct sway_output *output);
36
37void output_damage_surface(struct sway_output *output, double ox, double oy,
38 struct wlr_surface *surface, bool whole);
39
40void output_damage_view(struct sway_output *output, struct sway_view *view,
41 bool whole);
42
43void output_damage_whole_container(struct sway_output *output,
44 struct sway_container *con);
45
46struct sway_container *output_by_name(const char *name);
24 47
25#endif 48#endif
diff --git a/include/sway/server.h b/include/sway/server.h
new file mode 100644
index 00000000..296fbf22
--- /dev/null
+++ b/include/sway/server.h
@@ -0,0 +1,57 @@
1#ifndef _SWAY_SERVER_H
2#define _SWAY_SERVER_H
3#include <stdbool.h>
4#include <wayland-server.h>
5#include <wlr/backend.h>
6#include <wlr/backend/session.h>
7#include <wlr/types/wlr_compositor.h>
8#include <wlr/types/wlr_data_device.h>
9#include <wlr/types/wlr_layer_shell.h>
10#include <wlr/types/wlr_xdg_shell_v6.h>
11#include <wlr/render/wlr_renderer.h>
12// TODO WLR: make Xwayland optional
13#include <wlr/xwayland.h>
14
15struct sway_server {
16 struct wl_display *wl_display;
17 struct wl_event_loop *wl_event_loop;
18 const char *socket;
19
20 struct wlr_backend *backend;
21
22 struct wlr_compositor *compositor;
23 struct wlr_data_device_manager *data_device_manager;
24
25 struct sway_input_manager *input;
26
27 struct wl_listener new_output;
28
29 struct wlr_layer_shell *layer_shell;
30 struct wl_listener layer_shell_surface;
31
32 struct wlr_xdg_shell_v6 *xdg_shell_v6;
33 struct wl_listener xdg_shell_v6_surface;
34
35 struct wlr_xwayland *xwayland;
36 struct wlr_xcursor_manager *xcursor_manager;
37 struct wl_listener xwayland_surface;
38 struct wl_listener xwayland_ready;
39
40 struct wlr_wl_shell *wl_shell;
41 struct wl_listener wl_shell_surface;
42};
43
44struct sway_server server;
45
46bool server_init(struct sway_server *server);
47void server_fini(struct sway_server *server);
48void server_run(struct sway_server *server);
49
50void handle_new_output(struct wl_listener *listener, void *data);
51
52void handle_layer_shell_surface(struct wl_listener *listener, void *data);
53void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
54void handle_xwayland_surface(struct wl_listener *listener, void *data);
55void handle_wl_shell_surface(struct wl_listener *listener, void *data);
56
57#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
new file mode 100644
index 00000000..2a8b8aba
--- /dev/null
+++ b/include/sway/tree/container.h
@@ -0,0 +1,192 @@
1#ifndef _SWAY_CONTAINER_H
2#define _SWAY_CONTAINER_H
3#include <stdint.h>
4#include <sys/types.h>
5#include <wlr/types/wlr_box.h>
6#include <wlr/types/wlr_surface.h>
7#include "list.h"
8
9extern struct sway_container root_container;
10
11struct sway_view;
12struct sway_seat;
13
14/**
15 * Different kinds of containers.
16 *
17 * This enum is in order. A container will never be inside of a container below
18 * it on this list.
19 */
20enum sway_container_type {
21 C_ROOT,
22 C_OUTPUT,
23 C_WORKSPACE,
24 C_CONTAINER,
25 C_VIEW,
26
27 // Keep last
28 C_TYPES,
29};
30
31enum sway_container_layout {
32 L_NONE,
33 L_HORIZ,
34 L_VERT,
35 L_STACKED,
36 L_TABBED,
37 L_FLOATING,
38};
39
40enum sway_container_border {
41 B_NONE,
42 B_PIXEL,
43 B_NORMAL,
44};
45
46struct sway_root;
47struct sway_output;
48struct sway_view;
49
50struct sway_container {
51 union {
52 // TODO: Encapsulate state for other node types as well like C_CONTAINER
53 struct sway_root *sway_root;
54 struct sway_output *sway_output;
55 struct sway_view *sway_view;
56 };
57
58 /**
59 * A unique ID to identify this container. Primarily used in the
60 * get_tree JSON output.
61 */
62 size_t id;
63
64 char *name;
65
66 enum sway_container_type type;
67 enum sway_container_layout layout;
68 enum sway_container_layout prev_layout;
69 enum sway_container_layout workspace_layout;
70
71 // For C_ROOT, this has no meaning
72 // For C_OUTPUT, this is the output position in layout coordinates
73 // For other types, this is the position in output-local coordinates
74 double x, y;
75 // does not include borders or gaps.
76 double width, height;
77
78 list_t *children;
79
80 struct sway_container *parent;
81
82 list_t *marks; // list of char*
83
84 float alpha;
85
86 struct {
87 struct wl_signal destroy;
88 // Raised after the tree updates, but before arrange_windows
89 // Passed the previous parent
90 struct wl_signal reparent;
91 } events;
92};
93
94struct sway_container *container_create(enum sway_container_type type);
95
96const char *container_type_to_str(enum sway_container_type type);
97
98struct sway_container *output_create(struct sway_output *sway_output);
99
100/**
101 * Create a new container container. A container container can be a a child of
102 * a workspace container or another container container.
103 */
104struct sway_container *container_container_create();
105
106/**
107 * Create a new output. Outputs are children of the root container and have no
108 * order in the tree structure.
109 */
110struct sway_container *output_create(struct sway_output *sway_output);
111
112/**
113 * Create a new workspace container. Workspaces are children of an output
114 * container and are ordered alphabetically by name.
115 */
116struct sway_container *workspace_create(struct sway_container *output,
117 const char *name);
118
119/*
120 * Create a new view container. A view can be a child of a workspace container
121 * or a container container and are rendered in the order and structure of
122 * how they are attached to the tree.
123 */
124struct sway_container *container_view_create(
125 struct sway_container *sibling, struct sway_view *sway_view);
126
127struct sway_container *container_destroy(struct sway_container *container);
128
129struct sway_container *container_close(struct sway_container *container);
130
131void container_descendants(struct sway_container *root,
132 enum sway_container_type type,
133 void (*func)(struct sway_container *item, void *data), void *data);
134
135/**
136 * Search a container's descendants a container based on test criteria. Returns
137 * the first container that passes the test.
138 */
139struct sway_container *container_find(struct sway_container *container,
140 bool (*test)(struct sway_container *view, void *data), void *data);
141
142/**
143 * Finds a parent container with the given struct sway_containerype.
144 */
145struct sway_container *container_parent(struct sway_container *container,
146 enum sway_container_type type);
147
148/**
149 * Find a container at the given coordinates. Returns the the surface and
150 * surface-local coordinates of the given layout coordinates if the container
151 * is a view and the view contains a surface at those coordinates.
152 */
153struct sway_container *container_at(struct sway_container *container,
154 double lx, double ly, struct wlr_surface **surface,
155 double *sx, double *sy);
156
157/**
158 * Apply the function for each descendant of the container breadth first.
159 */
160void container_for_each_descendant_bfs(struct sway_container *container,
161 void (*f)(struct sway_container *container, void *data), void *data);
162
163/**
164 * Apply the function for each child of the container depth first.
165 */
166void container_for_each_descendant_dfs(struct sway_container *container,
167 void (*f)(struct sway_container *container, void *data), void *data);
168
169/**
170 * Returns true if the given container is an ancestor of this container.
171 */
172bool container_has_anscestor(struct sway_container *container,
173 struct sway_container *anscestor);
174
175/**
176 * Returns true if the given container is a child descendant of this container.
177 */
178bool container_has_child(struct sway_container *con,
179 struct sway_container *child);
180
181void container_create_notify(struct sway_container *container);
182
183void container_damage_whole(struct sway_container *container);
184
185bool container_reap_empty(struct sway_container *con);
186
187struct sway_container *container_reap_empty_recursive(
188 struct sway_container *con);
189
190struct sway_container *container_flatten(struct sway_container *container);
191
192#endif
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h
new file mode 100644
index 00000000..49ae00e4
--- /dev/null
+++ b/include/sway/tree/layout.h
@@ -0,0 +1,78 @@
1#ifndef _SWAY_LAYOUT_H
2#define _SWAY_LAYOUT_H
3#include <wlr/types/wlr_output_layout.h>
4#include <wlr/render/wlr_texture.h>
5#include "sway/tree/container.h"
6
7enum movement_direction {
8 MOVE_LEFT,
9 MOVE_RIGHT,
10 MOVE_UP,
11 MOVE_DOWN,
12 MOVE_PARENT,
13 MOVE_CHILD,
14};
15
16enum resize_edge {
17 RESIZE_EDGE_LEFT,
18 RESIZE_EDGE_RIGHT,
19 RESIZE_EDGE_TOP,
20 RESIZE_EDGE_BOTTOM,
21};
22
23struct sway_container;
24
25struct sway_root {
26 struct wlr_output_layout *output_layout;
27
28 struct wl_listener output_layout_change;
29
30 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link
31
32 struct wlr_texture *debug_tree;
33
34 struct {
35 struct wl_signal new_container;
36 } events;
37};
38
39void layout_init(void);
40
41void container_add_child(struct sway_container *parent,
42 struct sway_container *child);
43
44struct sway_container *container_add_sibling(struct sway_container *parent,
45 struct sway_container *child);
46
47struct sway_container *container_remove_child(struct sway_container *child);
48
49struct sway_container *container_replace_child(struct sway_container *child,
50 struct sway_container *new_child);
51
52struct sway_container *container_set_layout(struct sway_container *container,
53 enum sway_container_layout layout);
54
55void container_move_to(struct sway_container* container,
56 struct sway_container* destination);
57
58void container_move(struct sway_container *container,
59 enum movement_direction dir, int move_amt);
60
61enum sway_container_layout container_get_default_layout(
62 struct sway_container *con);
63
64void container_sort_workspaces(struct sway_container *output);
65
66void arrange_windows(struct sway_container *container,
67 double width, double height);
68
69struct sway_container *container_get_in_direction(struct sway_container
70 *container, struct sway_seat *seat, enum movement_direction dir);
71
72struct sway_container *container_split(struct sway_container *child,
73 enum sway_container_layout layout);
74
75void container_recursive_resize(struct sway_container *container,
76 double amount, enum resize_edge edge);
77
78#endif
diff --git a/include/sway/tree/output.h b/include/sway/tree/output.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/include/sway/tree/output.h
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
new file mode 100644
index 00000000..b51c54b5
--- /dev/null
+++ b/include/sway/tree/view.h
@@ -0,0 +1,186 @@
1#ifndef _SWAY_VIEW_H
2#define _SWAY_VIEW_H
3#include <wayland-server.h>
4#include <wlr/types/wlr_surface.h>
5#include <wlr/types/wlr_xdg_shell_v6.h>
6#include <wlr/xwayland.h>
7#include "sway/input/input-manager.h"
8#include "sway/input/seat.h"
9
10struct sway_container;
11
12enum sway_view_type {
13 SWAY_VIEW_WL_SHELL,
14 SWAY_VIEW_XDG_SHELL_V6,
15 SWAY_VIEW_XWAYLAND,
16};
17
18enum sway_view_prop {
19 VIEW_PROP_TITLE,
20 VIEW_PROP_APP_ID,
21 VIEW_PROP_CLASS,
22 VIEW_PROP_INSTANCE,
23};
24
25struct sway_view_impl {
26 const char *(*get_prop)(struct sway_view *view,
27 enum sway_view_prop prop);
28 void (*configure)(struct sway_view *view, double ox, double oy, int width,
29 int height);
30 void (*set_activated)(struct sway_view *view, bool activated);
31 void (*for_each_surface)(struct sway_view *view,
32 wlr_surface_iterator_func_t iterator, void *user_data);
33 void (*close)(struct sway_view *view);
34 void (*destroy)(struct sway_view *view);
35};
36
37struct sway_view {
38 enum sway_view_type type;
39 const struct sway_view_impl *impl;
40
41 struct sway_container *swayc; // NULL for unmanaged views
42 struct wlr_surface *surface; // NULL for unmapped views
43 int width, height;
44
45 union {
46 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
47 struct wlr_xwayland_surface *wlr_xwayland_surface;
48 struct wlr_wl_shell_surface *wlr_wl_shell_surface;
49 };
50
51 struct {
52 struct wl_signal unmap;
53 } events;
54
55 struct wl_listener surface_new_subsurface;
56 struct wl_listener container_reparent;
57};
58
59struct sway_xdg_shell_v6_view {
60 struct sway_view view;
61
62 struct wl_listener commit;
63 struct wl_listener request_move;
64 struct wl_listener request_resize;
65 struct wl_listener request_maximize;
66 struct wl_listener new_popup;
67 struct wl_listener map;
68 struct wl_listener unmap;
69 struct wl_listener destroy;
70
71 int pending_width, pending_height;
72};
73
74struct sway_xwayland_view {
75 struct sway_view view;
76
77 struct wl_listener commit;
78 struct wl_listener request_move;
79 struct wl_listener request_resize;
80 struct wl_listener request_maximize;
81 struct wl_listener request_configure;
82 struct wl_listener map;
83 struct wl_listener unmap;
84 struct wl_listener destroy;
85
86 int pending_width, pending_height;
87};
88
89struct sway_xwayland_unmanaged {
90 struct wlr_xwayland_surface *wlr_xwayland_surface;
91 struct wl_list link;
92
93 int lx, ly;
94
95 struct wl_listener request_configure;
96 struct wl_listener commit;
97 struct wl_listener map;
98 struct wl_listener unmap;
99 struct wl_listener destroy;
100};
101
102struct sway_wl_shell_view {
103 struct sway_view view;
104
105 struct wl_listener commit;
106 struct wl_listener request_move;
107 struct wl_listener request_resize;
108 struct wl_listener request_maximize;
109 struct wl_listener destroy;
110
111 int pending_width, pending_height;
112};
113
114struct sway_view_child;
115
116struct sway_view_child_impl {
117 void (*destroy)(struct sway_view_child *child);
118};
119
120/**
121 * A view child is a surface in the view tree, such as a subsurface or a popup.
122 */
123struct sway_view_child {
124 const struct sway_view_child_impl *impl;
125
126 struct sway_view *view;
127 struct wlr_surface *surface;
128
129 struct wl_listener surface_commit;
130 struct wl_listener surface_new_subsurface;
131 struct wl_listener surface_destroy;
132 struct wl_listener view_unmap;
133};
134
135struct sway_xdg_popup_v6 {
136 struct sway_view_child child;
137
138 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
139
140 struct wl_listener new_popup;
141 struct wl_listener unmap;
142 struct wl_listener destroy;
143};
144
145const char *view_get_title(struct sway_view *view);
146
147const char *view_get_app_id(struct sway_view *view);
148
149const char *view_get_class(struct sway_view *view);
150
151const char *view_get_instance(struct sway_view *view);
152
153void view_configure(struct sway_view *view, double ox, double oy, int width,
154 int height);
155
156void view_set_activated(struct sway_view *view, bool activated);
157
158void view_close(struct sway_view *view);
159
160void view_damage(struct sway_view *view, bool whole);
161
162void view_for_each_surface(struct sway_view *view,
163 wlr_surface_iterator_func_t iterator, void *user_data);
164
165// view implementation
166
167void view_init(struct sway_view *view, enum sway_view_type type,
168 const struct sway_view_impl *impl);
169
170void view_destroy(struct sway_view *view);
171
172void view_map(struct sway_view *view, struct wlr_surface *wlr_surface);
173
174void view_unmap(struct sway_view *view);
175
176void view_update_position(struct sway_view *view, double ox, double oy);
177
178void view_update_size(struct sway_view *view, int width, int height);
179
180void view_child_init(struct sway_view_child *child,
181 const struct sway_view_child_impl *impl, struct sway_view *view,
182 struct wlr_surface *surface);
183
184void view_child_destroy(struct sway_view_child *child);
185
186#endif
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
new file mode 100644
index 00000000..8d49fefb
--- /dev/null
+++ b/include/sway/tree/workspace.h
@@ -0,0 +1,26 @@
1#ifndef _SWAY_WORKSPACE_H
2#define _SWAY_WORKSPACE_H
3
4#include "sway/tree/container.h"
5
6extern char *prev_workspace_name;
7
8char *workspace_next_name(const char *output_name);
9
10bool workspace_switch(struct sway_container *workspace);
11
12struct sway_container *workspace_by_number(const char* name);
13
14struct sway_container *workspace_by_name(const char*);
15
16struct sway_container *workspace_output_next(struct sway_container *current);
17
18struct sway_container *workspace_next(struct sway_container *current);
19
20struct sway_container *workspace_output_prev(struct sway_container *current);
21
22struct sway_container *workspace_prev(struct sway_container *current);
23
24bool workspace_is_visible(struct sway_container *ws);
25
26#endif
diff --git a/include/sway/workspace.h b/include/sway/workspace.h
deleted file mode 100644
index c268fafa..00000000
--- a/include/sway/workspace.h
+++ /dev/null
@@ -1,22 +0,0 @@
1#ifndef _SWAY_WORKSPACE_H
2#define _SWAY_WORKSPACE_H
3
4#include <wlc/wlc.h>
5#include <unistd.h>
6#include "list.h"
7#include "layout.h"
8
9extern char *prev_workspace_name;
10
11char *workspace_next_name(const char *output_name);
12swayc_t *workspace_create(const char*);
13swayc_t *workspace_by_name(const char*);
14swayc_t *workspace_by_number(const char*);
15bool workspace_switch(swayc_t*);
16swayc_t *workspace_output_next();
17swayc_t *workspace_next();
18swayc_t *workspace_output_prev();
19swayc_t *workspace_prev();
20swayc_t *workspace_for_pid(pid_t pid);
21
22#endif
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 50d36e76..0037190b 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -1,36 +1,71 @@
1#ifndef _SWAYBAR_BAR_H 1#ifndef _SWAYBAR_BAR_H
2#define _SWAYBAR_BAR_H 2#define _SWAYBAR_BAR_H
3#include <wayland-client.h>
4#include "pool-buffer.h"
3 5
4#include "client/registry.h" 6struct swaybar_config;
5#include "client/window.h" 7struct swaybar_output;
6#include "list.h" 8struct swaybar_workspace;
7 9
8struct bar { 10struct swaybar_pointer {
9 struct config *config; 11 struct wl_pointer *pointer;
12 struct wl_cursor_theme *cursor_theme;
13 struct wl_cursor_image *cursor_image;
14 struct wl_surface *cursor_surface;
15 struct swaybar_output *current;
16 int x, y;
17};
18
19struct swaybar_hotspot {
20 struct wl_list link;
21 int x, y, width, height;
22 void (*callback)(struct swaybar_output *output,
23 int x, int y, uint32_t button, void *data);
24 void (*destroy)(void *data);
25 void *data;
26};
27
28struct swaybar {
29 struct wl_display *display;
30 struct wl_compositor *compositor;
31 struct zwlr_layer_shell_v1 *layer_shell;
32 struct wl_shm *shm;
33 struct wl_seat *seat;
34
35 struct swaybar_config *config;
36 struct swaybar_output *focused_output;
37 struct swaybar_pointer pointer;
10 struct status_line *status; 38 struct status_line *status;
11 list_t *outputs;
12 struct output *focused_output;
13 39
14 int ipc_event_socketfd; 40 int ipc_event_socketfd;
15 int ipc_socketfd; 41 int ipc_socketfd;
16 int status_read_fd; 42
17 int status_write_fd; 43 struct wl_list outputs;
18 pid_t status_command_pid;
19}; 44};
20 45
21struct output { 46struct swaybar_output {
22 struct window *window; 47 struct wl_list link;
23 struct registry *registry; 48 struct swaybar *bar;
24 list_t *workspaces; 49 struct wl_output *output;
25#ifdef ENABLE_TRAY 50 struct wl_surface *surface;
26 list_t *items; 51 struct zwlr_layer_surface_v1 *layer_surface;
27#endif 52 uint32_t wl_name;
53
54 struct wl_list workspaces;
55 struct wl_list hotspots;
56
28 char *name; 57 char *name;
29 int idx; 58 size_t index;
30 bool focused; 59 bool focused;
60
61 uint32_t width, height;
62 int32_t scale;
63 struct pool_buffer buffers[2];
64 struct pool_buffer *current_buffer;
31}; 65};
32 66
33struct workspace { 67struct swaybar_workspace {
68 struct wl_list link;
34 int num; 69 int num;
35 char *name; 70 char *name;
36 bool focused; 71 bool focused;
@@ -38,35 +73,10 @@ struct workspace {
38 bool urgent; 73 bool urgent;
39}; 74};
40 75
41/** Global bar state */ 76void bar_setup(struct swaybar *bar,
42extern struct bar swaybar; 77 const char *socket_path,
43 78 const char *bar_id);
44/** True if sway needs to render */ 79void bar_run(struct swaybar *bar);
45extern bool dirty; 80void bar_teardown(struct swaybar *bar);
46
47/**
48 * Setup bar.
49 */
50void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id);
51
52/**
53 * Create new output struct from name.
54 */
55struct output *new_output(const char *name);
56 81
57/** 82#endif
58 * Bar mainloop.
59 */
60void bar_run(struct bar *bar);
61
62/**
63 * free workspace list.
64 */
65void free_workspaces(list_t *workspaces);
66
67/**
68 * Teardown bar.
69 */
70void bar_teardown(struct bar *bar);
71
72#endif /* _SWAYBAR_BAR_H */
diff --git a/include/swaybar/config.h b/include/swaybar/config.h
index 651f0ee3..7f321df8 100644
--- a/include/swaybar/config.h
+++ b/include/swaybar/config.h
@@ -1,49 +1,36 @@
1#ifndef _SWAYBAR_CONFIG_H 1#ifndef _SWAYBAR_CONFIG_H
2#define _SWAYBAR_CONFIG_H 2#define _SWAYBAR_CONFIG_H
3
4#include <stdint.h>
5#include <stdbool.h> 3#include <stdbool.h>
6 4#include <stdint.h>
7#include "list.h" 5#include <wayland-client.h>
8#include "util.h" 6#include "util.h"
9 7
10/**
11 * Colors for a box with background, border and text colors.
12 */
13struct box_colors { 8struct box_colors {
14 uint32_t border; 9 uint32_t border;
15 uint32_t background; 10 uint32_t background;
16 uint32_t text; 11 uint32_t text;
17}; 12};
18 13
19/** 14struct config_output {
20 * Swaybar config. 15 struct wl_list link;
21 */ 16 char *name;
22struct config { 17 size_t index;
18};
19
20struct swaybar_config {
23 char *status_command; 21 char *status_command;
24 bool pango_markup; 22 bool pango_markup;
25 uint32_t position; 23 uint32_t position; // zwlr_layer_surface_v1_anchor
26 char *font; 24 char *font;
27 char *sep_symbol; 25 char *sep_symbol;
28 char *mode; 26 char *mode;
27 bool mode_pango_markup;
29 bool strip_workspace_numbers; 28 bool strip_workspace_numbers;
30 bool binding_mode_indicator; 29 bool binding_mode_indicator;
31 bool wrap_scroll; 30 bool wrap_scroll;
32 bool workspace_buttons; 31 bool workspace_buttons;
32 struct wl_list outputs;
33 bool all_outputs; 33 bool all_outputs;
34 list_t *outputs;
35
36#ifdef ENABLE_TRAY
37 // Tray
38 char *tray_output;
39 char *icon_theme;
40
41 uint32_t tray_padding;
42 uint32_t activate_button;
43 uint32_t context_button;
44 uint32_t secondary_button;
45#endif
46
47 int height; 34 int height;
48 35
49 struct { 36 struct {
@@ -63,24 +50,8 @@ struct config {
63 } colors; 50 } colors;
64}; 51};
65 52
66/** 53struct swaybar_config *init_config();
67 * Parse position top|bottom|left|right. 54void free_config(struct swaybar_config *config);
68 */
69uint32_t parse_position(const char *position); 55uint32_t parse_position(const char *position);
70 56
71/** 57#endif
72 * Parse font.
73 */
74char *parse_font(const char *font);
75
76/**
77 * Initialize default sway config.
78 */
79struct config *init_config();
80
81/**
82 * Free config struct.
83 */
84void free_config(struct config *config);
85
86#endif /* _SWAYBAR_CONFIG_H */
diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h
index a0cde07f..99f6ed36 100644
--- a/include/swaybar/event_loop.h
+++ b/include/swaybar/event_loop.h
@@ -1,6 +1,5 @@
1#ifndef _SWAYBAR_EVENT_LOOP_H 1#ifndef _SWAYBAR_EVENT_LOOP_H
2#define _SWAYBAR_EVENT_LOOP_H 2#define _SWAYBAR_EVENT_LOOP_H
3
4#include <stdbool.h> 3#include <stdbool.h>
5#include <time.h> 4#include <time.h>
6 5
@@ -23,4 +22,5 @@ bool remove_timer(timer_t timer);
23void event_loop_poll(); 22void event_loop_poll();
24 23
25void init_event_loop(); 24void init_event_loop();
26#endif /*_SWAYBAR_EVENT_LOOP_H */ 25
26#endif
diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h
index c11931d0..a1696bcf 100644
--- a/include/swaybar/ipc.h
+++ b/include/swaybar/ipc.h
@@ -1,23 +1,11 @@
1#ifndef _SWAYBAR_IPC_H 1#ifndef _SWAYBAR_IPC_H
2#define _SWAYBAR_IPC_H 2#define _SWAYBAR_IPC_H
3#include <stdbool.h>
4#include "swaybar/bar.h"
3 5
4#include "bar.h" 6void ipc_initialize(struct swaybar *bar, const char *bar_id);
5 7bool handle_ipc_readable(struct swaybar *bar);
6/** 8void ipc_get_workspaces(struct swaybar *bar);
7 * Initialize ipc connection to sway and get sway state, outputs, bar_config. 9void ipc_send_workspace_command(struct swaybar *bar, const char *ws);
8 */
9void ipc_bar_init(struct bar *bar, const char *bar_id);
10
11/**
12 * Handle ipc event from sway.
13 */
14bool handle_ipc_event(struct bar *bar);
15
16
17/**
18 * Send workspace command to sway
19 */
20void ipc_send_workspace_command(const char *workspace_name);
21
22#endif /* _SWAYBAR_IPC_H */
23 10
11#endif
diff --git a/include/swaybar/render.h b/include/swaybar/render.h
index 114f43f4..071e2298 100644
--- a/include/swaybar/render.h
+++ b/include/swaybar/render.h
@@ -1,22 +1,10 @@
1#ifndef _SWAYBAR_RENDER_H 1#ifndef _SWAYBAR_RENDER_H
2#define _SWAYBAR_RENDER_H 2#define _SWAYBAR_RENDER_H
3 3
4#include "config.h" 4struct swaybar;
5#include "bar.h" 5struct swaybar_output;
6struct swaybar_config;
6 7
7/** 8void render_frame(struct swaybar *bar, struct swaybar_output *output);
8 * Render swaybar.
9 */
10void render(struct output *output, struct config *config, struct status_line *line);
11 9
12/** 10#endif
13 * Set window height and modify internal spacing accordingly.
14 */
15void set_window_height(struct window *window, int height);
16
17/**
18 * Compute the size of a workspace name
19 */
20void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height);
21
22#endif /* _SWAYBAR_RENDER_H */
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h
index 0664ddee..3538f49c 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -1,25 +1,44 @@
1#ifndef _SWAYBAR_STATUS_LINE_H 1#ifndef _SWAYBAR_STATUS_LINE_H
2#define _SWAYBAR_STATUS_LINE_H 2#define _SWAYBAR_STATUS_LINE_H
3
4#include <stdint.h> 3#include <stdint.h>
4#include <stdio.h>
5#include <stdbool.h> 5#include <stdbool.h>
6
7#include "list.h"
8#include "bar.h" 6#include "bar.h"
9 7
10typedef enum {UNDEF, TEXT, I3BAR} command_protocol; 8enum status_protocol {
9 PROTOCOL_UNDEF,
10 PROTOCOL_ERROR,
11 PROTOCOL_TEXT,
12 PROTOCOL_I3BAR,
13};
14
15struct text_protocol_state {
16 char *buffer;
17 size_t buffer_size;
18};
11 19
12struct status_line { 20enum json_node_type {
13 list_t *block_line; 21 JSON_NODE_UNKNOWN,
14 const char *text_line; 22 JSON_NODE_ARRAY,
15 command_protocol protocol; 23 JSON_NODE_STRING,
24};
25
26struct i3bar_protocol_state {
16 bool click_events; 27 bool click_events;
28 char *buffer;
29 size_t buffer_size;
30 size_t buffer_index;
31 const char *current_node;
32 bool escape;
33 size_t depth;
34 enum json_node_type nodes[16];
17}; 35};
18 36
19struct status_block { 37struct i3bar_block {
38 struct wl_list link;
20 char *full_text, *short_text, *align; 39 char *full_text, *short_text, *align;
21 bool urgent; 40 bool urgent;
22 uint32_t color; 41 uint32_t *color;
23 int min_width; 42 int min_width;
24 char *name, *instance; 43 char *name, *instance;
25 bool separator; 44 bool separator;
@@ -32,30 +51,27 @@ struct status_block {
32 int border_bottom; 51 int border_bottom;
33 int border_left; 52 int border_left;
34 int border_right; 53 int border_right;
35
36 // Set during rendering
37 int x;
38 int width;
39}; 54};
40 55
41/** 56struct status_line {
42 * Initialize status line struct. 57 pid_t pid;
43 */ 58 int read_fd, write_fd;
44struct status_line *init_status_line(); 59 FILE *read, *write;
45 60
46/** 61 enum status_protocol protocol;
47 * handle status line activity. 62 const char *text;
48 */ 63 struct wl_list blocks; // i3bar_block::link
49bool handle_status_line(struct bar *bar);
50 64
51/** 65 struct text_protocol_state text_state;
52 * Handle mouse clicks. 66 struct i3bar_protocol_state i3bar_state;
53 */ 67};
54bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button);
55 68
56/** 69struct status_line *status_line_init(char *cmd);
57 * Free status line struct. 70void status_error(struct status_line *status, const char *text);
58 */ 71bool status_handle_readable(struct status_line *status);
59void free_status_line(struct status_line *line); 72void status_line_free(struct status_line *status);
73bool i3bar_handle_readable(struct status_line *status);
74void i3bar_block_send_click(struct status_line *status,
75 struct i3bar_block *block, int x, int y, uint32_t button);
60 76
61#endif /* _SWAYBAR_STATUS_LINE_H */ 77#endif
diff --git a/include/swaylock/seat.h b/include/swaylock/seat.h
new file mode 100644
index 00000000..44bc37d5
--- /dev/null
+++ b/include/swaylock/seat.h
@@ -0,0 +1,38 @@
1#ifndef _SWAYLOCK_SEAT_H
2#define _SWAYLOCK_SEAT_H
3#include <xkbcommon/xkbcommon.h>
4
5enum mod_bit {
6 MOD_SHIFT = 1<<0,
7 MOD_CAPS = 1<<1,
8 MOD_CTRL = 1<<2,
9 MOD_ALT = 1<<3,
10 MOD_MOD2 = 1<<4,
11 MOD_MOD3 = 1<<5,
12 MOD_LOGO = 1<<6,
13 MOD_MOD5 = 1<<7,
14};
15
16enum mask {
17 MASK_SHIFT,
18 MASK_CAPS,
19 MASK_CTRL,
20 MASK_ALT,
21 MASK_MOD2,
22 MASK_MOD3,
23 MASK_LOGO,
24 MASK_MOD5,
25 MASK_LAST
26};
27
28struct swaylock_xkb {
29 uint32_t modifiers;
30 struct xkb_state *state;
31 struct xkb_context *context;
32 struct xkb_keymap *keymap;
33 xkb_mod_mask_t masks[MASK_LAST];
34};
35
36extern const struct wl_seat_listener seat_listener;
37
38#endif
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h
index eeed094e..173e8b12 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -1,66 +1,64 @@
1#ifndef _SWAYLOCK_H 1#ifndef _SWAYLOCK_H
2#define _SWAYLOCK_H 2#define _SWAYLOCK_H
3 3#include <stdbool.h>
4#include "client/cairo.h" 4#include <stdint.h>
5 5#include <wayland-client.h>
6enum scaling_mode { 6#include "background-image.h"
7 SCALING_MODE_STRETCH, 7#include "cairo.h"
8 SCALING_MODE_FILL, 8#include "pool-buffer.h"
9 SCALING_MODE_FIT, 9#include "swaylock/seat.h"
10 SCALING_MODE_CENTER, 10#include "wlr-layer-shell-unstable-v1-client-protocol.h"
11 SCALING_MODE_TILE,
12};
13 11
14enum auth_state { 12enum auth_state {
15 AUTH_STATE_IDLE, 13 AUTH_STATE_IDLE,
16 AUTH_STATE_INPUT, 14 AUTH_STATE_INPUT,
17 AUTH_STATE_BACKSPACE, 15 AUTH_STATE_BACKSPACE,
18 AUTH_STATE_VALIDATING, 16 AUTH_STATE_VALIDATING,
19 AUTH_STATE_INVALID, 17 AUTH_STATE_INVALID,
20}; 18};
21 19
22enum line_source { 20struct swaylock_args {
23 LINE_SOURCE_DEFAULT,
24 LINE_SOURCE_RING,
25 LINE_SOURCE_INSIDE,
26};
27
28struct render_data {
29 list_t *surfaces;
30 // Output specific images
31 cairo_surface_t **images;
32 // OR one image for all outputs:
33 cairo_surface_t *image;
34 int num_images;
35 int color_set;
36 uint32_t color; 21 uint32_t color;
37 enum scaling_mode scaling_mode; 22 enum background_mode mode;
38 enum auth_state auth_state; 23 bool show_indicator;
39}; 24};
40 25
41struct lock_colors { 26struct swaylock_password {
42 uint32_t inner_ring; 27 size_t size;
43 uint32_t outer_ring; 28 size_t len;
29 char *buffer;
44}; 30};
45 31
46struct lock_config { 32struct swaylock_state {
47 char *font; 33 struct wl_display *display;
48 34 struct wl_compositor *compositor;
49 struct { 35 struct zwlr_layer_shell_v1 *layer_shell;
50 uint32_t text; 36 struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager;
51 uint32_t line; 37 struct wl_shm *shm;
52 uint32_t separator; 38 struct wl_list surfaces;
53 uint32_t input_cursor; 39 struct swaylock_args args;
54 uint32_t backspace_cursor; 40 struct swaylock_password password;
55 struct lock_colors normal; 41 struct swaylock_xkb xkb;
56 struct lock_colors validating; 42 enum auth_state auth_state;
57 struct lock_colors invalid; 43 bool run_display;
58 } colors; 44};
59 45
60 int radius; 46struct swaylock_surface {
61 int thickness; 47 cairo_surface_t *image;
48 struct swaylock_state *state;
49 struct wl_output *output;
50 struct wl_surface *surface;
51 struct zwlr_layer_surface_v1 *layer_surface;
52 struct pool_buffer buffers[2];
53 struct pool_buffer *current_buffer;
54 uint32_t width, height;
55 int32_t scale;
56 struct wl_list link;
62}; 57};
63 58
64void render(struct render_data* render_data, struct lock_config *config); 59void swaylock_handle_key(struct swaylock_state *state,
60 xkb_keysym_t keysym, uint32_t codepoint);
61void render_frame(struct swaylock_surface *surface);
62void render_frames(struct swaylock_state *state);
65 63
66#endif 64#endif
diff --git a/include/unicode.h b/include/unicode.h
new file mode 100644
index 00000000..e2ee9588
--- /dev/null
+++ b/include/unicode.h
@@ -0,0 +1,33 @@
1#ifndef _SWAY_UNICODE_H
2#define _SWAY_UNICODE_H
3#include <stddef.h>
4#include <stdint.h>
5
6// Technically UTF-8 supports up to 6 byte codepoints, but Unicode itself
7// doesn't really bother with more than 4.
8#define UTF8_MAX_SIZE 4
9
10#define UTF8_INVALID 0x80
11
12/**
13 * Grabs the next UTF-8 character and advances the string pointer
14 */
15uint32_t utf8_decode(const char **str);
16
17/**
18 * Encodes a character as UTF-8 and returns the length of that character.
19 */
20size_t utf8_encode(char *str, uint32_t ch);
21
22/**
23 * Returns the size of the next UTF-8 character
24 */
25int utf8_size(const char *str);
26
27/**
28 * Returns the size of a UTF-8 character
29 */
30size_t utf8_chsize(uint32_t ch);
31
32#endif
33
diff --git a/meson.build b/meson.build
new file mode 100644
index 00000000..bf266e5f
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,171 @@
1project(
2 'sway',
3 'c',
4 license: 'MIT',
5 default_options: [
6 'c_std=c11',
7 'warning_level=2',
8 'werror=true',
9 ],
10)
11
12add_project_arguments('-Wno-unused-parameter', language: 'c')
13add_project_arguments('-Wno-unused-function', language: 'c')
14add_project_arguments('-Wno-unused-result', language: 'c')
15
16cc = meson.get_compiler('c')
17
18is_freebsd = host_machine.system().startswith('freebsd')
19datadir = get_option('datadir')
20sysconfdir = get_option('sysconfdir')
21prefix = get_option('prefix')
22
23jsonc = dependency('json-c', version: '>=0.13')
24pcre = dependency('libpcre')
25wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
26wayland_server = dependency('wayland-server')
27wayland_client = dependency('wayland-client')
28wayland_cursor = dependency('wayland-cursor')
29wayland_egl = dependency('wayland-egl')
30wayland_protos = dependency('wayland-protocols')
31xkbcommon = dependency('xkbcommon')
32cairo = dependency('cairo')
33pango = dependency('pango')
34pangocairo = dependency('pangocairo')
35gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
36pixman = dependency('pixman-1')
37libcap = dependency('libcap')
38libinput = dependency('libinput', version: '>=1.6.0')
39libpam = cc.find_library('libpam')
40math = cc.find_library('m')
41rt = cc.find_library('rt')
42git = find_program('git', required: false)
43a2x = find_program('a2x', required: false)
44
45conf_data = configuration_data()
46
47if gdk_pixbuf.found()
48 conf_data.set('HAVE_GDK_PIXBUF', true)
49endif
50
51if a2x.found()
52 mandir = get_option('mandir')
53 man_files = [
54 'sway/sway.1.txt',
55 'sway/sway.5.txt',
56 'sway/sway-bar.5.txt',
57 'sway/sway-input.5.txt',
58 'sway/sway-security.7.txt',
59 'swaymsg/swaymsg.1.txt',
60 ]
61 foreach filename : man_files
62 topic = filename.split('.')[-3].split('/')[-1]
63 section = filename.split('.')[-2]
64
65 custom_target(
66 'man-@0@-@1@'.format(topic, section),
67 input: filename,
68 output: '@BASENAME@',
69 command: [
70 a2x,
71 '--no-xmllint',
72 '--doctype', 'manpage',
73 '--format', 'manpage',
74 '--destination-dir', meson.current_build_dir(),
75 '@INPUT@'
76 ],
77 install: true,
78 install_dir: '@0@/man@1@'.format(mandir, section)
79 )
80 endforeach
81endif
82
83add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c')
84
85version = get_option('sway_version')
86if version != ''
87 version = '"@0@"'.format(version)
88else
89 if not git.found()
90 error('git is required to make the version string')
91 endif
92
93 git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip()
94 git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip()
95 version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch)
96endif
97add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
98
99sway_inc = include_directories('include')
100
101subdir('include')
102subdir('protocols')
103subdir('common')
104subdir('sway')
105subdir('swaymsg')
106
107subdir('client')
108subdir('swaybg')
109subdir('swaybar')
110subdir('swaylock')
111
112config = configuration_data()
113config.set('sysconfdir', join_paths(prefix, sysconfdir))
114config.set('datadir', join_paths(prefix, datadir))
115config.set('prefix', prefix)
116
117configure_file(
118 configuration: config,
119 input: 'config.in',
120 output: '@BASENAME@',
121 install_dir: sysconfdir + '/sway'
122)
123
124if is_freebsd
125 configure_file(
126 configuration: config,
127 input: 'security.d/10-freebsd.in',
128 output: '@BASENAME@',
129 install_dir: sysconfdir + '/sway/security.d'
130 )
131else
132 configure_file(
133 configuration: config,
134 input: 'security.d/00-defaults.in',
135 output: '@BASENAME@',
136 install_dir: sysconfdir + '/sway/security.d'
137 )
138endif
139
140install_data(
141 'sway.desktop',
142 install_dir: datadir + '/wayland-sessions'
143)
144
145if (get_option('default_wallpaper'))
146 wallpaper_files = files(
147 'assets/Sway_Wallpaper_Blue_768x1024.png',
148 'assets/Sway_Wallpaper_Blue_768x1024_Portrait.png',
149 'assets/Sway_Wallpaper_Blue_1136x640.png',
150 'assets/Sway_Wallpaper_Blue_1136x640_Portrait.png',
151 'assets/Sway_Wallpaper_Blue_1366x768.png',
152 'assets/Sway_Wallpaper_Blue_1920x1080.png',
153 'assets/Sway_Wallpaper_Blue_2048x1536.png',
154 'assets/Sway_Wallpaper_Blue_2048x1536_Portrait.png',
155 )
156 wallpaper_install_dir = datadir + '/backgrounds/sway'
157
158 install_data(wallpaper_files, install_dir: wallpaper_install_dir)
159endif
160
161if (get_option('zsh_completions'))
162 zsh_files = files(
163 'completions/zsh/_sway',
164 'completions/zsh/_swaygrab',
165 'completions/zsh/_swaylock',
166 'completions/zsh/_swaymsg',
167 )
168 zsh_install_dir = datadir + '/zsh/site-functions'
169
170 install_data(zsh_files, install_dir: zsh_install_dir)
171endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 00000000..541ccf13
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,3 @@
1option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.')
2option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.')
3option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')
diff --git a/protocols/CMakeLists.txt b/protocols/CMakeLists.txt
deleted file mode 100644
index 8d8e58ad..00000000
--- a/protocols/CMakeLists.txt
+++ /dev/null
@@ -1,46 +0,0 @@
1include(Wayland)
2
3WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-xdg-shell
4 xdg-shell.xml
5 xdg-shell
6)
7WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-desktop-shell
8 desktop-shell.xml
9 desktop-shell
10)
11WAYLAND_ADD_PROTOCOL_SERVER(proto-server-desktop-shell
12 desktop-shell.xml
13 desktop-shell
14)
15
16WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-swaylock
17 swaylock.xml
18 swaylock
19)
20WAYLAND_ADD_PROTOCOL_SERVER(proto-server-swaylock
21 swaylock.xml
22 swaylock
23)
24
25WAYLAND_ADD_PROTOCOL_SERVER(proto-server-gamma-control
26 gamma-control.xml
27 gamma-control
28)
29
30WAYLAND_ADD_PROTOCOL_SERVER(proto-server-server-decoration
31 server-decoration.xml
32 server-decoration
33)
34
35add_library(sway-protocols STATIC
36 ${proto-client-xdg-shell}
37 ${proto-client-desktop-shell}
38 ${proto-server-desktop-shell}
39 ${proto-client-swaylock}
40 ${proto-server-swaylock}
41 ${proto-server-gamma-control}
42 ${proto-server-server-decoration}
43 )
44
45set(PROTOCOLS_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/protocols PARENT_SCOPE)
46INCLUDE_DIRECTORIES(${WAYLAND_INCLUDE_DIR} ${XKBCOMMON_INCLUDE_DIRS})
diff --git a/protocols/desktop-shell.xml b/protocols/desktop-shell.xml
deleted file mode 100644
index 581f0c5d..00000000
--- a/protocols/desktop-shell.xml
+++ /dev/null
@@ -1,138 +0,0 @@
1<protocol name="desktop">
2
3 <interface name="desktop_shell" version="3">
4 <description summary="create desktop widgets and helpers">
5 Traditional user interfaces can rely on this interface to define the
6 foundations of typical desktops. Currently it's possible to set up
7 background, panels and locking surfaces.
8 </description>
9
10 <request name="set_background">
11 <arg name="output" type="object" interface="wl_output"/>
12 <arg name="surface" type="object" interface="wl_surface"/>
13 </request>
14
15 <request name="set_panel">
16 <arg name="output" type="object" interface="wl_output"/>
17 <arg name="surface" type="object" interface="wl_surface"/>
18 </request>
19
20 <request name="set_lock_surface">
21 <arg name="surface" type="object" interface="wl_surface"/>
22 </request>
23
24 <request name="unlock"/>
25
26 <request name="set_grab_surface">
27 <description summary="set grab surface">
28 The surface set by this request will receive a fake
29 pointer.enter event during grabs at position 0, 0 and is
30 expected to set an appropriate cursor image as described by
31 the grab_cursor event sent just before the enter event.
32 </description>
33 <arg name="surface" type="object" interface="wl_surface"/>
34 </request>
35
36 <!-- We'll fold most of wl_shell into this interface and then
37 they'll share the configure event. -->
38 <event name="configure">
39 <arg name="edges" type="uint"/>
40 <arg name="surface" type="object" interface="wl_surface"/>
41 <arg name="width" type="int"/>
42 <arg name="height" type="int"/>
43 </event>
44
45 <event name="prepare_lock_surface">
46 <description summary="tell the client to create, set the lock surface">
47 Tell the client we want it to create and set the lock surface, which is
48 a GUI asking the user to unlock the screen. The lock surface is
49 announced with 'set_lock_surface'. Whether or not the client actually
50 implements locking, it MUST send 'unlock' request to let the normal
51 desktop resume.
52 </description>
53 </event>
54
55 <event name="grab_cursor">
56 <description summary="tell client what cursor to show during a grab">
57 This event will be sent immediately before a fake enter event on the
58 grab surface.
59 </description>
60 <arg name="cursor" type="uint"/>
61 </event>
62
63 <enum name="cursor">
64 <entry name="none" value="0"/>
65
66 <entry name="resize_top" value="1"/>
67 <entry name="resize_bottom" value="2"/>
68
69 <entry name="arrow" value="3"/>
70
71 <entry name="resize_left" value="4"/>
72 <entry name="resize_top_left" value="5"/>
73 <entry name="resize_bottom_left" value="6"/>
74
75 <entry name="move" value="7"/>
76
77 <entry name="resize_right" value="8"/>
78 <entry name="resize_top_right" value="9"/>
79 <entry name="resize_bottom_right" value="10"/>
80
81 <entry name="busy" value="11"/>
82 </enum>
83
84 <!-- Version 2 additions -->
85
86 <request name="desktop_ready" since="2">
87 <description summary="desktop is ready to be shown">
88 Tell the server, that enough desktop elements have been drawn
89 to make the desktop look ready for use. During start-up, the
90 server can wait for this request with a black screen before
91 starting to fade in the desktop, for instance. If the client
92 parts of a desktop take a long time to initialize, we avoid
93 showing temporary garbage.
94 </description>
95 </request>
96
97 <!-- Version 3 additions -->
98
99 <enum name="panel_position">
100 <entry name="top" value="0"/>
101 <entry name="bottom" value="1"/>
102 <entry name="left" value="2"/>
103 <entry name="right" value="3"/>
104 </enum>
105
106 <enum name="error">
107 <entry name="invalid_argument" value="0"
108 summary="an invalid argument was provided in a request"/>
109 </enum>
110
111 <request name="set_panel_position" since="3">
112 <description summary="set panel position">
113 Tell the shell which side of the screen the panel is
114 located. This is so that new windows do not overlap the panel
115 and maximized windows maximize properly.
116 </description>
117 <arg name="position" type="uint"/>
118 </request>
119
120 </interface>
121
122 <interface name="screensaver" version="1">
123 <description summary="interface for implementing screensavers">
124 Only one client can bind this interface at a time.
125 </description>
126
127 <request name="set_surface">
128 <description summary="set the surface type as a screensaver">
129 A screensaver surface is normally hidden, and only visible after an
130 idle timeout.
131 </description>
132
133 <arg name="surface" type="object" interface="wl_surface"/>
134 <arg name="output" type="object" interface="wl_output"/>
135 </request>
136
137 </interface>
138</protocol>
diff --git a/protocols/gamma-control.xml b/protocols/gamma-control.xml
deleted file mode 100644
index e6e33265..00000000
--- a/protocols/gamma-control.xml
+++ /dev/null
@@ -1,57 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="gamma_control">
3
4 <copyright>
5 Copyright © 2015 Giulio camuffo
6
7 Permission to use, copy, modify, distribute, and sell this
8 software and its documentation for any purpose is hereby granted
9 without fee, provided that the above copyright notice appear in
10 all copies and that both that copyright notice and this permission
11 notice appear in supporting documentation, and that the name of
12 the copyright holders not be used in advertising or publicity
13 pertaining to distribution of the software without specific,
14 written prior permission. The copyright holders make no
15 representations about the suitability of this software for any
16 purpose. It is provided "as is" without express or implied
17 warranty.
18
19 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
20 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
24 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
26 THIS SOFTWARE.
27 </copyright>
28
29 <interface name="gamma_control_manager" version="1">
30 <request name="destroy" type="destructor"/>
31
32 <request name="get_gamma_control">
33 <arg name="id" type="new_id" interface="gamma_control"/>
34 <arg name="output" type="object" interface="wl_output"/>
35 </request>
36 </interface>
37
38 <interface name="gamma_control" version="1">
39 <enum name="error">
40 <entry name="invalid_gamma" value="0"/>
41 </enum>
42
43 <request name="destroy" type="destructor"/>
44
45 <request name="set_gamma">
46 <arg name="red" type="array"/>
47 <arg name="green" type="array"/>
48 <arg name="blue" type="array"/>
49 </request>
50
51 <request name="reset_gamma"/>
52
53 <event name="gamma_size">
54 <arg name="size" type="uint"/>
55 </event>
56 </interface>
57</protocol>
diff --git a/protocols/meson.build b/protocols/meson.build
new file mode 100644
index 00000000..7f83b16b
--- /dev/null
+++ b/protocols/meson.build
@@ -0,0 +1,73 @@
1wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
2
3wayland_scanner = find_program('wayland-scanner')
4
5wayland_scanner_code = generator(
6 wayland_scanner,
7 output: '@BASENAME@-protocol.c',
8 arguments: ['code', '@INPUT@', '@OUTPUT@'],
9)
10
11wayland_scanner_client = generator(
12 wayland_scanner,
13 output: '@BASENAME@-client-protocol.h',
14 arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
15)
16
17wayland_scanner_server = generator(
18 wayland_scanner,
19 output: '@BASENAME@-protocol.h',
20 arguments: ['server-header', '@INPUT@', '@OUTPUT@'],
21)
22
23client_protocols = [
24 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
25 ['wlr-layer-shell-unstable-v1.xml'],
26 ['wlr-input-inhibitor-unstable-v1.xml']
27]
28
29server_protocols = [
30 [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
31 ['wlr-layer-shell-unstable-v1.xml'],
32 ['wlr-input-inhibitor-unstable-v1.xml']
33]
34
35client_protos_src = []
36client_protos_headers = []
37
38server_protos_src = []
39server_protos_headers = []
40
41foreach p : client_protocols
42 xml = join_paths(p)
43 client_protos_src += wayland_scanner_code.process(xml)
44 client_protos_headers += wayland_scanner_client.process(xml)
45endforeach
46
47foreach p : server_protocols
48 xml = join_paths(p)
49 server_protos_src += wayland_scanner_code.process(xml)
50 server_protos_headers += wayland_scanner_server.process(xml)
51endforeach
52
53lib_client_protos = static_library(
54 'client_protos',
55 client_protos_src + client_protos_headers,
56 dependencies: [wayland_client]
57) # for the include directory
58
59client_protos = declare_dependency(
60 link_with: lib_client_protos,
61 sources: client_protos_headers,
62)
63
64lib_server_protos = static_library(
65 'server_protos',
66 server_protos_src + server_protos_headers,
67 dependencies: [wayland_client]
68) # for the include directory
69
70server_protos = declare_dependency(
71 link_with: lib_server_protos,
72 sources: server_protos_headers,
73)
diff --git a/protocols/server-decoration.xml b/protocols/server-decoration.xml
deleted file mode 100644
index 8bc106c7..00000000
--- a/protocols/server-decoration.xml
+++ /dev/null
@@ -1,94 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="server_decoration">
3 <copyright><![CDATA[
4 Copyright (C) 2015 Martin Gräßlin
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 ]]></copyright>
19 <interface name="org_kde_kwin_server_decoration_manager" version="1">
20 <description summary="Server side window decoration manager">
21 This interface allows to coordinate whether the server should create
22 a server-side window decoration around a wl_surface representing a
23 shell surface (wl_shell_surface or similar). By announcing support
24 for this interface the server indicates that it supports server
25 side decorations.
26 </description>
27 <request name="create">
28 <description summary="Create a server-side decoration object for a given surface">
29 When a client creates a server-side decoration object it indicates
30 that it supports the protocol. The client is supposed to tell the
31 server whether it wants server-side decorations or will provide
32 client-side decorations.
33
34 If the client does not create a server-side decoration object for
35 a surface the server interprets this as lack of support for this
36 protocol and considers it as client-side decorated. Nevertheless a
37 client-side decorated surface should use this protocol to indicate
38 to the server that it does not want a server-side deco.
39 </description>
40 <arg name="id" type="new_id" interface="org_kde_kwin_server_decoration"/>
41 <arg name="surface" type="object" interface="wl_surface"/>
42 </request>
43 <enum name="mode">
44 <description summary="Possible values to use in request_mode and the event mode."/>
45 <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/>
46 <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
47 <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
48 </enum>
49 <event name="default_mode">
50 <description summary="The default mode used on the server">
51 This event is emitted directly after binding the interface. It contains
52 the default mode for the decoration. When a new server decoration object
53 is created this new object will be in the default mode until the first
54 request_mode is requested.
55
56 The server may change the default mode at any time.
57 </description>
58 <arg name="mode" type="uint" summary="The default decoration mode applied to newly created server decorations."/>
59 </event>
60 </interface>
61 <interface name="org_kde_kwin_server_decoration" version="1">
62 <request name="release" type="destructor">
63 <description summary="release the server decoration object"/>
64 </request>
65 <enum name="mode">
66 <description summary="Possible values to use in request_mode and the event mode."/>
67 <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/>
68 <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
69 <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
70 </enum>
71 <request name="request_mode">
72 <description summary="The decoration mode the surface wants to use."/>
73 <arg name="mode" type="uint" summary="The mode this surface wants to use."/>
74 </request>
75 <event name="mode">
76 <description summary="The new decoration mode applied by the server">
77 This event is emitted directly after the decoration is created and
78 represents the base decoration policy by the server. E.g. a server
79 which wants all surfaces to be client-side decorated will send Client,
80 a server which wants server-side decoration will send Server.
81
82 The client can request a different mode through the decoration request.
83 The server will acknowledge this by another event with the same mode. So
84 even if a server prefers server-side decoration it's possible to force a
85 client-side decoration.
86
87 The server may emit this event at any time. In this case the client can
88 again request a different mode. It's the responsibility of the server to
89 prevent a feedback loop.
90 </description>
91 <arg name="mode" type="uint" summary="The decoration mode applied to the surface by the server."/>
92 </event>
93 </interface>
94</protocol>
diff --git a/protocols/swaylock.xml b/protocols/swaylock.xml
deleted file mode 100644
index c7a102dd..00000000
--- a/protocols/swaylock.xml
+++ /dev/null
@@ -1,18 +0,0 @@
1<protocol name="lock">
2
3 <interface name="lock" version="1">
4 <description summary="create lock screen UIs">
5 The Weston desktop-shell protocol's locking functionality depends more
6 on the behavior of the compositor than of a screen locking client, so
7 another protocol is necessary.
8 </description>
9
10 <request name="set_lock_surface">
11 <arg name="output" type="object" interface="wl_output"/>
12 <arg name="surface" type="object" interface="wl_surface"/>
13 </request>
14
15 <request name="unlock"/>
16
17 </interface>
18</protocol>
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml
new file mode 100644
index 00000000..b62d1bb4
--- /dev/null
+++ b/protocols/wlr-input-inhibitor-unstable-v1.xml
@@ -0,0 +1,67 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="wlr_input_inhibit_unstable_v1">
3 <copyright>
4 Copyright © 2018 Drew DeVault
5
6 Permission to use, copy, modify, distribute, and sell this
7 software and its documentation for any purpose is hereby granted
8 without fee, provided that the above copyright notice appear in
9 all copies and that both that copyright notice and this permission
10 notice appear in supporting documentation, and that the name of
11 the copyright holders not be used in advertising or publicity
12 pertaining to distribution of the software without specific,
13 written prior permission. The copyright holders make no
14 representations about the suitability of this software for any
15 purpose. It is provided "as is" without express or implied
16 warranty.
17
18 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 THIS SOFTWARE.
26 </copyright>
27
28 <interface name="zwlr_input_inhibit_manager_v1" version="1">
29 <description summary="inhibits input events to other clients">
30 Clients can use this interface to prevent input events from being sent to
31 any surfaces but its own, which is useful for example in lock screen
32 software. It is assumed that access to this interface will be locked down
33 to whitelisted clients by the compositor.
34 </description>
35
36 <request name="get_inhibitor">
37 <description summary="inhibit input to other clients">
38 Activates the input inhibitor. As long as the inhibitor is active, the
39 compositor will not send input events to other clients.
40 </description>
41 <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
42 </request>
43
44 <enum name="error">
45 <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
46 </enum>
47 </interface>
48
49 <interface name="zwlr_input_inhibitor_v1" version="1">
50 <description summary="inhibits input to other clients">
51 While this resource exists, input to clients other than the owner of the
52 inhibitor resource will not receive input events. The client that owns
53 this resource will receive all input events normally. The compositor will
54 also disable all of its own input processing (such as keyboard shortcuts)
55 while the inhibitor is active.
56
57 The compositor may continue to send input events to selected clients,
58 such as an on-screen keyboard (via the input-method protocol).
59 </description>
60
61 <request name="destroy" type="destructor">
62 <description summary="destroy the input inhibitor object">
63 Destroy the inhibitor and allow other clients to receive input.
64 </description>
65 </request>
66 </interface>
67</protocol>
diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 00000000..3181c0bb
--- /dev/null
+++ b/protocols/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,281 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="wlr_layer_shell_unstable_v1">
3 <copyright>
4 Copyright © 2017 Drew DeVault
5
6 Permission to use, copy, modify, distribute, and sell this
7 software and its documentation for any purpose is hereby granted
8 without fee, provided that the above copyright notice appear in
9 all copies and that both that copyright notice and this permission
10 notice appear in supporting documentation, and that the name of
11 the copyright holders not be used in advertising or publicity
12 pertaining to distribution of the software without specific,
13 written prior permission. The copyright holders make no
14 representations about the suitability of this software for any
15 purpose. It is provided "as is" without express or implied
16 warranty.
17
18 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 THIS SOFTWARE.
26 </copyright>
27
28 <interface name="zwlr_layer_shell_v1" version="1">
29 <description summary="create surfaces that are layers of the desktop">
30 Clients can use this interface to assign the surface_layer role to
31 wl_surfaces. Such surfaces are assigned to a "layer" of the output and
32 rendered with a defined z-depth respective to each other. They may also be
33 anchored to the edges and corners of a screen and specify input handling
34 semantics. This interface should be suitable for the implementation of
35 many desktop shell components, and a broad number of other applications
36 that interact with the desktop.
37 </description>
38
39 <request name="get_layer_surface">
40 <description summary="create a layer_surface from a surface">
41 Create a layer surface for an existing surface. This assigns the role of
42 layer_surface, or raises a protocol error if another role is already
43 assigned.
44
45 Creating a layer surface from a wl_surface which has a buffer attached
46 or committed is a client error, and any attempts by a client to attach
47 or manipulate a buffer prior to the first layer_surface.configure call
48 must also be treated as errors.
49
50 Clients can specify a namespace that defines the purpose of the layer
51 surface.
52 </description>
53 <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
54 <arg name="surface" type="object" interface="wl_surface"/>
55 <arg name="output" type="object" interface="wl_output"/>
56 <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
57 <arg name="namespace" type="string" summary="namespace for the layer surface"/>
58 </request>
59
60 <enum name="error">
61 <entry name="role" value="0" summary="wl_surface has another role"/>
62 <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
63 <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
64 </enum>
65
66 <enum name="layer">
67 <description summary="available layers for surfaces">
68 These values indicate which layers a surface can be rendered in. They
69 are ordered by z depth, bottom-most first. Traditional shell surfaces
70 will typically be rendered between the bottom and top layers.
71 Fullscreen shell surfaces are typically rendered at the top layer.
72 Multiple surfaces can share a single layer, and ordering within a
73 single layer is undefined.
74 </description>
75
76 <entry name="background" value="0"/>
77 <entry name="bottom" value="1"/>
78 <entry name="top" value="2"/>
79 <entry name="overlay" value="3"/>
80 </enum>
81 </interface>
82
83 <interface name="zwlr_layer_surface_v1" version="1">
84 <description summary="layer metadata interface">
85 An interface that may be implemented by a wl_surface, for surfaces that
86 are designed to be rendered as a layer of a stacked desktop-like
87 environment.
88
89 Layer surface state (size, anchor, exclusive zone, margin, interactivity)
90 is double-buffered, and will be applied at the time wl_surface.commit of
91 the corresponding wl_surface is called.
92 </description>
93
94 <request name="set_size">
95 <description summary="sets the size of the surface">
96 Sets the size of the surface in surface-local coordinates. The
97 compositor will display the surface centered with respect to its
98 anchors.
99
100 If you pass 0 for either value, the compositor will assign it and
101 inform you of the assignment in the configure event. You must set your
102 anchor to opposite edges in the dimensions you omit; not doing so is a
103 protocol error. Both values are 0 by default.
104
105 Size is double-buffered, see wl_surface.commit.
106 </description>
107 <arg name="width" type="uint"/>
108 <arg name="height" type="uint"/>
109 </request>
110
111 <request name="set_anchor">
112 <description summary="configures the anchor point of the surface">
113 Requests that the compositor anchor the surface to the specified edges
114 and corners. If two orthoginal edges are specified (e.g. 'top' and
115 'left'), then the anchor point will be the intersection of the edges
116 (e.g. the top left corner of the output); otherwise the anchor point
117 will be centered on that edge, or in the center if none is specified.
118
119 Anchor is double-buffered, see wl_surface.commit.
120 </description>
121 <arg name="anchor" type="uint" enum="anchor"/>
122 </request>
123
124 <request name="set_exclusive_zone">
125 <description summary="configures the exclusive geometry of this surface">
126 Requests that the compositor avoids occluding an area of the surface
127 with other surfaces. The compositor's use of this information is
128 implementation-dependent - do not assume that this region will not
129 actually be occluded.
130
131 A positive value is only meaningful if the surface is anchored to an
132 edge, rather than a corner. The zone is the number of surface-local
133 coordinates from the edge that are considered exclusive.
134
135 Surfaces that do not wish to have an exclusive zone may instead specify
136 how they should interact with surfaces that do. If set to zero, the
137 surface indicates that it would like to be moved to avoid occluding
138 surfaces with a positive excluzive zone. If set to -1, the surface
139 indicates that it would not like to be moved to accomodate for other
140 surfaces, and the compositor should extend it all the way to the edges
141 it is anchored to.
142
143 For example, a panel might set its exclusive zone to 10, so that
144 maximized shell surfaces are not shown on top of it. A notification
145 might set its exclusive zone to 0, so that it is moved to avoid
146 occluding the panel, but shell surfaces are shown underneath it. A
147 wallpaper or lock screen might set their exclusive zone to -1, so that
148 they stretch below or over the panel.
149
150 The default value is 0.
151
152 Exclusive zone is double-buffered, see wl_surface.commit.
153 </description>
154 <arg name="zone" type="int"/>
155 </request>
156
157 <request name="set_margin">
158 <description summary="sets a margin from the anchor point">
159 Requests that the surface be placed some distance away from the anchor
160 point on the output, in surface-local coordinates. Setting this value
161 for edges you are not anchored to has no effect.
162
163 The exclusive zone includes the margin.
164
165 Margin is double-buffered, see wl_surface.commit.
166 </description>
167 <arg name="top" type="int"/>
168 <arg name="right" type="int"/>
169 <arg name="bottom" type="int"/>
170 <arg name="left" type="int"/>
171 </request>
172
173 <request name="set_keyboard_interactivity">
174 <description summary="requests keyboard events">
175 Set to 1 to request that the seat send keyboard events to this layer
176 surface. For layers below the shell surface layer, the seat will use
177 normal focus semantics. For layers above the shell surface layers, the
178 seat will always give exclusive keyboard focus to the top-most layer
179 which has keyboard interactivity set to true.
180
181 Layer surfaces receive pointer, touch, and tablet events normally. If
182 you do not want to receive them, set the input region on your surface
183 to an empty region.
184
185 Events is double-buffered, see wl_surface.commit.
186 </description>
187 <arg name="keyboard_interactivity" type="uint"/>
188 </request>
189
190 <request name="get_popup">
191 <description summary="assign this layer_surface as an xdg_popup parent">
192 This assigns an xdg_popup's parent to this layer_surface. This popup
193 should have been created via xdg_surface::get_popup with the parent set
194 to NULL, and this request must be invoked before committing the popup's
195 initial state.
196
197 See the documentation of xdg_popup for more details about what an
198 xdg_popup is and how it is used.
199 </description>
200 <arg name="popup" type="object" interface="xdg_popup"/>
201 </request>
202
203 <request name="ack_configure">
204 <description summary="ack a configure event">
205 When a configure event is received, if a client commits the
206 surface in response to the configure event, then the client
207 must make an ack_configure request sometime before the commit
208 request, passing along the serial of the configure event.
209
210 If the client receives multiple configure events before it
211 can respond to one, it only has to ack the last configure event.
212
213 A client is not required to commit immediately after sending
214 an ack_configure request - it may even ack_configure several times
215 before its next surface commit.
216
217 A client may send multiple ack_configure requests before committing, but
218 only the last request sent before a commit indicates which configure
219 event the client really is responding to.
220 </description>
221 <arg name="serial" type="uint" summary="the serial from the configure event"/>
222 </request>
223
224 <request name="destroy" type="destructor">
225 <description summary="destroy the layer_surface">
226 This request destroys the layer surface.
227 </description>
228 </request>
229
230 <event name="configure">
231 <description summary="suggest a surface change">
232 The configure event asks the client to resize its surface.
233
234 Clients should arrange their surface for the new states, and then send
235 an ack_configure request with the serial sent in this configure event at
236 some point before committing the new surface.
237
238 The client is free to dismiss all but the last configure event it
239 received.
240
241 The width and height arguments specify the size of the window in
242 surface-local coordinates.
243
244 The size is a hint, in the sense that the client is free to ignore it if
245 it doesn't resize, pick a smaller size (to satisfy aspect ratio or
246 resize in steps of NxM pixels). If the client picks a smaller size and
247 is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
248 surface will be centered on this axis.
249
250 If the width or height arguments are zero, it means the client should
251 decide its own window dimension.
252 </description>
253 <arg name="serial" type="uint"/>
254 <arg name="width" type="uint"/>
255 <arg name="height" type="uint"/>
256 </event>
257
258 <event name="closed">
259 <description summary="surface should be closed">
260 The closed event is sent by the compositor when the surface will no
261 longer be shown. The output may have been destroyed or the user may
262 have asked for it to be removed. Further changes to the surface will be
263 ignored. The client should destroy the resource after receiving this
264 event, and create a new surface if they so choose.
265 </description>
266 </event>
267
268 <enum name="error">
269 <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
270 <entry name="invalid_size" value="1" summary="size is invalid"/>
271 <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
272 </enum>
273
274 <enum name="anchor" bitfield="true">
275 <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
276 <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
277 <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
278 <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
279 </enum>
280 </interface>
281</protocol>
diff --git a/protocols/xdg-shell.xml b/protocols/xdg-shell.xml
deleted file mode 100644
index 7bf4ae3a..00000000
--- a/protocols/xdg-shell.xml
+++ /dev/null
@@ -1,430 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="xdg_shell">
3
4 <copyright>
5 Copyright © 2008-2013 Kristian Høgsberg
6 Copyright © 2013 Rafael Antognolli
7 Copyright © 2013 Jasper St. Pierre
8 Copyright © 2010-2013 Intel Corporation
9
10 Permission to use, copy, modify, distribute, and sell this
11 software and its documentation for any purpose is hereby granted
12 without fee, provided that the above copyright notice appear in
13 all copies and that both that copyright notice and this permission
14 notice appear in supporting documentation, and that the name of
15 the copyright holders not be used in advertising or publicity
16 pertaining to distribution of the software without specific,
17 written prior permission. The copyright holders make no
18 representations about the suitability of this software for any
19 purpose. It is provided "as is" without express or implied
20 warranty.
21
22 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
23 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
26 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
29 THIS SOFTWARE.
30 </copyright>
31
32 <interface name="xdg_shell" version="1">
33 <description summary="create desktop-style surfaces">
34 This interface is implemented by servers that provide
35 desktop-style user interfaces.
36
37 It allows clients to associate a xdg_surface with
38 a basic surface.
39 </description>
40
41 <enum name="version">
42 <description summary="latest protocol version">
43 The 'current' member of this enum gives the version of the
44 protocol. Implementations can compare this to the version
45 they implement using static_assert to ensure the protocol and
46 implementation versions match.
47 </description>
48 <entry name="current" value="4" summary="Always the latest version"/>
49 </enum>
50
51 <enum name="error">
52 <entry name="role" value="0" summary="given wl_surface has another role"/>
53 </enum>
54
55 <request name="use_unstable_version">
56 <description summary="enable use of this unstable version">
57 Negotiate the unstable version of the interface. This
58 mechanism is in place to ensure client and server agree on the
59 unstable versions of the protocol that they speak or exit
60 cleanly if they don't agree. This request will go away once
61 the xdg-shell protocol is stable.
62 </description>
63 <arg name="version" type="int"/>
64 </request>
65
66 <request name="get_xdg_surface">
67 <description summary="create a shell surface from a surface">
68 Create a shell surface for an existing surface.
69
70 This request gives the surface the role of xdg_surface. If the
71 surface already has another role, it raises a protocol error.
72
73 Only one shell or popup surface can be associated with a given
74 surface.
75 </description>
76 <arg name="id" type="new_id" interface="xdg_surface"/>
77 <arg name="surface" type="object" interface="wl_surface"/>
78 </request>
79
80 <request name="get_xdg_popup">
81 <description summary="create a shell surface from a surface">
82 Create a popup surface for an existing surface.
83
84 This request gives the surface the role of xdg_popup. If the
85 surface already has another role, it raises a protocol error.
86
87 Only one shell or popup surface can be associated with a given
88 surface.
89 </description>
90 <arg name="id" type="new_id" interface="xdg_popup"/>
91 <arg name="surface" type="object" interface="wl_surface"/>
92 <arg name="parent" type="object" interface="wl_surface"/>
93 <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
94 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
95 <arg name="x" type="int"/>
96 <arg name="y" type="int"/>
97 <arg name="flags" type="uint"/>
98 </request>
99
100 <event name="ping">
101 <description summary="check if the client is alive">
102 The ping event asks the client if it's still alive. Pass the
103 serial specified in the event back to the compositor by sending
104 a "pong" request back with the specified serial.
105
106 Compositors can use this to determine if the client is still
107 alive. It's unspecified what will happen if the client doesn't
108 respond to the ping request, or in what timeframe. Clients should
109 try to respond in a reasonable amount of time.
110 </description>
111 <arg name="serial" type="uint" summary="pass this to the callback"/>
112 </event>
113
114 <request name="pong">
115 <description summary="respond to a ping event">
116 A client must respond to a ping event with a pong request or
117 the client may be deemed unresponsive.
118 </description>
119 <arg name="serial" type="uint" summary="serial of the ping event"/>
120 </request>
121 </interface>
122
123 <interface name="xdg_surface" version="1">
124
125 <description summary="desktop-style metadata interface">
126 An interface that may be implemented by a wl_surface, for
127 implementations that provide a desktop-style user interface.
128
129 It provides requests to treat surfaces like windows, allowing to set
130 properties like maximized, fullscreen, minimized, and to move and resize
131 them, and associate metadata like title and app id.
132
133 On the server side the object is automatically destroyed when
134 the related wl_surface is destroyed. On client side,
135 xdg_surface.destroy() must be called before destroying
136 the wl_surface object.
137 </description>
138
139 <request name="destroy" type="destructor">
140 <description summary="remove xdg_surface interface">
141 The xdg_surface interface is removed from the wl_surface object
142 that was turned into a xdg_surface with
143 xdg_shell.get_xdg_surface request. The xdg_surface properties,
144 like maximized and fullscreen, are lost. The wl_surface loses
145 its role as a xdg_surface. The wl_surface is unmapped.
146 </description>
147 </request>
148
149 <request name="set_parent">
150 <description summary="surface is a child of another surface">
151 Child surfaces are stacked above their parents, and will be
152 unmapped if the parent is unmapped too. They should not appear
153 on task bars and alt+tab.
154 </description>
155 <arg name="parent" type="object" interface="wl_surface" allow-null="true"/>
156 </request>
157
158 <request name="set_title">
159 <description summary="set surface title">
160 Set a short title for the surface.
161
162 This string may be used to identify the surface in a task bar,
163 window list, or other user interface elements provided by the
164 compositor.
165
166 The string must be encoded in UTF-8.
167 </description>
168 <arg name="title" type="string"/>
169 </request>
170
171 <request name="set_app_id">
172 <description summary="set surface class">
173 Set an id for the surface.
174
175 The app id identifies the general class of applications to which
176 the surface belongs.
177
178 It should be the ID that appears in the new desktop entry
179 specification, the interface name.
180 </description>
181 <arg name="app_id" type="string"/>
182 </request>
183
184 <request name="show_window_menu">
185 <description summary="show the window menu">
186 Clients implementing client-side decorations might want to show
187 a context menu when right-clicking on the decorations, giving the
188 user a menu that they can use to maximize or minimize the window.
189
190 This request asks the compositor to pop up such a window menu at
191 the given position, relative to the parent surface. There are
192 no guarantees as to what the window menu contains.
193
194 Your surface must have focus on the seat passed in to pop up the
195 window menu.
196 </description>
197
198 <arg name="seat" type="object" interface="wl_seat" summary="the seat to pop the window up on"/>
199 <arg name="serial" type="uint" summary="serial of the event to pop up the window for"/>
200 <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
201 <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
202 </request>
203
204 <request name="move">
205 <description summary="start an interactive move">
206 Start a pointer-driven move of the surface.
207
208 This request must be used in response to a button press event.
209 The server may ignore move requests depending on the state of
210 the surface (e.g. fullscreen or maximized).
211 </description>
212 <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
213 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
214 </request>
215
216 <enum name="resize_edge">
217 <description summary="edge values for resizing">
218 These values are used to indicate which edge of a surface
219 is being dragged in a resize operation. The server may
220 use this information to adapt its behavior, e.g. choose
221 an appropriate cursor image.
222 </description>
223 <entry name="none" value="0"/>
224 <entry name="top" value="1"/>
225 <entry name="bottom" value="2"/>
226 <entry name="left" value="4"/>
227 <entry name="top_left" value="5"/>
228 <entry name="bottom_left" value="6"/>
229 <entry name="right" value="8"/>
230 <entry name="top_right" value="9"/>
231 <entry name="bottom_right" value="10"/>
232 </enum>
233
234 <request name="resize">
235 <description summary="start an interactive resize">
236 Start a pointer-driven resizing of the surface.
237
238 This request must be used in response to a button press event.
239 The server may ignore resize requests depending on the state of
240 the surface (e.g. fullscreen or maximized).
241 </description>
242 <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
243 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
244 <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
245 </request>
246
247 <enum name="state">
248 <description summary="types of state on the surface">
249 The different state values used on the surface. This is designed for
250 state values like maximized, fullscreen. It is paired with the
251 configure event to ensure that both the client and the compositor
252 setting the state can be synchronized.
253
254 States set in this way are double-buffered. They will get applied on
255 the next commit.
256
257 Desktop environments may extend this enum by taking up a range of
258 values and documenting the range they chose in this description.
259 They are not required to document the values for the range that they
260 chose. Ideally, any good extensions from a desktop environment should
261 make its way into standardization into this enum.
262
263 The current reserved ranges are:
264
265 0x0000 - 0x0FFF: xdg-shell core values, documented below.
266 0x1000 - 0x1FFF: GNOME
267 </description>
268 <entry name="maximized" value="1">
269 <description summary="the surface is maximized">
270 The surface is maximized. The window geometry specified in the configure
271 event must be obeyed by the client.
272 </description>
273 </entry>
274 <entry name="fullscreen" value="2">
275 <description summary="the surface is fullscreen">
276 The surface is fullscreen. The window geometry specified in the configure
277 event must be obeyed by the client.
278 </description>
279 </entry>
280 <entry name="resizing" value="3">
281 <description summary="the surface is being resized">
282 The surface is being resized. The window geometry specified in the
283 configure event is a maximum; the client cannot resize beyond it.
284 Clients that have aspect ratio or cell sizing configuration can use
285 a smaller size, however.
286 </description>
287 </entry>
288 <entry name="activated" value="4">
289 <description summary="the client window is active">
290 Client window decorations should be painted as if the window is
291 active. Do not assume this means that the window actually has
292 keyboard or pointer focus.
293 </description>
294 </entry>
295 </enum>
296
297 <event name="configure">
298 <description summary="suggest a surface change">
299 The configure event asks the client to resize its surface.
300
301 The width and height arguments specify a hint to the window
302 about how its surface should be resized in window geometry
303 coordinates. The states listed in the event specify how the
304 width/height arguments should be interpreted.
305
306 A client should arrange a new surface, and then send a
307 ack_configure request with the serial sent in this configure
308 event before attaching a new surface.
309
310 If the client receives multiple configure events before it
311 can respond to one, it is free to discard all but the last
312 event it received.
313 </description>
314
315 <arg name="width" type="int"/>
316 <arg name="height" type="int"/>
317 <arg name="states" type="array"/>
318 <arg name="serial" type="uint"/>
319 </event>
320
321 <request name="ack_configure">
322 <description summary="ack a configure event">
323 When a configure event is received, a client should then ack it
324 using the ack_configure request to ensure that the compositor
325 knows the client has seen the event.
326
327 By this point, the state is confirmed, and the next attach should
328 contain the buffer drawn for the configure event you are acking.
329 </description>
330 <arg name="serial" type="uint" summary="a serial to configure for"/>
331 </request>
332
333 <request name="set_window_geometry">
334 <description summary="set the new window geometry">
335 The window geometry of a window is its "visible bounds" from the
336 user's perspective. Client-side decorations often have invisible
337 portions like drop-shadows which should be ignored for the
338 purposes of aligning, placing and constraining windows.
339
340 The default value is the full bounds of the surface, including any
341 subsurfaces. Once the window geometry of the surface is set once,
342 it is not possible to unset it, and it will remain the same until
343 set_window_geometry is called again, even if a new subsurface or
344 buffer is attached.
345
346 If responding to a configure event, the window geometry in here
347 must respect the sizing negotiations specified by the states in
348 the configure event.
349 </description>
350 <arg name="x" type="int"/>
351 <arg name="y" type="int"/>
352 <arg name="width" type="int"/>
353 <arg name="height" type="int"/>
354 </request>
355
356 <request name="set_maximized" />
357 <request name="unset_maximized" />
358
359 <request name="set_fullscreen">
360 <description summary="set the window as fullscreen on a monitor">
361 Make the surface fullscreen.
362
363 You can specify an output that you would prefer to be fullscreen.
364 If this value is NULL, it's up to the compositor to choose which
365 display will be used to map this surface.
366 </description>
367 <arg name="output" type="object" interface="wl_output" allow-null="true"/>
368 </request>
369 <request name="unset_fullscreen" />
370
371 <request name="set_minimized" />
372
373 <event name="close">
374 <description summary="surface wants to be closed">
375 The close event is sent by the compositor when the user
376 wants the surface to be closed. This should be equivalent to
377 the user clicking the close button in client-side decorations,
378 if your application has any...
379
380 This is only a request that the user intends to close your
381 window. The client may choose to ignore this request, or show
382 a dialog to ask the user to save their data...
383 </description>
384 </event>
385 </interface>
386
387 <interface name="xdg_popup" version="1">
388 <description summary="desktop-style metadata interface">
389 An interface that may be implemented by a wl_surface, for
390 implementations that provide a desktop-style popups/menus. A popup
391 surface is a transient surface with an added pointer grab.
392
393 An existing implicit grab will be changed to owner-events mode,
394 and the popup grab will continue after the implicit grab ends
395 (i.e. releasing the mouse button does not cause the popup to be
396 unmapped).
397
398 The popup grab continues until the window is destroyed or a mouse
399 button is pressed in any other clients window. A click in any of
400 the clients surfaces is reported as normal, however, clicks in
401 other clients surfaces will be discarded and trigger the callback.
402
403 The x and y arguments specify the locations of the upper left
404 corner of the surface relative to the upper left corner of the
405 parent surface, in surface local coordinates.
406
407 xdg_popup surfaces are always transient for another surface.
408 </description>
409
410 <request name="destroy" type="destructor">
411 <description summary="remove xdg_surface interface">
412 The xdg_surface interface is removed from the wl_surface object
413 that was turned into a xdg_surface with
414 xdg_shell.get_xdg_surface request. The xdg_surface properties,
415 like maximized and fullscreen, are lost. The wl_surface loses
416 its role as a xdg_surface. The wl_surface is unmapped.
417 </description>
418 </request>
419
420 <event name="popup_done">
421 <description summary="popup interaction is done">
422 The popup_done event is sent out when a popup grab is broken,
423 that is, when the users clicks a surface that doesn't belong
424 to the client owning the popup surface.
425 </description>
426 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
427 </event>
428
429 </interface>
430</protocol>
diff --git a/security.d/00-defaults.in b/security.d/00-defaults.in
index 05098dea..e4626477 100644
--- a/security.d/00-defaults.in
+++ b/security.d/00-defaults.in
@@ -6,17 +6,17 @@
6# installation. 6# installation.
7# 7#
8# DO NOT CHANGE THIS FILE. Override these defaults by writing new files in 8# DO NOT CHANGE THIS FILE. Override these defaults by writing new files in
9# __SYSCONFDIR__/sway/security.d/* 9# @sysconfdir@/sway/security.d/*
10 10
11# Configures enabled compositor features for specific programs 11# Configures enabled compositor features for specific programs
12permit * fullscreen keyboard mouse 12permit * fullscreen keyboard mouse
13permit __PREFIX__/bin/swaylock lock 13permit @prefix@/bin/swaylock lock
14permit __PREFIX__/bin/swaybg background 14permit @prefix@/bin/swaybg background
15permit __PREFIX__/bin/swaygrab screenshot 15permit @prefix@/bin/swaygrab screenshot
16permit __PREFIX__/bin/swaybar panel 16permit @prefix@/bin/swaybar panel
17 17
18# Configures enabled IPC features for specific programs 18# Configures enabled IPC features for specific programs
19ipc __PREFIX__/bin/swaymsg { 19ipc @prefix@/bin/swaymsg {
20 * enabled 20 * enabled
21 21
22 events { 22 events {
@@ -24,7 +24,7 @@ ipc __PREFIX__/bin/swaymsg {
24 } 24 }
25} 25}
26 26
27ipc __PREFIX__/bin/swaybar { 27ipc @prefix@/bin/swaybar {
28 bar-config enabled 28 bar-config enabled
29 outputs enabled 29 outputs enabled
30 workspaces enabled 30 workspaces enabled
@@ -36,12 +36,12 @@ ipc __PREFIX__/bin/swaybar {
36 } 36 }
37} 37}
38 38
39ipc __PREFIX__/bin/swaygrab { 39ipc @prefix@/bin/swaygrab {
40 outputs enabled 40 outputs enabled
41 tree enabled 41 tree enabled
42} 42}
43 43
44ipc __PREFIX__/bin/swaylock { 44ipc @prefix@/bin/swaylock {
45 outputs enabled 45 outputs enabled
46} 46}
47 47
diff --git a/sway/CMakeLists.txt b/sway/CMakeLists.txt
deleted file mode 100644
index 48f7a7a8..00000000
--- a/sway/CMakeLists.txt
+++ /dev/null
@@ -1,107 +0,0 @@
1include_directories(
2 ${PROTOCOLS_INCLUDE_DIRS}
3 ${WLC_INCLUDE_DIRS}
4 ${PCRE_INCLUDE_DIRS}
5 ${JSONC_INCLUDE_DIRS}
6 ${XKBCOMMON_INCLUDE_DIRS}
7 ${LIBINPUT_INCLUDE_DIRS}
8 ${CAIRO_INCLUDE_DIRS}
9 ${PANGO_INCLUDE_DIRS}
10 ${WAYLAND_INCLUDE_DIR}
11)
12
13file(GLOB cmds
14 "commands/*.c"
15 "commands/bar/*.c"
16 "commands/input/*.c"
17)
18
19add_executable(sway
20 commands.c
21 ${cmds}
22 base64.c
23 config.c
24 container.c
25 criteria.c
26 debug_log.c
27 extensions.c
28 focus.c
29 handlers.c
30 input.c
31 input_state.c
32 ipc-json.c
33 ipc-server.c
34 layout.c
35 main.c
36 output.c
37 workspace.c
38 border.c
39 security.c
40)
41
42add_definitions(
43 -DSYSCONFDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}"
44)
45
46target_link_libraries(sway
47 sway-common
48 sway-protocols
49 sway-wayland
50 ${WLC_LIBRARIES}
51 ${XKBCOMMON_LIBRARIES}
52 ${PCRE_LIBRARIES}
53 ${JSONC_LIBRARIES}
54 ${WAYLAND_SERVER_LIBRARIES}
55 ${LIBINPUT_LIBRARIES}
56 ${PANGO_LIBRARIES}
57 ${JSONC_LIBRARIES}
58 m
59)
60
61if (CMAKE_SYSTEM_NAME STREQUAL Linux)
62 target_link_libraries(sway cap)
63endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
64
65install(
66 TARGETS sway
67 RUNTIME
68 DESTINATION bin
69 COMPONENT runtime
70)
71
72add_custom_target(configs ALL)
73
74function(add_config name source destination)
75 add_custom_command(
76 OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
77 COMMAND sed -r
78 's?__PREFIX__?${CMAKE_INSTALL_PREFIX}?g\; s?__SYSCONFDIR__?${CMAKE_INSTALL_FULL_SYSCONFDIR}?g\; s?__DATADIR__?${CMAKE_INSTALL_FULL_DATADIR}?g'
79 ${PROJECT_SOURCE_DIR}/${source}.in > ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
80 DEPENDS ${PROJECT_SOURCE_DIR}/${source}.in
81 COMMENT "Generating config file ${source}"
82 )
83
84 install(
85 FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
86 DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/${destination}
87 COMPONENT configuration
88 )
89
90 add_custom_target(config-${name} DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name})
91 add_dependencies(configs config-${name})
92endfunction()
93
94add_config(config config sway)
95add_config(00-defaults security.d/00-defaults sway/security.d)
96
97if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
98 add_config(10-freebsd security.d/10-freebsd sway/security.d)
99endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
100
101if (A2X_FOUND)
102 add_manpage(sway 1)
103 add_manpage(sway 5)
104 add_manpage(sway-input 5)
105 add_manpage(sway-bar 5)
106 add_manpage(sway-security 7)
107endif()
diff --git a/sway/border.c b/sway/border.c
deleted file mode 100644
index df0022ce..00000000
--- a/sway/border.c
+++ /dev/null
@@ -1,510 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <wlc/wlc-render.h>
3#include <cairo/cairo.h>
4#include <pango/pangocairo.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8#include <strings.h>
9#include <arpa/inet.h>
10#include "sway/border.h"
11#include "sway/container.h"
12#include "sway/config.h"
13#include "client/pango.h"
14
15void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
16 color = htonl(color);
17
18 cairo_set_source_rgba(cairo,
19 (color >> (2*8) & 0xFF) / 255.0,
20 (color >> (1*8) & 0xFF) / 255.0,
21 (color >> (0*8) & 0xFF) / 255.0,
22 (color >> (3*8) & 0xFF) / 255.0);
23}
24
25void border_clear(struct border *border) {
26 if (border && border->buffer) {
27 free(border->buffer);
28 border->buffer = NULL;
29 }
30}
31
32static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry g, cairo_surface_t **surface) {
33 if (view->border == NULL) {
34 view->border = malloc(sizeof(struct border));
35 if (!view->border) {
36 sway_log(L_ERROR, "Unable to allocate window border information");
37 return NULL;
38 }
39 }
40 cairo_t *cr;
41 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, g.size.w);
42 view->border->buffer = calloc(stride * g.size.h, sizeof(unsigned char));
43 view->border->geometry = g;
44 if (!view->border->buffer) {
45 sway_log(L_ERROR, "Unable to allocate window border buffer");
46 return NULL;
47 }
48 *surface = cairo_image_surface_create_for_data(view->border->buffer,
49 CAIRO_FORMAT_ARGB32, g.size.w, g.size.h, stride);
50 if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) {
51 border_clear(view->border);
52 sway_log(L_ERROR, "Unable to allocate window border surface");
53 return NULL;
54 }
55 cr = cairo_create(*surface);
56 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
57 if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
58 cairo_surface_destroy(*surface);
59 border_clear(view->border);
60 sway_log(L_ERROR, "Unable to create cairo context");
61 return NULL;
62 }
63 return cr;
64}
65
66// TODO: move to client/cairo.h when local set_source_u32 is fixed.
67/**
68 * Renders a sharp line of any width and height.
69 *
70 * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
71 * if the line has a width/height of one pixel, respectively.
72 */
73static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
74 cairo_set_source_u32(cairo, color);
75
76 if (width > 1 && height > 1) {
77 cairo_rectangle(cairo, x, y, width, height);
78 cairo_fill(cairo);
79 } else {
80 if (width == 1) {
81 x += 0.5;
82 height += y;
83 width = x;
84 }
85
86 if (height == 1) {
87 y += 0.5;
88 width += x;
89 height = y;
90 }
91
92 cairo_move_to(cairo, x, y);
93 cairo_set_line_width(cairo, 1.0);
94 cairo_line_to(cairo, width, height);
95 cairo_stroke(cairo);
96 }
97}
98
99int get_font_text_height(const char *font) {
100 cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
101 cairo_t *cr = cairo_create(surface);
102 int width, height;
103 get_text_size(cr, font, &width, &height, 1, false, "Gg");
104 cairo_surface_destroy(surface);
105 cairo_destroy(cr);
106 return height;
107}
108
109static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors, bool top) {
110 struct wlc_geometry *g = &view->border->geometry;
111 struct wlc_geometry *b = &view->border_geometry;
112 struct wlc_geometry *v = &view->actual_geometry;
113 enum swayc_layouts layout = view->parent->layout;
114 uint32_t color;
115
116 int x = b->origin.x - g->origin.x;
117 int y = b->origin.y - g->origin.y;
118
119 // draw vertical/horizontal indicator if container is the only child of its parent container
120 bool is_only_child = view->parent && view->parent->children && view->parent->children->length == 1;
121
122 // left border
123 int left_border = v->origin.x - b->origin.x;
124 if (left_border > 0) {
125 render_sharp_line(cr,
126 colors->child_border,
127 x, y,
128 left_border,
129 b->size.h);
130 }
131
132 // right border
133 int right_border = b->size.w - v->size.w - left_border;
134 if (right_border > 0) {
135 if (is_only_child && layout == L_HORIZ && !view->is_floating) {
136 color = colors->indicator;
137 } else {
138 color = colors->child_border;
139 }
140 render_sharp_line(cr,
141 color,
142 x + b->size.w - right_border,
143 y,
144 right_border,
145 b->size.h);
146 }
147
148 // top border
149 int top_border = v->origin.y - b->origin.y;
150 if (top && top_border > 0) {
151 render_sharp_line(cr,
152 colors->child_border,
153 x, y,
154 b->size.w,
155 top_border);
156 }
157
158 // bottom border
159 int bottom_border = b->size.h - (top_border + v->size.h);
160 if (bottom_border > 0) {
161 if (is_only_child && layout == L_VERT && !view->is_floating) {
162 color = colors->indicator;
163 } else {
164 color = colors->child_border;
165 }
166 render_sharp_line(cr,
167 color,
168 x,
169 y + b->size.h - bottom_border,
170 b->size.w,
171 bottom_border);
172 }
173}
174
175static void render_title_bar(swayc_t *view, cairo_t *cr, struct wlc_geometry *b, struct border_colors *colors) {
176 struct wlc_geometry *tb = &view->title_bar_geometry;
177 int x = MIN(tb->origin.x, tb->origin.x - b->origin.x);
178 int y = MIN(tb->origin.y, tb->origin.y - b->origin.y);
179
180 // title bar background
181 cairo_set_source_u32(cr, colors->background);
182 cairo_rectangle(cr, x, y, tb->size.w, tb->size.h);
183 cairo_fill(cr);
184
185 // header top line
186 render_sharp_line(cr, colors->border, x, y, tb->size.w, 1);
187
188 // text
189 if (view->name) {
190 int width, height;
191 get_text_size(cr, config->font, &width, &height, 1, false, "%s", view->name);
192 cairo_move_to(cr, x + 2, y + 2);
193 cairo_set_source_u32(cr, colors->text);
194 pango_printf(cr, config->font, 1, false, "%s", view->name);
195 }
196 // Marks
197 if (config->show_marks && view->marks) {
198 int total_len = 0;
199
200 for(int i = view->marks->length - 1; i >= 0; --i) {
201 char *mark = (char *)view->marks->items[i];
202 if (*mark != '_') {
203 int width, height;
204 get_text_size(cr, config->font, &width, &height, 1, false, "[%s]", mark);
205 total_len += width;
206 if ((int)tb->size.w + x - (total_len + 2) < x + 2) {
207 break;
208 } else {
209 cairo_move_to(cr, (int)tb->size.w + x - (total_len + 2), y + 2);
210 cairo_set_source_u32(cr, colors->text);
211 pango_printf(cr, config->font, 1, false, "[%s]", mark);
212 }
213 }
214 }
215 }
216
217 // titlebars has a border all around for tabbed layouts
218 if (view->parent->layout == L_TABBED) {
219 // header bottom line
220 render_sharp_line(cr, colors->border, x, y + tb->size.h - 1,
221 tb->size.w, 1);
222
223 // left border
224 render_sharp_line(cr, colors->border, x, y, 1, tb->size.h);
225
226 // right border
227 render_sharp_line(cr, colors->border, x + tb->size.w - 1, y,
228 1, tb->size.h);
229
230 return;
231 }
232
233 if ((uint32_t)(view->actual_geometry.origin.y - tb->origin.y) == tb->size.h) {
234 // header bottom line
235 render_sharp_line(cr, colors->border,
236 x + view->actual_geometry.origin.x - tb->origin.x,
237 y + tb->size.h - 1,
238 view->actual_geometry.size.w, 1);
239 } else {
240 // header bottom line
241 render_sharp_line(cr, colors->border, x,
242 y + tb->size.h - 1,
243 tb->size.w, 1);
244 }
245}
246
247/**
248 * Generate nested container title for tabbed/stacked layouts
249 */
250static char *generate_container_title(swayc_t *container) {
251 char layout = 'H';
252 char *name, *prev_name = NULL;
253 switch (container->layout) {
254 case L_TABBED:
255 layout = 'T';
256 break;
257 case L_STACKED:
258 layout = 'S';
259 break;
260 case L_VERT:
261 layout = 'V';
262 break;
263 default:
264 layout = 'H';
265 }
266 int len = 9;
267 name = malloc(len * sizeof(char));
268 if (!name) {
269 sway_log(L_ERROR, "Unable to allocate container title");
270 return NULL;
271 }
272 snprintf(name, len, "sway: %c[", layout);
273
274 int i;
275 for (i = 0; i < container->children->length; ++i) {
276 prev_name = name;
277 swayc_t* child = container->children->items[i];
278 const char *title = NULL;
279 if (child->type == C_VIEW) {
280 title = child->app_id ? child->app_id :
281 (child->instance ? child->instance :
282 (child->class ? child->class :"(null)"));
283 } else { //child->type == C_CONTAINER
284 title = generate_container_title(child);
285 }
286
287 len = strlen(name) + strlen(title) + 1;
288 if (i < container->children->length-1) {
289 len++;
290 }
291
292 name = malloc(len * sizeof(char));
293 if (!name) {
294 free(prev_name);
295 sway_log(L_ERROR, "Unable to allocate container title");
296 return NULL;
297 }
298 if (i < container->children->length-1) {
299 snprintf(name, len, "%s%s ", prev_name, title);
300 } else {
301 snprintf(name, len, "%s%s", prev_name, title);
302 }
303 free(prev_name);
304 }
305
306 prev_name = name;
307 len = strlen(name) + 2;
308 name = malloc(len * sizeof(char));
309 if (!name) {
310 free(prev_name);
311 sway_log(L_ERROR, "Unable to allocate container title");
312 return NULL;
313 }
314 snprintf(name, len, "%s]", prev_name);
315 free(prev_name);
316 free(container->name);
317 container->name = name;
318 return container->name + 6; // don't include "sway: "
319}
320
321void update_tabbed_stacked_titlebars(swayc_t *c, cairo_t *cr, struct wlc_geometry *g, swayc_t *focused, swayc_t *focused_inactive) {
322 if (c->type == C_CONTAINER) {
323 if (c->parent->focused == c) {
324 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
325 } else {
326 render_title_bar(c, cr, g, &config->border_colors.unfocused);
327 }
328
329 if (!c->visible) {
330 return;
331 }
332
333 int i;
334 for (i = 0; i < c->children->length; ++i) {
335 swayc_t *child = c->children->items[i];
336 update_tabbed_stacked_titlebars(child, cr, g, focused, focused_inactive);
337 }
338 } else {
339 bool is_child_of_focused = swayc_is_child_of(c, get_focused_container(&root_container));
340
341 if (focused == c || is_child_of_focused) {
342 render_title_bar(c, cr, g, &config->border_colors.focused);
343 } else if (focused_inactive == c) {
344 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
345 } else {
346 render_title_bar(c, cr, g, &config->border_colors.unfocused);
347 }
348 }
349}
350
351static void update_view_border(swayc_t *view) {
352 if (!view->visible) {
353 return;
354 }
355
356 cairo_t *cr = NULL;
357 cairo_surface_t *surface = NULL;
358
359 // clear previous border buffer.
360 border_clear(view->border);
361
362 // get focused and focused_inactive views
363 swayc_t *focused = get_focused_view(&root_container);
364 swayc_t *container = swayc_parent_by_type(view, C_CONTAINER);
365 swayc_t *focused_inactive = NULL;
366
367 bool is_child_of_focused = swayc_is_parent_of(get_focused_container(&root_container), view);
368
369 if (container) {
370 focused_inactive = swayc_focus_by_type(container, C_VIEW);
371 } else {
372 container = swayc_parent_by_type(view, C_WORKSPACE);
373 if (container) {
374 focused_inactive = swayc_focus_by_type(container, C_VIEW);
375 }
376 }
377
378 // for tabbed/stacked layouts the focused view has to draw all the
379 // titlebars of the hidden views.
380 swayc_t *p = NULL;
381 if (view->parent->focused == view && (p = swayc_tabbed_stacked_ancestor(view))) {
382 struct wlc_geometry g = {
383 .origin = {
384 .x = p->x,
385 .y = p->y
386 },
387 .size = {
388 .w = p->width,
389 .h = p->height
390 }
391 };
392 cr = create_border_buffer(view, g, &surface);
393 if (!cr) {
394 goto cleanup;
395 }
396
397 bool render_top = !should_hide_top_border(view, view->y);
398 if (view == focused || is_child_of_focused) {
399 render_borders(view, cr, &config->border_colors.focused, render_top);
400 } else {
401 render_borders(view, cr, &config->border_colors.focused_inactive, render_top);
402 }
403
404 // generate container titles
405 int i;
406 for (i = 0; i < p->children->length; ++i) {
407 swayc_t *child = p->children->items[i];
408 if (child->type == C_CONTAINER) {
409 generate_container_title(child);
410 }
411 }
412
413 update_tabbed_stacked_titlebars(p, cr, &g, focused, focused_inactive);
414 } else {
415 switch (view->border_type) {
416 case B_NONE:
417 break;
418 case B_PIXEL:
419 cr = create_border_buffer(view, view->border_geometry, &surface);
420 if (!cr) {
421 break;
422 }
423
424 if (focused == view || is_child_of_focused) {
425 render_borders(view, cr, &config->border_colors.focused, true);
426 } else if (focused_inactive == view) {
427 render_borders(view, cr, &config->border_colors.focused_inactive, true);
428 } else {
429 render_borders(view, cr, &config->border_colors.unfocused, true);
430 }
431
432 break;
433 case B_NORMAL:
434 cr = create_border_buffer(view, view->border_geometry, &surface);
435 if (!cr) {
436 break;
437 }
438
439 if (focused == view || is_child_of_focused) {
440 render_borders(view, cr, &config->border_colors.focused, false);
441 render_title_bar(view, cr, &view->border_geometry,
442 &config->border_colors.focused);
443 } else if (focused_inactive == view) {
444 render_borders(view, cr, &config->border_colors.focused_inactive, false);
445 render_title_bar(view, cr, &view->border_geometry,
446 &config->border_colors.focused_inactive);
447 } else {
448 render_borders(view, cr, &config->border_colors.unfocused, false);
449 render_title_bar(view, cr, &view->border_geometry,
450 &config->border_colors.unfocused);
451 }
452
453 break;
454 }
455 }
456
457cleanup:
458
459 if (surface) {
460 cairo_surface_flush(surface);
461 cairo_surface_destroy(surface);
462 }
463
464 if (cr) {
465 cairo_destroy(cr);
466 }
467}
468
469void update_container_border(swayc_t *container) {
470 if (container->type == C_VIEW) {
471 update_view_border(container);
472 return;
473 } else {
474 for (int i = 0; i < container->children->length; ++i) {
475 update_container_border(container->children->items[i]);
476 }
477 }
478}
479
480void render_view_borders(wlc_handle view) {
481 swayc_t *c = swayc_by_handle(view);
482
483
484 // emulate i3 behavior for drawing borders for tabbed and stacked layouts:
485 // if we are not the only child in the container, always draw borders,
486 // regardless of the border setting on the individual view
487 if (!c || (c->border_type == B_NONE
488 && !((c->parent->layout == L_TABBED || c->parent->layout == L_STACKED)
489 && c->parent->children->length > 1))) {
490 return;
491 }
492
493 if (c->border && c->border->buffer) {
494 wlc_pixels_write(WLC_RGBA8888, &c->border->geometry, c->border->buffer);
495 }
496}
497
498bool should_hide_top_border(swayc_t *con, double y) {
499 // returns true if container is child of tabbed/stacked layout and is
500 // sharing top border with tabbed titlebar
501 swayc_t *par = con->parent;
502 while (par->type != C_WORKSPACE) {
503 if (par->layout == L_TABBED || par->layout == L_STACKED) {
504 return con->y == y;
505 }
506 con = par;
507 par = par->parent;
508 }
509 return false;
510}
diff --git a/sway/commands.c b/sway/commands.c
index c7dbf731..54d84450 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -1,39 +1,18 @@
1#define _XOPEN_SOURCE 700 1#define _XOPEN_SOURCE 500
2#include <xkbcommon/xkbcommon.h> 2#include <ctype.h>
3#include <xkbcommon/xkbcommon-names.h> 3#include <stdarg.h>
4#include <wlc/wlc.h>
5#include <wlc/wlc-render.h>
6#include <stdio.h>
7#include <stdlib.h> 4#include <stdlib.h>
8#include <errno.h>
9#include <string.h> 5#include <string.h>
10#include <strings.h> 6#include <strings.h>
11#include <unistd.h> 7#include <stdio.h>
12#include <ctype.h> 8#include <json-c/json.h>
13#include <wordexp.h>
14#include <libgen.h>
15#include <sys/types.h>
16#include <sys/wait.h>
17#include <limits.h>
18#include <float.h>
19#include <libinput.h>
20#include "sway/layout.h"
21#include "sway/focus.h"
22#include "sway/workspace.h"
23#include "sway/commands.h" 9#include "sway/commands.h"
24#include "sway/container.h" 10#include "sway/config.h"
25#include "sway/output.h"
26#include "sway/handlers.h"
27#include "sway/input_state.h"
28#include "sway/criteria.h" 11#include "sway/criteria.h"
29#include "sway/ipc-server.h"
30#include "sway/security.h" 12#include "sway/security.h"
31#include "sway/input.h" 13#include "sway/input/input-manager.h"
32#include "sway/border.h" 14#include "sway/input/seat.h"
33#include "stringop.h" 15#include "stringop.h"
34#include "sway.h"
35#include "util.h"
36#include "list.h"
37#include "log.h" 16#include "log.h"
38 17
39struct cmd_handler { 18struct cmd_handler {
@@ -41,10 +20,6 @@ struct cmd_handler {
41 sway_cmd *handle; 20 sway_cmd *handle;
42}; 21};
43 22
44int sp_index = 0;
45
46swayc_t *current_container = NULL;
47
48// Returns error object, or NULL if check succeeds. 23// Returns error object, or NULL if check succeeds.
49struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) { 24struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
50 struct cmd_results *error = NULL; 25 struct cmd_results *error = NULL;
@@ -84,24 +59,7 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
84 return error; 59 return error;
85} 60}
86 61
87void hide_view_in_scratchpad(swayc_t *sp_view) { 62void apply_input_config(struct input_config *input) {
88 if (sp_view == NULL) {
89 return;
90 }
91
92 wlc_view_set_mask(sp_view->handle, 0);
93 sp_view->visible = false;
94 swayc_t *ws = sp_view->parent;
95 remove_child(sp_view);
96 if (swayc_active_workspace() != ws && ws->floating->length == 0 && ws->children->length == 0) {
97 destroy_workspace(ws);
98 } else {
99 arrange_windows(ws, -1, -1);
100 }
101 set_focused_container(container_under_pointer());
102}
103
104void input_cmd_apply(struct input_config *input) {
105 int i; 63 int i;
106 i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier); 64 i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
107 if (i >= 0) { 65 if (i >= 0) {
@@ -114,111 +72,41 @@ void input_cmd_apply(struct input_config *input) {
114 list_add(config->input_configs, input); 72 list_add(config->input_configs, input);
115 } 73 }
116 74
117 current_input_config = input; 75 input_manager_apply_input_config(input_manager, input);
118
119 if (input->identifier) {
120 // Try to find the input device and apply configuration now. If
121 // this is during startup then there will be no container and config
122 // will be applied during normal "new input" event from wlc.
123 struct libinput_device *device = NULL;
124 for (int i = 0; i < input_devices->length; ++i) {
125 device = input_devices->items[i];
126 char* dev_identifier = libinput_dev_unique_id(device);
127 if (!dev_identifier) {
128 break;
129 }
130 int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0;
131 free(dev_identifier);
132 if (match) {
133 apply_input_config(input, device);
134 break;
135 }
136 }
137 }
138} 76}
139 77
140void remove_view_from_scratchpad(swayc_t *view) { 78void apply_seat_config(struct seat_config *seat_config) {
141 int i; 79 int i;
142 for (i = 0; i < scratchpad->length; i++) { 80 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
143 if (scratchpad->items[i] == view) { 81 if (i >= 0) {
144 if (sp_index == 0) { 82 // merge existing config
145 sp_index = scratchpad->length - 1; 83 struct seat_config *sc = config->seat_configs->items[i];
146 } else { 84 merge_seat_config(sc, seat_config);
147 sp_index--; 85 free_seat_config(seat_config);
148 } 86 seat_config = sc;
149 list_del(scratchpad, sp_index); 87 } else {
150 sp_view = NULL; 88 list_add(config->seat_configs, seat_config);
151 }
152 } 89 }
90
91 input_manager_apply_seat_config(input_manager, seat_config);
153} 92}
154 93
155/* Keep alphabetized */ 94/* Keep alphabetized */
156static struct cmd_handler handlers[] = { 95static struct cmd_handler handlers[] = {
157 { "assign", cmd_assign },
158 { "bar", cmd_bar }, 96 { "bar", cmd_bar },
159 { "bindcode", cmd_bindcode }, 97 { "bindcode", cmd_bindcode },
160 { "bindsym", cmd_bindsym }, 98 { "bindsym", cmd_bindsym },
161 { "border", cmd_border },
162 { "client.background", cmd_client_background },
163 { "client.focused", cmd_client_focused },
164 { "client.focused_inactive", cmd_client_focused_inactive },
165 { "client.placeholder", cmd_client_placeholder },
166 { "client.unfocused", cmd_client_unfocused },
167 { "client.urgent", cmd_client_urgent },
168 { "clipboard", cmd_clipboard },
169 { "commands", cmd_commands },
170 { "debuglog", cmd_debuglog },
171 { "default_border", cmd_default_border },
172 { "default_floating_border", cmd_default_floating_border },
173 { "default_orientation", cmd_orientation },
174 { "exec", cmd_exec }, 99 { "exec", cmd_exec },
175 { "exec_always", cmd_exec_always }, 100 { "exec_always", cmd_exec_always },
176 { "exit", cmd_exit },
177 { "floating", cmd_floating },
178 { "floating_maximum_size", cmd_floating_maximum_size },
179 { "floating_minimum_size", cmd_floating_minimum_size },
180 { "floating_modifier", cmd_floating_mod },
181 { "floating_scroll", cmd_floating_scroll },
182 { "focus", cmd_focus },
183 { "focus_follows_mouse", cmd_focus_follows_mouse }, 101 { "focus_follows_mouse", cmd_focus_follows_mouse },
184 { "font", cmd_font },
185 { "for_window", cmd_for_window },
186 { "force_focus_wrapping", cmd_force_focus_wrapping },
187 { "fullscreen", cmd_fullscreen },
188 { "gaps", cmd_gaps },
189 { "hide_edge_borders", cmd_hide_edge_borders },
190 { "include", cmd_include }, 102 { "include", cmd_include },
191 { "input", cmd_input }, 103 { "input", cmd_input },
192 { "ipc", cmd_ipc },
193 { "kill", cmd_kill },
194 { "layout", cmd_layout },
195 { "log_colors", cmd_log_colors },
196 { "mark", cmd_mark },
197 { "mode", cmd_mode }, 104 { "mode", cmd_mode },
198 { "mouse_warping", cmd_mouse_warping }, 105 { "mouse_warping", cmd_mouse_warping },
199 { "move", cmd_move },
200 { "new_float", cmd_new_float },
201 { "new_window", cmd_new_window },
202 { "no_focus", cmd_no_focus },
203 { "output", cmd_output }, 106 { "output", cmd_output },
204 { "permit", cmd_permit }, 107 { "seat", cmd_seat },
205 { "reject", cmd_reject },
206 { "reload", cmd_reload },
207 { "resize", cmd_resize },
208 { "scratchpad", cmd_scratchpad },
209 { "seamless_mouse", cmd_seamless_mouse },
210 { "set", cmd_set },
211 { "show_marks", cmd_show_marks },
212 { "smart_gaps", cmd_smart_gaps },
213 { "split", cmd_split },
214 { "splith", cmd_splith },
215 { "splitt", cmd_splitt },
216 { "splitv", cmd_splitv },
217 { "sticky", cmd_sticky },
218 { "unmark", cmd_unmark },
219 { "workspace", cmd_workspace }, 108 { "workspace", cmd_workspace },
220 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, 109 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
221 { "workspace_layout", cmd_workspace_layout },
222}; 110};
223 111
224static struct cmd_handler bar_handlers[] = { 112static struct cmd_handler bar_handlers[] = {
@@ -248,54 +136,6 @@ static struct cmd_handler bar_handlers[] = {
248 { "wrap_scroll", bar_cmd_wrap_scroll }, 136 { "wrap_scroll", bar_cmd_wrap_scroll },
249}; 137};
250 138
251/**
252 * Check and add color to buffer.
253 *
254 * return error object, or NULL if color is valid.
255 */
256struct cmd_results *add_color(const char *name, char *buffer, const char *color) {
257 int len = strlen(color);
258 if (len != 7 && len != 9) {
259 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
260 }
261
262 if (color[0] != '#') {
263 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
264 }
265
266 int i;
267 for (i = 1; i < len; ++i) {
268 if (!isxdigit(color[i])) {
269 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
270 }
271 }
272
273 // copy color to buffer
274 strncpy(buffer, color, len);
275 // add default alpha channel if color was defined without it
276 if (len == 7) {
277 buffer[7] = 'f';
278 buffer[8] = 'f';
279 }
280 buffer[9] = '\0';
281
282 return NULL;
283}
284
285static struct cmd_handler input_handlers[] = {
286 { "accel_profile", input_cmd_accel_profile },
287 { "click_method", input_cmd_click_method },
288 { "drag_lock", input_cmd_drag_lock },
289 { "dwt", input_cmd_dwt },
290 { "events", input_cmd_events },
291 { "left_handed", input_cmd_left_handed },
292 { "middle_emulation", input_cmd_middle_emulation },
293 { "natural_scroll", input_cmd_natural_scroll },
294 { "pointer_accel", input_cmd_pointer_accel },
295 { "scroll_method", input_cmd_scroll_method },
296 { "tap", input_cmd_tap },
297};
298
299static struct cmd_handler bar_colors_handlers[] = { 139static struct cmd_handler bar_colors_handlers[] = {
300 { "active_workspace", bar_colors_cmd_active_workspace }, 140 { "active_workspace", bar_colors_cmd_active_workspace },
301 { "background", bar_colors_cmd_background }, 141 { "background", bar_colors_cmd_background },
@@ -310,26 +150,27 @@ static struct cmd_handler bar_colors_handlers[] = {
310 { "urgent_workspace", bar_colors_cmd_urgent_workspace }, 150 { "urgent_workspace", bar_colors_cmd_urgent_workspace },
311}; 151};
312 152
313static struct cmd_handler ipc_handlers[] = { 153/* Config-time only commands. Keep alphabetized */
314 { "*", cmd_ipc_cmd }, 154static struct cmd_handler config_handlers[] = {
315 { "bar-config", cmd_ipc_cmd }, 155 { "default_orientation", cmd_default_orientation },
316 { "command", cmd_ipc_cmd }, 156 { "set", cmd_set },
317 { "events", cmd_ipc_events }, 157 { "swaybg_command", cmd_swaybg_command },
318 { "inputs", cmd_ipc_cmd },
319 { "marks", cmd_ipc_cmd },
320 { "outputs", cmd_ipc_cmd },
321 { "tree", cmd_ipc_cmd },
322 { "workspaces", cmd_ipc_cmd },
323}; 158};
324 159
325static struct cmd_handler ipc_event_handlers[] = { 160/* Runtime-only commands. Keep alphabetized */
326 { "*", cmd_ipc_event_cmd }, 161static struct cmd_handler command_handlers[] = {
327 { "binding", cmd_ipc_event_cmd }, 162 { "exit", cmd_exit },
328 { "input", cmd_ipc_event_cmd }, 163 { "focus", cmd_focus },
329 { "mode", cmd_ipc_event_cmd }, 164 { "kill", cmd_kill },
330 { "output", cmd_ipc_event_cmd }, 165 { "layout", cmd_layout },
331 { "window", cmd_ipc_event_cmd }, 166 { "move", cmd_move },
332 { "workspace", cmd_ipc_event_cmd }, 167 { "opacity", cmd_opacity },
168 { "reload", cmd_reload },
169 { "resize", cmd_resize },
170 { "split", cmd_split },
171 { "splith", cmd_splith },
172 { "splitt", cmd_splitt },
173 { "splitv", cmd_splitv },
333}; 174};
334 175
335static int handler_compare(const void *_a, const void *_b) { 176static int handler_compare(const void *_a, const void *_b) {
@@ -338,42 +179,90 @@ static int handler_compare(const void *_a, const void *_b) {
338 return strcasecmp(a->command, b->command); 179 return strcasecmp(a->command, b->command);
339} 180}
340 181
182// must be in order for the bsearch
183static struct cmd_handler input_handlers[] = {
184 { "accel_profile", input_cmd_accel_profile },
185 { "click_method", input_cmd_click_method },
186 { "drag_lock", input_cmd_drag_lock },
187 { "dwt", input_cmd_dwt },
188 { "events", input_cmd_events },
189 { "left_handed", input_cmd_left_handed },
190 { "map_to_output", input_cmd_map_to_output },
191 { "middle_emulation", input_cmd_middle_emulation },
192 { "natural_scroll", input_cmd_natural_scroll },
193 { "pointer_accel", input_cmd_pointer_accel },
194 { "scroll_method", input_cmd_scroll_method },
195 { "tap", input_cmd_tap },
196 { "xkb_layout", input_cmd_xkb_layout },
197 { "xkb_model", input_cmd_xkb_model },
198 { "xkb_options", input_cmd_xkb_options },
199 { "xkb_rules", input_cmd_xkb_rules },
200 { "xkb_variant", input_cmd_xkb_variant },
201};
202
203// must be in order for the bsearch
204static struct cmd_handler seat_handlers[] = {
205 { "attach", seat_cmd_attach },
206 { "cursor", seat_cmd_cursor },
207 { "fallback", seat_cmd_fallback },
208};
209
341static struct cmd_handler *find_handler(char *line, enum cmd_status block) { 210static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
342 struct cmd_handler d = { .command=line }; 211 struct cmd_handler d = { .command=line };
343 struct cmd_handler *res = NULL; 212 struct cmd_handler *res = NULL;
344 sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT); 213 wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT);
214
215 bool config_loading = config->reading || !config->active;
216
345 if (block == CMD_BLOCK_BAR) { 217 if (block == CMD_BLOCK_BAR) {
346 res = bsearch(&d, bar_handlers, 218 return bsearch(&d, bar_handlers,
347 sizeof(bar_handlers) / sizeof(struct cmd_handler), 219 sizeof(bar_handlers) / sizeof(struct cmd_handler),
348 sizeof(struct cmd_handler), handler_compare); 220 sizeof(struct cmd_handler), handler_compare);
349 } else if (block == CMD_BLOCK_BAR_COLORS){ 221 } else if (block == CMD_BLOCK_BAR_COLORS) {
350 res = bsearch(&d, bar_colors_handlers, 222 return bsearch(&d, bar_colors_handlers,
351 sizeof(bar_colors_handlers) / sizeof(struct cmd_handler), 223 sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
352 sizeof(struct cmd_handler), handler_compare); 224 sizeof(struct cmd_handler), handler_compare);
353 } else if (block == CMD_BLOCK_INPUT) { 225 } else if (block == CMD_BLOCK_INPUT) {
354 res = bsearch(&d, input_handlers, 226 return bsearch(&d, input_handlers,
355 sizeof(input_handlers) / sizeof(struct cmd_handler), 227 sizeof(input_handlers) / sizeof(struct cmd_handler),
356 sizeof(struct cmd_handler), handler_compare); 228 sizeof(struct cmd_handler), handler_compare);
357 } else if (block == CMD_BLOCK_IPC) { 229 } else if (block == CMD_BLOCK_SEAT) {
358 res = bsearch(&d, ipc_handlers, 230 return bsearch(&d, seat_handlers,
359 sizeof(ipc_handlers) / sizeof(struct cmd_handler), 231 sizeof(seat_handlers) / sizeof(struct cmd_handler),
360 sizeof(struct cmd_handler), handler_compare); 232 sizeof(struct cmd_handler), handler_compare);
361 } else if (block == CMD_BLOCK_IPC_EVENTS) { 233 }
362 res = bsearch(&d, ipc_event_handlers, 234
363 sizeof(ipc_event_handlers) / sizeof(struct cmd_handler), 235 if (!config_loading) {
364 sizeof(struct cmd_handler), handler_compare); 236 res = bsearch(&d, command_handlers,
365 } else { 237 sizeof(command_handlers) / sizeof(struct cmd_handler),
366 res = bsearch(&d, handlers, 238 sizeof(struct cmd_handler), handler_compare);
239
240 if (res) {
241 return res;
242 }
243 }
244
245 if (config->reading) {
246 res = bsearch(&d, config_handlers,
247 sizeof(config_handlers) / sizeof(struct cmd_handler),
248 sizeof(struct cmd_handler), handler_compare);
249
250 if (res) {
251 return res;
252 }
253 }
254
255 res = bsearch(&d, handlers,
367 sizeof(handlers) / sizeof(struct cmd_handler), 256 sizeof(handlers) / sizeof(struct cmd_handler),
368 sizeof(struct cmd_handler), handler_compare); 257 sizeof(struct cmd_handler), handler_compare);
369 } 258
370 return res; 259 return res;
371} 260}
372 261
373struct cmd_results *handle_command(char *_exec, enum command_context context) { 262struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
374 // Even though this function will process multiple commands we will only 263 // Even though this function will process multiple commands we will only
375 // return the last error, if any (for now). (Since we have access to an 264 // return the last error, if any (for now). (Since we have access to an
376 // error string we could e.g. concatonate all errors there.) 265 // error string we could e.g. concatenate all errors there.)
377 struct cmd_results *results = NULL; 266 struct cmd_results *results = NULL;
378 char *exec = strdup(_exec); 267 char *exec = strdup(_exec);
379 char *head = exec; 268 char *head = exec;
@@ -381,10 +270,22 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
381 char *cmd; 270 char *cmd;
382 list_t *containers = NULL; 271 list_t *containers = NULL;
383 272
273 if (seat == NULL) {
274 // passing a NULL seat means we just pick the default seat
275 seat = input_manager_get_default_seat(input_manager);
276 if (!sway_assert(seat, "could not find a seat to run the command on")) {
277 return NULL;
278 }
279 }
280
281 config->handler_context.seat = seat;
282
384 head = exec; 283 head = exec;
385 do { 284 do {
386 // Extract criteria (valid for this command list only). 285 // Extract criteria (valid for this command list only).
286 bool has_criteria = false;
387 if (*head == '[') { 287 if (*head == '[') {
288 has_criteria = true;
388 ++head; 289 ++head;
389 char *criteria_string = argsep(&head, "]"); 290 char *criteria_string = argsep(&head, "]");
390 if (head) { 291 if (head) {
@@ -393,13 +294,14 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
393 char *error; 294 char *error;
394 295
395 if ((error = extract_crit_tokens(tokens, criteria_string))) { 296 if ((error = extract_crit_tokens(tokens, criteria_string))) {
297 wlr_log(L_DEBUG, "criteria string parse error: %s", error);
396 results = cmd_results_new(CMD_INVALID, criteria_string, 298 results = cmd_results_new(CMD_INVALID, criteria_string,
397 "Can't parse criteria string: %s", error); 299 "Can't parse criteria string: %s", error);
398 free(error); 300 free(error);
399 free(tokens); 301 free(tokens);
400 goto cleanup; 302 goto cleanup;
401 } 303 }
402 containers = container_for(tokens); 304 containers = container_for_crit_tokens(tokens);
403 305
404 free(tokens); 306 free(tokens);
405 } else { 307 } else {
@@ -419,10 +321,10 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
419 cmd = argsep(&cmdlist, ","); 321 cmd = argsep(&cmdlist, ",");
420 cmd += strspn(cmd, whitespace); 322 cmd += strspn(cmd, whitespace);
421 if (strcmp(cmd, "") == 0) { 323 if (strcmp(cmd, "") == 0) {
422 sway_log(L_INFO, "Ignoring empty command."); 324 wlr_log(L_INFO, "Ignoring empty command.");
423 continue; 325 continue;
424 } 326 }
425 sway_log(L_INFO, "Handling command '%s'", cmd); 327 wlr_log(L_INFO, "Handling command '%s'", cmd);
426 //TODO better handling of argv 328 //TODO better handling of argv
427 int argc; 329 int argc;
428 char **argv = split_args(cmd, &argc); 330 char **argv = split_args(cmd, &argc);
@@ -443,31 +345,16 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
443 free_argv(argc, argv); 345 free_argv(argc, argv);
444 goto cleanup; 346 goto cleanup;
445 } 347 }
446 if (!(get_command_policy_mask(argv[0]) & context)) {
447 if (results) {
448 free_cmd_results(results);
449 }
450 results = cmd_results_new(CMD_INVALID, cmd,
451 "Permission denied for %s via %s", cmd,
452 command_policy_str(context));
453 free_argv(argc, argv);
454 goto cleanup;
455 }
456 int i = 0;
457 do {
458 if (!containers) {
459 current_container = get_focused_container(&root_container);
460 } else if (containers->length == 0) {
461 if (results) {
462 free_cmd_results(results);
463 }
464 results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container");
465 goto cleanup;
466 } else {
467 current_container = (swayc_t *)containers->items[i];
468 }
469 sway_log(L_INFO, "Running on container '%s'", current_container->name);
470 348
349 if (!has_criteria) {
350 // without criteria, the command acts upon the focused
351 // container
352 config->handler_context.current_container =
353 seat_get_focus_inactive(seat, &root_container);
354 if (!sway_assert(config->handler_context.current_container,
355 "could not get focus-inactive for root container")) {
356 return NULL;
357 }
471 struct cmd_results *res = handler->handle(argc-1, argv+1); 358 struct cmd_results *res = handler->handle(argc-1, argv+1);
472 if (res->status != CMD_SUCCESS) { 359 if (res->status != CMD_SUCCESS) {
473 free_argv(argc, argv); 360 free_argv(argc, argv);
@@ -478,35 +365,39 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
478 goto cleanup; 365 goto cleanup;
479 } 366 }
480 free_cmd_results(res); 367 free_cmd_results(res);
481 ++i; 368 } else {
482 } while(containers && i < containers->length); 369 for (int i = 0; i < containers->length; ++i) {
483 370 config->handler_context.current_container = containers->items[i];
371 struct cmd_results *res = handler->handle(argc-1, argv+1);
372 if (res->status != CMD_SUCCESS) {
373 free_argv(argc, argv);
374 if (results) {
375 free_cmd_results(results);
376 }
377 results = res;
378 goto cleanup;
379 }
380 free_cmd_results(res);
381 }
382 }
484 free_argv(argc, argv); 383 free_argv(argc, argv);
485 } while(cmdlist); 384 } while(cmdlist);
486
487 if (containers) {
488 list_free(containers);
489 containers = NULL;
490 }
491 } while(head); 385 } while(head);
492 cleanup: 386cleanup:
493 free(exec); 387 free(exec);
494 if (containers) {
495 free(containers);
496 }
497 if (!results) { 388 if (!results) {
498 results = cmd_results_new(CMD_SUCCESS, NULL, NULL); 389 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
499 } 390 }
500 return results; 391 return results;
501} 392}
502 393
503// this is like handle_command above, except: 394// this is like execute_command above, except:
504// 1) it ignores empty commands (empty lines) 395// 1) it ignores empty commands (empty lines)
505// 2) it does variable substitution 396// 2) it does variable substitution
506// 3) it doesn't split commands (because the multiple commands are supposed to 397// 3) it doesn't split commands (because the multiple commands are supposed to
507// be chained together) 398// be chained together)
508// 4) handle_command handles all state internally while config_command has some 399// 4) execute_command handles all state internally while config_command has
509// state handled outside (notably the block mode, in read_config) 400// some state handled outside (notably the block mode, in read_config)
510struct cmd_results *config_command(char *exec, enum cmd_status block) { 401struct cmd_results *config_command(char *exec, enum cmd_status block) {
511 struct cmd_results *results = NULL; 402 struct cmd_results *results = NULL;
512 int argc; 403 int argc;
@@ -516,7 +407,7 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
516 goto cleanup; 407 goto cleanup;
517 } 408 }
518 409
519 sway_log(L_INFO, "handling config command '%s'", exec); 410 wlr_log(L_INFO, "handling config command '%s'", exec);
520 // Endblock 411 // Endblock
521 if (**argv == '}') { 412 if (**argv == '}') {
522 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); 413 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
@@ -530,12 +421,13 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
530 } 421 }
531 int i; 422 int i;
532 // Var replacement, for all but first argument of set 423 // Var replacement, for all but first argument of set
424 // TODO commands
533 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 425 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
534 argv[i] = do_var_replacement(argv[i]); 426 argv[i] = do_var_replacement(argv[i]);
535 unescape_string(argv[i]); 427 unescape_string(argv[i]);
536 } 428 }
537 /* Strip quotes for first argument. 429 // Strip quotes for first argument.
538 * TODO This part needs to be handled much better */ 430 // TODO This part needs to be handled much better
539 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { 431 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
540 strip_quotes(argv[1]); 432 strip_quotes(argv[1]);
541 } 433 }
@@ -619,7 +511,7 @@ struct cmd_results *config_commands_command(char *exec) {
619 } 511 }
620 policy->context = context; 512 policy->context = context;
621 513
622 sway_log(L_INFO, "Set command policy for %s to %d", 514 wlr_log(L_INFO, "Set command policy for %s to %d",
623 policy->command, policy->context); 515 policy->command, policy->context);
624 516
625 results = cmd_results_new(CMD_SUCCESS, NULL, NULL); 517 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -629,10 +521,11 @@ cleanup:
629 return results; 521 return results;
630} 522}
631 523
632struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) { 524struct cmd_results *cmd_results_new(enum cmd_status status,
525 const char *input, const char *format, ...) {
633 struct cmd_results *results = malloc(sizeof(struct cmd_results)); 526 struct cmd_results *results = malloc(sizeof(struct cmd_results));
634 if (!results) { 527 if (!results) {
635 sway_log(L_ERROR, "Unable to allocate command results"); 528 wlr_log(L_ERROR, "Unable to allocate command results");
636 return NULL; 529 return NULL;
637 } 530 }
638 results->status = status; 531 results->status = status;
@@ -669,12 +562,15 @@ void free_cmd_results(struct cmd_results *results) {
669const char *cmd_results_to_json(struct cmd_results *results) { 562const char *cmd_results_to_json(struct cmd_results *results) {
670 json_object *result_array = json_object_new_array(); 563 json_object *result_array = json_object_new_array();
671 json_object *root = json_object_new_object(); 564 json_object *root = json_object_new_object();
672 json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS)); 565 json_object_object_add(root, "success",
566 json_object_new_boolean(results->status == CMD_SUCCESS));
673 if (results->input) { 567 if (results->input) {
674 json_object_object_add(root, "input", json_object_new_string(results->input)); 568 json_object_object_add(
569 root, "input", json_object_new_string(results->input));
675 } 570 }
676 if (results->error) { 571 if (results->error) {
677 json_object_object_add(root, "error", json_object_new_string(results->error)); 572 json_object_object_add(
573 root, "error", json_object_new_string(results->error));
678 } 574 }
679 json_object_array_add(result_array, root); 575 json_object_array_add(result_array, root);
680 const char *json = json_object_to_json_string(result_array); 576 const char *json = json_object_to_json_string(result_array);
@@ -682,3 +578,35 @@ const char *cmd_results_to_json(struct cmd_results *results) {
682 free(root); 578 free(root);
683 return json; 579 return json;
684} 580}
581
582/**
583 * Check and add color to buffer.
584 *
585 * return error object, or NULL if color is valid.
586 */
587struct cmd_results *add_color(const char *name,
588 char *buffer, const char *color) {
589 int len = strlen(color);
590 if (len != 7 && len != 9) {
591 return cmd_results_new(CMD_INVALID, name,
592 "Invalid color definition %s", color);
593 }
594 if (color[0] != '#') {
595 return cmd_results_new(CMD_INVALID, name,
596 "Invalid color definition %s", color);
597 }
598 for (int i = 1; i < len; ++i) {
599 if (!isxdigit(color[i])) {
600 return cmd_results_new(CMD_INVALID, name,
601 "Invalid color definition %s", color);
602 }
603 }
604 strncpy(buffer, color, len);
605 // add default alpha channel if color was defined without it
606 if (len == 7) {
607 buffer[7] = 'f';
608 buffer[8] = 'f';
609 }
610 buffer[9] = '\0';
611 return NULL;
612}
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
deleted file mode 100644
index c3b03bbc..00000000
--- a/sway/commands/assign.c
+++ /dev/null
@@ -1,57 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <stdio.h>
3#include <string.h>
4#include "sway/commands.h"
5#include "sway/criteria.h"
6#include "list.h"
7#include "log.h"
8
9struct cmd_results *cmd_assign(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "assign", EXPECTED_AT_LEAST, 2))) {
12 return error;
13 }
14
15 char *criteria = *argv++;
16
17 if (strncmp(*argv, "→", strlen("→")) == 0) {
18 if (argc < 3) {
19 return cmd_results_new(CMD_INVALID, "assign", "Missing workspace");
20 }
21 argv++;
22 }
23
24 char *movecmd = "move container to workspace ";
25 int arglen = strlen(movecmd) + strlen(*argv) + 1;
26 char *cmdlist = calloc(1, arglen);
27 if (!cmdlist) {
28 return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate command list");
29 }
30 snprintf(cmdlist, arglen, "%s%s", movecmd, *argv);
31
32 struct criteria *crit = malloc(sizeof(struct criteria));
33 if (!crit) {
34 free(cmdlist);
35 return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate criteria");
36 }
37 crit->crit_raw = strdup(criteria);
38 crit->cmdlist = cmdlist;
39 crit->tokens = create_list();
40 char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
41
42 if (err_str) {
43 error = cmd_results_new(CMD_INVALID, "assign", err_str);
44 free(err_str);
45 free_criteria(crit);
46 } else if (crit->tokens->length == 0) {
47 error = cmd_results_new(CMD_INVALID, "assign", "Found no name/value pairs in criteria");
48 free_criteria(crit);
49 } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
50 sway_log(L_DEBUG, "assign: Duplicate, skipping.");
51 free_criteria(crit);
52 } else {
53 sway_log(L_DEBUG, "assign: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
54 list_add(config->criteria, crit);
55 }
56 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
57}
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index 04745a6e..ff111163 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -1,9 +1,8 @@
1#include <stdio.h>
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
3#include <wlr/util/log.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h" 5#include "sway/config.h"
6#include "log.h"
7#include "util.h" 6#include "util.h"
8 7
9struct cmd_results *cmd_bar(int argc, char **argv) { 8struct cmd_results *cmd_bar(int argc, char **argv) {
@@ -27,7 +26,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
27 return bar_cmd_hidden_state(argc-1, argv + 1); 26 return bar_cmd_hidden_state(argc-1, argv + 1);
28 } 27 }
29 } 28 }
30
31 return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file."); 29 return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file.");
32 } 30 }
33 31
@@ -38,15 +36,15 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
38 } 36 }
39 37
40 // set bar id 38 // set bar id
41 int i; 39 for (int i = 0; i < config->bars->length; ++i) {
42 for (i = 0; i < config->bars->length; ++i) {
43 if (bar == config->bars->items[i]) { 40 if (bar == config->bars->items[i]) {
44 const int len = 5 + numlen(i); // "bar-" + i + \0 41 const int len = 5 + numlen(i); // "bar-" + i + \0
45 bar->id = malloc(len * sizeof(char)); 42 bar->id = malloc(len * sizeof(char));
46 if (bar->id) { 43 if (bar->id) {
47 snprintf(bar->id, len, "bar-%d", i); 44 snprintf(bar->id, len, "bar-%d", i);
48 } else { 45 } else {
49 return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar ID"); 46 return cmd_results_new(CMD_FAILURE,
47 "bar", "Unable to allocate bar ID");
50 } 48 }
51 break; 49 break;
52 } 50 }
@@ -54,6 +52,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
54 52
55 // Set current bar 53 // Set current bar
56 config->current_bar = bar; 54 config->current_bar = bar;
57 sway_log(L_DEBUG, "Configuring bar %s", bar->id); 55 wlr_log(L_DEBUG, "Configuring bar %s", bar->id);
58 return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL); 56 return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL);
59} 57}
diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c
index 32a1d3e5..7310e7ec 100644
--- a/sway/commands/bar/activate_button.c
+++ b/sway/commands/bar/activate_button.c
@@ -3,24 +3,6 @@
3#include "log.h" 3#include "log.h"
4 4
5struct cmd_results *bar_cmd_activate_button(int argc, char **argv) { 5struct cmd_results *bar_cmd_activate_button(int argc, char **argv) {
6 const char *cmd_name = "activate_button"; 6 // TODO TRAY
7#ifndef ENABLE_TRAY 7 return cmd_results_new(CMD_INVALID, "activate_button", "TODO TRAY");
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 // User should be able to prefix with 0x or whatever they want
22 config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25#endif
26} 8}
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c
index 64f5b84f..3ba5f33f 100644
--- a/sway/commands/bar/binding_mode_indicator.c
+++ b/sway/commands/bar/binding_mode_indicator.c
@@ -5,23 +5,23 @@
5 5
6struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { 6struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
7 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc,
9 "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
9 return error; 10 return error;
10 } 11 }
11
12 if (!config->current_bar) { 12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined."); 13 return cmd_results_new(CMD_FAILURE,
14 "binding_mode_indicator", "No bar defined.");
14 } 15 }
15
16 if (strcasecmp("yes", argv[0]) == 0) { 16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->binding_mode_indicator = true; 17 config->current_bar->binding_mode_indicator = true;
18 sway_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id); 18 wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s",
19 config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) { 20 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->binding_mode_indicator = false; 21 config->current_bar->binding_mode_indicator = false;
21 sway_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id); 22 wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s",
22 } else { 23 config->current_bar->id);
23 error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]);
24 return error;
25 } 24 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
26 "Invalid value %s", argv[0]);
27} 27}
diff --git a/sway/commands/bar/bindsym.c b/sway/commands/bar/bindsym.c
index 5f90b51a..ac09a03f 100644
--- a/sway/commands/bar/bindsym.c
+++ b/sway/commands/bar/bindsym.c
@@ -7,42 +7,5 @@
7#include "stringop.h" 7#include "stringop.h"
8 8
9struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { 9struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
10 struct cmd_results *error = NULL; 10 return cmd_results_new(CMD_FAILURE, "bindsym", "TODO"); // TODO
11 if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
12 return error;
13 } else if (!config->reading) {
14 return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, "bindsym", "No bar defined.");
19 }
20
21 if (strlen(argv[1]) != 7) {
22 return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
23 }
24 uint32_t numbutton = (uint32_t)atoi(argv[1] + 6);
25 if (numbutton < 1 || numbutton > 5 || strncmp(argv[1], "button", 6) != 0) {
26 return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
27 }
28 struct sway_mouse_binding *binding = malloc(sizeof(struct sway_mouse_binding));
29 if (!binding) {
30 return cmd_results_new(CMD_FAILURE, "bindsym", "Unable to allocate binding");
31 }
32 binding->button = numbutton;
33 binding->command = join_args(argv + 1, argc - 1);
34
35 struct bar_config *bar = config->current_bar;
36 int i = list_seq_find(bar->bindings, sway_mouse_binding_cmp_buttons, binding);
37 if (i > -1) {
38 sway_log(L_DEBUG, "bindsym - '%s' for swaybar already exists, overwriting", argv[0]);
39 struct sway_mouse_binding *dup = bar->bindings->items[i];
40 free_sway_mouse_binding(dup);
41 list_del(bar->bindings, i);
42 }
43 list_add(bar->bindings, binding);
44 list_qsort(bar->bindings, sway_mouse_binding_cmp_qsort);
45
46 sway_log(L_DEBUG, "bindsym - Bound %s to command %s when clicking swaybar", argv[0], binding->command);
47 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
48} 11}
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
index 8b3b0aac..17ba9b7c 100644
--- a/sway/commands/bar/colors.c
+++ b/sway/commands/bar/colors.c
@@ -1,47 +1,38 @@
1#include <string.h> 1#include <string.h>
2#include "sway/commands.h" 2#include "sway/commands.h"
3 3
4static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) { 4static struct cmd_results *parse_single_color(char **color,
5 const char *cmd_name, int argc, char **argv) {
5 struct cmd_results *error = NULL; 6 struct cmd_results *error = NULL;
6 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { 7 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
7 return error; 8 return error;
8 } 9 }
9 10 if (!*color && !(*color = malloc(10))) {
10 if (!*color) { 11 return NULL;
11 *color = malloc(10);
12 if (!*color) {
13 return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
14 }
15 } 12 }
16
17 error = add_color(cmd_name, *color, argv[0]); 13 error = add_color(cmd_name, *color, argv[0]);
18 if (error) { 14 if (error) {
19 return error; 15 return error;
20 } 16 }
21
22 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 17 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
23} 18}
24 19
25static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) { 20static struct cmd_results *parse_three_colors(char ***colors,
21 const char *cmd_name, int argc, char **argv) {
26 struct cmd_results *error = NULL; 22 struct cmd_results *error = NULL;
27 if (argc != 3) { 23 if (argc != 3) {
28 return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly three color values"); 24 return cmd_results_new(CMD_INVALID,
25 cmd_name, "Requires exactly three color values");
29 } 26 }
30 27 for (size_t i = 0; i < 3; i++) {
31 int i; 28 if (!*colors[i] && !(*(colors[i]) = malloc(10))) {
32 for (i = 0; i < 3; i++) { 29 return NULL;
33 if (!*colors[i]) {
34 *(colors[i]) = malloc(10);
35 if (!*(colors[i])) {
36 return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
37 }
38 } 30 }
39 error = add_color(cmd_name, *(colors[i]), argv[i]); 31 error = add_color(cmd_name, *(colors[i]), argv[i]);
40 if (error) { 32 if (error) {
41 return error; 33 return error;
42 } 34 }
43 } 35 }
44
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46} 37}
47 38
@@ -50,12 +41,10 @@ struct cmd_results *bar_cmd_colors(int argc, char **argv) {
50 if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) { 41 if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) {
51 return error; 42 return error;
52 } 43 }
53
54 if (strcmp("{", argv[0]) != 0) { 44 if (strcmp("{", argv[0]) != 0) {
55 return cmd_results_new(CMD_INVALID, "colors", 45 return cmd_results_new(CMD_INVALID, "colors",
56 "Expected '{' at the start of colors config definition."); 46 "Expected '{' at the start of colors config definition.");
57 } 47 }
58
59 return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL); 48 return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL);
60} 49}
61 50
@@ -69,11 +58,13 @@ struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {
69} 58}
70 59
71struct cmd_results *bar_colors_cmd_background(int argc, char **argv) { 60struct cmd_results *bar_colors_cmd_background(int argc, char **argv) {
72 return parse_single_color(&(config->current_bar->colors.background), "background", argc, argv); 61 return parse_single_color(&(config->current_bar->colors.background),
62 "background", argc, argv);
73} 63}
74 64
75struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) { 65struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) {
76 return parse_single_color(&(config->current_bar->colors.focused_background), "focused_background", argc, argv); 66 return parse_single_color(&(config->current_bar->colors.focused_background),
67 "focused_background", argc, argv);
77} 68}
78 69
79struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) { 70struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) {
@@ -104,19 +95,23 @@ struct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) {
104} 95}
105 96
106struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) { 97struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) {
107 return parse_single_color(&(config->current_bar->colors.separator), "separator", argc, argv); 98 return parse_single_color(&(config->current_bar->colors.separator),
99 "separator", argc, argv);
108} 100}
109 101
110struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) { 102struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) {
111 return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv); 103 return parse_single_color(&(config->current_bar->colors.focused_separator),
104 "focused_separator", argc, argv);
112} 105}
113 106
114struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) { 107struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
115 return parse_single_color(&(config->current_bar->colors.statusline), "statusline", argc, argv); 108 return parse_single_color(&(config->current_bar->colors.statusline),
109 "statusline", argc, argv);
116} 110}
117 111
118struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) { 112struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {
119 return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv); 113 return parse_single_color(&(config->current_bar->colors.focused_separator),
114 "focused_separator", argc, argv);
120} 115}
121 116
122struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { 117struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) {
diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c
index 6d7d7aec..3b76885a 100644
--- a/sway/commands/bar/context_button.c
+++ b/sway/commands/bar/context_button.c
@@ -3,24 +3,6 @@
3#include "log.h" 3#include "log.h"
4 4
5struct cmd_results *bar_cmd_context_button(int argc, char **argv) { 5struct cmd_results *bar_cmd_context_button(int argc, char **argv) {
6 const char *cmd_name = "context_button"; 6 // TODO TRAY
7#ifndef ENABLE_TRAY 7 return cmd_results_new(CMD_INVALID, "context_button", "TODO TRAY");
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 // User should be able to prefix with 0x or whatever they want
22 config->current_bar->context_button = strtoul(argv[0], NULL, 0);
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25#endif
26} 8}
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index c586c5bc..80b7a593 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -1,3 +1,4 @@
1#define _POSIX_C_SOURCE 200809L
1#include <string.h> 2#include <string.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "log.h" 4#include "log.h"
@@ -8,19 +9,13 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
8 if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
11
12 if (!config->current_bar) { 12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "font", "No bar defined."); 13 return cmd_results_new(CMD_FAILURE, "font", "No bar defined.");
14 } 14 }
15
16 char *font = join_args(argv, argc); 15 char *font = join_args(argv, argc);
17 free(config->current_bar->font); 16 free(config->current_bar->font);
18 if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) { 17 config->current_bar->font = strdup(font);
19 config->current_bar->font = font; 18 wlr_log(L_DEBUG, "Settings font '%s' for bar: %s",
20 } else { 19 config->current_bar->font, config->current_bar->id);
21 config->current_bar->font = font;
22 }
23
24 sway_log(L_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 21}
diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c
index eb576ab3..3160caed 100644
--- a/sway/commands/bar/height.c
+++ b/sway/commands/bar/height.c
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_height(int argc, char **argv) {
8 if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) {
9 return error; 9 return error;
10 } 10 }
11
12 int height = atoi(argv[0]); 11 int height = atoi(argv[0]);
13 if (height < 0) { 12 if (height < 0) {
14 return cmd_results_new(CMD_INVALID, "height", 13 return cmd_results_new(CMD_INVALID, "height",
15 "Invalid height value: %s", argv[0]); 14 "Invalid height value: %s", argv[0]);
16 } 15 }
17
18 config->current_bar->height = height; 16 config->current_bar->height = height;
19 sway_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id); 17 wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s",
18 height, config->current_bar->id);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21} 20}
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 0b49aa6b..6641f184 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -6,7 +6,8 @@
6#include "sway/ipc-server.h" 6#include "sway/ipc-server.h"
7#include "log.h" 7#include "log.h"
8 8
9static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const char *hidden_state) { 9static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,
10 const char *hidden_state) {
10 char *old_state = bar->hidden_state; 11 char *old_state = bar->hidden_state;
11 if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) { 12 if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) {
12 if (strcasecmp("hide", bar->hidden_state) == 0) { 13 if (strcasecmp("hide", bar->hidden_state) == 0) {
@@ -19,16 +20,16 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const ch
19 } else if (strcasecmp("show", hidden_state) == 0) { 20 } else if (strcasecmp("show", hidden_state) == 0) {
20 bar->hidden_state = strdup("show"); 21 bar->hidden_state = strdup("show");
21 } else { 22 } else {
22 return cmd_results_new(CMD_INVALID, "hidden_state", "Invalid value %s", hidden_state); 23 return cmd_results_new(CMD_INVALID, "hidden_state",
24 "Invalid value %s", hidden_state);
23 } 25 }
24
25 if (strcmp(old_state, bar->hidden_state) != 0) { 26 if (strcmp(old_state, bar->hidden_state) != 0) {
26 if (!config->reading) { 27 if (!config->reading) {
27 ipc_event_barconfig_update(bar); 28 ipc_event_barconfig_update(bar);
28 } 29 }
29 sway_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id); 30 wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s",
31 bar->hidden_state, bar->id);
30 } 32 }
31
32 // free old mode 33 // free old mode
33 free(old_state); 34 free(old_state);
34 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 35 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -42,13 +43,12 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
42 if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) { 43 if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) {
43 return error; 44 return error;
44 } 45 }
45
46 if (config->reading && argc > 1) { 46 if (config->reading && argc > 1) {
47 return cmd_results_new(CMD_INVALID, "hidden_state", "Unexpected value %s in config mode", argv[1]); 47 return cmd_results_new(CMD_INVALID, "hidden_state",
48 "Unexpected value %s in config mode", argv[1]);
48 } 49 }
49 50
50 const char *state = argv[0]; 51 const char *state = argv[0];
51
52 if (config->reading) { 52 if (config->reading) {
53 return bar_set_hidden_state(config->current_bar, state); 53 return bar_set_hidden_state(config->current_bar, state);
54 } 54 }
@@ -57,10 +57,8 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
57 if (argc == 2) { 57 if (argc == 2) {
58 id = argv[1]; 58 id = argv[1];
59 } 59 }
60
61 int i;
62 struct bar_config *bar; 60 struct bar_config *bar;
63 for (i = 0; i < config->bars->length; ++i) { 61 for (int i = 0; i < config->bars->length; ++i) {
64 bar = config->bars->items[i]; 62 bar = config->bars->items[i];
65 if (id && strcmp(id, bar->id) == 0) { 63 if (id && strcmp(id, bar->id) == 0) {
66 return bar_set_hidden_state(bar, state); 64 return bar_set_hidden_state(bar, state);
@@ -71,9 +69,5 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
71 return error; 69 return error;
72 } 70 }
73 } 71 }
74
75 // active bar modifiers might have changed.
76 update_active_bar_modifiers();
77
78 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 72 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
79} 73}
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index cbfc0be5..44cd3076 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -3,23 +3,6 @@
3#include "sway/commands.h" 3#include "sway/commands.h"
4 4
5struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { 5struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {
6 const char *cmd_name = "tray_output"; 6 // TODO TRAY
7#ifndef ENABLE_TRAY 7 return cmd_results_new(CMD_INVALID, "icon_theme", "TODO TRAY");
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 config->current_bar->icon_theme = strdup(argv[0]);
22
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24#endif
25} 8}
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index 1221ebf6..c1e56f03 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -11,10 +11,8 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {
11 11
12 const char *name = argv[0]; 12 const char *name = argv[0];
13 const char *oldname = config->current_bar->id; 13 const char *oldname = config->current_bar->id;
14
15 // check if id is used by a previously defined bar 14 // check if id is used by a previously defined bar
16 int i; 15 for (int i = 0; i < config->bars->length; ++i) {
17 for (i = 0; i < config->bars->length; ++i) {
18 struct bar_config *find = config->bars->items[i]; 16 struct bar_config *find = config->bars->items[i];
19 if (strcmp(name, find->id) == 0 && config->current_bar != find) { 17 if (strcmp(name, find->id) == 0 && config->current_bar != find) {
20 return cmd_results_new(CMD_FAILURE, "id", 18 return cmd_results_new(CMD_FAILURE, "id",
@@ -23,11 +21,10 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {
23 } 21 }
24 } 22 }
25 23
26 sway_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); 24 wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
27 25
28 // free old bar id 26 // free old bar id
29 free(config->current_bar->id); 27 free(config->current_bar->id);
30
31 config->current_bar->id = strdup(name); 28 config->current_bar->id = strdup(name);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 30}
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 36816b93..34bb0a4f 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -27,11 +27,8 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode
27 if (strcmp(old_mode, bar->mode) != 0) { 27 if (strcmp(old_mode, bar->mode) != 0) {
28 if (!config->reading) { 28 if (!config->reading) {
29 ipc_event_barconfig_update(bar); 29 ipc_event_barconfig_update(bar);
30
31 // active bar modifiers might have changed.
32 update_active_bar_modifiers();
33 } 30 }
34 sway_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); 31 wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
35 } 32 }
36 33
37 // free old mode 34 // free old mode
@@ -47,13 +44,12 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
47 if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) { 44 if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) {
48 return error; 45 return error;
49 } 46 }
50
51 if (config->reading && argc > 1) { 47 if (config->reading && argc > 1) {
52 return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]); 48 return cmd_results_new(CMD_INVALID,
49 "mode", "Unexpected value %s in config mode", argv[1]);
53 } 50 }
54 51
55 const char *mode = argv[0]; 52 const char *mode = argv[0];
56
57 if (config->reading) { 53 if (config->reading) {
58 return bar_set_mode(config->current_bar, mode); 54 return bar_set_mode(config->current_bar, mode);
59 } 55 }
@@ -63,19 +59,16 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
63 id = argv[1]; 59 id = argv[1];
64 } 60 }
65 61
66 int i;
67 struct bar_config *bar; 62 struct bar_config *bar;
68 for (i = 0; i < config->bars->length; ++i) { 63 for (int i = 0; i < config->bars->length; ++i) {
69 bar = config->bars->items[i]; 64 bar = config->bars->items[i];
70 if (id && strcmp(id, bar->id) == 0) { 65 if (id && strcmp(id, bar->id) == 0) {
71 return bar_set_mode(bar, mode); 66 return bar_set_mode(bar, mode);
72 } 67 }
73
74 error = bar_set_mode(bar, mode); 68 error = bar_set_mode(bar, mode);
75 if (error) { 69 if (error) {
76 return error; 70 return error;
77 } 71 }
78 } 72 }
79
80 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 73 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
81} 74}
diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c
index 153d87e6..7ba4b125 100644
--- a/sway/commands/bar/modifier.c
+++ b/sway/commands/bar/modifier.c
@@ -15,7 +15,6 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
15 } 15 }
16 16
17 uint32_t mod = 0; 17 uint32_t mod = 0;
18
19 list_t *split = split_string(argv[0], "+"); 18 list_t *split = split_string(argv[0], "+");
20 for (int i = 0; i < split->length; ++i) { 19 for (int i = 0; i < split->length; ++i) {
21 uint32_t tmp_mod; 20 uint32_t tmp_mod;
@@ -24,12 +23,13 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
24 continue; 23 continue;
25 } else { 24 } else {
26 free_flat_list(split); 25 free_flat_list(split);
27 return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]); 26 return cmd_results_new(CMD_INVALID, "modifier",
27 "Unknown modifier '%s'", split->items[i]);
28 } 28 }
29 } 29 }
30 free_flat_list(split); 30 free_flat_list(split);
31
32 config->current_bar->modifier = mod; 31 config->current_bar->modifier = mod;
33 sway_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); 32 wlr_log(L_DEBUG,
33 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
34 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 34 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
35} 35}
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index a5710bc0..f7ca0aa4 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,5 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <stdbool.h>
2#include <string.h> 3#include <string.h>
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "list.h" 5#include "list.h"
@@ -9,7 +10,6 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
9 if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) { 10 if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) {
10 return error; 11 return error;
11 } 12 }
12
13 if (!config->current_bar) { 13 if (!config->current_bar) {
14 return cmd_results_new(CMD_FAILURE, "output", "No bar defined."); 14 return cmd_results_new(CMD_FAILURE, "output", "No bar defined.");
15 } 15 }
@@ -21,21 +21,20 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
21 config->current_bar->outputs = outputs; 21 config->current_bar->outputs = outputs;
22 } 22 }
23 23
24 int i; 24 bool add_output = true;
25 int add_output = 1;
26 if (strcmp("*", output) == 0) { 25 if (strcmp("*", output) == 0) {
27 // remove all previous defined outputs and replace with '*' 26 // remove all previous defined outputs and replace with '*'
28 for (i = 0; i < outputs->length; ++i) { 27 for (int i = 0; i < outputs->length; ++i) {
29 free(outputs->items[i]); 28 free(outputs->items[i]);
30 list_del(outputs, i); 29 list_del(outputs, i);
31 } 30 }
32 } else { 31 } else {
33 // only add output if not already defined with either the same 32 // only add output if not already defined with either the same
34 // name or as '*' 33 // name or as '*'
35 for (i = 0; i < outputs->length; ++i) { 34 for (int i = 0; i < outputs->length; ++i) {
36 const char *find = outputs->items[i]; 35 const char *find = outputs->items[i];
37 if (strcmp("*", find) == 0 || strcmp(output, find) == 0) { 36 if (strcmp("*", find) == 0 || strcmp(output, find) == 0) {
38 add_output = 0; 37 add_output = false;
39 break; 38 break;
40 } 39 }
41 } 40 }
@@ -43,8 +42,8 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
43 42
44 if (add_output) { 43 if (add_output) {
45 list_add(outputs, strdup(output)); 44 list_add(outputs, strdup(output));
46 sway_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output); 45 wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'",
46 config->current_bar->id, output);
47 } 47 }
48
49 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 48 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
50} 49}
diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c
index f69e882f..480af724 100644
--- a/sway/commands/bar/pango_markup.c
+++ b/sway/commands/bar/pango_markup.c
@@ -8,19 +8,20 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {
8 if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) {
9 return error; 9 return error;
10 } 10 }
11
12 if (!config->current_bar) { 11 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined."); 12 return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined.");
14 } 13 }
15
16 if (strcasecmp("enabled", argv[0]) == 0) { 14 if (strcasecmp("enabled", argv[0]) == 0) {
17 config->current_bar->pango_markup = true; 15 config->current_bar->pango_markup = true;
18 sway_log(L_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id); 16 wlr_log(L_DEBUG, "Enabling pango markup for bar: %s",
17 config->current_bar->id);
19 } else if (strcasecmp("disabled", argv[0]) == 0) { 18 } else if (strcasecmp("disabled", argv[0]) == 0) {
20 config->current_bar->pango_markup = false; 19 config->current_bar->pango_markup = false;
21 sway_log(L_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id); 20 wlr_log(L_DEBUG, "Disabling pango markup for bar: %s",
21 config->current_bar->id);
22 } else { 22 } else {
23 error = cmd_results_new(CMD_INVALID, "pango_markup", "Invalid value %s", argv[0]); 23 error = cmd_results_new(CMD_INVALID, "pango_markup",
24 "Invalid value %s", argv[0]);
24 return error; 25 return error;
25 } 26 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index 50de58e2..9c580483 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -1,3 +1,4 @@
1#define _POSIX_C_SOURCE 200809L
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
3#include "sway/commands.h" 4#include "sway/commands.h"
@@ -8,26 +9,18 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) {
8 if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) {
9 return error; 10 return error;
10 } 11 }
11
12 if (!config->current_bar) { 12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "position", "No bar defined."); 13 return cmd_results_new(CMD_FAILURE, "position", "No bar defined.");
14 } 14 }
15 15 char *valid[] = { "top", "bottom", "left", "right" };
16 if (strcasecmp("top", argv[0]) == 0) { 16 for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) {
17 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_TOP; 17 if (strcasecmp(valid[i], argv[0]) == 0) {
18 } else if (strcasecmp("bottom", argv[0]) == 0) { 18 wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s",
19 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; 19 argv[0], config->current_bar->id);
20 } else if (strcasecmp("left", argv[0]) == 0) { 20 config->current_bar->position = strdup(argv[0]);
21 sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV"); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_LEFT; 22 }
23 } else if (strcasecmp("right", argv[0]) == 0) {
24 sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV");
25 config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_RIGHT;
26 } else {
27 error = cmd_results_new(CMD_INVALID, "position", "Invalid value %s", argv[0]);
28 return error;
29 } 23 }
30 24 return cmd_results_new(CMD_INVALID,
31 sway_log(L_DEBUG, "Setting bar position '%s' for bar: %s", argv[0], config->current_bar->id); 25 "position", "Invalid value %s", argv[0]);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 26}
diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c
index 745045c5..449124cb 100644
--- a/sway/commands/bar/secondary_button.c
+++ b/sway/commands/bar/secondary_button.c
@@ -3,24 +3,6 @@
3#include "log.h" 3#include "log.h"
4 4
5struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) { 5struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) {
6 const char *cmd_name = "secondary_button"; 6 // TODO TRAY
7#ifndef ENABLE_TRAY 7 return cmd_results_new(CMD_INVALID, "secondary_button", "TODO TRAY");
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 // User should be able to prefix with 0x or whatever they want
22 config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25#endif
26} 8}
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 2766d8a2..1e08df6d 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) {
8 if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) {
9 return error; 9 return error;
10 } 10 }
11
12 if (!config->current_bar) { 11 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined."); 12 return cmd_results_new(CMD_FAILURE,
13 "separator_symbol", "No bar defined.");
14 } 14 }
15
16 free(config->current_bar->separator_symbol); 15 free(config->current_bar->separator_symbol);
17 config->current_bar->separator_symbol = strdup(argv[0]); 16 config->current_bar->separator_symbol = strdup(argv[0]);
18 sway_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id); 17 wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s",
19 18 config->current_bar->separator_symbol, config->current_bar->id);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21} 20}
diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c
index b227ac47..5e199cde 100644
--- a/sway/commands/bar/status_command.c
+++ b/sway/commands/bar/status_command.c
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) {
8 if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) { 8 if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) {
9 return error; 9 return error;
10 } 10 }
11
12 if (!config->current_bar) { 11 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined."); 12 return cmd_results_new(CMD_FAILURE,
13 "status_command", "No bar defined.");
14 } 14 }
15
16 free(config->current_bar->status_command); 15 free(config->current_bar->status_command);
17 config->current_bar->status_command = join_args(argv, argc); 16 config->current_bar->status_command = join_args(argv, argc);
18 sway_log(L_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command); 17 wlr_log(L_DEBUG, "Feeding bar with status command: %s",
19 18 config->current_bar->status_command);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21} 20}
diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c
index 9ac32482..4f24a356 100644
--- a/sway/commands/bar/strip_workspace_numbers.c
+++ b/sway/commands/bar/strip_workspace_numbers.c
@@ -5,23 +5,25 @@
5 5
6struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { 6struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
7 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc,
9 "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
9 return error; 10 return error;
10 } 11 }
11
12 if (!config->current_bar) { 12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined."); 13 return cmd_results_new(CMD_FAILURE,
14 "strip_workspace_numbers", "No bar defined.");
14 } 15 }
15
16 if (strcasecmp("yes", argv[0]) == 0) { 16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->strip_workspace_numbers = true; 17 config->current_bar->strip_workspace_numbers = true;
18 sway_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id); 18 wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s",
19 config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) { 20 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->strip_workspace_numbers = false; 21 config->current_bar->strip_workspace_numbers = false;
21 sway_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id); 22 wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s",
23 config->current_bar->id);
22 } else { 24 } else {
23 error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]); 25 return cmd_results_new(CMD_INVALID,
24 return error; 26 "strip_workspace_numbers", "Invalid value %s", argv[0]);
25 } 27 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 29}
diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c
index 452e2df5..520cdd11 100644
--- a/sway/commands/bar/swaybar_command.c
+++ b/sway/commands/bar/swaybar_command.c
@@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) {
8 if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) { 8 if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) {
9 return error; 9 return error;
10 } 10 }
11
12 if (!config->current_bar) { 11 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined."); 12 return cmd_results_new(CMD_FAILURE,
13 "swaybar_command", "No bar defined.");
14 } 14 }
15
16 free(config->current_bar->swaybar_command); 15 free(config->current_bar->swaybar_command);
17 config->current_bar->swaybar_command = join_args(argv, argc); 16 config->current_bar->swaybar_command = join_args(argv, argc);
18 sway_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command); 17 wlr_log(L_DEBUG, "Using custom swaybar command: %s",
19 18 config->current_bar->swaybar_command);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21} 20}
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index 012304a9..6ab16731 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -3,27 +3,6 @@
3#include "sway/commands.h" 3#include "sway/commands.h"
4 4
5struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { 5struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
6 const char *cmd_name = "tray_output"; 6 // TODO TRAY
7#ifndef ENABLE_TRAY 7 return cmd_results_new(CMD_INVALID, "tray_output", "TODO TRAY");
8 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
9 "%s called, but sway was compiled without tray support",
10 cmd_name, cmd_name);
11#else
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16
17 if (!config->current_bar) {
18 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
19 }
20
21 if (strcmp(argv[0], "all") == 0) {
22 // Default behaviour
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24 }
25 config->current_bar->tray_output = strdup(argv[0]);
26
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28#endif
29} 8}
diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c
index ac0572ce..91c56f19 100644
--- a/sway/commands/bar/tray_padding.c
+++ b/sway/commands/bar/tray_padding.c
@@ -4,31 +4,6 @@
4#include "log.h" 4#include "log.h"
5 5
6struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { 6struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
7 const char *cmd_name = "tray_padding"; 7 // TODO TRAY
8#ifndef ENABLE_TRAY 8 return cmd_results_new(CMD_INVALID, "tray_padding", "TODO TRAY");
9 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command"
10 "%s called, but sway was compiled without tray support",
11 cmd_name, cmd_name);
12#else
13 struct cmd_results *error = NULL;
14 if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 1))) {
15 return error;
16 }
17
18 if (!config->current_bar) {
19 return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
20 }
21
22 if (argc == 1 || (argc == 2 && strcasecmp("px", argv[1]) == 0)) {
23 char *inv;
24 uint32_t padding = strtoul(argv[0], &inv, 10);
25 if (*inv == '\0' || strcasecmp(inv, "px") == 0) {
26 config->current_bar->tray_padding = padding;
27 sway_log(L_DEBUG, "Enabling tray padding of %d px on bar: %s", padding, config->current_bar->id);
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29 }
30 }
31 return cmd_results_new(CMD_FAILURE, cmd_name,
32 "Expected 'tray_padding <padding>[px]'");
33#endif
34} 9}
diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c
index 67dd2d31..6edc3a0d 100644
--- a/sway/commands/bar/workspace_buttons.c
+++ b/sway/commands/bar/workspace_buttons.c
@@ -8,20 +8,21 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {
8 if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) {
9 return error; 9 return error;
10 } 10 }
11
12 if (!config->current_bar) { 11 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined."); 12 return cmd_results_new(CMD_FAILURE,
13 "workspace_buttons", "No bar defined.");
14 } 14 }
15
16 if (strcasecmp("yes", argv[0]) == 0) { 15 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->workspace_buttons = true; 16 config->current_bar->workspace_buttons = true;
18 sway_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id); 17 wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s",
18 config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) { 19 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->workspace_buttons = false; 20 config->current_bar->workspace_buttons = false;
21 sway_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id); 21 wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s",
22 config->current_bar->id);
22 } else { 23 } else {
23 error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]); 24 return cmd_results_new(CMD_INVALID, "workspace_buttons",
24 return error; 25 "Invalid value %s", argv[0]);
25 } 26 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 28}
diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c
index 4ed1f12a..7386f82c 100644
--- a/sway/commands/bar/wrap_scroll.c
+++ b/sway/commands/bar/wrap_scroll.c
@@ -8,20 +8,20 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {
8 if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) {
9 return error; 9 return error;
10 } 10 }
11
12 if (!config->current_bar) { 11 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined."); 12 return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined.");
14 } 13 }
15
16 if (strcasecmp("yes", argv[0]) == 0) { 14 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->wrap_scroll = true; 15 config->current_bar->wrap_scroll = true;
18 sway_log(L_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id); 16 wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s",
17 config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) { 18 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->wrap_scroll = false; 19 config->current_bar->wrap_scroll = false;
21 sway_log(L_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id); 20 wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s",
21 config->current_bar->id);
22 } else { 22 } else {
23 error = cmd_results_new(CMD_INVALID, "wrap_scroll", "Invalid value %s", argv[0]); 23 return cmd_results_new(CMD_INVALID,
24 return error; 24 "wrap_scroll", "Invalid value %s", argv[0]);
25 } 25 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 27}
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index d9ea37b7..cbabb07b 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,9 +1,13 @@
1#ifdef __linux__
2#include <linux/input-event-codes.h>
3#elif __FreeBSD__
4#include <dev/evdev/input-event-codes.h>
5#endif
1#include <xkbcommon/xkbcommon.h> 6#include <xkbcommon/xkbcommon.h>
2#include <xkbcommon/xkbcommon-names.h> 7#include <xkbcommon/xkbcommon-names.h>
3#include <strings.h> 8#include <strings.h>
4#include "sway/commands.h" 9#include "sway/commands.h"
5#include "sway/config.h" 10#include "sway/config.h"
6#include "sway/input_state.h"
7#include "list.h" 11#include "list.h"
8#include "log.h" 12#include "log.h"
9#include "stringop.h" 13#include "stringop.h"
@@ -11,13 +15,67 @@
11 15
12int binding_order = 0; 16int binding_order = 0;
13 17
18void free_sway_binding(struct sway_binding *binding) {
19 if (!binding) {
20 return;
21 }
22
23 if (binding->keys) {
24 free_flat_list(binding->keys);
25 }
26 free(binding->command);
27 free(binding);
28}
29
30/**
31 * 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
33 * not be equivalent on some layouts.
34 */
35bool binding_key_compare(struct sway_binding *binding_a,
36 struct sway_binding *binding_b) {
37 if (binding_a->release != binding_b->release) {
38 return false;
39 }
40
41 if (binding_a->bindcode != binding_b->bindcode) {
42 return false;
43 }
44
45 if (binding_a->modifiers ^ binding_b->modifiers) {
46 return false;
47 }
48
49 if (binding_a->keys->length != binding_b->keys->length) {
50 return false;
51 }
52
53 int keys_len = binding_a->keys->length;
54 for (int i = 0; i < keys_len; ++i) {
55 uint32_t key_a = *(uint32_t*)binding_a->keys->items[i];
56 bool found = false;
57 for (int j = 0; j < keys_len; ++j) {
58 uint32_t key_b = *(uint32_t*)binding_b->keys->items[j];
59 if (key_b == key_a) {
60 found = true;
61 break;
62 }
63 }
64 if (!found) {
65 return false;
66 }
67 }
68
69 return true;
70}
71
14struct cmd_results *cmd_bindsym(int argc, char **argv) { 72struct cmd_results *cmd_bindsym(int argc, char **argv) {
15 struct cmd_results *error = NULL; 73 struct cmd_results *error = NULL;
16 if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) { 74 if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
17 return error; 75 return error;
18 } 76 }
19 77
20 struct sway_binding *binding = malloc(sizeof(struct sway_binding)); 78 struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
21 if (!binding) { 79 if (!binding) {
22 return cmd_results_new(CMD_FAILURE, "bindsym", 80 return cmd_results_new(CMD_FAILURE, "bindsym",
23 "Unable to allocate binding"); 81 "Unable to allocate binding");
@@ -58,7 +116,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
58 // Check for mouse binding 116 // Check for mouse binding
59 if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && 117 if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
60 strlen(split->items[i]) == strlen("button0")) { 118 strlen(split->items[i]) == strlen("button0")) {
61 sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK; 119 sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT;
62 } 120 }
63 if (!sym) { 121 if (!sym) {
64 struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", 122 struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
@@ -67,7 +125,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
67 free_flat_list(split); 125 free_flat_list(split);
68 return ret; 126 return ret;
69 } 127 }
70 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); 128 xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t));
71 if (!key) { 129 if (!key) {
72 free_sway_binding(binding); 130 free_sway_binding(binding);
73 free_flat_list(split); 131 free_flat_list(split);
@@ -78,20 +136,29 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
78 list_add(binding->keys, key); 136 list_add(binding->keys, key);
79 } 137 }
80 free_flat_list(split); 138 free_flat_list(split);
139 binding->order = binding_order++;
81 140
82 struct sway_mode *mode = config->current_mode; 141 list_t *mode_bindings = config->current_mode->keysym_bindings;
83 int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding); 142
84 if (i > -1) { 143 // overwrite the binding if it already exists
85 sway_log(L_DEBUG, "bindsym - '%s' already exists, overwriting", argv[0]); 144 bool overwritten = false;
86 struct sway_binding *dup = mode->bindings->items[i]; 145 for (int i = 0; i < mode_bindings->length; ++i) {
87 free_sway_binding(dup); 146 struct sway_binding *config_binding = mode_bindings->items[i];
88 list_del(mode->bindings, i); 147 if (binding_key_compare(binding, config_binding)) {
148 wlr_log(L_DEBUG, "overwriting old binding with command '%s'",
149 config_binding->command);
150 free_sway_binding(config_binding);
151 mode_bindings->items[i] = binding;
152 overwritten = true;
153 }
89 } 154 }
90 binding->order = binding_order++;
91 list_add(mode->bindings, binding);
92 list_qsort(mode->bindings, sway_binding_cmp_qsort);
93 155
94 sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command); 156 if (!overwritten) {
157 list_add(mode_bindings, binding);
158 }
159
160 wlr_log(L_DEBUG, "bindsym - Bound %s to command %s",
161 argv[0], binding->command);
95 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 162 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
96} 163}
97 164
@@ -101,7 +168,7 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
101 return error; 168 return error;
102 } 169 }
103 170
104 struct sway_binding *binding = malloc(sizeof(struct sway_binding)); 171 struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
105 if (!binding) { 172 if (!binding) {
106 return cmd_results_new(CMD_FAILURE, "bindsym", 173 return cmd_results_new(CMD_FAILURE, "bindsym",
107 "Unable to allocate binding"); 174 "Unable to allocate binding");
@@ -138,33 +205,41 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
138 // parse keycode 205 // parse keycode
139 xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10); 206 xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10);
140 if (!xkb_keycode_is_legal_ext(keycode)) { 207 if (!xkb_keycode_is_legal_ext(keycode)) {
141 error = cmd_results_new(CMD_INVALID, "bindcode", "Invalid keycode '%s'", (char *)split->items[i]); 208 error =
209 cmd_results_new(CMD_INVALID, "bindcode",
210 "Invalid keycode '%s'", (char *)split->items[i]);
142 free_sway_binding(binding); 211 free_sway_binding(binding);
143 list_free(split); 212 list_free(split);
144 return error; 213 return error;
145 } 214 }
146 xkb_keycode_t *key = malloc(sizeof(xkb_keycode_t)); 215 xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t));
147 *key = keycode - 8; 216 *key = keycode - 8;
148 list_add(binding->keys, key); 217 list_add(binding->keys, key);
149 } 218 }
150 free_flat_list(split); 219 free_flat_list(split);
151 220
152 struct sway_mode *mode = config->current_mode; 221 binding->order = binding_order++;
153 int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding); 222
154 if (i > -1) { 223 list_t *mode_bindings = config->current_mode->keycode_bindings;
155 struct sway_binding *dup = mode->bindings->items[i]; 224
156 if (dup->bindcode) { 225 // overwrite the binding if it already exists
157 sway_log(L_DEBUG, "bindcode - '%s' already exists, overwriting", argv[0]); 226 bool overwritten = false;
158 } else { 227 for (int i = 0; i < mode_bindings->length; ++i) {
159 sway_log(L_DEBUG, "bindcode - '%s' already exists as bindsym, overwriting", argv[0]); 228 struct sway_binding *config_binding = mode_bindings->items[i];
229 if (binding_key_compare(binding, config_binding)) {
230 wlr_log(L_DEBUG, "overwriting old binding with command '%s'",
231 config_binding->command);
232 free_sway_binding(config_binding);
233 mode_bindings->items[i] = binding;
234 overwritten = true;
160 } 235 }
161 free_sway_binding(dup);
162 list_del(mode->bindings, i);
163 } 236 }
164 binding->order = binding_order++;
165 list_add(mode->bindings, binding);
166 list_qsort(mode->bindings, sway_binding_cmp_qsort);
167 237
168 sway_log(L_DEBUG, "bindcode - Bound %s to command %s", argv[0], binding->command); 238 if (!overwritten) {
239 list_add(mode_bindings, binding);
240 }
241
242 wlr_log(L_DEBUG, "bindcode - Bound %s to command %s",
243 argv[0], binding->command);
169 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 244 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
170} 245}
diff --git a/sway/commands/border.c b/sway/commands/border.c
deleted file mode 100644
index c888622e..00000000
--- a/sway/commands/border.c
+++ /dev/null
@@ -1,65 +0,0 @@
1#include <errno.h>
2#include <stdlib.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/container.h"
7#include "sway/focus.h"
8
9struct cmd_results *cmd_border(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if (!config->active) {
12 return cmd_results_new(CMD_FAILURE, "border", "Can only be used when sway is running.");
13 }
14 if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) {
15 return error;
16 }
17
18 if (argc > 2) {
19 return cmd_results_new(CMD_INVALID, "border",
20 "Expected 'border <normal|pixel|none|toggle> [<n>]");
21 }
22
23 swayc_t *view = current_container;
24 enum swayc_border_types border = view->border_type;
25 int thickness = view->border_thickness;
26
27 if (strcasecmp(argv[0], "none") == 0) {
28 border = B_NONE;
29 } else if (strcasecmp(argv[0], "normal") == 0) {
30 border = B_NORMAL;
31 } else if (strcasecmp(argv[0], "pixel") == 0) {
32 border = B_PIXEL;
33 } else if (strcasecmp(argv[0], "toggle") == 0) {
34 switch (border) {
35 case B_NONE:
36 border = B_PIXEL;
37 break;
38 case B_NORMAL:
39 border = B_NONE;
40 break;
41 case B_PIXEL:
42 border = B_NORMAL;
43 break;
44 }
45 } else {
46 return cmd_results_new(CMD_INVALID, "border",
47 "Expected 'border <normal|pixel|none|toggle>");
48 }
49
50 if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
51 thickness = (int)strtol(argv[1], NULL, 10);
52 if (errno == ERANGE || thickness < 0) {
53 errno = 0;
54 return cmd_results_new(CMD_INVALID, "border", "Number is out of range.");
55 }
56 }
57
58 if (view) {
59 view->border_type = border;
60 view->border_thickness = thickness;
61 update_geometry(view);
62 }
63
64 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
65}
diff --git a/sway/commands/client.c b/sway/commands/client.c
deleted file mode 100644
index f3d879cd..00000000
--- a/sway/commands/client.c
+++ /dev/null
@@ -1,72 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4
5static struct cmd_results *parse_border_color(struct border_colors *border_colors, const char *cmd_name, int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if (argc < 3 || argc > 5) {
8 return cmd_results_new(CMD_INVALID, cmd_name, "Requires between three and five color values");
9 }
10
11 uint32_t *colors[5] = {
12 &border_colors->border,
13 &border_colors->background,
14 &border_colors->text,
15 &border_colors->indicator,
16 &border_colors->child_border
17 };
18 int i;
19 for (i = 0; i < argc; i++) {
20 char buffer[10];
21 error = add_color(cmd_name, buffer, argv[i]);
22 if (error) {
23 return error;
24 }
25 *colors[i] = strtoul(buffer + 1, NULL, 16);
26 }
27
28 if (argc < 5) {
29 border_colors->child_border = border_colors->background;
30 }
31
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33}
34
35struct cmd_results *cmd_client_focused(int argc, char **argv) {
36 return parse_border_color(&config->border_colors.focused, "client.focused", argc, argv);
37}
38
39struct cmd_results *cmd_client_focused_inactive(int argc, char **argv) {
40 return parse_border_color(&config->border_colors.focused_inactive, "client.focused_inactive", argc, argv);
41}
42
43struct cmd_results *cmd_client_unfocused(int argc, char **argv) {
44 return parse_border_color(&config->border_colors.unfocused, "client.unfocused", argc, argv);
45}
46
47struct cmd_results *cmd_client_urgent(int argc, char **argv) {
48 return parse_border_color(&config->border_colors.urgent, "client.urgent", argc, argv);
49}
50
51struct cmd_results *cmd_client_placeholder(int argc, char **argv) {
52 return parse_border_color(&config->border_colors.placeholder, "client.placeholder", argc, argv);
53}
54
55struct cmd_results *cmd_client_background(int argc, char **argv) {
56 char buffer[10];
57 struct cmd_results *error = NULL;
58 uint32_t background;
59
60 if (argc != 1) {
61 return cmd_results_new(CMD_INVALID, "client.background", "Requires exactly one color value");
62 }
63
64 error = add_color("client.background", buffer, argv[0]);
65 if (error) {
66 return error;
67 }
68
69 background = strtoul(buffer+1, NULL, 16);
70 config->border_colors.background = background;
71 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
72}
diff --git a/sway/commands/clipboard.c b/sway/commands/clipboard.c
deleted file mode 100644
index 95514e78..00000000
--- a/sway/commands/clipboard.c
+++ /dev/null
@@ -1,38 +0,0 @@
1#include <wlc/wlc.h>
2#include <unistd.h>
3#include <string.h>
4#include "sway/commands.h"
5#include "stringop.h"
6
7static void send_clipboard(void *data, const char *type, int fd) {
8 if (strcmp(type, "text/plain") != 0
9 && strcmp(type, "text/plain;charset=utf-8") != 0) {
10 close(fd);
11 return;
12 }
13
14 const char *str = data;
15 write(fd, str, strlen(str));
16 close(fd);
17}
18
19struct cmd_results *cmd_clipboard(int argc, char **argv) {
20 static char *current_data = NULL;
21
22 struct cmd_results *error = NULL;
23 if ((error = checkarg(argc, "clipboard", EXPECTED_AT_LEAST, 1))) {
24 return error;
25 }
26
27 static const char *types[2] = {
28 "text/plain",
29 "text/plain;charset=utf-8"
30 };
31
32 char *str = join_args(argv, argc);
33 wlc_set_selection(str, types, 2, &send_clipboard);
34
35 free(current_data);
36 current_data = str;
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38}
diff --git a/sway/commands/commands.c b/sway/commands/commands.c
deleted file mode 100644
index 0c64970c..00000000
--- a/sway/commands/commands.c
+++ /dev/null
@@ -1,26 +0,0 @@
1#include <stdbool.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "list.h"
6#include "log.h"
7
8struct cmd_results *cmd_commands(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if ((error = check_security_config())) {
14 return error;
15 }
16
17 if (strcmp(argv[0], "{") != 0) {
18 return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration");
19 }
20
21 if (!config->reading) {
22 return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file.");
23 }
24
25 return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL);
26}
diff --git a/sway/commands/debuglog.c b/sway/commands/debuglog.c
deleted file mode 100644
index 658d6165..00000000
--- a/sway/commands/debuglog.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_debuglog(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "debuglog", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 } else if (strcasecmp(argv[0], "toggle") == 0) {
11 if (config->reading) {
12 return cmd_results_new(CMD_FAILURE, "debuglog toggle", "Can't be used in config file.");
13 }
14 if (toggle_debug_logging()) {
15 sway_log(L_DEBUG, "Debuglog turned on.");
16 }
17 } else if (strcasecmp(argv[0], "on") == 0) {
18 set_log_level(L_DEBUG);
19 sway_log(L_DEBUG, "Debuglog turned on.");
20 } else if (strcasecmp(argv[0], "off") == 0) {
21 reset_log_level();
22 } else {
23 return cmd_results_new(CMD_FAILURE, "debuglog", "Expected 'debuglog on|off|toggle'");
24 }
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
27
diff --git a/sway/commands/default_border.c b/sway/commands/default_border.c
deleted file mode 100644
index 8fbe8d19..00000000
--- a/sway/commands/default_border.c
+++ /dev/null
@@ -1,44 +0,0 @@
1#include <errno.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6
7struct cmd_results *cmd_default_border(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "default_border", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12
13 if (argc > 2) {
14 return cmd_results_new(CMD_INVALID, "default_border",
15 "Expected 'default_border <normal|none|pixel> [<n>]");
16 }
17
18 enum swayc_border_types border = config->border;
19 int thickness = config->border_thickness;
20
21 if (strcasecmp(argv[0], "none") == 0) {
22 border = B_NONE;
23 } else if (strcasecmp(argv[0], "normal") == 0) {
24 border = B_NORMAL;
25 } else if (strcasecmp(argv[0], "pixel") == 0) {
26 border = B_PIXEL;
27 } else {
28 return cmd_results_new(CMD_INVALID, "default_border",
29 "Expected 'default_border <normal|none|pixel> [<n>]");
30 }
31
32 if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
33 thickness = (int)strtol(argv[1], NULL, 10);
34 if (errno == ERANGE || thickness < 0) {
35 errno = 0;
36 return cmd_results_new(CMD_INVALID, "default_border", "Number is out out of range.");
37 }
38 }
39
40 config->border = border;
41 config->border_thickness = thickness;
42
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
44}
diff --git a/sway/commands/default_floating_border.c b/sway/commands/default_floating_border.c
deleted file mode 100644
index fb48c1c0..00000000
--- a/sway/commands/default_floating_border.c
+++ /dev/null
@@ -1,45 +0,0 @@
1#include <errno.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6
7struct cmd_results *cmd_default_floating_border(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "default_floating_border", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12
13 if (argc > 2) {
14 return cmd_results_new(CMD_INVALID, "default_floating_border",
15 "Expected 'default_floating_border <normal|none|pixel> [<n>]");
16 }
17
18 enum swayc_border_types border = config->floating_border;
19 int thickness = config->floating_border_thickness;
20
21 if (strcasecmp(argv[0], "none") == 0) {
22 border = B_NONE;
23 } else if (strcasecmp(argv[0], "normal") == 0) {
24 border = B_NORMAL;
25 } else if (strcasecmp(argv[0], "pixel") == 0) {
26 border = B_PIXEL;
27 } else {
28 return cmd_results_new(CMD_INVALID, "default_floating_border",
29 "Expected 'default_floating_border <normal|none|pixel> [<n>]");
30 }
31
32 if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
33 thickness = (int)strtol(argv[1], NULL, 10);
34 if (errno == ERANGE || thickness < 0) {
35 errno = 0;
36 return cmd_results_new(CMD_INVALID, "default_floating_border",
37 "Number is out out of range.");
38 }
39 }
40
41 config->floating_border = border;
42 config->floating_border_thickness = thickness;
43
44 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
45}
diff --git a/sway/commands/orientation.c b/sway/commands/default_orientation.c
index e54b60ee..a5347ce2 100644
--- a/sway/commands/orientation.c
+++ b/sway/commands/default_orientation.c
@@ -2,10 +2,9 @@
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4 4
5struct cmd_results *cmd_orientation(int argc, char **argv) { 5struct cmd_results *cmd_default_orientation(int argc, char **argv) {
6 struct cmd_results *error = NULL; 6 struct cmd_results *error = NULL;
7 if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file."); 7 if ((error = checkarg(argc, "default_orientation", EXPECTED_EQUAL_TO, 1))) {
8 if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
9 return error; 8 return error;
10 } 9 }
11 if (strcasecmp(argv[0], "horizontal") == 0) { 10 if (strcasecmp(argv[0], "horizontal") == 0) {
@@ -15,7 +14,8 @@ struct cmd_results *cmd_orientation(int argc, char **argv) {
15 } else if (strcasecmp(argv[0], "auto") == 0) { 14 } else if (strcasecmp(argv[0], "auto") == 0) {
16 // Do nothing 15 // Do nothing
17 } else { 16 } else {
18 return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'"); 17 return cmd_results_new(CMD_INVALID, "default_orientation",
18 "Expected 'orientation <horizontal|vertical|auto>'");
19 } 19 }
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21} 21}
diff --git a/sway/commands/exec.c b/sway/commands/exec.c
index 58ef5f94..363d5bef 100644
--- a/sway/commands/exec.c
+++ b/sway/commands/exec.c
@@ -1,5 +1,6 @@
1#include <string.h> 1#include <string.h>
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/config.h"
3#include "log.h" 4#include "log.h"
4#include "stringop.h" 5#include "stringop.h"
5 6
@@ -7,10 +8,9 @@ struct cmd_results *cmd_exec(int argc, char **argv) {
7 if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); 8 if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
8 if (config->reloading) { 9 if (config->reloading) {
9 char *args = join_args(argv, argc); 10 char *args = join_args(argv, argc);
10 sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); 11 wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
11 free(args); 12 free(args);
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 13 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13 } 14 }
14 return cmd_exec_always(argc, argv); 15 return cmd_exec_always(argc, argv);
15} 16}
16
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index ab2d8622..954950e7 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,9 +1,13 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdint.h>
2#include <string.h> 4#include <string.h>
3#include <sys/wait.h> 5#include <sys/wait.h>
4#include <unistd.h> 6#include <unistd.h>
5#include "sway/commands.h" 7#include "sway/commands.h"
6#include "sway/config.h" 8#include "sway/config.h"
9#include "sway/tree/container.h"
10#include "sway/tree/workspace.h"
7#include "log.h" 11#include "log.h"
8#include "stringop.h" 12#include "stringop.h"
9 13
@@ -16,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
16 20
17 char *tmp = NULL; 21 char *tmp = NULL;
18 if (strcmp((char*)*argv, "--no-startup-id") == 0) { 22 if (strcmp((char*)*argv, "--no-startup-id") == 0) {
19 sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); 23 wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
20 if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) { 24 if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
21 return error; 25 return error;
22 } 26 }
@@ -31,11 +35,11 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
31 strncpy(cmd, tmp, sizeof(cmd)); 35 strncpy(cmd, tmp, sizeof(cmd));
32 cmd[sizeof(cmd) - 1] = 0; 36 cmd[sizeof(cmd) - 1] = 0;
33 free(tmp); 37 free(tmp);
34 sway_log(L_DEBUG, "Executing %s", cmd); 38 wlr_log(L_DEBUG, "Executing %s", cmd);
35 39
36 int fd[2]; 40 int fd[2];
37 if (pipe(fd) != 0) { 41 if (pipe(fd) != 0) {
38 sway_log(L_ERROR, "Unable to create pipe for fork"); 42 wlr_log(L_ERROR, "Unable to create pipe for fork");
39 } 43 }
40 44
41 pid_t pid; 45 pid_t pid;
@@ -49,7 +53,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
49 setsid(); 53 setsid();
50 if ((*child = fork()) == 0) { 54 if ((*child = fork()) == 0) {
51 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); 55 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
52 /* Not reached */ 56 // Not reached
53 } 57 }
54 close(fd[0]); 58 close(fd[0]);
55 ssize_t s = 0; 59 ssize_t s = 0;
@@ -70,13 +74,9 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
70 close(fd[0]); 74 close(fd[0]);
71 // cleanup child process 75 // cleanup child process
72 wait(0); 76 wait(0);
73 swayc_t *ws = swayc_active_workspace(); 77 if (*child > 0) {
74 if (*child > 0 && ws) { 78 wlr_log(L_DEBUG, "Child process created with pid %d", *child);
75 sway_log(L_DEBUG, "Child process created with pid %d for workspace %s", *child, ws->name); 79 // TODO: add PID to active workspace
76 struct pid_workspace *pw = malloc(sizeof(struct pid_workspace));
77 pw->pid = child;
78 pw->workspace = strdup(ws->name);
79 pid_workspace_add(pw);
80 } else { 80 } else {
81 free(child); 81 free(child);
82 } 82 }
diff --git a/sway/commands/exit.c b/sway/commands/exit.c
index f192f86a..d5353c20 100644
--- a/sway/commands/exit.c
+++ b/sway/commands/exit.c
@@ -1,17 +1,14 @@
1#include <stddef.h>
1#include "sway/commands.h" 2#include "sway/commands.h"
2#include "sway/container.h" 3#include "sway/config.h"
3 4
4void sway_terminate(int exit_code); 5void sway_terminate(int exit_code);
5 6
6struct cmd_results *cmd_exit(int argc, char **argv) { 7struct cmd_results *cmd_exit(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if (config->reading) return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
9 if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) { 9 if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
10 return error; 10 return error;
11 } 11 }
12 // Close all views 12 sway_terminate(0);
13 close_views(&root_container);
14 sway_terminate(EXIT_SUCCESS);
15 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 13 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
16} 14}
17
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
deleted file mode 100644
index ccfde532..00000000
--- a/sway/commands/floating.c
+++ /dev/null
@@ -1,81 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/container.h"
5#include "sway/ipc-server.h"
6#include "sway/layout.h"
7#include "list.h"
8#include "log.h"
9
10struct cmd_results *cmd_floating(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if (config->reading) return cmd_results_new(CMD_FAILURE, "floating", "Can't be used in config file.");
13 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
14 return error;
15 }
16 swayc_t *view = current_container;
17 bool wants_floating;
18 if (strcasecmp(argv[0], "enable") == 0) {
19 wants_floating = true;
20 } else if (strcasecmp(argv[0], "disable") == 0) {
21 wants_floating = false;
22 } else if (strcasecmp(argv[0], "toggle") == 0) {
23 wants_floating = !view->is_floating;
24 } else {
25 return cmd_results_new(CMD_FAILURE, "floating",
26 "Expected 'floating <enable|disable|toggle>");
27 }
28
29 // Prevent running floating commands on things like workspaces
30 if (view->type != C_VIEW) {
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32 }
33
34 // Change from nonfloating to floating
35 if (!view->is_floating && wants_floating) {
36 // Remove view from its current location
37 destroy_container(remove_child(view));
38
39 // and move it into workspace floating
40 add_floating(swayc_active_workspace(), view);
41 view->x = (swayc_active_workspace()->width - view->width)/2;
42 view->y = (swayc_active_workspace()->height - view->height)/2;
43 if (view->desired_width != -1) {
44 view->width = view->desired_width;
45 }
46 if (view->desired_height != -1) {
47 view->height = view->desired_height;
48 }
49 arrange_windows(swayc_active_workspace(), -1, -1);
50
51 } else if (view->is_floating && !wants_floating) {
52 // Delete the view from the floating list and unset its is_floating flag
53 remove_child(view);
54 view->is_floating = false;
55 // Get the properly focused container, and add in the view there
56 swayc_t *focused = container_under_pointer();
57 // If focused is null, it's because the currently focused container is a workspace
58 if (focused == NULL || focused->is_floating) {
59 focused = swayc_active_workspace();
60 }
61 set_focused_container(focused);
62
63 sway_log(L_DEBUG, "Non-floating focused container is %p", focused);
64
65 // Case of focused workspace, just create as child of it
66 if (focused->type == C_WORKSPACE) {
67 add_child(focused, view);
68 }
69 // Regular case, create as sibling of current container
70 else {
71 add_sibling(focused, view);
72 }
73 // Refocus on the view once its been put back into the layout
74 view->width = view->height = 0;
75 arrange_windows(swayc_active_workspace(), -1, -1);
76 remove_view_from_scratchpad(view);
77 ipc_event_window(view, "floating");
78 }
79 set_focused_container(view);
80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
81}
diff --git a/sway/commands/floating_maximum_size.c b/sway/commands/floating_maximum_size.c
deleted file mode 100644
index 5bca4d7c..00000000
--- a/sway/commands/floating_maximum_size.c
+++ /dev/null
@@ -1,38 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 int32_t width;
9 int32_t height;
10 char *ptr;
11
12 if ((error = checkarg(argc, "floating_maximum_size", EXPECTED_EQUAL_TO, 3))) {
13 return error;
14 }
15 width = strtol(argv[0], &ptr, 10);
16 height = strtol(argv[2], &ptr, 10);
17
18 if (width < -1) {
19 sway_log(L_DEBUG, "floating_maximum_size invalid width value: '%s'", argv[0]);
20
21 } else {
22 config->floating_maximum_width = width;
23
24 }
25
26 if (height < -1) {
27 sway_log(L_DEBUG, "floating_maximum_size invalid height value: '%s'", argv[2]);
28 }
29 else {
30 config->floating_maximum_height = height;
31
32 }
33
34 sway_log(L_DEBUG, "New floating_maximum_size: '%d' x '%d'", config->floating_maximum_width,
35 config->floating_maximum_height);
36
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38}
diff --git a/sway/commands/floating_minimum_size.c b/sway/commands/floating_minimum_size.c
deleted file mode 100644
index ba72454c..00000000
--- a/sway/commands/floating_minimum_size.c
+++ /dev/null
@@ -1,38 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 int32_t width;
9 int32_t height;
10 char *ptr;
11
12 if ((error = checkarg(argc, "floating_minimum_size", EXPECTED_EQUAL_TO, 3))) {
13 return error;
14 }
15 width = strtol(argv[0], &ptr, 10);
16 height = strtol(argv[2], &ptr, 10);
17
18 if (width <= 0) {
19 sway_log(L_DEBUG, "floating_minimum_size invalid width value: '%s'", argv[0]);
20
21 } else {
22 config->floating_minimum_width = width;
23
24 }
25
26 if (height <= 0) {
27 sway_log(L_DEBUG, "floating_minimum_size invalid height value: '%s'", argv[2]);
28 }
29 else {
30 config->floating_minimum_height = height;
31
32 }
33
34 sway_log(L_DEBUG, "New floating_minimum_size: '%d' x '%d'", config->floating_minimum_width,
35 config->floating_minimum_height);
36
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38}
diff --git a/sway/commands/floating_mod.c b/sway/commands/floating_mod.c
deleted file mode 100644
index b8e81ab9..00000000
--- a/sway/commands/floating_mod.c
+++ /dev/null
@@ -1,42 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input_state.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8#include "util.h"
9
10struct cmd_results *cmd_floating_mod(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) {
13 return error;
14 }
15 int i;
16 list_t *split = split_string(argv[0], "+");
17 config->floating_mod = 0;
18
19 // set modifier keys
20 for (i = 0; i < split->length; ++i) {
21 config->floating_mod |= get_modifier_mask_by_name(split->items[i]);
22 }
23 free_flat_list(split);
24 if (!config->floating_mod) {
25 error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
26 return error;
27 }
28
29 if (argc >= 2) {
30 if (strcasecmp("inverse", argv[1]) == 0) {
31 config->dragging_key = M_RIGHT_CLICK;
32 config->resizing_key = M_LEFT_CLICK;
33 } else if (strcasecmp("normal", argv[1]) == 0) {
34 config->dragging_key = M_LEFT_CLICK;
35 config->resizing_key = M_RIGHT_CLICK;
36 } else {
37 error = cmd_results_new(CMD_INVALID, "floating_modifier", "Invalid definition %s", argv[1]);
38 return error;
39 }
40 }
41 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
42}
diff --git a/sway/commands/floating_scroll.c b/sway/commands/floating_scroll.c
deleted file mode 100644
index 8c50c5bd..00000000
--- a/sway/commands/floating_scroll.c
+++ /dev/null
@@ -1,46 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "log.h"
6#include "stringop.h"
7
8struct cmd_results *cmd_floating_scroll(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "floating_scroll", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 if (!strcasecmp("up", argv[0])) {
14 free(config->floating_scroll_up_cmd);
15 if (argc < 2) {
16 config->floating_scroll_up_cmd = strdup("");
17 } else {
18 config->floating_scroll_up_cmd = join_args(argv + 1, argc - 1);
19 }
20 } else if (!strcasecmp("down", argv[0])) {
21 free(config->floating_scroll_down_cmd);
22 if (argc < 2) {
23 config->floating_scroll_down_cmd = strdup("");
24 } else {
25 config->floating_scroll_down_cmd = join_args(argv + 1, argc - 1);
26 }
27 } else if (!strcasecmp("left", argv[0])) {
28 free(config->floating_scroll_left_cmd);
29 if (argc < 2) {
30 config->floating_scroll_left_cmd = strdup("");
31 } else {
32 config->floating_scroll_left_cmd = join_args(argv + 1, argc - 1);
33 }
34 } else if (!strcasecmp("right", argv[0])) {
35 free(config->floating_scroll_right_cmd);
36 if (argc < 2) {
37 config->floating_scroll_right_cmd = strdup("");
38 } else {
39 config->floating_scroll_right_cmd = join_args(argv + 1, argc - 1);
40 }
41 } else {
42 error = cmd_results_new(CMD_INVALID, "floating_scroll", "Unknown command: '%s'", argv[0]);
43 return error;
44 }
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46}
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index defaba29..74d9d535 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -1,99 +1,57 @@
1#include <string.h>
2#include <strings.h> 1#include <strings.h>
3#include <wlc/wlc.h> 2#include <wlr/util/log.h>
3#include "log.h"
4#include "sway/input/input-manager.h"
5#include "sway/input/seat.h"
6#include "sway/tree/view.h"
4#include "sway/commands.h" 7#include "sway/commands.h"
5#include "sway/container.h" 8
6#include "sway/focus.h" 9static bool parse_movement_direction(const char *name,
7#include "sway/input_state.h" 10 enum movement_direction *out) {
8#include "sway/output.h" 11 if (strcasecmp(name, "left") == 0) {
9#include "sway/workspace.h" 12 *out = MOVE_LEFT;
13 } else if (strcasecmp(name, "right") == 0) {
14 *out = MOVE_RIGHT;
15 } else if (strcasecmp(name, "up") == 0) {
16 *out = MOVE_UP;
17 } else if (strcasecmp(name, "down") == 0) {
18 *out = MOVE_DOWN;
19 } else if (strcasecmp(name, "parent") == 0) {
20 *out = MOVE_PARENT;
21 } else if (strcasecmp(name, "child") == 0) {
22 *out = MOVE_CHILD;
23 } else {
24 return false;
25 }
26
27 return true;
28}
10 29
11struct cmd_results *cmd_focus(int argc, char **argv) { 30struct cmd_results *cmd_focus(int argc, char **argv) {
12 if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file."); 31 struct sway_container *con = config->handler_context.current_container;
13 struct cmd_results *error = NULL; 32 struct sway_seat *seat = config->handler_context.seat;
14 if (argc > 0 && strcasecmp(argv[0], "output") == 0) { 33 if (con->type < C_WORKSPACE) {
15 swayc_t *output = NULL; 34 return cmd_results_new(CMD_FAILURE, "focus",
16 struct wlc_point abs_pos; 35 "Command 'focus' cannot be used above the workspace level");
17 get_absolute_center_position(get_focused_container(&root_container), &abs_pos); 36 }
18 if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) { 37
19 return error; 38 if (argc == 0) {
20 } else if (!(output = output_by_name(argv[1], &abs_pos))) { 39 seat_set_focus(seat, con);
21 return cmd_results_new(CMD_FAILURE, "focus output",
22 "Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y);
23 } else if (!workspace_switch(swayc_active_workspace_for(output))) {
24 return cmd_results_new(CMD_FAILURE, "focus output",
25 "Switching to workspace on output '%s' was blocked", argv[1]);
26 } else if (config->mouse_warping) {
27 swayc_t *focused = get_focused_view(output);
28 if (focused && focused->type == C_VIEW) {
29 center_pointer_on(focused);
30 }
31 }
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33 } else if (argc == 0) {
34 set_focused_container(current_container);
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 40 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36 } else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
37 return error;
38 } 41 }
39 static int floating_toggled_index = 0; 42
40 static int tiled_toggled_index = 0; 43 // TODO mode_toggle
41 if (strcasecmp(argv[0], "left") == 0) { 44 enum movement_direction direction = 0;
42 move_focus(MOVE_LEFT); 45 if (!parse_movement_direction(argv[0], &direction)) {
43 } else if (strcasecmp(argv[0], "right") == 0) {
44 move_focus(MOVE_RIGHT);
45 } else if (strcasecmp(argv[0], "up") == 0) {
46 move_focus(MOVE_UP);
47 } else if (strcasecmp(argv[0], "down") == 0) {
48 move_focus(MOVE_DOWN);
49 } else if (strcasecmp(argv[0], "parent") == 0) {
50 move_focus(MOVE_PARENT);
51 } else if (strcasecmp(argv[0], "child") == 0) {
52 move_focus(MOVE_CHILD);
53 } else if (strcasecmp(argv[0], "next") == 0) {
54 move_focus(MOVE_NEXT);
55 } else if (strcasecmp(argv[0], "prev") == 0) {
56 move_focus(MOVE_PREV);
57 } else if (strcasecmp(argv[0], "mode_toggle") == 0) {
58 int i;
59 swayc_t *workspace = swayc_active_workspace();
60 swayc_t *focused = get_focused_view(workspace);
61 if (focused->is_floating) {
62 if (workspace->children->length > 0) {
63 for (i = 0;i < workspace->floating->length; i++) {
64 if (workspace->floating->items[i] == focused) {
65 floating_toggled_index = i;
66 break;
67 }
68 }
69 if (workspace->children->length > tiled_toggled_index) {
70 set_focused_container(get_focused_view(workspace->children->items[tiled_toggled_index]));
71 } else {
72 set_focused_container(get_focused_view(workspace->children->items[0]));
73 tiled_toggled_index = 0;
74 }
75 }
76 } else {
77 if (workspace->floating->length > 0) {
78 for (i = 0;i < workspace->children->length; i++) {
79 if (workspace->children->items[i] == focused) {
80 tiled_toggled_index = i;
81 break;
82 }
83 }
84 if (workspace->floating->length > floating_toggled_index) {
85 swayc_t *floating = workspace->floating->items[floating_toggled_index];
86 set_focused_container(get_focused_view(floating));
87 } else {
88 swayc_t *floating = workspace->floating->items[workspace->floating->length - 1];
89 set_focused_container(get_focused_view(floating));
90 tiled_toggled_index = workspace->floating->length - 1;
91 }
92 }
93 }
94 } else {
95 return cmd_results_new(CMD_INVALID, "focus", 46 return cmd_results_new(CMD_INVALID, "focus",
96 "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'"); 47 "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
97 } 48 }
49
50 struct sway_container *next_focus = container_get_in_direction(
51 con, seat, direction);
52 if (next_focus) {
53 seat_set_focus(seat, next_focus);
54 }
55
98 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 56 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
99} 57}
diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c
index 7c9c2b13..661e7852 100644
--- a/sway/commands/focus_follows_mouse.c
+++ b/sway/commands/focus_follows_mouse.c
@@ -7,7 +7,6 @@ struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
7 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { 7 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
8 return error; 8 return error;
9 } 9 }
10
11 config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); 10 config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 11 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13} 12}
diff --git a/sway/commands/font.c b/sway/commands/font.c
deleted file mode 100644
index 32994f8a..00000000
--- a/sway/commands/font.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/border.h"
4#include "sway/commands.h"
5#include "log.h"
6#include "stringop.h"
7
8struct cmd_results *cmd_font(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13
14 char *font = join_args(argv, argc);
15 free(config->font);
16 if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
17 config->font = strdup(font + 6);
18 free(font);
19 } else {
20 config->font = font;
21 }
22
23 config->font_height = get_font_text_height(config->font);
24
25 sway_log(L_DEBUG, "Settings font %s", config->font);
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
deleted file mode 100644
index d1fd1641..00000000
--- a/sway/commands/for_window.c
+++ /dev/null
@@ -1,41 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/criteria.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8
9struct cmd_results *cmd_for_window(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) {
12 return error;
13 }
14 // add command to a criteria/command pair that is run against views when they appear.
15 char *criteria = argv[0], *cmdlist = join_args(argv + 1, argc - 1);
16
17 struct criteria *crit = malloc(sizeof(struct criteria));
18 if (!crit) {
19 return cmd_results_new(CMD_FAILURE, "for_window", "Unable to allocate criteria");
20 }
21 crit->crit_raw = strdup(criteria);
22 crit->cmdlist = cmdlist;
23 crit->tokens = create_list();
24 char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
25
26 if (err_str) {
27 error = cmd_results_new(CMD_INVALID, "for_window", err_str);
28 free(err_str);
29 free_criteria(crit);
30 } else if (crit->tokens->length == 0) {
31 error = cmd_results_new(CMD_INVALID, "for_window", "Found no name/value pairs in criteria");
32 free_criteria(crit);
33 } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
34 sway_log(L_DEBUG, "for_window: Duplicate, skipping.");
35 free_criteria(crit);
36 } else {
37 sway_log(L_DEBUG, "for_window: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
38 list_add(config->criteria, crit);
39 }
40 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
41}
diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c
deleted file mode 100644
index f19dd163..00000000
--- a/sway/commands/force_focus_wrapping.c
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "force_focus_wrapping", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 config->force_focus_wrapping = !strcasecmp(argv[0], "yes");
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13}
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
deleted file mode 100644
index bfff82f9..00000000
--- a/sway/commands/fullscreen.c
+++ /dev/null
@@ -1,60 +0,0 @@
1#include <stdbool.h>
2#include <string.h>
3#include <wlc/wlc.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6#include "sway/focus.h"
7#include "sway/ipc-server.h"
8#include "sway/layout.h"
9
10struct cmd_results *cmd_fullscreen(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
13 if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
14 if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
15 return error;
16 }
17 swayc_t *container = current_container;
18 if(container->type != C_VIEW){
19 return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen");
20 }
21 swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
22 bool current = swayc_is_fullscreen(container);
23 wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
24
25 if (container->is_floating) {
26 if (current) {
27 // set dimensions back to what they were before we fullscreened this
28 container->x = container->cached_geometry.origin.x;
29 container->y = container->cached_geometry.origin.y;
30 container->width = container->cached_geometry.size.w;
31 container->height = container->cached_geometry.size.h;
32 } else {
33 // cache dimensions so we can reset them after we "unfullscreen" this
34 struct wlc_geometry geo = {
35 .origin = {
36 .x = container->x,
37 .y = container->y
38 },
39 .size = {
40 .w = container->width,
41 .h = container->height
42 }
43 };
44 container->cached_geometry = geo;
45 }
46 }
47
48 // Resize workspace if going from fullscreen -> notfullscreen
49 // otherwise just resize container
50 if (!current) {
51 arrange_windows(workspace, -1, -1);
52 workspace->fullscreen = container;
53 } else {
54 arrange_windows(container, -1, -1);
55 workspace->fullscreen = NULL;
56 }
57 ipc_event_window(container, "fullscreen_mode");
58
59 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
60}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
deleted file mode 100644
index 0a48592d..00000000
--- a/sway/commands/gaps.c
+++ /dev/null
@@ -1,166 +0,0 @@
1#include <ctype.h>
2#include <errno.h>
3#include <stdlib.h>
4#include <string.h>
5#include <strings.h>
6#include "sway/commands.h"
7#include "sway/container.h"
8#include "sway/focus.h"
9#include "sway/layout.h"
10
11struct cmd_results *cmd_gaps(int argc, char **argv) {
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
14 return error;
15 }
16 const char *expected_syntax =
17 "Expected 'gaps edge_gaps <on|off|toggle>' or "
18 "'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'";
19 const char *amount_str = argv[0];
20 // gaps amount
21 if (argc >= 1 && isdigit(*amount_str)) {
22 int amount = (int)strtol(amount_str, NULL, 10);
23 if (errno == ERANGE) {
24 errno = 0;
25 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
26 }
27 config->gaps_inner = config->gaps_outer = amount;
28 arrange_windows(&root_container, -1, -1);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30 }
31 // gaps inner|outer n
32 else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
33 int amount = (int)strtol(amount_str, NULL, 10);
34 if (errno == ERANGE) {
35 errno = 0;
36 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
37 }
38 const char *target_str = argv[0];
39 if (strcasecmp(target_str, "inner") == 0) {
40 config->gaps_inner = amount;
41 } else if (strcasecmp(target_str, "outer") == 0) {
42 config->gaps_outer = amount;
43 }
44 arrange_windows(&root_container, -1, -1);
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46 } else if (argc == 2 && strcasecmp(argv[0], "edge_gaps") == 0) {
47 // gaps edge_gaps <on|off|toggle>
48 if (strcasecmp(argv[1], "toggle") == 0) {
49 if (config->reading) {
50 return cmd_results_new(CMD_FAILURE, "gaps edge_gaps toggle",
51 "Can't be used in config file.");
52 }
53 config->edge_gaps = !config->edge_gaps;
54 } else {
55 config->edge_gaps =
56 (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "on") == 0);
57 }
58 arrange_windows(&root_container, -1, -1);
59 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
60 }
61 // gaps inner|outer current|all set|plus|minus n
62 if (argc < 4 || config->reading) {
63 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
64 }
65 // gaps inner|outer ...
66 const char *inout_str = argv[0];
67 enum {INNER, OUTER} inout;
68 if (strcasecmp(inout_str, "inner") == 0) {
69 inout = INNER;
70 } else if (strcasecmp(inout_str, "outer") == 0) {
71 inout = OUTER;
72 } else {
73 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
74 }
75
76 // gaps ... current|all ...
77 const char *target_str = argv[1];
78 enum {CURRENT, WORKSPACE, ALL} target;
79 if (strcasecmp(target_str, "current") == 0) {
80 target = CURRENT;
81 } else if (strcasecmp(target_str, "all") == 0) {
82 target = ALL;
83 } else if (strcasecmp(target_str, "workspace") == 0) {
84 if (inout == OUTER) {
85 target = CURRENT;
86 } else {
87 // Set gap for views in workspace
88 target = WORKSPACE;
89 }
90 } else {
91 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
92 }
93
94 // gaps ... n
95 amount_str = argv[3];
96 int amount = (int)strtol(amount_str, NULL, 10);
97 if (errno == ERANGE) {
98 errno = 0;
99 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
100 }
101
102 // gaps ... set|plus|minus ...
103 const char *method_str = argv[2];
104 enum {SET, ADD} method;
105 if (strcasecmp(method_str, "set") == 0) {
106 method = SET;
107 } else if (strcasecmp(method_str, "plus") == 0) {
108 method = ADD;
109 } else if (strcasecmp(method_str, "minus") == 0) {
110 method = ADD;
111 amount *= -1;
112 } else {
113 return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
114 }
115
116 if (target == CURRENT) {
117 swayc_t *cont;
118 if (inout == OUTER) {
119 if ((cont = swayc_active_workspace()) == NULL) {
120 return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
121 }
122 } else {
123 if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
124 return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
125 }
126 }
127 cont->gaps = swayc_gap(cont);
128 if (method == SET) {
129 cont->gaps = amount;
130 } else if ((cont->gaps += amount) < 0) {
131 cont->gaps = 0;
132 }
133 arrange_windows(cont->parent, -1, -1);
134 } else if (inout == OUTER) {
135 //resize all workspace.
136 int i,j;
137 for (i = 0; i < root_container.children->length; ++i) {
138 swayc_t *op = root_container.children->items[i];
139 for (j = 0; j < op->children->length; ++j) {
140 swayc_t *ws = op->children->items[j];
141 if (method == SET) {
142 ws->gaps = amount;
143 } else if ((ws->gaps += amount) < 0) {
144 ws->gaps = 0;
145 }
146 }
147 }
148 arrange_windows(&root_container, -1, -1);
149 } else {
150 // Resize gaps for all views in workspace
151 swayc_t *top;
152 if (target == WORKSPACE) {
153 if ((top = swayc_active_workspace()) == NULL) {
154 return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
155 }
156 } else {
157 top = &root_container;
158 }
159 int top_gap = top->gaps;
160 container_map(top, method == SET ? set_gaps : add_gaps, &amount);
161 top->gaps = top_gap;
162 arrange_windows(top, -1, -1);
163 }
164
165 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
166}
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
deleted file mode 100644
index ee2a2644..00000000
--- a/sway/commands/hide_edge_borders.c
+++ /dev/null
@@ -1,27 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 if (strcasecmp(argv[0], "none") == 0) {
12 config->hide_edge_borders = E_NONE;
13 } else if (strcasecmp(argv[0], "vertical") == 0) {
14 config->hide_edge_borders = E_VERTICAL;
15 } else if (strcasecmp(argv[0], "horizontal") == 0) {
16 config->hide_edge_borders = E_HORIZONTAL;
17 } else if (strcasecmp(argv[0], "both") == 0) {
18 config->hide_edge_borders = E_BOTH;
19 } else if (strcasecmp(argv[0], "smart") == 0) {
20 config->hide_edge_borders = E_SMART;
21 } else {
22 return cmd_results_new(CMD_INVALID, "hide_edge_borders",
23 "Expected 'hide_edge_borders <none|vertical|horizontal|both>'");
24 }
25
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index ad53d272..fa9cf05a 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -1,7 +1,7 @@
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 "sway/input.h" 4#include "sway/input/input-manager.h"
5#include "log.h" 5#include "log.h"
6 6
7struct cmd_results *cmd_input(int argc, char **argv) { 7struct cmd_results *cmd_input(int argc, char **argv) {
@@ -11,45 +11,73 @@ struct cmd_results *cmd_input(int argc, char **argv) {
11 } 11 }
12 12
13 if (config->reading && strcmp("{", argv[1]) == 0) { 13 if (config->reading && strcmp("{", argv[1]) == 0) {
14 current_input_config = new_input_config(argv[0]); 14 free_input_config(config->handler_context.input_config);
15 sway_log(L_DEBUG, "entering input block: %s", current_input_config->identifier); 15 config->handler_context.input_config = new_input_config(argv[0]);
16 if (!config->handler_context.input_config) {
17 return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
18 }
19 wlr_log(L_DEBUG, "entering input block: %s", argv[0]);
16 return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL); 20 return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
17 } 21 }
18 22
19 if (argc > 2) { 23 if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 3))) {
20 int argc_new = argc-2; 24 return error;
21 char **argv_new = argv+2; 25 }
22 26
23 struct cmd_results *res; 27 bool has_context = (config->handler_context.input_config != NULL);
24 current_input_config = new_input_config(argv[0]); 28 if (!has_context) {
25 if (strcasecmp("accel_profile", argv[1]) == 0) { 29 // caller did not give a context so create one just for this command
26 res = input_cmd_accel_profile(argc_new, argv_new); 30 config->handler_context.input_config = new_input_config(argv[0]);
27 } else if (strcasecmp("click_method", argv[1]) == 0) { 31 if (!config->handler_context.input_config) {
28 res = input_cmd_click_method(argc_new, argv_new); 32 return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
29 } else if (strcasecmp("drag_lock", argv[1]) == 0) {
30 res = input_cmd_drag_lock(argc_new, argv_new);
31 } else if (strcasecmp("dwt", argv[1]) == 0) {
32 res = input_cmd_dwt(argc_new, argv_new);
33 } else if (strcasecmp("events", argv[1]) == 0) {
34 res = input_cmd_events(argc_new, argv_new);
35 } else if (strcasecmp("left_handed", argv[1]) == 0) {
36 res = input_cmd_left_handed(argc_new, argv_new);
37 } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
38 res = input_cmd_middle_emulation(argc_new, argv_new);
39 } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
40 res = input_cmd_natural_scroll(argc_new, argv_new);
41 } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
42 res = input_cmd_pointer_accel(argc_new, argv_new);
43 } else if (strcasecmp("scroll_method", argv[1]) == 0) {
44 res = input_cmd_scroll_method(argc_new, argv_new);
45 } else if (strcasecmp("tap", argv[1]) == 0) {
46 res = input_cmd_tap(argc_new, argv_new);
47 } else {
48 res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
49 } 33 }
50 current_input_config = NULL;
51 return res;
52 } 34 }
53 35
54 return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL); 36 int argc_new = argc-2;
37 char **argv_new = argv+2;
38
39 struct cmd_results *res;
40 if (strcasecmp("accel_profile", argv[1]) == 0) {
41 res = input_cmd_accel_profile(argc_new, argv_new);
42 } else if (strcasecmp("click_method", argv[1]) == 0) {
43 res = input_cmd_click_method(argc_new, argv_new);
44 } else if (strcasecmp("drag_lock", argv[1]) == 0) {
45 res = input_cmd_drag_lock(argc_new, argv_new);
46 } else if (strcasecmp("dwt", argv[1]) == 0) {
47 res = input_cmd_dwt(argc_new, argv_new);
48 } else if (strcasecmp("events", argv[1]) == 0) {
49 res = input_cmd_events(argc_new, argv_new);
50 } else if (strcasecmp("left_handed", argv[1]) == 0) {
51 res = input_cmd_left_handed(argc_new, argv_new);
52 } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
53 res = input_cmd_middle_emulation(argc_new, argv_new);
54 } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
55 res = input_cmd_natural_scroll(argc_new, argv_new);
56 } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
57 res = input_cmd_pointer_accel(argc_new, argv_new);
58 } else if (strcasecmp("scroll_method", argv[1]) == 0) {
59 res = input_cmd_scroll_method(argc_new, argv_new);
60 } else if (strcasecmp("tap", argv[1]) == 0) {
61 res = input_cmd_tap(argc_new, argv_new);
62 } else if (strcasecmp("xkb_layout", argv[1]) == 0) {
63 res = input_cmd_xkb_layout(argc_new, argv_new);
64 } else if (strcasecmp("xkb_model", argv[1]) == 0) {
65 res = input_cmd_xkb_model(argc_new, argv_new);
66 } else if (strcasecmp("xkb_options", argv[1]) == 0) {
67 res = input_cmd_xkb_options(argc_new, argv_new);
68 } else if (strcasecmp("xkb_rules", argv[1]) == 0) {
69 res = input_cmd_xkb_rules(argc_new, argv_new);
70 } else if (strcasecmp("xkb_variant", argv[1]) == 0) {
71 res = input_cmd_xkb_variant(argc_new, argv_new);
72 } else {
73 res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
74 }
75
76 if (!has_context) {
77 // clean up the context we created earlier
78 free_input_config(config->handler_context.input_config);
79 config->handler_context.input_config = NULL;
80 }
81
82 return res;
55} 83}
diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c
index 8288c1ad..37d6e133 100644
--- a/sway/commands/input/accel_profile.c
+++ b/sway/commands/input/accel_profile.c
@@ -1,17 +1,22 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { 7struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "accel_profile",
16 "No input device defined.");
13 } 17 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
15 20
16 if (strcasecmp(argv[0], "adaptive") == 0) { 21 if (strcasecmp(argv[0], "adaptive") == 0) {
17 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; 22 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
@@ -22,6 +27,6 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
22 "Expected 'accel_profile <adaptive|flat>'"); 27 "Expected 'accel_profile <adaptive|flat>'");
23 } 28 }
24 29
25 input_cmd_apply(new_config); 30 apply_input_config(new_config);
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 32}
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c
index 5e9d3dcb..8f1f0aa7 100644
--- a/sway/commands/input/click_method.c
+++ b/sway/commands/input/click_method.c
@@ -1,19 +1,23 @@
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 "sway/input.h" 4#include "sway/config.h"
5#include "sway/input/input-manager.h"
5#include "log.h" 6#include "log.h"
6 7
7struct cmd_results *input_cmd_click_method(int argc, char **argv) { 8struct cmd_results *input_cmd_click_method(int argc, char **argv) {
8 sway_log(L_DEBUG, "click_method for device: %d %s", current_input_config==NULL, current_input_config->identifier);
9 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config =
14 config->handler_context.input_config;
13 if (!current_input_config) { 15 if (!current_input_config) {
14 return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined."); 16 return cmd_results_new(CMD_FAILURE, "click_method",
17 "No input device defined.");
15 } 18 }
16 struct input_config *new_config = new_input_config(current_input_config->identifier); 19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
17 21
18 if (strcasecmp(argv[0], "none") == 0) { 22 if (strcasecmp(argv[0], "none") == 0) {
19 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; 23 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
@@ -22,9 +26,10 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) {
22 } else if (strcasecmp(argv[0], "clickfinger") == 0) { 26 } else if (strcasecmp(argv[0], "clickfinger") == 0) {
23 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; 27 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
24 } else { 28 } else {
25 return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'"); 29 return cmd_results_new(CMD_INVALID, "click_method",
30 "Expected 'click_method <none|button_areas|clickfinger'");
26 } 31 }
27 32
28 input_cmd_apply(new_config); 33 apply_input_config(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 34 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 35}
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
index f5a7beb4..8273a7d4 100644
--- a/sway/commands/input/drag_lock.c
+++ b/sway/commands/input/drag_lock.c
@@ -1,26 +1,32 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { 7struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE,
16 "drag_lock", "No input device defined.");
13 } 17 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
15 20
16 if (strcasecmp(argv[0], "enabled") == 0) { 21 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; 22 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
18 } else if (strcasecmp(argv[0], "disabled") == 0) { 23 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; 24 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
20 } else { 25 } else {
21 return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock <enabled|disabled>'"); 26 return cmd_results_new(CMD_INVALID, "drag_lock",
27 "Expected 'drag_lock <enabled|disabled>'");
22 } 28 }
23 29
24 input_cmd_apply(new_config); 30 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 32}
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
index 557b2207..995a2f47 100644
--- a/sway/commands/input/dwt.c
+++ b/sway/commands/input/dwt.c
@@ -1,26 +1,31 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_dwt(int argc, char **argv) { 7struct cmd_results *input_cmd_dwt(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
13 } 16 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
15 19
16 if (strcasecmp(argv[0], "enabled") == 0) { 20 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; 21 new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
18 } else if (strcasecmp(argv[0], "disabled") == 0) { 22 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; 23 new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
20 } else { 24 } else {
21 return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt <enabled|disabled>'"); 25 return cmd_results_new(CMD_INVALID, "dwt",
26 "Expected 'dwt <enabled|disabled>'");
22 } 27 }
23 28
24 input_cmd_apply(new_config); 29 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 31}
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index 9d54287a..2217f5ce 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,30 +1,38 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5#include "log.h" 6#include "log.h"
6 7
7struct cmd_results *input_cmd_events(int argc, char **argv) { 8struct cmd_results *input_cmd_events(int argc, char **argv) {
8 sway_log(L_DEBUG, "events for device: %s", current_input_config->identifier);
9 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config =
14 config->handler_context.input_config;
13 if (!current_input_config) { 15 if (!current_input_config) {
14 return cmd_results_new(CMD_FAILURE, "events", "No input device defined."); 16 return cmd_results_new(CMD_FAILURE, "events",
17 "No input device defined.");
15 } 18 }
16 struct input_config *new_config = new_input_config(current_input_config->identifier); 19 wlr_log(L_DEBUG, "events for device: %s",
20 current_input_config->identifier);
21 struct input_config *new_config =
22 new_input_config(current_input_config->identifier);
17 23
18 if (strcasecmp(argv[0], "enabled") == 0) { 24 if (strcasecmp(argv[0], "enabled") == 0) {
19 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; 25 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
20 } else if (strcasecmp(argv[0], "disabled") == 0) { 26 } else if (strcasecmp(argv[0], "disabled") == 0) {
21 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; 27 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
22 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { 28 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
23 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 29 new_config->send_events =
30 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
24 } else { 31 } else {
25 return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); 32 return cmd_results_new(CMD_INVALID, "events",
33 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
26 } 34 }
27 35
28 input_cmd_apply(new_config); 36 apply_input_config(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 38}
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
index 6c913e70..94b8e03e 100644
--- a/sway/commands/input/left_handed.c
+++ b/sway/commands/input/left_handed.c
@@ -1,26 +1,32 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_left_handed(int argc, char **argv) { 7struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "left_handed",
16 "No input device defined.");
13 } 17 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
15 20
16 if (strcasecmp(argv[0], "enabled") == 0) { 21 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->left_handed = 1; 22 new_config->left_handed = 1;
18 } else if (strcasecmp(argv[0], "disabled") == 0) { 23 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->left_handed = 0; 24 new_config->left_handed = 0;
20 } else { 25 } else {
21 return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed <enabled|disabled>'"); 26 return cmd_results_new(CMD_INVALID, "left_handed",
27 "Expected 'left_handed <enabled|disabled>'");
22 } 28 }
23 29
24 input_cmd_apply(new_config); 30 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 32}
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c
new file mode 100644
index 00000000..60e4608e
--- /dev/null
+++ b/sway/commands/input/map_to_output.c
@@ -0,0 +1,27 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include <strings.h>
4#include "sway/config.h"
5#include "sway/commands.h"
6#include "sway/input/input-manager.h"
7#include "log.h"
8
9struct cmd_results *input_cmd_map_to_output(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) {
12 return error;
13 }
14 struct input_config *current_input_config =
15 config->handler_context.input_config;
16 if (!current_input_config) {
17 return cmd_results_new(CMD_FAILURE, "map_to_output",
18 "No input device defined.");
19 }
20 struct input_config *new_config =
21 new_input_config(current_input_config->identifier);
22
23 new_config->mapped_output = strdup(argv[0]);
24 apply_input_config(new_config);
25
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index 33cdd7d6..a551fd51 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -1,26 +1,33 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { 7struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "middle_emulation",
16 "No input device defined.");
13 } 17 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
15 20
16 if (strcasecmp(argv[0], "enabled") == 0) { 21 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; 22 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
18 } else if (strcasecmp(argv[0], "disabled") == 0) { 23 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; 24 new_config->middle_emulation =
25 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
20 } else { 26 } else {
21 return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation <enabled|disabled>'"); 27 return cmd_results_new(CMD_INVALID, "middle_emulation",
28 "Expected 'middle_emulation <enabled|disabled>'");
22 } 29 }
23 30
24 input_cmd_apply(new_config); 31 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 33}
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
index 7bc8b8d0..c4e19b78 100644
--- a/sway/commands/input/natural_scroll.c
+++ b/sway/commands/input/natural_scroll.c
@@ -1,26 +1,32 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { 7struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "natural_scoll",
16 "No input device defined.");
13 } 17 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
15 20
16 if (strcasecmp(argv[0], "enabled") == 0) { 21 if (strcasecmp(argv[0], "enabled") == 0) {
17 new_config->natural_scroll = 1; 22 new_config->natural_scroll = 1;
18 } else if (strcasecmp(argv[0], "disabled") == 0) { 23 } else if (strcasecmp(argv[0], "disabled") == 0) {
19 new_config->natural_scroll = 0; 24 new_config->natural_scroll = 0;
20 } else { 25 } else {
21 return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll <enabled|disabled>'"); 26 return cmd_results_new(CMD_INVALID, "natural_scroll",
27 "Expected 'natural_scroll <enabled|disabled>'");
22 } 28 }
23 29
24 input_cmd_apply(new_config); 30 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 32}
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c
index 94f595d1..171063aa 100644
--- a/sway/commands/input/pointer_accel.c
+++ b/sway/commands/input/pointer_accel.c
@@ -1,24 +1,30 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include <string.h> 2#include <string.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { 7struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE,
16 "pointer_accel", "No input device defined.");
13 } 17 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
15 20
16 float pointer_accel = atof(argv[0]); 21 float pointer_accel = atof(argv[0]);
17 if (pointer_accel < -1 || pointer_accel > 1) { 22 if (pointer_accel < -1 || pointer_accel > 1) {
18 return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]"); 23 return cmd_results_new(CMD_INVALID, "pointer_accel",
24 "Input out of range [-1, 1]");
19 } 25 }
20 new_config->pointer_accel = pointer_accel; 26 new_config->pointer_accel = pointer_accel;
21 27
22 input_cmd_apply(new_config); 28 apply_input_config(new_config);
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24} 30}
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c
index 5c6c3d7a..0a1c57ac 100644
--- a/sway/commands/input/scroll_method.c
+++ b/sway/commands/input/scroll_method.c
@@ -1,17 +1,22 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5 6
6struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { 7struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) {
9 return error; 10 return error;
10 } 11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
11 if (!current_input_config) { 14 if (!current_input_config) {
12 return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "scroll_method",
16 "No input device defined.");
13 } 17 }
14 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
15 20
16 if (strcasecmp(argv[0], "none") == 0) { 21 if (strcasecmp(argv[0], "none") == 0) {
17 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; 22 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
@@ -22,9 +27,10 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
22 } else if (strcasecmp(argv[0], "on_button_down") == 0) { 27 } else if (strcasecmp(argv[0], "on_button_down") == 0) {
23 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; 28 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
24 } else { 29 } else {
25 return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); 30 return cmd_results_new(CMD_INVALID, "scroll_method",
31 "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
26 } 32 }
27 33
28 input_cmd_apply(new_config); 34 apply_input_config(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 35 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 36}
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index 9e3ca2af..e7f03058 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -1,29 +1,34 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/input.h" 5#include "sway/input/input-manager.h"
5#include "log.h" 6#include "log.h"
6 7
7struct cmd_results *input_cmd_tap(int argc, char **argv) { 8struct cmd_results *input_cmd_tap(int argc, char **argv) {
8 sway_log(L_DEBUG, "tap for device: %s", current_input_config->identifier);
9 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config =
14 config->handler_context.input_config;
13 if (!current_input_config) { 15 if (!current_input_config) {
14 return cmd_results_new(CMD_FAILURE, "tap", "No input device defined."); 16 return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
15 } 17 }
16 struct input_config *new_config = new_input_config(current_input_config->identifier); 18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
17 20
18 if (strcasecmp(argv[0], "enabled") == 0) { 21 if (strcasecmp(argv[0], "enabled") == 0) {
19 new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; 22 new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
20 } else if (strcasecmp(argv[0], "disabled") == 0) { 23 } else if (strcasecmp(argv[0], "disabled") == 0) {
21 new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; 24 new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
22 } else { 25 } else {
23 return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap <enabled|disabled>'"); 26 return cmd_results_new(CMD_INVALID, "tap",
27 "Expected 'tap <enabled|disabled>'");
24 } 28 }
25 29
26 sway_log(L_DEBUG, "apply-tap for device: %s", current_input_config->identifier); 30 wlr_log(L_DEBUG, "apply-tap for device: %s",
27 input_cmd_apply(new_config); 31 current_input_config->identifier);
32 apply_input_config(new_config);
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 33 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 34}
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
new file mode 100644
index 00000000..867e65d3
--- /dev/null
+++ b/sway/commands/input/xkb_layout.c
@@ -0,0 +1,26 @@
1#define _XOPEN_SOURCE 700
2#include "sway/config.h"
3#include "sway/commands.h"
4#include "sway/input/input-manager.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 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_layout", "No input device defined.");
16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19
20 new_config->xkb_layout = strdup(argv[0]);
21
22 wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s",
23 current_input_config->identifier, new_config->xkb_layout);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
new file mode 100644
index 00000000..e8c8e04e
--- /dev/null
+++ b/sway/commands/input/xkb_model.c
@@ -0,0 +1,26 @@
1#define _XOPEN_SOURCE 700
2#include "sway/config.h"
3#include "sway/commands.h"
4#include "sway/input/input-manager.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 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_model", "No input device defined.");
16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19
20 new_config->xkb_model = strdup(argv[0]);
21
22 wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s",
23 current_input_config->identifier, new_config->xkb_model);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
new file mode 100644
index 00000000..e9ddd6e3
--- /dev/null
+++ b/sway/commands/input/xkb_options.c
@@ -0,0 +1,26 @@
1#define _XOPEN_SOURCE 700
2#include "sway/config.h"
3#include "sway/commands.h"
4#include "sway/input/input-manager.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 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_options", "No input device defined.");
16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19
20 new_config->xkb_options = strdup(argv[0]);
21
22 wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s",
23 current_input_config->identifier, new_config->xkb_options);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
new file mode 100644
index 00000000..926d0ac1
--- /dev/null
+++ b/sway/commands/input/xkb_rules.c
@@ -0,0 +1,26 @@
1#define _XOPEN_SOURCE 700
2#include "sway/config.h"
3#include "sway/commands.h"
4#include "sway/input/input-manager.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 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_rules", "No input device defined.");
16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19
20 new_config->xkb_rules = strdup(argv[0]);
21
22 wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s",
23 current_input_config->identifier, new_config->xkb_rules);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
new file mode 100644
index 00000000..0e3ffd41
--- /dev/null
+++ b/sway/commands/input/xkb_variant.c
@@ -0,0 +1,26 @@
1#define _XOPEN_SOURCE 700
2#include "sway/config.h"
3#include "sway/commands.h"
4#include "sway/input/input-manager.h"
5#include "log.h"
6
7struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 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_variant", "No input device defined.");
16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19
20 new_config->xkb_variant = strdup(argv[0]);
21
22 wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s",
23 current_input_config->identifier, new_config->xkb_variant);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/ipc.c b/sway/commands/ipc.c
deleted file mode 100644
index 0c678961..00000000
--- a/sway/commands/ipc.c
+++ /dev/null
@@ -1,162 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdio.h>
3#include <string.h>
4#include "sway/security.h"
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "ipc.h"
8#include "log.h"
9#include "util.h"
10
11static struct ipc_policy *current_policy = NULL;
12
13struct cmd_results *cmd_ipc(int argc, char **argv) {
14 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) {
16 return error;
17 }
18 if ((error = check_security_config())) {
19 return error;
20 }
21
22 char *program = NULL;
23
24 if (!strcmp(argv[0], "*")) {
25 program = strdup(argv[0]);
26 } else if (!(program = resolve_path(argv[0]))) {
27 return cmd_results_new(
28 CMD_INVALID, "ipc", "Unable to resolve IPC Policy target.");
29 }
30 if (config->reading && strcmp("{", argv[1]) != 0) {
31 return cmd_results_new(CMD_INVALID, "ipc",
32 "Expected '{' at start of IPC config definition.");
33 }
34
35 if (!config->reading) {
36 return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
37 }
38
39 current_policy = alloc_ipc_policy(program);
40 list_add(config->ipc_policies, current_policy);
41
42 free(program);
43 return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL);
44}
45
46struct cmd_results *cmd_ipc_events(int argc, char **argv) {
47 struct cmd_results *error = NULL;
48 if ((error = checkarg(argc, "events", EXPECTED_EQUAL_TO, 1))) {
49 return error;
50 }
51
52 if (config->reading && strcmp("{", argv[0]) != 0) {
53 return cmd_results_new(CMD_INVALID, "events",
54 "Expected '{' at start of IPC event config definition.");
55 }
56
57 if (!config->reading) {
58 return cmd_results_new(CMD_FAILURE, "events", "Can only be used in config file.");
59 }
60
61 return cmd_results_new(CMD_BLOCK_IPC_EVENTS, NULL, NULL);
62}
63
64struct cmd_results *cmd_ipc_cmd(int argc, char **argv) {
65 struct cmd_results *error = NULL;
66 if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
67 return error;
68 }
69
70 bool enabled;
71 if (strcmp(argv[0], "enabled") == 0) {
72 enabled = true;
73 } else if (strcmp(argv[0], "disabled") == 0) {
74 enabled = false;
75 } else {
76 return cmd_results_new(CMD_INVALID, argv[-1],
77 "Argument must be one of 'enabled' or 'disabled'");
78 }
79
80 struct {
81 char *name;
82 enum ipc_feature type;
83 } types[] = {
84 { "*", IPC_FEATURE_ALL_COMMANDS },
85 { "command", IPC_FEATURE_COMMAND },
86 { "workspaces", IPC_FEATURE_GET_WORKSPACES },
87 { "outputs", IPC_FEATURE_GET_OUTPUTS },
88 { "tree", IPC_FEATURE_GET_TREE },
89 { "marks", IPC_FEATURE_GET_MARKS },
90 { "bar-config", IPC_FEATURE_GET_BAR_CONFIG },
91 { "inputs", IPC_FEATURE_GET_INPUTS },
92 { "clipboard", IPC_FEATURE_GET_CLIPBOARD },
93 };
94
95 uint32_t type = 0;
96
97 for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
98 if (strcmp(types[i].name, argv[-1]) == 0) {
99 type = types[i].type;
100 break;
101 }
102 }
103
104 if (enabled) {
105 current_policy->features |= type;
106 sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]);
107 } else {
108 current_policy->features &= ~type;
109 sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]);
110 }
111
112 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
113}
114
115struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) {
116 struct cmd_results *error = NULL;
117 if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
118 return error;
119 }
120
121 bool enabled;
122 if (strcmp(argv[0], "enabled") == 0) {
123 enabled = true;
124 } else if (strcmp(argv[0], "disabled") == 0) {
125 enabled = false;
126 } else {
127 return cmd_results_new(CMD_INVALID, argv[-1],
128 "Argument must be one of 'enabled' or 'disabled'");
129 }
130
131 struct {
132 char *name;
133 enum ipc_feature type;
134 } types[] = {
135 { "*", IPC_FEATURE_ALL_EVENTS },
136 { "workspace", IPC_FEATURE_EVENT_WORKSPACE },
137 { "output", IPC_FEATURE_EVENT_OUTPUT },
138 { "mode", IPC_FEATURE_EVENT_MODE },
139 { "window", IPC_FEATURE_EVENT_WINDOW },
140 { "binding", IPC_FEATURE_EVENT_BINDING },
141 { "input", IPC_FEATURE_EVENT_INPUT },
142 };
143
144 uint32_t type = 0;
145
146 for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
147 if (strcmp(types[i].name, argv[-1]) == 0) {
148 type = types[i].type;
149 break;
150 }
151 }
152
153 if (enabled) {
154 current_policy->features |= type;
155 sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]);
156 } else {
157 current_policy->features &= ~type;
158 sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]);
159 }
160
161 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
162}
diff --git a/sway/commands/kill.c b/sway/commands/kill.c
index 742e2b86..f3fa52f1 100644
--- a/sway/commands/kill.c
+++ b/sway/commands/kill.c
@@ -1,12 +1,16 @@
1#include <wlr/util/log.h>
2#include "log.h"
3#include "sway/input/input-manager.h"
4#include "sway/input/seat.h"
5#include "sway/tree/view.h"
6#include "sway/tree/container.h"
1#include "sway/commands.h" 7#include "sway/commands.h"
2#include "sway/container.h"
3#include "sway/focus.h"
4 8
5struct cmd_results *cmd_kill(int argc, char **argv) { 9struct cmd_results *cmd_kill(int argc, char **argv) {
6 if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file."); 10 struct sway_container *con =
7 if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running."); 11 config->handler_context.current_container;
12
13 container_close(con);
8 14
9 swayc_t *container = current_container;
10 close_views(container);
11 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 15 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
12} 16}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 57a86565..4c49a627 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -1,196 +1,55 @@
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 "sway/container.h" 4#include "sway/tree/container.h"
5#include "sway/layout.h" 5#include "sway/tree/layout.h"
6 6#include "log.h"
7/**
8 * handle "layout auto" command group
9 */
10static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv);
11 7
12struct cmd_results *cmd_layout(int argc, char **argv) { 8struct cmd_results *cmd_layout(int argc, char **argv) {
13 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
14 if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
15 if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
16 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { 10 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
17 return error; 11 return error;
18 } 12 }
19 swayc_t *parent = current_container; 13 struct sway_container *parent = config->handler_context.current_container;
14
15 // TODO: floating
16 /*
20 if (parent->is_floating) { 17 if (parent->is_floating) {
21 return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows"); 18 return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
22 } 19 }
20 */
23 21
24 while (parent->type == C_VIEW) { 22 while (parent->type == C_VIEW) {
25 parent = parent->parent; 23 parent = parent->parent;
26 } 24 }
27 25
28 enum swayc_layouts old_layout = parent->layout; 26 // TODO: stacks and tabs
29 27
30 if (strcasecmp(argv[0], "default") == 0) { 28 if (strcasecmp(argv[0], "default") == 0) {
31 swayc_change_layout(parent, parent->prev_layout); 29 container_set_layout(parent, parent->prev_layout);
32 if (parent->layout == L_NONE) { 30 if (parent->layout == L_NONE) {
33 swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT); 31 container_set_layout(parent, container_get_default_layout(parent));
34 swayc_change_layout(parent, default_layout(output));
35 } 32 }
36 } else { 33 } else {
37 if (parent->layout != L_TABBED && parent->layout != L_STACKED) { 34 if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
38 parent->prev_layout = parent->layout; 35 parent->prev_layout = parent->layout;
39 } 36 }
40 37
41 if (strcasecmp(argv[0], "tabbed") == 0) { 38 if (strcasecmp(argv[0], "splith") == 0) {
42 if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){ 39 container_set_layout(parent, L_HORIZ);
43 parent = new_container(parent, L_TABBED);
44 }
45
46 swayc_change_layout(parent, L_TABBED);
47 } else if (strcasecmp(argv[0], "stacking") == 0) {
48 if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)) {
49 parent = new_container(parent, L_STACKED);
50 }
51
52 swayc_change_layout(parent, L_STACKED);
53 } else if (strcasecmp(argv[0], "splith") == 0) {
54 swayc_change_layout(parent, L_HORIZ);
55 } else if (strcasecmp(argv[0], "splitv") == 0) { 40 } else if (strcasecmp(argv[0], "splitv") == 0) {
56 swayc_change_layout(parent, L_VERT); 41 container_set_layout(parent, L_VERT);
57 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { 42 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
58 if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE 43 if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
59 || parent->workspace_layout == L_HORIZ)) { 44 || parent->workspace_layout == L_HORIZ)) {
60 swayc_change_layout(parent, L_VERT); 45 container_set_layout(parent, L_VERT);
61 } else { 46 } else {
62 swayc_change_layout(parent, L_HORIZ); 47 container_set_layout(parent, L_HORIZ);
63 } 48 }
64 } else if (strcasecmp(argv[0], "auto") == 0) {
65 return cmd_layout_auto(parent, argc, argv);
66 } 49 }
67 } 50 }
68 51
69 update_layout_geometry(parent, old_layout);
70 update_geometry(parent);
71
72 arrange_windows(parent, parent->width, parent->height); 52 arrange_windows(parent, parent->width, parent->height);
73 53
74 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 54 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
75} 55}
76
77static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv) {
78 // called after checking that argv[0] is auto, so just continue parsing from there
79 struct cmd_results *error = NULL;
80 const char *cmd_name = "layout auto";
81 const char *set_inc_cmd_name = "layout auto [master|ncol] [set|inc]";
82 const char *err_msg = "Allowed arguments are <right|left|top|bottom|next|prev|master|ncol>";
83
84 bool need_layout_update = false;
85 enum swayc_layouts old_layout = container->layout;
86 enum swayc_layouts layout = old_layout;
87
88 if ((error = checkarg(argc, "layout auto", EXPECTED_MORE_THAN, 1))) {
89 return error;
90 }
91
92 if (strcasecmp(argv[1], "left") == 0) {
93 layout = L_AUTO_LEFT;
94 } else if (strcasecmp(argv[1], "right") == 0) {
95 layout = L_AUTO_RIGHT;
96 } else if (strcasecmp(argv[1], "top") == 0) {
97 layout = L_AUTO_TOP;
98 } else if (strcasecmp(argv[1], "bottom") == 0) {
99 layout = L_AUTO_BOTTOM;
100 } else if (strcasecmp(argv[1], "next") == 0) {
101 if (is_auto_layout(container->layout) && container->layout < L_AUTO_LAST) {
102 layout = container->layout + 1;
103 } else {
104 layout = L_AUTO_FIRST;
105 }
106 } else if (strcasecmp(argv[1], "prev") == 0) {
107 if (is_auto_layout(container->layout) && container->layout > L_AUTO_FIRST) {
108 layout = container->layout - 1;
109 } else {
110 layout = L_AUTO_LAST;
111 }
112 } else {
113 bool is_nmaster;
114 bool is_set;
115 if (strcasecmp(argv[1], "master") == 0) {
116 is_nmaster = true;
117 } else if (strcasecmp(argv[1], "ncol") == 0) {
118 is_nmaster = false;
119 } else {
120 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command. %s",
121 cmd_name, err_msg);
122 }
123 if ((error = checkarg(argc, "auto <master|ncol>", EXPECTED_EQUAL_TO, 4))) {
124 return error;
125 }
126 if (strcasecmp(argv[2], "set") == 0) {
127 is_set = true;
128 } else if (strcasecmp(argv[2], "inc") == 0) {
129 is_set = false;
130 } else {
131 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command. %s, "
132 "Argument must be on of <set|inc>",
133 set_inc_cmd_name);
134 }
135 char *end;
136 int n = (int)strtol(argv[3], &end, 10);
137 if (*end) {
138 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
139 "(argument must be an integer)", set_inc_cmd_name);
140 }
141 if (is_auto_layout(container->layout)) {
142 int inc = 0; /* difference between current master/ncol and requested value */
143 if (is_nmaster) {
144 if (is_set) {
145 if (n < 0) {
146 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
147 "(master must be >= 0)", set_inc_cmd_name);
148 }
149 inc = n - (int)container->nb_master;
150 } else { /* inc command */
151 if ((int)container->nb_master + n >= 0) {
152 inc = n;
153 }
154 }
155 if (inc) {
156 for (int i = container->nb_master;
157 i >= 0 && i < container->children->length
158 && i != (int)container->nb_master + inc;) {
159 ((swayc_t *)container->children->items[i])->height = -1;
160 ((swayc_t *)container->children->items[i])->width = -1;
161 i += inc > 0 ? 1 : -1;
162 }
163 container->nb_master += inc;
164 need_layout_update = true;
165 }
166 } else { /* ncol modification */
167 if (is_set) {
168 if (n <= 0) {
169 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
170 "(ncol must be > 0)", set_inc_cmd_name);
171 }
172 inc = n - (int)container->nb_slave_groups;
173 } else { /* inc command */
174 if ((int)container->nb_slave_groups + n > 0) {
175 inc = n;
176 }
177 }
178 if (inc) {
179 container->nb_slave_groups += inc;
180 need_layout_update = true;
181 }
182 }
183 }
184 }
185
186 if (layout != old_layout) {
187 swayc_change_layout(container, layout);
188 update_layout_geometry(container, old_layout);
189 need_layout_update = true;
190 }
191 if (need_layout_update) {
192 update_geometry(container);
193 arrange_windows(container, container->width, container->height);
194 }
195 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
196}
diff --git a/sway/commands/log_colors.c b/sway/commands/log_colors.c
deleted file mode 100644
index 815d1942..00000000
--- a/sway/commands/log_colors.c
+++ /dev/null
@@ -1,22 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *cmd_log_colors(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
9 if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 if (strcasecmp(argv[0], "no") == 0) {
13 sway_log_colors(0);
14 } else if (strcasecmp(argv[0], "yes") == 0) {
15 sway_log_colors(1);
16 } else {
17 error = cmd_results_new(CMD_FAILURE, "log_colors",
18 "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
19 return error;
20 }
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22}
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
deleted file mode 100644
index c1d959df..00000000
--- a/sway/commands/mark.c
+++ /dev/null
@@ -1,87 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include <stdbool.h>
4#include "sway/commands.h"
5#include "list.h"
6#include "stringop.h"
7
8static void find_marks_callback(swayc_t *container, void *_mark) {
9 char *mark = (char *)_mark;
10
11 int index;
12 if (container->marks && ((index = list_seq_find(container->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1)) {
13 list_del(container->marks, index);
14 }
15}
16
17struct cmd_results *cmd_mark(int argc, char **argv) {
18 struct cmd_results *error = NULL;
19 if (config->reading) return cmd_results_new(CMD_FAILURE, "mark", "Can't be used in config file.");
20 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
21 return error;
22 }
23
24 swayc_t *view = current_container;
25 bool add = false;
26 bool toggle = false;
27
28 if (strcmp(argv[0], "--add") == 0) {
29 --argc; ++argv;
30 add = true;
31 } else if (strcmp(argv[0], "--replace") == 0) {
32 --argc; ++argv;
33 }
34
35 if (argc && strcmp(argv[0], "--toggle") == 0) {
36 --argc; ++argv;
37 toggle = true;
38 }
39
40 if (argc) {
41 char *mark = join_args(argv, argc);
42
43 // Remove all existing marks of this type
44 container_map(&root_container, find_marks_callback, mark);
45
46 if (view->marks) {
47 if (add) {
48 int index;
49 if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
50 if (toggle) {
51 free(view->marks->items[index]);
52 list_del(view->marks, index);
53
54 if (0 == view->marks->length) {
55 list_free(view->marks);
56 view->marks = NULL;
57 }
58 }
59 free(mark);
60 } else {
61 list_add(view->marks, mark);
62 }
63 } else {
64 if (toggle && list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark) != -1) {
65 // Delete the list
66 list_foreach(view->marks, free);
67 list_free(view->marks);
68 view->marks = NULL;
69 } else {
70 // Delete and replace with a new list
71 list_foreach(view->marks, free);
72 list_free(view->marks);
73
74 view->marks = create_list();
75 list_add(view->marks, mark);
76 }
77 }
78 } else {
79 view->marks = create_list();
80 list_add(view->marks, mark);
81 }
82 } else {
83 return cmd_results_new(CMD_FAILURE, "mark",
84 "Expected 'mark [--add|--replace] [--toggle] <mark>'");
85 }
86 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
87}
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index d2985c54..c30a8bac 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -15,43 +15,45 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
15 } 15 }
16 16
17 const char *mode_name = argv[0]; 17 const char *mode_name = argv[0];
18 bool mode_make = (argc == 2 && strcmp(argv[1], "{") == 0); 18 bool new_mode = (argc == 2 && strcmp(argv[1], "{") == 0);
19 if (mode_make) { 19 if (new_mode && !config->reading) {
20 if (!config->reading) 20 return cmd_results_new(CMD_FAILURE,
21 return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file."); 21 "mode", "Can only be used in config file.");
22 } 22 }
23 struct sway_mode *mode = NULL; 23 struct sway_mode *mode = NULL;
24 // Find mode 24 // Find mode
25 int i, len = config->modes->length; 25 for (int i = 0; i < config->modes->length; ++i) {
26 for (i = 0; i < len; ++i) { 26 struct sway_mode *test = config->modes->items[i];
27 struct sway_mode *find = config->modes->items[i]; 27 if (strcasecmp(test->name, mode_name) == 0) {
28 if (strcasecmp(find->name, mode_name) == 0) { 28 mode = test;
29 mode = find;
30 break; 29 break;
31 } 30 }
32 } 31 }
33 // Create mode if it doesn't exist 32 // Create mode if it doesn't exist
34 if (!mode && mode_make) { 33 if (!mode && new_mode) {
35 mode = malloc(sizeof(struct sway_mode)); 34 mode = calloc(1, sizeof(struct sway_mode));
36 if (!mode) { 35 if (!mode) {
37 return cmd_results_new(CMD_FAILURE, "mode", "Unable to allocate mode"); 36 return cmd_results_new(CMD_FAILURE,
37 "mode", "Unable to allocate mode");
38 } 38 }
39 mode->name = strdup(mode_name); 39 mode->name = strdup(mode_name);
40 mode->bindings = create_list(); 40 mode->keysym_bindings = create_list();
41 mode->keycode_bindings = create_list();
41 list_add(config->modes, mode); 42 list_add(config->modes, mode);
42 } 43 }
43 if (!mode) { 44 if (!mode) {
44 error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name); 45 error = cmd_results_new(CMD_INVALID,
46 "mode", "Unknown mode `%s'", mode_name);
45 return error; 47 return error;
46 } 48 }
47 if ((config->reading && mode_make) || (!config->reading && !mode_make)) { 49 if ((config->reading && new_mode) || (!config->reading && !new_mode)) {
48 sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); 50 wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name);
49 } 51 }
50 // Set current mode 52 // Set current mode
51 config->current_mode = mode; 53 config->current_mode = mode;
52 if (!mode_make) { 54 if (!new_mode) {
53 // trigger IPC mode event 55 // trigger IPC mode event
54 ipc_event_mode(config->current_mode->name); 56 ipc_event_mode(config->current_mode->name);
55 } 57 }
56 return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL); 58 return cmd_results_new(new_mode ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
57} 59}
diff --git a/sway/commands/mouse_warping.c b/sway/commands/mouse_warping.c
index 5596d483..eef32ce7 100644
--- a/sway/commands/mouse_warping.c
+++ b/sway/commands/mouse_warping.c
@@ -11,7 +11,9 @@ struct cmd_results *cmd_mouse_warping(int argc, char **argv) {
11 } else if (strcasecmp(argv[0], "none") == 0) { 11 } else if (strcasecmp(argv[0], "none") == 0) {
12 config->mouse_warping = false; 12 config->mouse_warping = false;
13 } else { 13 } else {
14 return cmd_results_new(CMD_FAILURE, "mouse_warping", "Expected 'mouse_warping output|none'"); 14 return cmd_results_new(CMD_FAILURE, "mouse_warping",
15 "Expected 'mouse_warping output|none'");
15 } 16 }
16 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 17 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
17} 18}
19
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 8d89f2ef..a5273ba4 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,186 +1,198 @@
1#define _XOPEN_SOURCE 500
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
3#include <wlc/wlc.h> 4#include <wlr/types/wlr_output.h>
5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/util/log.h>
4#include "sway/commands.h" 7#include "sway/commands.h"
5#include "sway/container.h" 8#include "sway/input/seat.h"
6#include "sway/layout.h"
7#include "sway/output.h" 9#include "sway/output.h"
8#include "sway/workspace.h" 10#include "sway/tree/container.h"
9#include "list.h" 11#include "sway/tree/layout.h"
12#include "sway/tree/workspace.h"
10#include "stringop.h" 13#include "stringop.h"
14#include "list.h"
15
16static const char* expected_syntax =
17 "Expected 'move <left|right|up|down> <[px] px>' or "
18 "'move <container|window> to workspace <name>' or "
19 "'move <container|window|workspace> to output <name|direction>' or "
20 "'move position mouse'";
21
22static struct sway_container *output_in_direction(const char *direction,
23 struct wlr_output *reference, int ref_ox, int ref_oy) {
24 int ref_lx = ref_ox + reference->lx,
25 ref_ly = ref_oy + reference->ly;
26 struct {
27 char *name;
28 enum wlr_direction direction;
29 } names[] = {
30 { "up", WLR_DIRECTION_UP },
31 { "down", WLR_DIRECTION_DOWN },
32 { "left", WLR_DIRECTION_LEFT },
33 { "right", WLR_DIRECTION_RIGHT },
34 };
35 for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
36 if (strcasecmp(names[i].name, direction) == 0) {
37 struct wlr_output *adjacent = wlr_output_layout_adjacent_output(
38 root_container.sway_root->output_layout,
39 names[i].direction, reference, ref_lx, ref_ly);
40 if (adjacent) {
41 struct sway_output *sway_output = adjacent->data;
42 return sway_output->swayc;
43 }
44 break;
45 }
46 }
47 return output_by_name(direction);
48}
49
50static struct cmd_results *cmd_move_container(struct sway_container *current,
51 int argc, char **argv) {
52 struct cmd_results *error = NULL;
53 if ((error = checkarg(argc, "move container/window",
54 EXPECTED_AT_LEAST, 4))) {
55 return error;
56 } else if (strcasecmp(argv[1], "to") == 0
57 && strcasecmp(argv[2], "workspace") == 0) {
58 // move container to workspace x
59 if (current->type == C_WORKSPACE) {
60 // TODO: Wrap children in a container and move that
61 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
62 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
63 return cmd_results_new(CMD_FAILURE, "move",
64 "Can only move containers and views.");
65 }
66 struct sway_container *ws;
67 char *ws_name = NULL;
68 if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
69 // move "container to workspace number x"
70 ws_name = strdup(argv[4]);
71 ws = workspace_by_number(ws_name);
72 } else {
73 ws_name = join_args(argv + 3, argc - 3);
74 ws = workspace_by_name(ws_name);
75 }
76
77 if (config->auto_back_and_forth && prev_workspace_name) {
78 // auto back and forth move
79 struct sway_container *curr_ws = container_parent(current, C_WORKSPACE);
80 if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) {
81 // if target workspace is the current one
82 free(ws_name);
83 ws_name = strdup(prev_workspace_name);
84 ws = workspace_by_name(ws_name);
85 }
86 }
87
88 if (!ws) {
89 ws = workspace_create(NULL, ws_name);
90 }
91 free(ws_name);
92 struct sway_container *old_parent = current->parent;
93 struct sway_container *focus = seat_get_focus_inactive(
94 config->handler_context.seat, ws);
95 container_move_to(current, focus);
96 seat_set_focus(config->handler_context.seat, old_parent);
97 container_reap_empty(old_parent);
98 container_reap_empty(focus->parent);
99 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
100 } else if (strcasecmp(argv[1], "to") == 0
101 && strcasecmp(argv[2], "output") == 0) {
102 if (current->type == C_WORKSPACE) {
103 // TODO: Wrap children in a container and move that
104 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
105 } else if (current->type != C_CONTAINER
106 && current->type != C_VIEW) {
107 return cmd_results_new(CMD_FAILURE, "move",
108 "Can only move containers and views.");
109 }
110 struct sway_container *source = container_parent(current, C_OUTPUT);
111 struct sway_container *destination = output_in_direction(argv[3],
112 source->sway_output->wlr_output, current->x, current->y);
113 if (!destination) {
114 return cmd_results_new(CMD_FAILURE, "move workspace",
115 "Can't find output with name/direction '%s'", argv[3]);
116 }
117 struct sway_container *focus = seat_get_focus_inactive(
118 config->handler_context.seat, destination);
119 if (!focus) {
120 // We've never been to this output before
121 focus = destination->children->items[0];
122 }
123 struct sway_container *old_parent = current->parent;
124 container_move_to(current, focus);
125 seat_set_focus(config->handler_context.seat, old_parent);
126 container_reap_empty(old_parent);
127 container_reap_empty(focus->parent);
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 }
130 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
131}
132
133static struct cmd_results *cmd_move_workspace(struct sway_container *current,
134 int argc, char **argv) {
135 struct cmd_results *error = NULL;
136 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
137 return error;
138 } else if (strcasecmp(argv[1], "to") != 0
139 || strcasecmp(argv[2], "output") != 0) {
140 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
141 }
142 struct sway_container *source = container_parent(current, C_OUTPUT);
143 int center_x = current->width / 2 + current->x,
144 center_y = current->height / 2 + current->y;
145 struct sway_container *destination = output_in_direction(argv[3],
146 source->sway_output->wlr_output, center_x, center_y);
147 if (!destination) {
148 return cmd_results_new(CMD_FAILURE, "move workspace",
149 "Can't find output with name/direction '%s'", argv[3]);
150 }
151 if (current->type != C_WORKSPACE) {
152 current = container_parent(current, C_WORKSPACE);
153 }
154 container_move_to(current, destination);
155 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
156}
11 157
12struct cmd_results *cmd_move(int argc, char **argv) { 158struct cmd_results *cmd_move(int argc, char **argv) {
13 struct cmd_results *error = NULL; 159 struct cmd_results *error = NULL;
14 int move_amt = 10; 160 int move_amt = 10;
15
16 if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
17 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 161 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
18 return error; 162 return error;
19 } 163 }
20 const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first> <[px] px>' or " 164 struct sway_container *current = config->handler_context.current_container;
21 "'move <container|window> to workspace <name>' or "
22 "'move <container|window|workspace> to output <name|direction>' or "
23 "'move position mouse'";
24 swayc_t *view = current_container;
25 165
26 if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) { 166 if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) {
27 char *inv; 167 char *inv;
28 move_amt = (int)strtol(argv[1], &inv, 10); 168 move_amt = (int)strtol(argv[1], &inv, 10);
29 if (*inv != '\0' && strcasecmp(inv, "px") != 0) { 169 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
30 move_amt = 10; 170 return cmd_results_new(CMD_FAILURE, "move",
171 "Invalid distance specified");
31 } 172 }
32 } 173 }
33 174
34 if (strcasecmp(argv[0], "left") == 0) { 175 if (strcasecmp(argv[0], "left") == 0) {
35 move_container(view, MOVE_LEFT, move_amt); 176 container_move(current, MOVE_LEFT, move_amt);
36 } else if (strcasecmp(argv[0], "right") == 0) { 177 } else if (strcasecmp(argv[0], "right") == 0) {
37 move_container(view, MOVE_RIGHT, move_amt); 178 container_move(current, MOVE_RIGHT, move_amt);
38 } else if (strcasecmp(argv[0], "up") == 0) { 179 } else if (strcasecmp(argv[0], "up") == 0) {
39 move_container(view, MOVE_UP, move_amt); 180 container_move(current, MOVE_UP, move_amt);
40 } else if (strcasecmp(argv[0], "down") == 0) { 181 } else if (strcasecmp(argv[0], "down") == 0) {
41 move_container(view, MOVE_DOWN, move_amt); 182 container_move(current, MOVE_DOWN, move_amt);
42 } else if (strcasecmp(argv[0], "next") == 0) { 183 } else if (strcasecmp(argv[0], "container") == 0
43 move_container(view, MOVE_NEXT, move_amt); 184 || strcasecmp(argv[0], "window") == 0) {
44 } else if (strcasecmp(argv[0], "prev") == 0) { 185 return cmd_move_container(current, argc, argv);
45 move_container(view, MOVE_PREV, move_amt);
46 } else if (strcasecmp(argv[0], "first") == 0) {
47 move_container(view, MOVE_FIRST, move_amt);
48 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
49 // "move container ...
50 if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
51 return error;
52 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "workspace") == 0) {
53 // move container to workspace x
54 if (view->type == C_WORKSPACE) {
55 if (!view->children || view->children->length == 0) {
56 return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
57 }
58 view = new_container(view, view->workspace_layout);
59 } if (view->type != C_CONTAINER && view->type != C_VIEW) {
60 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
61 }
62
63 swayc_t *ws;
64 const char *num_name = NULL;
65 char *ws_name = NULL;
66 if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
67 // move "container to workspace number x"
68 num_name = argv[4];
69 ws = workspace_by_number(num_name);
70 } else {
71 ws_name = join_args(argv + 3, argc - 3);
72 ws = workspace_by_name(ws_name);
73 }
74
75 if (ws == NULL) {
76 ws = workspace_create(ws_name ? ws_name : num_name);
77 }
78 if (ws_name) {
79 free(ws_name);
80 }
81 move_container_to(view, get_focused_container(ws));
82 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
83 // move container to output x
84 swayc_t *output = NULL;
85 struct wlc_point abs_pos;
86 get_absolute_center_position(view, &abs_pos);
87 if (view->type == C_WORKSPACE) {
88 if (!view->children || view->children->length == 0) {
89 return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
90 }
91 view = new_container(view, view->workspace_layout);
92 } else if (view->type != C_CONTAINER && view->type != C_VIEW) {
93 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
94 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
95 return cmd_results_new(CMD_FAILURE, "move",
96 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
97 }
98
99 swayc_t *container = get_focused_container(output);
100 if (container->is_floating) {
101 move_container_to(view, container->parent);
102 } else {
103 move_container_to(view, container);
104 }
105 } else {
106 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
107 }
108 } else if (strcasecmp(argv[0], "workspace") == 0) { 186 } else if (strcasecmp(argv[0], "workspace") == 0) {
109 // move workspace (to output x) 187 return cmd_move_workspace(current, argc, argv);
110 swayc_t *output = NULL; 188 } else if (strcasecmp(argv[0], "scratchpad") == 0
111 struct wlc_point abs_pos; 189 || (strcasecmp(argv[0], "to") == 0
112 get_absolute_center_position(view, &abs_pos); 190 && strcasecmp(argv[1], "scratchpad") == 0)) {
113 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { 191 // TODO: scratchpad
114 return error; 192 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
115 } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) {
116 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
117 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
118 return cmd_results_new(CMD_FAILURE, "move workspace",
119 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
120 }
121 if (view->type == C_WORKSPACE) {
122 // This probably means we're moving an empty workspace, but
123 // that's fine.
124 move_workspace_to(view, output);
125 } else {
126 swayc_t *workspace = swayc_parent_by_type(view, C_WORKSPACE);
127 move_workspace_to(workspace, output);
128 }
129 } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && strcasecmp(argv[1], "scratchpad") == 0)) {
130 // move scratchpad ...
131 if (view->type != C_CONTAINER && view->type != C_VIEW) {
132 return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
133 }
134 swayc_t *view = current_container;
135 int i;
136 for (i = 0; i < scratchpad->length; i++) {
137 if (scratchpad->items[i] == view) {
138 hide_view_in_scratchpad(view);
139 sp_view = NULL;
140 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
141 }
142 }
143 list_add(scratchpad, view);
144 if (!view->is_floating) {
145 destroy_container(remove_child(view));
146 } else {
147 remove_child(view);
148 }
149 wlc_view_set_mask(view->handle, 0);
150 arrange_windows(swayc_active_workspace(), -1, -1);
151 swayc_t *focused = container_under_pointer();
152 if (focused == NULL) {
153 focused = swayc_active_workspace();
154 }
155 set_focused_container(focused);
156 } else if (strcasecmp(argv[0], "position") == 0) { 193 } else if (strcasecmp(argv[0], "position") == 0) {
157 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 2))) { 194 // TODO: floating
158 return error; 195 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
159 }
160 if (strcasecmp(argv[1], "mouse")) {
161 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
162 }
163
164 if (view->is_floating) {
165 swayc_t *output = swayc_parent_by_type(view, C_OUTPUT);
166 struct wlc_geometry g;
167 wlc_view_get_visible_geometry(view->handle, &g);
168 const struct wlc_size *size = wlc_output_get_resolution(output->handle);
169
170 double x_pos, y_pos;
171 wlc_pointer_get_position_v2(&x_pos, &y_pos);
172
173 int32_t x = x_pos - g.size.w / 2;
174 int32_t y = y_pos - g.size.h / 2;
175
176 uint32_t w = size->w - g.size.w;
177 uint32_t h = size->h - g.size.h;
178
179 view->x = g.origin.x = MIN((int32_t)w, MAX(x, 0));
180 view->y = g.origin.y = MIN((int32_t)h, MAX(y, 0));
181
182 wlc_view_set_geometry(view->handle, 0, &g);
183 }
184 } else { 196 } else {
185 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 197 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
186 } 198 }
diff --git a/sway/commands/new_float.c b/sway/commands/new_float.c
deleted file mode 100644
index d0f96093..00000000
--- a/sway/commands/new_float.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include "log.h"
2#include "sway/commands.h"
3
4struct cmd_results *cmd_new_float(int argc, char **argv) {
5 sway_log(L_INFO, "`new_float` is deprecated and will be removed in the future. "
6 "Please use `default_floating_border` instead.");
7 return cmd_default_floating_border(argc, argv);
8}
diff --git a/sway/commands/new_window.c b/sway/commands/new_window.c
deleted file mode 100644
index 574a4527..00000000
--- a/sway/commands/new_window.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include "log.h"
2#include "sway/commands.h"
3
4struct cmd_results *cmd_new_window(int argc, char **argv) {
5 sway_log(L_INFO, "`new_window` is deprecated and will be removed in the future. "
6 "Please use `default_border` instead.");
7 return cmd_default_border(argc, argv);
8}
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
deleted file mode 100644
index b3b88e5a..00000000
--- a/sway/commands/no_focus.c
+++ /dev/null
@@ -1,41 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/criteria.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8
9struct cmd_results *cmd_no_focus(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "no_focus", EXPECTED_EQUAL_TO, 1))) {
12 return error;
13 }
14 // add command to a criteria/command pair that is run against views when they appear.
15 char *criteria = argv[0];
16
17 struct criteria *crit = malloc(sizeof(struct criteria));
18 if (!crit) {
19 return cmd_results_new(CMD_FAILURE, "no_focus", "Unable to allocate criteria");
20 }
21 crit->crit_raw = strdup(criteria);
22 crit->tokens = create_list();
23 crit->cmdlist = NULL;
24 char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
25
26 if (err_str) {
27 error = cmd_results_new(CMD_INVALID, "no_focus", err_str);
28 free(err_str);
29 free_criteria(crit);
30 } else if (crit->tokens->length == 0) {
31 error = cmd_results_new(CMD_INVALID, "no_focus", "Found no name/value pairs in criteria");
32 free_criteria(crit);
33 } else if (list_seq_find(config->no_focus, criteria_cmp, crit) != -1) {
34 sway_log(L_DEBUG, "no_focus: Duplicate, skipping.");
35 free_criteria(crit);
36 } else {
37 sway_log(L_DEBUG, "no_focus: '%s' added", crit->crit_raw);
38 list_add(config->no_focus, crit);
39 }
40 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
41}
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
new file mode 100644
index 00000000..68fd9f42
--- /dev/null
+++ b/sway/commands/opacity.c
@@ -0,0 +1,36 @@
1#include <assert.h>
2#include <stdlib.h>
3#include "sway/commands.h"
4#include "sway/tree/view.h"
5#include "log.h"
6
7static bool parse_opacity(const char *opacity, float *val) {
8 char *err;
9 *val = strtof(opacity, &err);
10 if (*val < 0 || *val > 1 || *err) {
11 return false;
12 }
13 return true;
14}
15
16struct cmd_results *cmd_opacity(int argc, char **argv) {
17 struct cmd_results *error = NULL;
18 if ((error = checkarg(argc, "layout", EXPECTED_EQUAL_TO, 1))) {
19 return error;
20 }
21
22 struct sway_container *con =
23 config->handler_context.current_container;
24
25 float opacity = 0.0f;
26
27 if (!parse_opacity(argv[0], &opacity)) {
28 return cmd_results_new(CMD_INVALID, "opacity <value>",
29 "Invalid value (expected 0..1): %s", argv[0]);
30 }
31
32 con->alpha = opacity;
33 container_damage_whole(con);
34
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36}
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 911391d2..f7e3372c 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -17,196 +17,299 @@ static char *bg_options[] = {
17 "center", 17 "center",
18 "fill", 18 "fill",
19 "fit", 19 "fit",
20 "tile" 20 "tile",
21}; 21};
22 22
23struct cmd_results *cmd_output(int argc, char **argv) { 23static struct cmd_results *cmd_output_mode(struct output_config *output,
24 struct cmd_results *error = NULL; 24 int *i, int argc, char **argv) {
25 if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) { 25 if (++*i >= argc) {
26 return error; 26 return cmd_results_new(CMD_INVALID, "output", "Missing mode argument.");
27 } 27 }
28 const char *name = argv[0];
29 28
30 struct output_config *output = calloc(1, sizeof(struct output_config)); 29 char *end;
31 if (!output) { 30 output->width = strtol(argv[*i], &end, 10);
32 return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config"); 31 if (*end) {
32 // Format is 1234x4321
33 if (*end != 'x') {
34 return cmd_results_new(CMD_INVALID, "output",
35 "Invalid mode width.");
36 }
37 ++end;
38 output->height = strtol(end, &end, 10);
39 if (*end) {
40 if (*end != '@') {
41 return cmd_results_new(CMD_INVALID, "output",
42 "Invalid mode height.");
43 }
44 ++end;
45 output->refresh_rate = strtof(end, &end);
46 if (strcasecmp("Hz", end) != 0) {
47 return cmd_results_new(CMD_INVALID, "output",
48 "Invalid mode refresh rate.");
49 }
50 }
51 } else {
52 // Format is 1234 4321
53 if (++*i >= argc) {
54 return cmd_results_new(CMD_INVALID, "output",
55 "Missing mode argument (height).");
56 }
57 output->height = strtol(argv[*i], &end, 10);
58 if (*end) {
59 return cmd_results_new(CMD_INVALID, "output",
60 "Invalid mode height.");
61 }
33 } 62 }
34 output->x = output->y = output->width = output->height = -1;
35 output->name = strdup(name);
36 output->enabled = -1;
37 output->scale = 1;
38 63
39 // TODO: atoi doesn't handle invalid numbers 64 return NULL;
65}
40 66
41 int i; 67static struct cmd_results *cmd_output_position(struct output_config *output,
42 for (i = 1; i < argc; ++i) { 68 int *i, int argc, char **argv) {
43 const char *command = argv[i]; 69 if (++*i >= argc) {
70 return cmd_results_new(CMD_INVALID, "output",
71 "Missing position argument.");
72 }
44 73
45 if (strcasecmp(command, "disable") == 0) { 74 char *end;
46 output->enabled = 0; 75 output->x = strtol(argv[*i], &end, 10);
47 } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) { 76 if (*end) {
48 if (++i >= argc) { 77 // Format is 1234,4321
49 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument."); 78 if (*end != ',') {
50 goto fail; 79 return cmd_results_new(CMD_INVALID, "output",
51 } 80 "Invalid position x.");
52 char *res = argv[i]; 81 }
53 char *x = strchr(res, 'x'); 82 ++end;
54 int width = -1, height = -1; 83 output->y = strtol(end, &end, 10);
55 if (x != NULL) { 84 if (*end) {
56 // Format is 1234x4321 85 return cmd_results_new(CMD_INVALID, "output",
57 *x = '\0'; 86 "Invalid position y.");
58 width = atoi(res); 87 }
59 height = atoi(x + 1); 88 } else {
60 *x = 'x'; 89 // Format is 1234 4321 (legacy)
61 } else { 90 if (++*i >= argc) {
62 // Format is 1234 4321 91 return cmd_results_new(CMD_INVALID, "output",
63 width = atoi(res); 92 "Missing position argument (y).");
64 if (++i >= argc) { 93 }
65 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height)."); 94 output->y = strtol(argv[*i], &end, 10);
66 goto fail; 95 if (*end) {
96 return cmd_results_new(CMD_INVALID, "output",
97 "Invalid position y.");
98 }
99 }
100
101 return NULL;
102}
103
104static struct cmd_results *cmd_output_scale(struct output_config *output,
105 int *i, int argc, char **argv) {
106 if (++*i >= argc) {
107 return cmd_results_new(CMD_INVALID, "output",
108 "Missing scale argument.");
109 }
110
111 char *end;
112 output->scale = strtof(argv[*i], &end);
113 if (*end) {
114 return cmd_results_new(CMD_INVALID, "output", "Invalid scale.");
115 }
116
117 return NULL;
118}
119
120static struct cmd_results *cmd_output_transform(struct output_config *output,
121 int *i, int argc, char **argv) {
122 if (++*i >= argc) {
123 return cmd_results_new(CMD_INVALID, "output",
124 "Missing transform argument.");
125 }
126
127 char *value = argv[*i];
128 if (strcmp(value, "normal") == 0) {
129 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
130 } else if (strcmp(value, "90") == 0) {
131 output->transform = WL_OUTPUT_TRANSFORM_90;
132 } else if (strcmp(value, "180") == 0) {
133 output->transform = WL_OUTPUT_TRANSFORM_180;
134 } else if (strcmp(value, "270") == 0) {
135 output->transform = WL_OUTPUT_TRANSFORM_270;
136 } else if (strcmp(value, "flipped") == 0) {
137 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
138 } else if (strcmp(value, "flipped-90") == 0) {
139 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
140 } else if (strcmp(value, "flipped-180") == 0) {
141 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
142 } else if (strcmp(value, "flipped-270") == 0) {
143 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
144 } else {
145 return cmd_results_new(CMD_INVALID, "output",
146 "Invalid output transform.");
147 }
148
149 return NULL;
150}
151
152static struct cmd_results *cmd_output_background(struct output_config *output,
153 int *i, int argc, char **argv) {
154 if (++*i >= argc) {
155 return cmd_results_new(CMD_INVALID, "output",
156 "Missing background file or color specification.");
157 }
158 const char *background = argv[*i];
159 if (*i + 1 >= argc) {
160 return cmd_results_new(CMD_INVALID, "output",
161 "Missing background scaling mode or `solid_color`.");
162 }
163 const char *background_option = argv[*i];
164
165 if (strcasecmp(background_option, "solid_color") == 0) {
166 output->background = strdup(background);
167 output->background_option = strdup("solid_color");
168 } else {
169 bool valid = false;
170 char *mode;
171 size_t j;
172 for (j = 0; j < (size_t)(argc - *i); ++j) {
173 mode = argv[*i + j];
174 size_t n = sizeof(bg_options) / sizeof(char *);
175 for (size_t k = 0; k < n; ++k) {
176 if (strcasecmp(mode, bg_options[k]) == 0) {
177 valid = true;
178 break;
67 } 179 }
68 res = argv[i];
69 height = atoi(res);
70 } 180 }
71 output->width = width; 181 if (valid) {
72 output->height = height; 182 break;
73 } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
74 if (++i >= argc) {
75 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
76 goto fail;
77 } 183 }
78 char *res = argv[i]; 184 }
79 char *c = strchr(res, ','); 185 if (!valid) {
80 int x = -1, y = -1; 186 return cmd_results_new(CMD_INVALID, "output",
81 if (c != NULL) { 187 "Missing background scaling mode.");
82 // Format is 1234,4321 188 }
83 *c = '\0'; 189
84 x = atoi(res); 190 wordexp_t p;
85 y = atoi(c + 1); 191 char *src = join_args(argv + *i, j);
86 *c = ','; 192 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
87 } else { 193 return cmd_results_new(CMD_INVALID, "output",
88 // Format is 1234 4321 194 "Invalid syntax (%s).", src);
89 x = atoi(res); 195 }
90 if (++i >= argc) { 196 free(src);
91 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y)."); 197 src = p.we_wordv[0];
92 goto fail; 198 if (config->reading && *src != '/') {
199 char *conf = strdup(config->current_config);
200 if (conf) {
201 char *conf_path = dirname(conf);
202 src = malloc(strlen(conf_path) + strlen(src) + 2);
203 if (src) {
204 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
205 } else {
206 wlr_log(L_ERROR,
207 "Unable to allocate background source");
93 } 208 }
94 res = argv[i]; 209 free(conf);
95 y = atoi(res);
96 }
97 output->x = x;
98 output->y = y;
99 } else if (strcasecmp(command, "scale") == 0) {
100 if (++i >= argc) {
101 error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter.");
102 goto fail;
103 }
104 output->scale = atoi(argv[i]);
105 } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
106 wordexp_t p;
107 if (++i >= argc) {
108 error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
109 goto fail;
110 }
111 if (i + 1 >= argc) {
112 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
113 goto fail;
114 }
115 if (strcasecmp(argv[i + 1], "solid_color") == 0) {
116 output->background = strdup(argv[argc - 2]);
117 output->background_option = strdup("solid_color");
118 } else { 210 } else {
119 // argv[i+j]=bg_option 211 wlr_log(L_ERROR, "Unable to allocate background source");
120 bool valid = false; 212 }
121 char *mode; 213 }
122 size_t j; 214 if (!src || access(src, F_OK) == -1) {
123 for (j = 0; j < (size_t) (argc - i); ++j) { 215 wordfree(&p);
124 mode = argv[i + j]; 216 return cmd_results_new(CMD_INVALID, "output",
125 for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) { 217 "Background file unreadable (%s).", src);
126 if (strcasecmp(mode, bg_options[k]) == 0) { 218 }
127 valid = true;
128 break;
129 }
130 }
131 if (valid) {
132 break;
133 }
134 }
135 if (!valid) {
136 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
137 goto fail;
138 }
139 219
140 char *src = join_args(argv + i, j); 220 output->background = strdup(src);
141 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { 221 output->background_option = strdup(mode);
142 error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); 222 if (src != p.we_wordv[0]) {
143 goto fail; 223 free(src);
144 } 224 }
145 free(src); 225 wordfree(&p);
146 src = p.we_wordv[0];
147 if (config->reading && *src != '/') {
148 char *conf = strdup(config->current_config);
149 if (conf) {
150 char *conf_path = dirname(conf);
151 src = malloc(strlen(conf_path) + strlen(src) + 2);
152 if (src) {
153 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
154 } else {
155 sway_log(L_ERROR, "Unable to allocate background source");
156 }
157 free(conf);
158 } else {
159 sway_log(L_ERROR, "Unable to allocate background source");
160 }
161 }
162 if (!src || access(src, F_OK) == -1) {
163 error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
164 wordfree(&p);
165 goto fail;
166 }
167 226
168 output->background = strdup(src); 227 *i += j;
169 output->background_option = strdup(mode); 228 }
170 if (src != p.we_wordv[0]) {
171 free(src);
172 }
173 wordfree(&p);
174 229
175 i += j; 230 return NULL;
176 } 231}
232
233struct cmd_results *cmd_output(int argc, char **argv) {
234 struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1);
235 if (error != NULL) {
236 return error;
237 }
238
239 struct output_config *output = new_output_config(argv[0]);
240 if (!output) {
241 wlr_log(L_ERROR, "Failed to allocate output config");
242 return NULL;
243 }
244
245 for (int i = 1; i < argc; ++i) {
246 const char *command = argv[i];
247
248 if (strcasecmp(command, "enable") == 0) {
249 output->enabled = 1;
250 } else if (strcasecmp(command, "disable") == 0) {
251 output->enabled = 0;
252 } else if (strcasecmp(command, "mode") == 0 ||
253 strcasecmp(command, "resolution") == 0 ||
254 strcasecmp(command, "res") == 0) {
255 error = cmd_output_mode(output, &i, argc, argv);
256 } else if (strcasecmp(command, "position") == 0 ||
257 strcasecmp(command, "pos") == 0) {
258 error = cmd_output_position(output, &i, argc, argv);
259 } else if (strcasecmp(command, "scale") == 0) {
260 error = cmd_output_scale(output, &i, argc, argv);
261 } else if (strcasecmp(command, "transform") == 0) {
262 error = cmd_output_transform(output, &i, argc, argv);
263 } else if (strcasecmp(command, "background") == 0 ||
264 strcasecmp(command, "bg") == 0) {
265 error = cmd_output_background(output, &i, argc, argv);
266 } else {
267 error = cmd_results_new(CMD_INVALID, "output",
268 "Invalid output subcommand: %s.", command);
269 }
270
271 if (error != NULL) {
272 goto fail;
177 } 273 }
178 } 274 }
179 275
180 i = list_seq_find(config->output_configs, output_name_cmp, name); 276 int i = list_seq_find(config->output_configs, output_name_cmp, output->name);
181 if (i >= 0) { 277 if (i >= 0) {
182 // merge existing config 278 // Merge existing config
183 struct output_config *oc = config->output_configs->items[i]; 279 struct output_config *current = config->output_configs->items[i];
184 merge_output_config(oc, output); 280 merge_output_config(current, output);
185 free_output_config(output); 281 free_output_config(output);
186 output = oc; 282 output = current;
187 } else { 283 } else {
188 list_add(config->output_configs, output); 284 list_add(config->output_configs, output);
189 } 285 }
190 286
191 sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ %d, %d scale %d) (bg %s %s)", 287 wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
192 output->name, output->enabled, output->width, 288 "position %d,%d scale %f transform %d) (bg %s %s)",
193 output->height, output->x, output->y, output->scale, 289 output->name, output->enabled, output->width, output->height,
194 output->background, output->background_option); 290 output->refresh_rate, output->x, output->y, output->scale,
291 output->transform, output->background, output->background_option);
195 292
196 if (output->name) { 293 // Try to find the output container and apply configuration now. If
197 // Try to find the output container and apply configuration now. If 294 // this is during startup then there will be no container and config
198 // this is during startup then there will be no container and config 295 // will be applied during normal "new output" event from wlroots.
199 // will be applied during normal "new output" event from wlc. 296 char identifier[128];
200 swayc_t *cont = NULL; 297 bool all = strcmp(output->name, "*") == 0;
201 for (int i = 0; i < root_container.children->length; ++i) { 298 for (int i = 0; i < root_container.children->length; ++i) {
202 cont = root_container.children->items[i]; 299 struct sway_container *cont = root_container.children->items[i];
203 if (cont->name && ((strcmp(cont->name, output->name) == 0) || (strcmp(output->name, "*") == 0))) { 300 if (cont->type != C_OUTPUT) {
204 apply_output_config(output, cont); 301 continue;
302 }
205 303
206 if (strcmp(output->name, "*") != 0) { 304 output_get_identifier(identifier, sizeof(identifier), cont->sway_output);
207 // stop looking if the output config isn't applicable to all outputs 305 if (all || strcmp(cont->name, output->name) == 0 ||
208 break; 306 strcmp(identifier, output->name) == 0) {
209 } 307 apply_output_config(output, cont);
308
309 if (!all) {
310 // Stop looking if the output config isn't applicable to all
311 // outputs
312 break;
210 } 313 }
211 } 314 }
212 } 315 }
diff --git a/sway/commands/permit.c b/sway/commands/permit.c
deleted file mode 100644
index 7a5e06f7..00000000
--- a/sway/commands/permit.c
+++ /dev/null
@@ -1,108 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "sway/security.h"
6#include "util.h"
7#include "log.h"
8
9static enum secure_feature get_features(int argc, char **argv,
10 struct cmd_results **error) {
11 enum secure_feature features = 0;
12
13 struct {
14 char *name;
15 enum secure_feature feature;
16 } feature_names[] = {
17 { "lock", FEATURE_LOCK },
18 { "panel", FEATURE_PANEL },
19 { "background", FEATURE_BACKGROUND },
20 { "screenshot", FEATURE_SCREENSHOT },
21 { "fullscreen", FEATURE_FULLSCREEN },
22 { "keyboard", FEATURE_KEYBOARD },
23 { "mouse", FEATURE_MOUSE },
24 };
25
26 for (int i = 1; i < argc; ++i) {
27 size_t j;
28 for (j = 0; j < sizeof(feature_names) / sizeof(feature_names[0]); ++j) {
29 if (strcmp(feature_names[j].name, argv[i]) == 0) {
30 break;
31 }
32 }
33 if (j == sizeof(feature_names) / sizeof(feature_names[0])) {
34 *error = cmd_results_new(CMD_INVALID,
35 "permit", "Invalid feature grant %s", argv[i]);
36 return 0;
37 }
38 features |= feature_names[j].feature;
39 }
40 return features;
41}
42
43struct cmd_results *cmd_permit(int argc, char **argv) {
44 struct cmd_results *error = NULL;
45 if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
46 return error;
47 }
48 if ((error = check_security_config())) {
49 return error;
50 }
51
52 bool assign_perms = true;
53 char *program = NULL;
54
55 if (!strcmp(argv[0], "*")) {
56 program = strdup(argv[0]);
57 } else {
58 program = resolve_path(argv[0]);
59 }
60 if (!program) {
61 sway_assert(program, "Unable to resolve IPC permit target '%s'."
62 " will issue empty policy", argv[0]);
63 assign_perms = false;
64 program = strdup(argv[0]);
65 }
66
67 struct feature_policy *policy = get_feature_policy(program);
68 if (policy && assign_perms) {
69 policy->features |= get_features(argc, argv, &error);
70 sway_log(L_DEBUG, "Permissions granted to %s for features %d",
71 policy->program, policy->features);
72 }
73
74 free(program);
75 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
76}
77
78struct cmd_results *cmd_reject(int argc, char **argv) {
79 struct cmd_results *error = NULL;
80 if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
81 return error;
82 }
83 if ((error = check_security_config())) {
84 return error;
85 }
86
87 char *program = NULL;
88 if (!strcmp(argv[0], "*")) {
89 program = strdup(argv[0]);
90 } else {
91 program = resolve_path(argv[0]);
92 }
93 if (!program) {
94 // Punt
95 sway_log(L_INFO, "Unable to resolve IPC reject target '%s'."
96 " Will use provided path", argv[0]);
97 program = strdup(argv[0]);
98 }
99
100 struct feature_policy *policy = get_feature_policy(program);
101 policy->features &= ~get_features(argc, argv, &error);
102
103 sway_log(L_DEBUG, "Permissions granted to %s for features %d",
104 policy->program, policy->features);
105
106 free(program);
107 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
108}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 01fcc5ba..5bca6cde 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,10 +1,9 @@
1#include "sway/commands.h" 1#include "sway/commands.h"
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/layout.h" 3#include "sway/tree/layout.h"
4 4
5struct cmd_results *cmd_reload(int argc, char **argv) { 5struct cmd_results *cmd_reload(int argc, char **argv) {
6 struct cmd_results *error = NULL; 6 struct cmd_results *error = NULL;
7 if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
8 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { 7 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
9 return error; 8 return error;
10 } 9 }
@@ -13,7 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
13 } 12 }
14 13
15 load_swaybars(); 14 load_swaybars();
16
17 arrange_windows(&root_container, -1, -1); 15 arrange_windows(&root_container, -1, -1);
18 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 16 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
19} 17}
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ef52bb07..93c1fe7d 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -4,372 +4,281 @@
4#include <stdlib.h> 4#include <stdlib.h>
5#include <string.h> 5#include <string.h>
6#include <strings.h> 6#include <strings.h>
7#include <wlc/wlc.h> 7#include <wlr/util/log.h>
8#include "sway/commands.h" 8#include "sway/commands.h"
9#include "sway/layout.h" 9#include "sway/tree/layout.h"
10#include "sway/focus.h"
11#include "sway/input_state.h"
12#include "sway/handlers.h"
13#include "log.h" 10#include "log.h"
14 11
15enum resize_dim_types { 12static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
16 RESIZE_DIM_PX,
17 RESIZE_DIM_PPT,
18 RESIZE_DIM_DEFAULT,
19};
20
21static bool set_size_floating(int new_dimension, bool use_width) {
22 swayc_t *view = current_container;
23 if (view) {
24 if (use_width) {
25 int current_width = view->width;
26 view->desired_width = new_dimension;
27 floating_view_sane_size(view);
28
29 int new_x = view->x + (int)(((view->desired_width - current_width) / 2) * -1);
30 view->width = view->desired_width;
31 view->x = new_x;
32 13
33 update_geometry(view); 14enum resize_unit {
34 } else { 15 RESIZE_UNIT_PX,
35 int current_height = view->height; 16 RESIZE_UNIT_PPT,
36 view->desired_height = new_dimension; 17 RESIZE_UNIT_DEFAULT,
37 floating_view_sane_size(view); 18 RESIZE_UNIT_INVALID,
38 19};
39 int new_y = view->y + (int)(((view->desired_height - current_height) / 2) * -1);
40 view->height = view->desired_height;
41 view->y = new_y;
42 20
43 update_geometry(view); 21enum resize_axis {
44 } 22 RESIZE_AXIS_HORIZONTAL,
23 RESIZE_AXIS_VERTICAL,
24 RESIZE_AXIS_INVALID,
25};
45 26
46 return true; 27static enum resize_unit parse_resize_unit(const char *unit) {
28 if (strcasecmp(unit, "px") == 0) {
29 return RESIZE_UNIT_PX;
47 } 30 }
48 31 if (strcasecmp(unit, "ppt") == 0) {
49 return false; 32 return RESIZE_UNIT_PPT;
33 }
34 if (strcasecmp(unit, "default") == 0) {
35 return RESIZE_UNIT_DEFAULT;
36 }
37 return RESIZE_UNIT_INVALID;
50} 38}
51 39
52static bool resize_floating(int amount, bool use_width) { 40static enum resize_axis parse_resize_axis(const char *axis) {
53 swayc_t *view = current_container; 41 if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) {
54 42 return RESIZE_AXIS_HORIZONTAL;
55 if (view) { 43 }
56 if (use_width) { 44 if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) {
57 return set_size_floating(view->width + amount, true); 45 return RESIZE_AXIS_VERTICAL;
58 } else {
59 return set_size_floating(view->height + amount, false);
60 }
61 } 46 }
47 return RESIZE_AXIS_INVALID;
48}
62 49
63 return false; 50static int parallel_coord(struct sway_container *c, enum resize_axis a) {
51 return a == RESIZE_AXIS_HORIZONTAL ? c->x : c->y;
64} 52}
65 53
66static bool resize_tiled(int amount, bool use_width) { 54static int parallel_size(struct sway_container *c, enum resize_axis a) {
67 swayc_t *container = current_container; 55 return a == RESIZE_AXIS_HORIZONTAL ? c->width : c->height;
68 swayc_t *parent = container->parent; 56}
69 int idx_focused = 0;
70 bool use_major = false;
71 size_t nb_before = 0;
72 size_t nb_after = 0;
73
74 // 1. Identify a container ancestor that will allow the focused child to grow in the requested
75 // direction.
76 while (container->parent) {
77 parent = container->parent;
78 if ((parent->children && parent->children->length > 1)
79 && (is_auto_layout(parent->layout)
80 || (use_width ? parent->layout == L_HORIZ : parent->layout == L_VERT))) {
81 // check if container has siblings that can provide/absorb the space needed for
82 // the resize operation.
83 use_major = use_width
84 ? parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
85 : parent->layout == L_AUTO_TOP || parent->layout == L_AUTO_BOTTOM;
86 // Note: use_major will be false for L_HORIZ and L_VERT
87
88 idx_focused = index_child(container);
89 if (idx_focused < 0) {
90 sway_log(L_ERROR, "Something weird is happening, child container not "
91 "present in its parent's children list.");
92 continue;
93 }
94 if (use_major) {
95 nb_before = auto_group_index(parent, idx_focused);
96 nb_after = auto_group_count(parent) - nb_before - 1;
97 } else {
98 nb_before = idx_focused - auto_group_start_index(parent, idx_focused);
99 nb_after = auto_group_end_index(parent, idx_focused) - idx_focused - 1;
100 sway_log(L_DEBUG, "+++ focused: %d, start: %d, end: %d, before: %d, after: %d",
101 idx_focused,
102 (int)auto_group_start_index(parent, idx_focused),
103 (int)auto_group_end_index(parent, idx_focused),
104 (int)nb_before, (int)nb_after);
105 57
106 } 58static void resize_tiled(int amount, enum resize_axis axis) {
107 if (nb_before || nb_after) { 59 struct sway_container *parent = config->handler_context.current_container;
108 break; 60 struct sway_container *focused = parent;
109 } 61 if (!parent) {
110 } 62 return;
111 container = parent; /* continue up the tree to the next ancestor */
112 }
113 if (parent == &root_container) {
114 return true;
115 } 63 }
116 sway_log(L_DEBUG, "Found the proper parent: %p. It has %zu before conts, " 64
117 "and %zu after conts", parent, nb_before, nb_after); 65 enum sway_container_layout parallel_layout =
118 // 2. Ensure that the resize operation will not make one of the resized containers drop 66 axis == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
119 // below the "sane" size threshold. 67 int minor_weight = 0;
120 bool valid = true; 68 int major_weight = 0;
121 swayc_t *focused = parent->children->items[idx_focused]; 69 while (parent->parent) {
122 int start = use_major ? 0 : auto_group_start_index(parent, idx_focused); 70 struct sway_container *next = parent->parent;
123 int end = use_major ? parent->children->length : auto_group_end_index(parent, idx_focused); 71 if (next->layout == parallel_layout) {
124 sway_log(L_DEBUG, "Check children of container %p [%d,%d[", container, start, end); 72 for (int i = 0; i < next->children->length; i++) {
125 for (int i = start; i < end; ) { 73 struct sway_container *sibling = next->children->items[i];
126 swayc_t *sibling = parent->children->items[i]; 74
127 double pixels = amount; 75 int sibling_pos = parallel_coord(sibling, axis);
128 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y; 76 int focused_pos = parallel_coord(focused, axis);
129 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y; 77 int parent_pos = parallel_coord(parent, axis);
130 if (is_before || is_after) { 78
131 pixels = -pixels; 79 if (sibling_pos != focused_pos) {
132 pixels /= is_before ? nb_before : nb_after; 80 if (sibling_pos < parent_pos) {
133 if (nb_after != 0 && nb_before != 0) { 81 minor_weight++;
134 pixels /= 2; 82 } else if (sibling_pos > parent_pos) {
135 } 83 major_weight++;
136 }
137 sway_log(L_DEBUG, "Check container %p: width %g vs %d, height %g vs %d", sibling, sibling->width + pixels, min_sane_w, sibling->height + pixels, min_sane_h);
138 if (use_width ?
139 sibling->width + pixels < min_sane_w :
140 sibling->height + pixels < min_sane_h) {
141 valid = false;
142 sway_log(L_DEBUG, "Container size no longer sane");
143 break;
144 }
145 i = use_major ? auto_group_end_index(parent, i) : (i + 1);
146 sway_log(L_DEBUG, "+++++ check %i", i);
147 }
148 // 3. Apply the size change
149 if (valid) {
150 for (int i = start; i < end; ) {
151 int next_i = use_major ? auto_group_end_index(parent, i) : (i + 1);
152 swayc_t *sibling = parent->children->items[i];
153 double pixels = amount;
154 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
155 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
156 if (is_before || is_after) {
157 pixels = -pixels;
158 pixels /= is_before ? nb_before : nb_after;
159 if (nb_after != 0 && nb_before != 0) {
160 pixels /= 2;
161 }
162 sway_log(L_DEBUG, "%p: %s", sibling, is_before ? "before" : "after");
163 if (use_major) {
164 for (int j = i; j < next_i; ++j) {
165 recursive_resize(parent->children->items[j], pixels,
166 use_width ?
167 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
168 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
169 }
170 } else {
171 recursive_resize(sibling, pixels,
172 use_width ?
173 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
174 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
175 }
176 } else {
177 if (use_major) {
178 for (int j = i; j < next_i; ++j) {
179 recursive_resize(parent->children->items[j], pixels / 2,
180 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
181 recursive_resize(parent->children->items[j], pixels / 2,
182 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
183 } 84 }
184 } else {
185 recursive_resize(sibling, pixels / 2,
186 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
187 recursive_resize(sibling, pixels / 2,
188 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
189 } 85 }
190 } 86 }
191 i = next_i; 87 if (major_weight || minor_weight) {
88 break;
89 }
192 } 90 }
193 // Recursive resize does not handle positions, let arrange_windows 91 parent = next;
194 // take care of that.
195 arrange_windows(swayc_active_workspace(), -1, -1);
196 } 92 }
197 return true;
198}
199
200static bool set_size_tiled(int amount, bool use_width) {
201 int desired;
202 swayc_t *focused = current_container;
203 93
204 if (use_width) { 94 if (parent->type == C_ROOT) {
205 desired = amount - focused->width; 95 return;
206 } else {
207 desired = amount - focused->height;
208 } 96 }
209 97
210 return resize_tiled(desired, use_width); 98 wlr_log(L_DEBUG,
211} 99 "Found the proper parent: %p. It has %d l conts, and %d r conts",
212 100 parent->parent, minor_weight, major_weight);
213static bool set_size(int dimension, bool use_width) {
214 swayc_t *focused = current_container;
215 101
216 if (focused) { 102 int min_sane = axis == RESIZE_AXIS_HORIZONTAL ? MIN_SANE_W : MIN_SANE_H;
217 if (focused->is_floating) {
218 return set_size_floating(dimension, use_width);
219 } else {
220 return set_size_tiled(dimension, use_width);
221 }
222 }
223 103
224 return false; 104 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
225} 105 // ^ ?????
226 106
227static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) { 107 for (int i = 0; i < parent->parent->children->length; i++) {
228 swayc_t *focused = current_container; 108 struct sway_container *sibling = parent->parent->children->items[i];
229 109
230 // translate "10 ppt" (10%) to appropriate # of pixels in case we need it 110 int sibling_pos = parallel_coord(sibling, axis);
231 float ppt_dim = (float)dimension / 100; 111 int focused_pos = parallel_coord(focused, axis);
112 int parent_pos = parallel_coord(parent, axis);
232 113
233 if (use_width) { 114 int sibling_size = parallel_size(sibling, axis);
234 ppt_dim = focused->width * ppt_dim; 115 int parent_size = parallel_size(parent, axis);
235 } else {
236 ppt_dim = focused->height * ppt_dim;
237 }
238 116
239 if (focused) { 117 if (sibling_pos != focused_pos) {
240 if (focused->is_floating) { 118 if (sibling_pos < parent_pos) {
241 // floating view resize dimensions should default to px, so only 119 double pixels = -amount / minor_weight;
242 // use ppt if specified 120 if (major_weight && (sibling_size + pixels / 2) < min_sane) {
243 if (dim_type == RESIZE_DIM_PPT) { 121 return; // Too small
244 dimension = (int)ppt_dim; 122 } else if ((sibling_size + pixels) < min_sane) {
123 return; // Too small
124 }
125 } else if (sibling_pos > parent_pos) {
126 double pixels = -amount / major_weight;
127 if (minor_weight && (sibling_size + pixels / 2) < min_sane) {
128 return; // Too small
129 } else if ((sibling_size + pixels) < min_sane) {
130 return; // Too small
131 }
245 } 132 }
246
247 return resize_floating(dimension, use_width);
248 } else { 133 } else {
249 // tiled view resize dimensions should default to ppt, so only use 134 double pixels = amount;
250 // px if specified 135 if (parent_size + pixels < min_sane) {
251 if (dim_type != RESIZE_DIM_PX) { 136 return; // Too small
252 dimension = (int)ppt_dim;
253 } 137 }
254
255 return resize_tiled(dimension, use_width);
256 } 138 }
257 } 139 }
258 140
259 return false; 141 enum resize_edge minor_edge = axis == RESIZE_AXIS_HORIZONTAL ?
260} 142 RESIZE_EDGE_LEFT : RESIZE_EDGE_TOP;
143 enum resize_edge major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
144 RESIZE_EDGE_RIGHT : RESIZE_EDGE_BOTTOM;
261 145
262static struct cmd_results *cmd_resize_set(int argc, char **argv) { 146 for (int i = 0; i < parent->parent->children->length; i++) {
263 struct cmd_results *error = NULL; 147 struct sway_container *sibling = parent->parent->children->items[i];
264 if ((error = checkarg(argc, "resize set", EXPECTED_AT_LEAST, 2))) {
265 return error;
266 }
267 148
268 if (strcasecmp(argv[0], "width") == 0 || strcasecmp(argv[0], "height") == 0) { 149 int sibling_pos = parallel_coord(sibling, axis);
269 // handle `reset set width 100 px height 100 px` syntax, also allows 150 int focused_pos = parallel_coord(focused, axis);
270 // specifying only one dimension for a `resize set` 151 int parent_pos = parallel_coord(parent, axis);
271 int cmd_num = 0;
272 int dim;
273
274 while ((cmd_num + 1) < argc) {
275 dim = (int)strtol(argv[cmd_num + 1], NULL, 10);
276 if (errno == ERANGE || dim == 0) {
277 errno = 0;
278 return cmd_results_new(CMD_INVALID, "resize set",
279 "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
280 }
281 152
282 if (strcasecmp(argv[cmd_num], "width") == 0) { 153 if (sibling_pos != focused_pos) {
283 set_size(dim, true); 154 if (sibling_pos < parent_pos) {
284 } else if (strcasecmp(argv[cmd_num], "height") == 0) { 155 double pixels = -1 * amount;
285 set_size(dim, false); 156 pixels /= minor_weight;
286 } else { 157 if (major_weight) {
287 return cmd_results_new(CMD_INVALID, "resize set", 158 container_recursive_resize(sibling, pixels / 2, major_edge);
288 "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'"); 159 } else {
160 container_recursive_resize(sibling, pixels, major_edge);
161 }
162 } else if (sibling_pos > parent_pos) {
163 double pixels = -1 * amount;
164 pixels /= major_weight;
165 if (minor_weight) {
166 container_recursive_resize(sibling, pixels / 2, minor_edge);
167 } else {
168 container_recursive_resize(sibling, pixels, minor_edge);
169 }
289 } 170 }
290 171 } else {
291 cmd_num += 2; 172 if (major_weight != 0 && minor_weight != 0) {
292 173 double pixels = amount;
293 if (cmd_num < argc && strcasecmp(argv[cmd_num], "px") == 0) { 174 pixels /= 2;
294 // if this was `resize set width 400 px height 300 px`, disregard the `px` arg 175 container_recursive_resize(parent, pixels, minor_edge);
295 cmd_num++; 176 container_recursive_resize(parent, pixels, major_edge);
177 } else if (major_weight) {
178 container_recursive_resize(parent, amount, major_edge);
179 } else if (minor_weight) {
180 container_recursive_resize(parent, amount, minor_edge);
296 } 181 }
297 } 182 }
298 } else { 183 }
299 // handle `reset set 100 px 100 px` syntax
300 int width = (int)strtol(argv[0], NULL, 10);
301 if (errno == ERANGE || width == 0) {
302 errno = 0;
303 return cmd_results_new(CMD_INVALID, "resize set",
304 "Expected 'resize set <width> [px] <height> [px]'");
305 }
306 184
307 int height_arg = 1; 185 arrange_windows(parent->parent, -1, -1);
308 if (strcasecmp(argv[1], "px") == 0) { 186}
309 height_arg = 2;
310 }
311 187
312 int height = (int)strtol(argv[height_arg], NULL, 10); 188static void resize(int amount, enum resize_axis axis, enum resize_unit unit) {
313 if (errno == ERANGE || height == 0) { 189 struct sway_container *current = config->handler_context.current_container;
314 errno = 0; 190 if (unit == RESIZE_UNIT_DEFAULT) {
315 return cmd_results_new(CMD_INVALID, "resize set", 191 // Default for tiling; TODO floating should be px
316 "Expected 'resize set <width> [px] <height> [px]'"); 192 unit = RESIZE_UNIT_PPT;
317 } 193 }
318 194
319 set_size(width, true); 195 if (unit == RESIZE_UNIT_PPT) {
320 set_size(height, false); 196 float pct = amount / 100.0f;
197 switch (axis) {
198 case RESIZE_AXIS_HORIZONTAL:
199 amount = (float)current->width * pct;
200 break;
201 case RESIZE_AXIS_VERTICAL:
202 amount = (float)current->height * pct;
203 break;
204 default:
205 sway_assert(0, "invalid resize axis");
206 return;
207 }
321 } 208 }
322 209
323 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 210 return resize_tiled(amount, axis);
324} 211}
325 212
326struct cmd_results *cmd_resize(int argc, char **argv) { 213struct cmd_results *cmd_resize(int argc, char **argv) {
327 struct cmd_results *error = NULL; 214 struct sway_container *current = config->handler_context.current_container;
328 if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file."); 215 if (!current) {
329 if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running."); 216 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
330 217 }
218 if (current->type != C_VIEW && current->type != C_CONTAINER) {
219 return cmd_results_new(CMD_INVALID, "resize",
220 "Can only resize views/containers");
221 }
331 if (strcasecmp(argv[0], "set") == 0) { 222 if (strcasecmp(argv[0], "set") == 0) {
332 return cmd_resize_set(argc - 1, &argv[1]); 223 // TODO
224 //return cmd_resize_set(argc - 1, &argv[1]);
225 return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented");
333 } 226 }
334 227 struct cmd_results *error;
335 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 228 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
336 return error; 229 return error;
337 } 230 }
338 231
339 int dim_arg = argc - 1; 232 // TODO: resize grow|shrink left|right|up|down
340 233
341 enum resize_dim_types dim_type = RESIZE_DIM_DEFAULT; 234 const char *usage = "Expected 'resize <shrink|grow> "
342 if (strcasecmp(argv[dim_arg], "ppt") == 0) { 235 "<width|height> [<amount>] [px|ppt]'";
343 dim_type = RESIZE_DIM_PPT; 236
344 dim_arg--; 237 int multiplier = 0;
345 } else if (strcasecmp(argv[dim_arg], "px") == 0) { 238 if (strcasecmp(*argv, "grow") == 0) {
346 dim_type = RESIZE_DIM_PX; 239 multiplier = 1;
347 dim_arg--; 240 } else if (strcasecmp(*argv, "shrink") == 0) {
241 multiplier = -1;
242 } else {
243 return cmd_results_new(CMD_INVALID, "resize", usage);
348 } 244 }
245 --argc; ++argv;
349 246
350 int amount = (int)strtol(argv[dim_arg], NULL, 10); 247 enum resize_axis axis = parse_resize_axis(*argv);
351 if (errno == ERANGE || amount == 0) { 248 if (axis == RESIZE_AXIS_INVALID) {
352 errno = 0; 249 return cmd_results_new(CMD_INVALID, "resize", usage);
353 amount = 10; // this is the default resize dimension used by i3 for both px and ppt 250 }
354 sway_log(L_DEBUG, "Tried to get resize dimension out of '%s' but failed; setting dimension to default %d", 251 --argc; ++argv;
355 argv[dim_arg], amount); 252
253 int amount = 10; // Default amount
254 enum resize_unit unit = RESIZE_UNIT_DEFAULT;
255
256 if (argc) {
257 char *err;
258 amount = (int)strtol(*argv, &err, 10);
259 if (*err) {
260 // e.g. `resize grow width 10px`
261 unit = parse_resize_unit(err);
262 if (unit == RESIZE_UNIT_INVALID) {
263 return cmd_results_new(CMD_INVALID, "resize", usage);
264 }
265 }
266 --argc; ++argv;
356 } 267 }
357 268
358 bool use_width = false; 269 if (argc) {
359 if (strcasecmp(argv[1], "width") == 0) { 270 unit = parse_resize_unit(*argv);
360 use_width = true; 271 if (unit == RESIZE_UNIT_INVALID) {
361 } else if (strcasecmp(argv[1], "height") != 0) { 272 return cmd_results_new(CMD_INVALID, "resize", usage);
362 return cmd_results_new(CMD_INVALID, "resize", 273 }
363 "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'"); 274 --argc; ++argv;
364 } 275 }
365 276
366 if (strcasecmp(argv[0], "shrink") == 0) { 277 if (argc) {
367 amount *= -1; 278 // Provied too many args, the bastard
368 } else if (strcasecmp(argv[0], "grow") != 0) { 279 return cmd_results_new(CMD_INVALID, "resize", usage);
369 return cmd_results_new(CMD_INVALID, "resize",
370 "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
371 } 280 }
372 281
373 resize(amount, use_width, dim_type); 282 resize(amount * multiplier, axis, unit);
374 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 283 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
375} 284}
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
deleted file mode 100644
index 6c5c92df..00000000
--- a/sway/commands/scratchpad.c
+++ /dev/null
@@ -1,72 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include <wlc/wlc.h>
4#include "sway/commands.h"
5#include "sway/container.h"
6#include "sway/focus.h"
7#include "sway/layout.h"
8
9static swayc_t *fetch_view_from_scratchpad() {
10 sp_index = (sp_index + 1) % scratchpad->length;
11 swayc_t *view = scratchpad->items[sp_index];
12
13 if (wlc_view_get_output(view->handle) != swayc_active_output()->handle) {
14 wlc_view_set_output(view->handle, swayc_active_output()->handle);
15 }
16 if (!view->is_floating) {
17 view->width = swayc_active_workspace()->width * 0.5;
18 view->height = swayc_active_workspace()->height * 0.75;
19 view->x = (swayc_active_workspace()->width - view->width)/2;
20 view->y = (swayc_active_workspace()->height - view->height)/2;
21 }
22 if (swayc_active_workspace()->width < view->x + 20 || view->x + view->width < 20) {
23 view->x = (swayc_active_workspace()->width - view->width)/2;
24 }
25 if (swayc_active_workspace()->height < view->y + 20 || view->y + view->height < 20) {
26 view->y = (swayc_active_workspace()->height - view->height)/2;
27 }
28
29 add_floating(swayc_active_workspace(), view);
30 wlc_view_set_mask(view->handle, VISIBLE);
31 view->visible = true;
32 arrange_windows(swayc_active_workspace(), -1, -1);
33 set_focused_container(view);
34 return view;
35}
36
37struct cmd_results *cmd_scratchpad(int argc, char **argv) {
38 struct cmd_results *error = NULL;
39 if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
40 if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
41 if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
42 return error;
43 }
44
45 if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
46 if (!sp_view) {
47 if (current_container) {
48 // Haxor the scratchpad index if criteria'd
49 for (int i = 0; i < scratchpad->length; ++i) {
50 if (scratchpad->items[i] == current_container) {
51 sp_index = (i - 1) % scratchpad->length;
52 }
53 }
54 }
55 sp_view = fetch_view_from_scratchpad();
56 } else {
57 if (swayc_active_workspace() != sp_view->parent) {
58 hide_view_in_scratchpad(sp_view);
59 if (sp_index == 0) {
60 sp_index = scratchpad->length;
61 }
62 sp_index--;
63 sp_view = fetch_view_from_scratchpad();
64 } else {
65 hide_view_in_scratchpad(sp_view);
66 sp_view = NULL;
67 }
68 }
69 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
70 }
71 return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
72}
diff --git a/sway/commands/seamless_mouse.c b/sway/commands/seamless_mouse.c
deleted file mode 100644
index 7760e88d..00000000
--- a/sway/commands/seamless_mouse.c
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13}
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
new file mode 100644
index 00000000..5916015f
--- /dev/null
+++ b/sway/commands/seat.c
@@ -0,0 +1,61 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/input/input-manager.h"
5#include "log.h"
6
7struct cmd_results *cmd_seat(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 2))) {
10 return error;
11 }
12
13 if (config->reading && strcmp("{", argv[1]) == 0) {
14 free_seat_config(config->handler_context.seat_config);
15 config->handler_context.seat_config = new_seat_config(argv[0]);
16 if (!config->handler_context.seat_config) {
17 return cmd_results_new(CMD_FAILURE, NULL,
18 "Couldn't allocate config");
19 }
20 wlr_log(L_DEBUG, "entering seat block: %s", argv[0]);
21 return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
22 }
23
24 if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 3))) {
25 return error;
26 }
27
28 bool has_context = (config->handler_context.seat_config != NULL);
29 if (!has_context) {
30 config->handler_context.seat_config = new_seat_config(argv[0]);
31 if (!config->handler_context.seat_config) {
32 return cmd_results_new(CMD_FAILURE, NULL,
33 "Couldn't allocate config");
34 }
35 }
36
37 int argc_new = argc-2;
38 char **argv_new = argv+2;
39
40 struct cmd_results *res;
41 if (strcasecmp("attach", argv[1]) == 0) {
42 res = seat_cmd_attach(argc_new, argv_new);
43 } else if (strcasecmp("cursor", argv[1]) == 0) {
44 res = seat_cmd_cursor(argc_new, argv_new);
45 } else if (strcasecmp("fallback", argv[1]) == 0) {
46 res = seat_cmd_fallback(argc_new, argv_new);
47 } else {
48 res =
49 cmd_results_new(CMD_INVALID,
50 "seat <name>", "Unknown command %s",
51 argv[1]);
52 }
53
54 if (!has_context) {
55 // clean up the context we created earlier
56 free_seat_config(config->handler_context.seat_config);
57 config->handler_context.seat_config = NULL;
58 }
59
60 return res;
61}
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
new file mode 100644
index 00000000..3e771c00
--- /dev/null
+++ b/sway/commands/seat/attach.c
@@ -0,0 +1,28 @@
1#define _XOPEN_SOURCE 700
2#include <string.h>
3#include <strings.h>
4#include "sway/input/input-manager.h"
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "log.h"
8#include "stringop.h"
9
10struct cmd_results *seat_cmd_attach(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) {
13 return error;
14 }
15 struct seat_config *current_seat_config =
16 config->handler_context.seat_config;
17 if (!current_seat_config) {
18 return cmd_results_new(CMD_FAILURE, "attach", "No seat defined");
19 }
20
21 struct seat_config *new_config = new_seat_config(current_seat_config->name);
22 struct seat_attachment_config *new_attachment = seat_attachment_config_new();
23 new_attachment->identifier = strdup(argv[0]);
24 list_add(new_config->attachments, new_attachment);
25
26 apply_seat_config(new_config);
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28}
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
new file mode 100644
index 00000000..5dad97f1
--- /dev/null
+++ b/sway/commands/seat/cursor.c
@@ -0,0 +1,89 @@
1#define _XOPEN_SOURCE 700
2#ifdef __linux__
3#include <linux/input-event-codes.h>
4#elif __FreeBSD__
5#include <dev/evdev/input-event-codes.h>
6#endif
7
8#include <strings.h>
9#include <wlr/types/wlr_cursor.h>
10#include "sway/commands.h"
11#include "sway/input/cursor.h"
12
13static struct cmd_results *press_or_release(struct sway_cursor *cursor,
14 char *action, char *button_str, uint32_t time);
15
16static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or "
17 "'cursor <set> <x> <y>' or "
18 "'curor <press|release> <left|right|1|2|3...>'";
19
20struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
21 struct cmd_results *error = NULL;
22 if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) {
23 return error;
24 }
25 struct sway_seat *seat = config->handler_context.seat;
26 if (!seat) {
27 return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined");
28 }
29
30 struct sway_cursor *cursor = seat->cursor;
31
32 struct timespec now;
33 clock_gettime(CLOCK_MONOTONIC, &now);
34 uint32_t time = now.tv_nsec / 1000;
35
36 if (strcasecmp(argv[0], "move") == 0) {
37 if (argc < 3) {
38 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
39 }
40 int delta_x = strtol(argv[1], NULL, 10);
41 int delta_y = strtol(argv[2], NULL, 10);
42 wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y);
43 cursor_send_pointer_motion(cursor, time);
44 } else if (strcasecmp(argv[0], "set") == 0) {
45 if (argc < 3) {
46 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
47 }
48 // map absolute coords (0..1,0..1) to root container coords
49 float x = strtof(argv[1], NULL) / root_container.width;
50 float y = strtof(argv[2], NULL) / root_container.height;
51 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
52 cursor_send_pointer_motion(cursor, time);
53 } else {
54 if (argc < 2) {
55 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
56 }
57 if ((error = press_or_release(cursor, argv[0], argv[1], time))) {
58 return error;
59 }
60 }
61
62 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
63}
64
65static struct cmd_results *press_or_release(struct sway_cursor *cursor,
66 char *action, char *button_str, uint32_t time) {
67 enum wlr_button_state state;
68 uint32_t button;
69 if (strcasecmp(action, "press") == 0) {
70 state = WLR_BUTTON_PRESSED;
71 } else if (strcasecmp(action, "release") == 0) {
72 state = WLR_BUTTON_RELEASED;
73 } else {
74 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
75 }
76
77 if (strcasecmp(button_str, "left") == 0) {
78 button = BTN_LEFT;
79 } else if (strcasecmp(button_str, "right") == 0) {
80 button = BTN_RIGHT;
81 } else {
82 button = strtol(button_str, NULL, 10);
83 if (button == 0) {
84 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
85 }
86 }
87 dispatch_cursor_button(cursor, time, button, state);
88 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
89}
diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c
new file mode 100644
index 00000000..56feaab5
--- /dev/null
+++ b/sway/commands/seat/fallback.c
@@ -0,0 +1,32 @@
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
7struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "fallback", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12 struct seat_config *current_seat_config =
13 config->handler_context.seat_config;
14 if (!current_seat_config) {
15 return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined");
16 }
17 struct seat_config *new_config =
18 new_seat_config(current_seat_config->name);
19
20 if (strcasecmp(argv[0], "true") == 0) {
21 new_config->fallback = 1;
22 } else if (strcasecmp(argv[0], "false") == 0) {
23 new_config->fallback = 0;
24 } else {
25 free_seat_config(new_config);
26 return cmd_results_new(CMD_INVALID, "fallback",
27 "Expected 'fallback <true|false>'");
28 }
29
30 apply_seat_config(new_config);
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32}
diff --git a/sway/commands/set.c b/sway/commands/set.c
index 46fc6d38..84e9b792 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -5,6 +5,7 @@
5#include "sway/commands.h" 5#include "sway/commands.h"
6#include "sway/config.h" 6#include "sway/config.h"
7#include "list.h" 7#include "list.h"
8#include "log.h"
8#include "stringop.h" 9#include "stringop.h"
9 10
10// sort in order of longest->shortest 11// sort in order of longest->shortest
@@ -14,16 +15,24 @@ static int compare_set_qsort(const void *_l, const void *_r) {
14 return strlen(r->name) - strlen(l->name); 15 return strlen(r->name) - strlen(l->name);
15} 16}
16 17
18void free_sway_variable(struct sway_variable *var) {
19 if (!var) {
20 return;
21 }
22 free(var->name);
23 free(var->value);
24 free(var);
25}
26
17struct cmd_results *cmd_set(int argc, char **argv) { 27struct cmd_results *cmd_set(int argc, char **argv) {
18 char *tmp; 28 char *tmp;
19 struct cmd_results *error = NULL; 29 struct cmd_results *error = NULL;
20 if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
21 if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) { 30 if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
22 return error; 31 return error;
23 } 32 }
24 33
25 if (argv[0][0] != '$') { 34 if (argv[0][0] != '$') {
26 sway_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); 35 wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);
27 36
28 size_t size = snprintf(NULL, 0, "$%s", argv[0]); 37 size_t size = snprintf(NULL, 0, "$%s", argv[0]);
29 tmp = malloc(size + 1); 38 tmp = malloc(size + 1);
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
deleted file mode 100644
index ed56d9e5..00000000
--- a/sway/commands/show_marks.c
+++ /dev/null
@@ -1,13 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_show_marks(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "show_marks", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 config->show_marks = !strcasecmp(argv[0], "on");
12 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
13}
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
deleted file mode 100644
index 815fc501..00000000
--- a/sway/commands/smart_gaps.c
+++ /dev/null
@@ -1,20 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "smart_gaps", EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10
11 if (strcasecmp(argv[0], "on") == 0) {
12 config->smart_gaps = true;
13 } else if (strcasecmp(argv[0], "off") == 0) {
14 config->smart_gaps = false;
15 } else {
16 return cmd_results_new(CMD_INVALID, "smart_gaps", "Expected 'smart_gaps <on|off>'");
17 }
18
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index e3045a4f..130ed31f 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -1,76 +1,41 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include <wlc/wlc-render.h>
4#include "sway/border.h"
5#include "sway/commands.h" 3#include "sway/commands.h"
6#include "sway/container.h" 4#include "sway/tree/container.h"
7#include "sway/focus.h" 5#include "sway/tree/layout.h"
8#include "sway/layout.h" 6#include "sway/tree/view.h"
7#include "sway/input/input-manager.h"
8#include "sway/input/seat.h"
9#include "log.h" 9#include "log.h"
10 10
11static struct cmd_results *_do_split(int argc, char **argv, int layout) { 11static struct cmd_results *do_split(int layout) {
12 char *name = layout == L_VERT ? "splitv" : 12 struct sway_container *con = config->handler_context.current_container;
13 layout == L_HORIZ ? "splith" : "split"; 13 struct sway_container *parent = container_split(con, layout);
14 struct cmd_results *error = NULL; 14 container_create_notify(parent);
15 if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file."); 15 arrange_windows(parent, -1, -1);
16 if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
17 if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
18 return error;
19 }
20 swayc_t *focused = current_container;
21
22 // Case of floating window, don't split
23 if (focused->is_floating) {
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25 }
26 /* Case that focus is on an workspace with 0/1 children.change its layout */
27 if (focused->type == C_WORKSPACE && focused->children->length <= 1) {
28 sway_log(L_DEBUG, "changing workspace layout");
29 swayc_change_layout(focused, layout);
30 } else if (focused->type != C_WORKSPACE && focused->parent->children->length == 1) {
31 /* Case of no siblings. change parent layout */
32 sway_log(L_DEBUG, "changing container layout");
33 swayc_change_layout(focused->parent, layout);
34 } else {
35 /* regular case where new split container is build around focused container
36 * or in case of workspace, container inherits its children */
37 sway_log(L_DEBUG, "Adding new container around current focused container");
38 sway_log(L_INFO, "FOCUSED SIZE: %.f %.f", focused->width, focused->height);
39 swayc_t *parent = new_container(focused, layout);
40 set_focused_container(focused);
41 arrange_windows(parent, -1, -1);
42 }
43
44 // update container every time
45 // if it is tabbed/stacked then the title must change
46 // if the indicator color is different then the border must change
47 update_container_border(focused);
48 swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
49 // schedule render to make changes take effect right away,
50 // otherwise we would have to wait for the view to render,
51 // which is unpredictable.
52 wlc_output_schedule_render(output->handle);
53 16
54 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 17 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
55} 18}
56 19
57struct cmd_results *cmd_split(int argc, char **argv) { 20struct cmd_results *cmd_split(int argc, char **argv) {
58 struct cmd_results *error = NULL; 21 struct cmd_results *error = NULL;
59 if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
60 if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
61 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { 22 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
62 return error; 23 return error;
63 } 24 }
64 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { 25 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
65 _do_split(argc - 1, argv + 1, L_VERT); 26 do_split(L_VERT);
66 } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { 27 } else if (strcasecmp(argv[0], "h") == 0 ||
67 _do_split(argc - 1, argv + 1, L_HORIZ); 28 strcasecmp(argv[0], "horizontal") == 0) {
68 } else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) { 29 do_split(L_HORIZ);
69 swayc_t *focused = current_container; 30 } else if (strcasecmp(argv[0], "t") == 0 ||
31 strcasecmp(argv[0], "toggle") == 0) {
32 struct sway_container *focused =
33 config->handler_context.current_container;
34
70 if (focused->parent->layout == L_VERT) { 35 if (focused->parent->layout == L_VERT) {
71 _do_split(argc - 1, argv + 1, L_HORIZ); 36 do_split(L_HORIZ);
72 } else { 37 } else {
73 _do_split(argc - 1, argv + 1, L_VERT); 38 do_split(L_VERT);
74 } 39 }
75 } else { 40 } else {
76 error = cmd_results_new(CMD_FAILURE, "split", 41 error = cmd_results_new(CMD_FAILURE, "split",
@@ -81,18 +46,32 @@ struct cmd_results *cmd_split(int argc, char **argv) {
81} 46}
82 47
83struct cmd_results *cmd_splitv(int argc, char **argv) { 48struct cmd_results *cmd_splitv(int argc, char **argv) {
84 return _do_split(argc, argv, L_VERT); 49 struct cmd_results *error = NULL;
50 if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
51 return error;
52 }
53 return do_split(L_VERT);
85} 54}
86 55
87struct cmd_results *cmd_splith(int argc, char **argv) { 56struct cmd_results *cmd_splith(int argc, char **argv) {
88 return _do_split(argc, argv, L_HORIZ); 57 struct cmd_results *error = NULL;
58 if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
59 return error;
60 }
61 return do_split(L_HORIZ);
89} 62}
90 63
91struct cmd_results *cmd_splitt(int argc, char **argv) { 64struct cmd_results *cmd_splitt(int argc, char **argv) {
92 swayc_t *focused = current_container; 65 struct cmd_results *error = NULL;
93 if (focused->parent->layout == L_VERT) { 66 if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
94 return _do_split(argc, argv, L_HORIZ); 67 return error;
68 }
69
70 struct sway_container *con = config->handler_context.current_container;
71
72 if (con->parent->layout == L_VERT) {
73 return do_split(L_HORIZ);
95 } else { 74 } else {
96 return _do_split(argc, argv, L_VERT); 75 return do_split(L_VERT);
97 } 76 }
98} 77}
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
deleted file mode 100644
index 4899e061..00000000
--- a/sway/commands/sticky.c
+++ /dev/null
@@ -1,25 +0,0 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/focus.h"
4
5struct cmd_results *cmd_sticky(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if (config->reading) return cmd_results_new(CMD_FAILURE, "sticky", "Can't be used in config file.");
8 if (!config->active) return cmd_results_new(CMD_FAILURE, "sticky", "Can only be used when sway is running.");
9 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 char *action = argv[0];
13 swayc_t *cont = get_focused_view(&root_container);
14 if (strcmp(action, "toggle") == 0) {
15 cont->sticky = !cont->sticky;
16 } else if (strcmp(action, "enable") == 0) {
17 cont->sticky = true;
18 } else if (strcmp(action, "disable") == 0) {
19 cont->sticky = false;
20 } else {
21 return cmd_results_new(CMD_FAILURE, "sticky",
22 "Expected 'sticky enable|disable|toggle'");
23 }
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25}
diff --git a/sway/commands/swaybg_command.c b/sway/commands/swaybg_command.c
new file mode 100644
index 00000000..770d4821
--- /dev/null
+++ b/sway/commands/swaybg_command.c
@@ -0,0 +1,20 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *cmd_swaybg_command(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "swaybg_command", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11
12 if (config->swaybg_command) {
13 free(config->swaybg_command);
14 }
15 config->swaybg_command = join_args(argv, argc);
16 wlr_log(L_DEBUG, "Using custom swaybg command: %s",
17 config->swaybg_command);
18
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
deleted file mode 100644
index ac213261..00000000
--- a/sway/commands/unmark.c
+++ /dev/null
@@ -1,31 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "list.h"
5#include "stringop.h"
6
7struct cmd_results *cmd_unmark(int argc, char **argv) {
8 swayc_t *view = current_container;
9
10 if (view->marks) {
11 if (argc) {
12 char *mark = join_args(argv, argc);
13 int index;
14 if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
15 free(view->marks->items[index]);
16 list_del(view->marks, index);
17
18 if (view->marks->length == 0) {
19 list_free(view->marks);
20 view->marks = NULL;
21 }
22 }
23 free(mark);
24 } else {
25 list_foreach(view->marks, free);
26 list_free(view->marks);
27 view->marks = NULL;
28 }
29 }
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31}
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index a7839746..958b3222 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -3,8 +3,8 @@
3#include <strings.h> 3#include <strings.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h" 5#include "sway/config.h"
6#include "sway/input_state.h" 6#include "sway/input/seat.h"
7#include "sway/workspace.h" 7#include "sway/tree/workspace.h"
8#include "list.h" 8#include "list.h"
9#include "log.h" 9#include "log.h"
10#include "stringop.h" 10#include "stringop.h"
@@ -17,6 +17,17 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
17 17
18 int output_location = -1; 18 int output_location = -1;
19 19
20 struct sway_container *current_container = config->handler_context.current_container;
21 struct sway_container *old_workspace = NULL, *old_output = NULL;
22 if (current_container) {
23 if (current_container->type == C_WORKSPACE) {
24 old_workspace = current_container;
25 } else {
26 old_workspace = container_parent(current_container, C_WORKSPACE);
27 }
28 old_output = container_parent(current_container, C_OUTPUT);
29 }
30
20 for (int i = 0; i < argc; ++i) { 31 for (int i = 0; i < argc; ++i) {
21 if (strcasecmp(argv[i], "output") == 0) { 32 if (strcasecmp(argv[i], "output") == 0) {
22 output_location = i; 33 output_location = i;
@@ -40,52 +51,51 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
40 free(old); // workspaces can only be assigned to a single output 51 free(old); // workspaces can only be assigned to a single output
41 list_del(config->workspace_outputs, i); 52 list_del(config->workspace_outputs, i);
42 } 53 }
43 sway_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); 54 wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
44 list_add(config->workspace_outputs, wso); 55 list_add(config->workspace_outputs, wso);
45 } else { 56 } else {
46 if (config->reading || !config->active) { 57 if (config->reading || !config->active) {
47 return cmd_results_new(CMD_DEFER, "workspace", NULL); 58 return cmd_results_new(CMD_DEFER, "workspace", NULL);
48 } 59 }
49 swayc_t *ws = NULL; 60 struct sway_container *ws = NULL;
50 if (strcasecmp(argv[0], "number") == 0) { 61 if (strcasecmp(argv[0], "number") == 0) {
51 if (!(ws = workspace_by_number(argv[1]))) { 62 if (!(ws = workspace_by_number(argv[1]))) {
52 char *name = join_args(argv + 1, argc - 1); 63 char *name = join_args(argv + 1, argc - 1);
53 ws = workspace_create(name); 64 ws = workspace_create(NULL, name);
54 free(name); 65 free(name);
55 } 66 }
56 } else if (strcasecmp(argv[0], "next") == 0) { 67 } else if (strcasecmp(argv[0], "next") == 0) {
57 ws = workspace_next(); 68 ws = workspace_next(old_workspace);
58 } else if (strcasecmp(argv[0], "prev") == 0) { 69 } else if (strcasecmp(argv[0], "prev") == 0) {
59 ws = workspace_prev(); 70 ws = workspace_prev(old_workspace);
60 } else if (strcasecmp(argv[0], "next_on_output") == 0) { 71 } else if (strcasecmp(argv[0], "next_on_output") == 0) {
61 ws = workspace_output_next(); 72 ws = workspace_output_next(old_output);
62 } else if (strcasecmp(argv[0], "prev_on_output") == 0) { 73 } else if (strcasecmp(argv[0], "prev_on_output") == 0) {
63 ws = workspace_output_prev(); 74 ws = workspace_output_prev(old_output);
64 } else if (strcasecmp(argv[0], "back_and_forth") == 0) { 75 } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
65 // if auto_back_and_forth is enabled, workspace_switch will swap 76 // if auto_back_and_forth is enabled, workspace_switch will swap
66 // the workspaces. If we created prev_workspace here, workspace_switch 77 // the workspaces. If we created prev_workspace here, workspace_switch
67 // would put us back on original workspace. 78 // would put us back on original workspace.
68 if (config->auto_back_and_forth) { 79 if (config->auto_back_and_forth) {
69 ws = swayc_active_workspace(); 80 ws = old_workspace;
70 } else if (prev_workspace_name && !(ws = workspace_by_name(prev_workspace_name))) { 81 } else if (prev_workspace_name
71 ws = workspace_create(prev_workspace_name); 82 && !(ws = workspace_by_name(prev_workspace_name))) {
83 ws = workspace_create(NULL, prev_workspace_name);
72 } 84 }
73 } else { 85 } else {
74 char *name = join_args(argv, argc); 86 char *name = join_args(argv, argc);
75 if (!(ws = workspace_by_name(name))) { 87 if (!(ws = workspace_by_name(name))) {
76 ws = workspace_create(name); 88 ws = workspace_create(NULL, name);
77 } 89 }
78 free(name); 90 free(name);
79 } 91 }
80 swayc_t *old_output = swayc_active_output();
81 workspace_switch(ws); 92 workspace_switch(ws);
82 swayc_t *new_output = swayc_active_output(); 93 current_container =
94 seat_get_focus(config->handler_context.seat);
95 struct sway_container *new_output = container_parent(current_container, C_OUTPUT);
83 96
84 if (config->mouse_warping && old_output != new_output) { 97 if (config->mouse_warping && old_output != new_output) {
85 swayc_t *focused = get_focused_view(ws); 98 // TODO: Warp mouse
86 if (focused && focused->type == C_VIEW) {
87 center_pointer_on(focused);
88 }
89 } 99 }
90 } 100 }
91 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 101 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/workspace_layout.c b/sway/commands/workspace_layout.c
deleted file mode 100644
index 9ac84be2..00000000
--- a/sway/commands/workspace_layout.c
+++ /dev/null
@@ -1,40 +0,0 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4
5struct cmd_results *cmd_workspace_layout(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "workspace_layout", EXPECTED_AT_LEAST, 1))) {
8 return error;
9 }
10
11 if (strcasecmp(argv[0], "default") == 0) {
12 config->default_layout = L_NONE;
13 } else if (strcasecmp(argv[0], "stacking") == 0) {
14 config->default_layout = L_STACKED;
15 } else if (strcasecmp(argv[0], "tabbed") == 0) {
16 config->default_layout = L_TABBED;
17 } else if (strcasecmp(argv[0], "auto") == 0) {
18 if (argc == 1) {
19 config->default_layout = L_AUTO_FIRST;
20 } else {
21 if ((error = checkarg(argc, "workspace_layout auto", EXPECTED_EQUAL_TO, 2))) {
22 return error;
23 }
24 if (strcasecmp(argv[1], "left") == 0) {
25 config->default_layout = L_AUTO_LEFT;
26 } else if (strcasecmp(argv[1], "right") == 0) {
27 config->default_layout = L_AUTO_RIGHT;
28 } else if (strcasecmp(argv[1], "top") == 0) {
29 config->default_layout = L_AUTO_TOP;
30 } else if (strcasecmp(argv[1], "bottom") == 0) {
31 config->default_layout = L_AUTO_BOTTOM;
32 } else {
33 return cmd_results_new(CMD_INVALID, "workspace_layout auto", "Expected 'workspace_layout auto <left|right|top|bottom>'");
34 }
35 }
36 } else {
37 return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed|auto|auto left|auto right|auto top|auto bottom>'");
38 }
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40}
diff --git a/sway/commands/workspace_auto_back_and_forth.c b/sway/commands/ws_auto_back_and_forth.c
index d58ae5c8..2485db35 100644
--- a/sway/commands/workspace_auto_back_and_forth.c
+++ b/sway/commands/ws_auto_back_and_forth.c
@@ -7,12 +7,6 @@ struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) {
7 if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) { 7 if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
8 return error; 8 return error;
9 } 9 }
10 if (strcasecmp(argv[0], "yes") == 0) { 10 config->auto_back_and_forth = !strcasecmp(argv[0], "yes");
11 config->auto_back_and_forth = true;
12 } else if (strcasecmp(argv[0], "no") == 0) {
13 config->auto_back_and_forth = false;
14 } else {
15 return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
16 }
17 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 11 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
18} 12}
diff --git a/sway/config.c b/sway/config.c
index a33b8edc..90b833ab 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -12,17 +12,19 @@
12#include <signal.h> 12#include <signal.h>
13#include <libinput.h> 13#include <libinput.h>
14#include <limits.h> 14#include <limits.h>
15#include <float.h>
16#include <dirent.h> 15#include <dirent.h>
17#include <strings.h> 16#include <strings.h>
18#include "wayland-desktop-shell-server-protocol.h" 17#ifdef __linux__
18#include <linux/input-event-codes.h>
19#elif __FreeBSD__
20#include <dev/evdev/input-event-codes.h>
21#endif
22#include <wlr/types/wlr_output.h>
23#include "sway/input/input-manager.h"
24#include "sway/input/seat.h"
19#include "sway/commands.h" 25#include "sway/commands.h"
20#include "sway/config.h" 26#include "sway/config.h"
21#include "sway/layout.h" 27#include "sway/tree/layout.h"
22#include "sway/input_state.h"
23#include "sway/criteria.h"
24#include "sway/input.h"
25#include "sway/border.h"
26#include "readline.h" 28#include "readline.h"
27#include "stringop.h" 29#include "stringop.h"
28#include "list.h" 30#include "list.h"
@@ -30,269 +32,102 @@
30 32
31struct sway_config *config = NULL; 33struct sway_config *config = NULL;
32 34
33static void terminate_swaybar(pid_t pid);
34
35static void free_variable(struct sway_variable *var) {
36 if (!var) {
37 return;
38 }
39 free(var->name);
40 free(var->value);
41 free(var);
42}
43
44static void free_binding(struct sway_binding *bind) {
45 if (!bind) {
46 return;
47 }
48 free_flat_list(bind->keys);
49 free(bind->command);
50 free(bind);
51}
52
53static void free_mode(struct sway_mode *mode) { 35static void free_mode(struct sway_mode *mode) {
54 if (!mode) {
55 return;
56 }
57 free(mode->name);
58 int i;
59 for (i = 0; mode->bindings && i < mode->bindings->length; ++i) {
60 free_binding(mode->bindings->items[i]);
61 }
62 list_free(mode->bindings);
63 free(mode);
64}
65
66static void free_bar(struct bar_config *bar) {
67 if (!bar) {
68 return;
69 }
70 free(bar->mode);
71 free(bar->hidden_state);
72#ifdef ENABLE_TRAY
73 free(bar->tray_output);
74 free(bar->icon_theme);
75#endif
76 free(bar->status_command);
77 free(bar->font);
78 free(bar->separator_symbol);
79 int i; 36 int i;
80 for (i = 0; bar->bindings && i < bar->bindings->length; ++i) {
81 free_sway_mouse_binding(bar->bindings->items[i]);
82 }
83 list_free(bar->bindings);
84
85 if (bar->outputs) {
86 free_flat_list(bar->outputs);
87 }
88
89 if (bar->pid != 0) {
90 terminate_swaybar(bar->pid);
91 }
92
93 free(bar->colors.background);
94 free(bar->colors.statusline);
95 free(bar->colors.separator);
96 free(bar->colors.focused_background);
97 free(bar->colors.focused_statusline);
98 free(bar->colors.focused_separator);
99 free(bar->colors.focused_workspace_border);
100 free(bar->colors.focused_workspace_bg);
101 free(bar->colors.focused_workspace_text);
102 free(bar->colors.active_workspace_border);
103 free(bar->colors.active_workspace_bg);
104 free(bar->colors.active_workspace_text);
105 free(bar->colors.inactive_workspace_border);
106 free(bar->colors.inactive_workspace_bg);
107 free(bar->colors.inactive_workspace_text);
108 free(bar->colors.urgent_workspace_border);
109 free(bar->colors.urgent_workspace_bg);
110 free(bar->colors.urgent_workspace_text);
111 free(bar->colors.binding_mode_border);
112 free(bar->colors.binding_mode_bg);
113 free(bar->colors.binding_mode_text);
114
115 free(bar);
116}
117 37
118void free_input_config(struct input_config *ic) { 38 if (!mode) {
119 if (!ic) {
120 return;
121 }
122 free(ic->identifier);
123 free(ic);
124}
125
126void free_output_config(struct output_config *oc) {
127 if (!oc) {
128 return;
129 }
130 free(oc->name);
131 free(oc->background);
132 free(oc->background_option);
133 free(oc);
134}
135
136static void free_workspace_output(struct workspace_output *wo) {
137 if (!wo) {
138 return; 39 return;
139 } 40 }
140 free(wo->output); 41 free(mode->name);
141 free(wo->workspace); 42 if (mode->keysym_bindings) {
142 free(wo); 43 for (i = 0; i < mode->keysym_bindings->length; i++) {
143} 44 free_sway_binding(mode->keysym_bindings->items[i]);
144
145static void pid_workspace_cleanup() {
146 struct timespec ts;
147 struct pid_workspace *pw = NULL;
148
149 clock_gettime(CLOCK_MONOTONIC, &ts);
150
151 // work backwards through list and remove any entries
152 // older than PID_WORKSPACE_TIMEOUT
153 for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
154 pw = config->pid_workspaces->items[i];
155
156 if (difftime(ts.tv_sec, *pw->time_added) >= PID_WORKSPACE_TIMEOUT) {
157 free_pid_workspace(config->pid_workspaces->items[i]);
158 list_del(config->pid_workspaces, i);
159 } 45 }
46 list_free(mode->keysym_bindings);
160 } 47 }
161} 48 if (mode->keycode_bindings) {
162 49 for (i = 0; i < mode->keycode_bindings->length; i++) {
163// de-dupe pid_workspaces to ensure pid uniqueness 50 free_sway_binding(mode->keycode_bindings->items[i]);
164void pid_workspace_add(struct pid_workspace *pw) {
165 struct pid_workspace *list_pw = NULL;
166 struct timespec ts;
167 time_t *now = malloc(sizeof(time_t));
168 if (!now) {
169 sway_log(L_ERROR, "Allocating time for pid_workspace failed");
170 return;
171 }
172
173 pid_workspace_cleanup();
174
175 // add current time to pw
176 clock_gettime(CLOCK_MONOTONIC, &ts);
177 *now = ts.tv_sec;
178
179 pw->time_added = now;
180
181 // work backwards through list and delete any entries that
182 // have the same pid as that in our new pid_workspace
183 for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
184 list_pw = config->pid_workspaces->items[i];
185
186 if (pw->pid == list_pw->pid) {
187 free_pid_workspace(config->pid_workspaces->items[i]);
188 list_del(config->pid_workspaces, i);
189 } 51 }
52 list_free(mode->keycode_bindings);
190 } 53 }
191 54 free(mode);
192 list_add(config->pid_workspaces, pw);
193}
194
195void free_pid_workspace(struct pid_workspace *pw) {
196 if (!pw) {
197 return;
198 }
199 free(pw->pid);
200 free(pw->workspace);
201 free(pw->time_added);
202 free(pw);
203}
204
205void free_command_policy(struct command_policy *policy) {
206 if (!policy) {
207 return;
208 }
209 free(policy->command);
210 free(policy);
211}
212
213void free_feature_policy(struct feature_policy *policy) {
214 if (!policy) {
215 return;
216 }
217 free(policy->program);
218 free(policy);
219} 55}
220 56
221void free_config(struct sway_config *config) { 57void free_config(struct sway_config *config) {
58 config_clear_handler_context(config);
59
222 if (!config) { 60 if (!config) {
223 return; 61 return;
224 } 62 }
225 int i;
226 for (i = 0; config->symbols && i < config->symbols->length; ++i) {
227 free_variable(config->symbols->items[i]);
228 }
229 list_free(config->symbols);
230 63
231 for (i = 0; config->modes && i < config->modes->length; ++i) { 64 // TODO: handle all currently unhandled lists as we add implementations
232 free_mode(config->modes->items[i]); 65 if (config->symbols) {
66 for (int i = 0; i < config->symbols->length; ++i) {
67 free_sway_variable(config->symbols->items[i]);
68 }
69 list_free(config->symbols);
233 } 70 }
234 list_free(config->modes); 71 if (config->modes) {
235 72 for (int i = 0; i < config->modes->length; ++i) {
236 for (i = 0; config->bars && i < config->bars->length; ++i) { 73 free_mode(config->modes->items[i]);
237 free_bar(config->bars->items[i]); 74 }
75 list_free(config->modes);
238 } 76 }
239 list_free(config->bars); 77 if (config->bars) {
240 78 for (int i = 0; i < config->bars->length; ++i) {
241 free_flat_list(config->cmd_queue); 79 free_bar_config(config->bars->items[i]);
242 80 }
243 for (i = 0; config->workspace_outputs && i < config->workspace_outputs->length; ++i) { 81 list_free(config->bars);
244 free_workspace_output(config->workspace_outputs->items[i]);
245 } 82 }
83 list_free(config->cmd_queue);
246 list_free(config->workspace_outputs); 84 list_free(config->workspace_outputs);
247
248 for (i = 0; config->pid_workspaces && i < config->pid_workspaces->length; ++i) {
249 free_pid_workspace(config->pid_workspaces->items[i]);
250 }
251 list_free(config->pid_workspaces); 85 list_free(config->pid_workspaces);
252 86 list_free(config->output_configs);
253 for (i = 0; config->criteria && i < config->criteria->length; ++i) { 87 if (config->input_configs) {
254 free_criteria(config->criteria->items[i]); 88 for (int i = 0; i < config->input_configs->length; i++) {
89 free_input_config(config->input_configs->items[i]);
90 }
91 list_free(config->input_configs);
255 } 92 }
256 list_free(config->criteria); 93 if (config->seat_configs) {
257 94 for (int i = 0; i < config->seat_configs->length; i++) {
258 for (i = 0; config->no_focus && i < config->no_focus->length; ++i) { 95 free_seat_config(config->seat_configs->items[i]);
259 free_criteria(config->no_focus->items[i]); 96 }
97 list_free(config->seat_configs);
260 } 98 }
99 list_free(config->criteria);
261 list_free(config->no_focus); 100 list_free(config->no_focus);
262 101 list_free(config->active_bar_modifiers);
263 for (i = 0; config->input_configs && i < config->input_configs->length; ++i) { 102 list_free(config->config_chain);
264 free_input_config(config->input_configs->items[i]);
265 }
266 list_free(config->input_configs);
267
268 for (i = 0; config->output_configs && i < config->output_configs->length; ++i) {
269 free_output_config(config->output_configs->items[i]);
270 }
271 list_free(config->output_configs);
272
273 for (i = 0; config->command_policies && i < config->command_policies->length; ++i) {
274 free_command_policy(config->command_policies->items[i]);
275 }
276 list_free(config->command_policies); 103 list_free(config->command_policies);
277
278 for (i = 0; config->feature_policies && i < config->feature_policies->length; ++i) {
279 free_feature_policy(config->feature_policies->items[i]);
280 }
281 list_free(config->feature_policies); 104 list_free(config->feature_policies);
282 105 list_free(config->ipc_policies);
283 list_free(config->active_bar_modifiers); 106 free(config->current_bar);
284 free_flat_list(config->config_chain);
285 free(config->font);
286 free(config->floating_scroll_up_cmd); 107 free(config->floating_scroll_up_cmd);
287 free(config->floating_scroll_down_cmd); 108 free(config->floating_scroll_down_cmd);
288 free(config->floating_scroll_left_cmd); 109 free(config->floating_scroll_left_cmd);
289 free(config->floating_scroll_right_cmd); 110 free(config->floating_scroll_right_cmd);
111 free(config->font);
112 free((char *)config->current_config);
290 free(config); 113 free(config);
291} 114}
292 115
293 116static void destroy_removed_seats(struct sway_config *old_config,
294static bool file_exists(const char *path) { 117 struct sway_config *new_config) {
295 return path && access(path, R_OK) != -1; 118 struct seat_config *seat_config;
119 struct sway_seat *seat;
120 int i;
121 for (i = 0; i < old_config->seat_configs->length; i++) {
122 seat_config = old_config->seat_configs->items[i];
123 /* Also destroy seats that aren't present in new config */
124 if (new_config && list_seq_find(new_config->seat_configs,
125 seat_name_cmp, seat_config->name) < 0) {
126 seat = input_manager_get_seat(input_manager,
127 seat_config->name);
128 seat_destroy(seat);
129 }
130 }
296} 131}
297 132
298static void config_defaults(struct sway_config *config) { 133static void config_defaults(struct sway_config *config) {
@@ -304,19 +139,22 @@ static void config_defaults(struct sway_config *config) {
304 if (!(config->criteria = create_list())) goto cleanup; 139 if (!(config->criteria = create_list())) goto cleanup;
305 if (!(config->no_focus = create_list())) goto cleanup; 140 if (!(config->no_focus = create_list())) goto cleanup;
306 if (!(config->input_configs = create_list())) goto cleanup; 141 if (!(config->input_configs = create_list())) goto cleanup;
142 if (!(config->seat_configs = create_list())) goto cleanup;
307 if (!(config->output_configs = create_list())) goto cleanup; 143 if (!(config->output_configs = create_list())) goto cleanup;
308 144
309 if (!(config->cmd_queue = create_list())) goto cleanup; 145 if (!(config->cmd_queue = create_list())) goto cleanup;
310 146
311 if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) goto cleanup; 147 if (!(config->current_mode = malloc(sizeof(struct sway_mode))))
148 goto cleanup;
312 if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup; 149 if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup;
313 strcpy(config->current_mode->name, "default"); 150 strcpy(config->current_mode->name, "default");
314 if (!(config->current_mode->bindings = create_list())) goto cleanup; 151 if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;
152 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
315 list_add(config->modes, config->current_mode); 153 list_add(config->modes, config->current_mode);
316 154
317 config->floating_mod = 0; 155 config->floating_mod = 0;
318 config->dragging_key = M_LEFT_CLICK; 156 config->dragging_key = BTN_LEFT;
319 config->resizing_key = M_RIGHT_CLICK; 157 config->resizing_key = BTN_RIGHT;
320 if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; 158 if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup;
321 if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; 159 if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup;
322 if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; 160 if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup;
@@ -324,7 +162,8 @@ static void config_defaults(struct sway_config *config) {
324 config->default_layout = L_NONE; 162 config->default_layout = L_NONE;
325 config->default_orientation = L_NONE; 163 config->default_orientation = L_NONE;
326 if (!(config->font = strdup("monospace 10"))) goto cleanup; 164 if (!(config->font = strdup("monospace 10"))) goto cleanup;
327 config->font_height = get_font_text_height(config->font); 165 // TODO: border
166 //config->font_height = get_font_text_height(config->font);
328 167
329 // floating view 168 // floating view
330 config->floating_maximum_width = 0; 169 config->floating_maximum_width = 0;
@@ -339,7 +178,6 @@ static void config_defaults(struct sway_config *config) {
339 config->active = false; 178 config->active = false;
340 config->failed = false; 179 config->failed = false;
341 config->auto_back_and_forth = false; 180 config->auto_back_and_forth = false;
342 config->seamless_mouse = true;
343 config->reading = false; 181 config->reading = false;
344 config->show_marks = true; 182 config->show_marks = true;
345 183
@@ -403,29 +241,8 @@ cleanup:
403 sway_abort("Unable to allocate config structures"); 241 sway_abort("Unable to allocate config structures");
404} 242}
405 243
406static int compare_modifiers(const void *left, const void *right) { 244static bool file_exists(const char *path) {
407 uint32_t a = *(uint32_t *)left; 245 return path && access(path, R_OK) != -1;
408 uint32_t b = *(uint32_t *)right;
409
410 return a - b;
411}
412
413void update_active_bar_modifiers() {
414 if (config->active_bar_modifiers->length > 0) {
415 list_free(config->active_bar_modifiers);
416 config->active_bar_modifiers = create_list();
417 }
418
419 struct bar_config *bar;
420 int i;
421 for (i = 0; i < config->bars->length; ++i) {
422 bar = config->bars->items[i];
423 if (strcmp(bar->mode, "hide") == 0 && strcmp(bar->hidden_state, "hide") == 0) {
424 if (list_seq_find(config->active_bar_modifiers, compare_modifiers, &bar->modifier) < 0) {
425 list_add(config->active_bar_modifiers, &bar->modifier);
426 }
427 }
428 }
429} 246}
430 247
431static char *get_config_path(void) { 248static char *get_config_path(void) {
@@ -442,12 +259,12 @@ static char *get_config_path(void) {
442 char *home = getenv("HOME"); 259 char *home = getenv("HOME");
443 char *config_home = malloc(strlen(home) + strlen("/.config") + 1); 260 char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
444 if (!config_home) { 261 if (!config_home) {
445 sway_log(L_ERROR, "Unable to allocate $HOME/.config"); 262 wlr_log(L_ERROR, "Unable to allocate $HOME/.config");
446 } else { 263 } else {
447 strcpy(config_home, home); 264 strcpy(config_home, home);
448 strcat(config_home, "/.config"); 265 strcat(config_home, "/.config");
449 setenv("XDG_CONFIG_HOME", config_home, 1); 266 setenv("XDG_CONFIG_HOME", config_home, 1);
450 sway_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); 267 wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
451 free(config_home); 268 free(config_home);
452 } 269 }
453 } 270 }
@@ -463,6 +280,7 @@ static char *get_config_path(void) {
463 if (file_exists(path)) { 280 if (file_exists(path)) {
464 return path; 281 return path;
465 } 282 }
283 free(path);
466 } 284 }
467 } 285 }
468 286
@@ -472,7 +290,7 @@ static char *get_config_path(void) {
472const char *current_config_path; 290const char *current_config_path;
473 291
474static bool load_config(const char *path, struct sway_config *config) { 292static bool load_config(const char *path, struct sway_config *config) {
475 sway_log(L_INFO, "Loading config from %s", path); 293 wlr_log(L_INFO, "Loading config from %s", path);
476 current_config_path = path; 294 current_config_path = path;
477 295
478 struct stat sb; 296 struct stat sb;
@@ -481,13 +299,13 @@ static bool load_config(const char *path, struct sway_config *config) {
481 } 299 }
482 300
483 if (path == NULL) { 301 if (path == NULL) {
484 sway_log(L_ERROR, "Unable to find a config file!"); 302 wlr_log(L_ERROR, "Unable to find a config file!");
485 return false; 303 return false;
486 } 304 }
487 305
488 FILE *f = fopen(path, "r"); 306 FILE *f = fopen(path, "r");
489 if (!f) { 307 if (!f) {
490 sway_log(L_ERROR, "Unable to open %s for reading", path); 308 wlr_log(L_ERROR, "Unable to open %s for reading", path);
491 return false; 309 return false;
492 } 310 }
493 311
@@ -495,20 +313,14 @@ static bool load_config(const char *path, struct sway_config *config) {
495 fclose(f); 313 fclose(f);
496 314
497 if (!config_load_success) { 315 if (!config_load_success) {
498 sway_log(L_ERROR, "Error(s) loading config!"); 316 wlr_log(L_ERROR, "Error(s) loading config!");
499 } 317 }
500 318
501 current_config_path = NULL; 319 current_config_path = NULL;
502 return true; 320 return true;
503} 321}
504 322
505static int qstrcmp(const void* a, const void* b) {
506 return strcmp(*((char**) a), *((char**) b));
507}
508
509bool load_main_config(const char *file, bool is_active) { 323bool load_main_config(const char *file, bool is_active) {
510 input_init();
511
512 char *path; 324 char *path;
513 if (file != NULL) { 325 if (file != NULL) {
514 path = strdup(file); 326 path = strdup(file);
@@ -524,7 +336,7 @@ bool load_main_config(const char *file, bool is_active) {
524 336
525 config_defaults(config); 337 config_defaults(config);
526 if (is_active) { 338 if (is_active) {
527 sway_log(L_DEBUG, "Performing configuration file reload"); 339 wlr_log(L_DEBUG, "Performing configuration file reload");
528 config->reloading = true; 340 config->reloading = true;
529 config->active = true; 341 config->active = true;
530 } 342 }
@@ -535,11 +347,14 @@ bool load_main_config(const char *file, bool is_active) {
535 config->reading = true; 347 config->reading = true;
536 348
537 // Read security configs 349 // Read security configs
350 // TODO: Security
538 bool success = true; 351 bool success = true;
352 /*
539 DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); 353 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
540 if (!dir) { 354 if (!dir) {
541 sway_log(L_ERROR, "%s does not exist, sway will have no security configuration" 355 wlr_log(L_ERROR,
542 " and will probably be broken", SYSCONFDIR "/sway/security.d"); 356 "%s does not exist, sway will have no security configuration"
357 " and will probably be broken", SYSCONFDIR "/sway/security.d");
543 } else { 358 } else {
544 list_t *secconfigs = create_list(); 359 list_t *secconfigs = create_list();
545 char *base = SYSCONFDIR "/sway/security.d/"; 360 char *base = SYSCONFDIR "/sway/security.d/";
@@ -563,8 +378,12 @@ bool load_main_config(const char *file, bool is_active) {
563 list_qsort(secconfigs, qstrcmp); 378 list_qsort(secconfigs, qstrcmp);
564 for (int i = 0; i < secconfigs->length; ++i) { 379 for (int i = 0; i < secconfigs->length; ++i) {
565 char *_path = secconfigs->items[i]; 380 char *_path = secconfigs->items[i];
566 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) { 381 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
567 sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644 or 444", _path); 382 (((s.st_mode & 0777) != 0644) &&
383 (s.st_mode & 0777) != 0444)) {
384 wlr_log(L_ERROR,
385 "Refusing to load %s - it must be owned by root "
386 "and mode 644 or 444", _path);
568 success = false; 387 success = false;
569 } else { 388 } else {
570 success = success && load_config(_path, config); 389 success = success && load_config(_path, config);
@@ -573,6 +392,7 @@ bool load_main_config(const char *file, bool is_active) {
573 392
574 free_flat_list(secconfigs); 393 free_flat_list(secconfigs);
575 } 394 }
395 */
576 396
577 success = success && load_config(path, config); 397 success = success && load_config(path, config);
578 398
@@ -581,18 +401,15 @@ bool load_main_config(const char *file, bool is_active) {
581 } 401 }
582 402
583 if (old_config) { 403 if (old_config) {
404 destroy_removed_seats(old_config, config);
584 free_config(old_config); 405 free_config(old_config);
585 } 406 }
586 config->reading = false; 407 config->reading = false;
587
588 if (success) {
589 update_active_bar_modifiers();
590 }
591
592 return success; 408 return success;
593} 409}
594 410
595static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) { 411static bool load_include_config(const char *path, const char *parent_dir,
412 struct sway_config *config) {
596 // save parent config 413 // save parent config
597 const char *parent_config = config->current_config; 414 const char *parent_config = config->current_config;
598 415
@@ -602,7 +419,8 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
602 len = len + strlen(parent_dir) + 2; 419 len = len + strlen(parent_dir) + 2;
603 full_path = malloc(len * sizeof(char)); 420 full_path = malloc(len * sizeof(char));
604 if (!full_path) { 421 if (!full_path) {
605 sway_log(L_ERROR, "Unable to allocate full path to included config"); 422 wlr_log(L_ERROR,
423 "Unable to allocate full path to included config");
606 return false; 424 return false;
607 } 425 }
608 snprintf(full_path, len, "%s/%s", parent_dir, path); 426 snprintf(full_path, len, "%s/%s", parent_dir, path);
@@ -612,7 +430,7 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
612 free(full_path); 430 free(full_path);
613 431
614 if (real_path == NULL) { 432 if (real_path == NULL) {
615 sway_log(L_DEBUG, "%s not found.", path); 433 wlr_log(L_DEBUG, "%s not found.", path);
616 return false; 434 return false;
617 } 435 }
618 436
@@ -621,7 +439,9 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
621 for (j = 0; j < config->config_chain->length; ++j) { 439 for (j = 0; j < config->config_chain->length; ++j) {
622 char *old_path = config->config_chain->items[j]; 440 char *old_path = config->config_chain->items[j];
623 if (strcmp(real_path, old_path) == 0) { 441 if (strcmp(real_path, old_path) == 0) {
624 sway_log(L_DEBUG, "%s already included once, won't be included again.", real_path); 442 wlr_log(L_DEBUG,
443 "%s already included once, won't be included again.",
444 real_path);
625 free(real_path); 445 free(real_path);
626 return false; 446 return false;
627 } 447 }
@@ -673,7 +493,7 @@ bool load_include_configs(const char *path, struct sway_config *config) {
673 // restore wd 493 // restore wd
674 if (chdir(wd) < 0) { 494 if (chdir(wd) < 0) {
675 free(wd); 495 free(wd);
676 sway_log(L_ERROR, "failed to restore working directory"); 496 wlr_log(L_ERROR, "failed to restore working directory");
677 return false; 497 return false;
678 } 498 }
679 499
@@ -681,13 +501,11 @@ bool load_include_configs(const char *path, struct sway_config *config) {
681 return true; 501 return true;
682} 502}
683 503
684struct cmd_results *check_security_config() { 504void config_clear_handler_context(struct sway_config *config) {
685 if (!current_config_path || strncmp(SYSCONFDIR "/sway/security.d/", current_config_path, 505 free_input_config(config->handler_context.input_config);
686 strlen(SYSCONFDIR "/sway/security.d/")) != 0) { 506 free_seat_config(config->handler_context.seat_config);
687 return cmd_results_new(CMD_INVALID, "permit", 507
688 "This command is only permitted to run from " SYSCONFDIR "/sway/security.d/*"); 508 memset(&config->handler_context, 0, sizeof(config->handler_context));
689 }
690 return NULL;
691} 509}
692 510
693bool read_config(FILE *file, struct sway_config *config) { 511bool read_config(FILE *file, struct sway_config *config) {
@@ -717,13 +535,13 @@ bool read_config(FILE *file, struct sway_config *config) {
717 switch(res->status) { 535 switch(res->status) {
718 case CMD_FAILURE: 536 case CMD_FAILURE:
719 case CMD_INVALID: 537 case CMD_INVALID:
720 sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, line, 538 wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number,
721 res->error, config->current_config); 539 line, res->error, config->current_config);
722 success = false; 540 success = false;
723 break; 541 break;
724 542
725 case CMD_DEFER: 543 case CMD_DEFER:
726 sway_log(L_DEBUG, "Defferring command `%s'", line); 544 wlr_log(L_DEBUG, "Deferring command `%s'", line);
727 list_add(config->cmd_queue, strdup(line)); 545 list_add(config->cmd_queue, strdup(line));
728 break; 546 break;
729 547
@@ -731,7 +549,7 @@ bool read_config(FILE *file, struct sway_config *config) {
731 if (block == CMD_BLOCK_END) { 549 if (block == CMD_BLOCK_END) {
732 block = CMD_BLOCK_MODE; 550 block = CMD_BLOCK_MODE;
733 } else { 551 } else {
734 sway_log(L_ERROR, "Invalid block '%s'", line); 552 wlr_log(L_ERROR, "Invalid block '%s'", line);
735 } 553 }
736 break; 554 break;
737 555
@@ -739,7 +557,15 @@ bool read_config(FILE *file, struct sway_config *config) {
739 if (block == CMD_BLOCK_END) { 557 if (block == CMD_BLOCK_END) {
740 block = CMD_BLOCK_INPUT; 558 block = CMD_BLOCK_INPUT;
741 } else { 559 } else {
742 sway_log(L_ERROR, "Invalid block '%s'", line); 560 wlr_log(L_ERROR, "Invalid block '%s'", line);
561 }
562 break;
563
564 case CMD_BLOCK_SEAT:
565 if (block == CMD_BLOCK_END) {
566 block = CMD_BLOCK_SEAT;
567 } else {
568 wlr_log(L_ERROR, "Invalid block '%s'", line);
743 } 569 }
744 break; 570 break;
745 571
@@ -747,7 +573,7 @@ bool read_config(FILE *file, struct sway_config *config) {
747 if (block == CMD_BLOCK_END) { 573 if (block == CMD_BLOCK_END) {
748 block = CMD_BLOCK_BAR; 574 block = CMD_BLOCK_BAR;
749 } else { 575 } else {
750 sway_log(L_ERROR, "Invalid block '%s'", line); 576 wlr_log(L_ERROR, "Invalid block '%s'", line);
751 } 577 }
752 break; 578 break;
753 579
@@ -755,7 +581,7 @@ bool read_config(FILE *file, struct sway_config *config) {
755 if (block == CMD_BLOCK_BAR) { 581 if (block == CMD_BLOCK_BAR) {
756 block = CMD_BLOCK_BAR_COLORS; 582 block = CMD_BLOCK_BAR_COLORS;
757 } else { 583 } else {
758 sway_log(L_ERROR, "Invalid block '%s'", line); 584 wlr_log(L_ERROR, "Invalid block '%s'", line);
759 } 585 }
760 break; 586 break;
761 587
@@ -763,7 +589,7 @@ bool read_config(FILE *file, struct sway_config *config) {
763 if (block == CMD_BLOCK_END) { 589 if (block == CMD_BLOCK_END) {
764 block = CMD_BLOCK_COMMANDS; 590 block = CMD_BLOCK_COMMANDS;
765 } else { 591 } else {
766 sway_log(L_ERROR, "Invalid block '%s'", line); 592 wlr_log(L_ERROR, "Invalid block '%s'", line);
767 } 593 }
768 break; 594 break;
769 595
@@ -771,7 +597,7 @@ bool read_config(FILE *file, struct sway_config *config) {
771 if (block == CMD_BLOCK_END) { 597 if (block == CMD_BLOCK_END) {
772 block = CMD_BLOCK_IPC; 598 block = CMD_BLOCK_IPC;
773 } else { 599 } else {
774 sway_log(L_ERROR, "Invalid block '%s'", line); 600 wlr_log(L_ERROR, "Invalid block '%s'", line);
775 } 601 }
776 break; 602 break;
777 603
@@ -779,56 +605,61 @@ bool read_config(FILE *file, struct sway_config *config) {
779 if (block == CMD_BLOCK_IPC) { 605 if (block == CMD_BLOCK_IPC) {
780 block = CMD_BLOCK_IPC_EVENTS; 606 block = CMD_BLOCK_IPC_EVENTS;
781 } else { 607 } else {
782 sway_log(L_ERROR, "Invalid block '%s'", line); 608 wlr_log(L_ERROR, "Invalid block '%s'", line);
783 } 609 }
784 break; 610 break;
785 611
786 case CMD_BLOCK_END: 612 case CMD_BLOCK_END:
787 switch(block) { 613 switch(block) {
788 case CMD_BLOCK_MODE: 614 case CMD_BLOCK_MODE:
789 sway_log(L_DEBUG, "End of mode block"); 615 wlr_log(L_DEBUG, "End of mode block");
790 config->current_mode = config->modes->items[0]; 616 config->current_mode = config->modes->items[0];
791 block = CMD_BLOCK_END; 617 block = CMD_BLOCK_END;
792 break; 618 break;
793 619
794 case CMD_BLOCK_INPUT: 620 case CMD_BLOCK_INPUT:
795 sway_log(L_DEBUG, "End of input block"); 621 wlr_log(L_DEBUG, "End of input block");
796 current_input_config = NULL; 622 block = CMD_BLOCK_END;
623 break;
624
625 case CMD_BLOCK_SEAT:
626 wlr_log(L_DEBUG, "End of seat block");
797 block = CMD_BLOCK_END; 627 block = CMD_BLOCK_END;
798 break; 628 break;
799 629
800 case CMD_BLOCK_BAR: 630 case CMD_BLOCK_BAR:
801 sway_log(L_DEBUG, "End of bar block"); 631 wlr_log(L_DEBUG, "End of bar block");
802 config->current_bar = NULL; 632 config->current_bar = NULL;
803 block = CMD_BLOCK_END; 633 block = CMD_BLOCK_END;
804 break; 634 break;
805 635
806 case CMD_BLOCK_BAR_COLORS: 636 case CMD_BLOCK_BAR_COLORS:
807 sway_log(L_DEBUG, "End of bar colors block"); 637 wlr_log(L_DEBUG, "End of bar colors block");
808 block = CMD_BLOCK_BAR; 638 block = CMD_BLOCK_BAR;
809 break; 639 break;
810 640
811 case CMD_BLOCK_COMMANDS: 641 case CMD_BLOCK_COMMANDS:
812 sway_log(L_DEBUG, "End of commands block"); 642 wlr_log(L_DEBUG, "End of commands block");
813 block = CMD_BLOCK_END; 643 block = CMD_BLOCK_END;
814 break; 644 break;
815 645
816 case CMD_BLOCK_IPC: 646 case CMD_BLOCK_IPC:
817 sway_log(L_DEBUG, "End of IPC block"); 647 wlr_log(L_DEBUG, "End of IPC block");
818 block = CMD_BLOCK_END; 648 block = CMD_BLOCK_END;
819 break; 649 break;
820 650
821 case CMD_BLOCK_IPC_EVENTS: 651 case CMD_BLOCK_IPC_EVENTS:
822 sway_log(L_DEBUG, "End of IPC events block"); 652 wlr_log(L_DEBUG, "End of IPC events block");
823 block = CMD_BLOCK_IPC; 653 block = CMD_BLOCK_IPC;
824 break; 654 break;
825 655
826 case CMD_BLOCK_END: 656 case CMD_BLOCK_END:
827 sway_log(L_ERROR, "Unmatched }"); 657 wlr_log(L_ERROR, "Unmatched }");
828 break; 658 break;
829 659
830 default:; 660 default:;
831 } 661 }
662 config_clear_handler_context(config);
832 default:; 663 default:;
833 } 664 }
834 free(line); 665 free(line);
@@ -838,365 +669,6 @@ bool read_config(FILE *file, struct sway_config *config) {
838 return success; 669 return success;
839} 670}
840 671
841int input_identifier_cmp(const void *item, const void *data) {
842 const struct input_config *ic = item;
843 const char *identifier = data;
844 return strcmp(ic->identifier, identifier);
845}
846
847int output_name_cmp(const void *item, const void *data) {
848 const struct output_config *output = item;
849 const char *name = data;
850
851 return strcmp(output->name, name);
852}
853
854void merge_input_config(struct input_config *dst, struct input_config *src) {
855 if (src->identifier) {
856 if (dst->identifier) {
857 free(dst->identifier);
858 }
859 dst->identifier = strdup(src->identifier);
860 }
861 if (src->accel_profile != INT_MIN) {
862 dst->accel_profile = src->accel_profile;
863 }
864 if (src->click_method != INT_MIN) {
865 dst->click_method = src->click_method;
866 }
867 if (src->drag_lock != INT_MIN) {
868 dst->drag_lock = src->drag_lock;
869 }
870 if (src->dwt != INT_MIN) {
871 dst->dwt = src->dwt;
872 }
873 if (src->middle_emulation != INT_MIN) {
874 dst->middle_emulation = src->middle_emulation;
875 }
876 if (src->natural_scroll != INT_MIN) {
877 dst->natural_scroll = src->natural_scroll;
878 }
879 if (src->pointer_accel != FLT_MIN) {
880 dst->pointer_accel = src->pointer_accel;
881 }
882 if (src->scroll_method != INT_MIN) {
883 dst->scroll_method = src->scroll_method;
884 }
885 if (src->send_events != INT_MIN) {
886 dst->send_events = src->send_events;
887 }
888 if (src->tap != INT_MIN) {
889 dst->tap = src->tap;
890 }
891}
892
893void merge_output_config(struct output_config *dst, struct output_config *src) {
894 if (src->name) {
895 if (dst->name) {
896 free(dst->name);
897 }
898 dst->name = strdup(src->name);
899 }
900 if (src->enabled != -1) {
901 dst->enabled = src->enabled;
902 }
903 if (src->width != -1) {
904 dst->width = src->width;
905 }
906 if (src->height != -1) {
907 dst->height = src->height;
908 }
909 if (src->x != -1) {
910 dst->x = src->x;
911 }
912 if (src->y != -1) {
913 dst->y = src->y;
914 }
915 if (src->scale != -1) {
916 dst->scale = src->scale;
917 }
918 if (src->background) {
919 if (dst->background) {
920 free(dst->background);
921 }
922 dst->background = strdup(src->background);
923 }
924 if (src->background_option) {
925 if (dst->background_option) {
926 free(dst->background_option);
927 }
928 dst->background_option = strdup(src->background_option);
929 }
930}
931
932static void invoke_swaybar(struct bar_config *bar) {
933 // Pipe to communicate errors
934 int filedes[2];
935 if (pipe(filedes) == -1) {
936 sway_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
937 return;
938 }
939
940 bar->pid = fork();
941 if (bar->pid == 0) {
942 close(filedes[0]);
943 if (!bar->swaybar_command) {
944 char *const cmd[] = {
945 "swaybar",
946 "-b",
947 bar->id,
948 NULL,
949 };
950
951 close(filedes[1]);
952 execvp(cmd[0], cmd);
953 _exit(EXIT_SUCCESS);
954 } else {
955 // run custom swaybar
956 int len = strlen(bar->swaybar_command) + strlen(bar->id) + 5;
957 char *command = malloc(len * sizeof(char));
958 if (!command) {
959 const char msg[] = "Unable to allocate swaybar command string";
960 int len = sizeof(msg);
961 if (write(filedes[1], &len, sizeof(int))) {};
962 if (write(filedes[1], msg, len)) {};
963 close(filedes[1]);
964 _exit(EXIT_FAILURE);
965 }
966 snprintf(command, len, "%s -b %s", bar->swaybar_command, bar->id);
967
968 char *const cmd[] = {
969 "sh",
970 "-c",
971 command,
972 NULL,
973 };
974
975 close(filedes[1]);
976 execvp(cmd[0], cmd);
977 free(command);
978 _exit(EXIT_SUCCESS);
979 }
980 }
981 close(filedes[0]);
982 int len;
983 if(read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
984 char *buf = malloc(len);
985 if(!buf) {
986 sway_log(L_ERROR, "Cannot allocate error string");
987 return;
988 }
989 if(read(filedes[1], buf, len)) {
990 sway_log(L_ERROR, "%s", buf);
991 }
992 free(buf);
993 }
994 close(filedes[1]);
995}
996
997static void terminate_swaybar(pid_t pid) {
998 int ret = kill(pid, SIGTERM);
999 if (ret != 0) {
1000 sway_log(L_ERROR, "Unable to terminate swaybar [pid: %d]", pid);
1001 } else {
1002 int status;
1003 waitpid(pid, &status, 0);
1004 }
1005}
1006
1007void terminate_swaybg(pid_t pid) {
1008 int ret = kill(pid, SIGTERM);
1009 if (ret != 0) {
1010 sway_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
1011 } else {
1012 int status;
1013 waitpid(pid, &status, 0);
1014 }
1015}
1016
1017static bool active_output(const char *name) {
1018 int i;
1019 swayc_t *cont = NULL;
1020 for (i = 0; i < root_container.children->length; ++i) {
1021 cont = root_container.children->items[i];
1022 if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
1023 return true;
1024 }
1025 }
1026
1027 return false;
1028}
1029
1030void load_swaybars() {
1031 // Check for bars
1032 list_t *bars = create_list();
1033 struct bar_config *bar = NULL;
1034 int i;
1035 for (i = 0; i < config->bars->length; ++i) {
1036 bar = config->bars->items[i];
1037 bool apply = false;
1038 if (bar->outputs) {
1039 int j;
1040 for (j = 0; j < bar->outputs->length; ++j) {
1041 char *o = bar->outputs->items[j];
1042 if (!strcmp(o, "*") || active_output(o)) {
1043 apply = true;
1044 break;
1045 }
1046 }
1047 } else {
1048 apply = true;
1049 }
1050 if (apply) {
1051 list_add(bars, bar);
1052 }
1053 }
1054
1055 for (i = 0; i < bars->length; ++i) {
1056 bar = bars->items[i];
1057 if (bar->pid != 0) {
1058 terminate_swaybar(bar->pid);
1059 }
1060 sway_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
1061 invoke_swaybar(bar);
1062 }
1063
1064 list_free(bars);
1065}
1066
1067void apply_input_config(struct input_config *ic, struct libinput_device *dev) {
1068 if (!ic) {
1069 return;
1070 }
1071
1072 sway_log(L_DEBUG, "apply_input_config(%s)", ic->identifier);
1073
1074 if (ic->accel_profile != INT_MIN) {
1075 sway_log(L_DEBUG, "apply_input_config(%s) accel_set_profile(%d)", ic->identifier, ic->accel_profile);
1076 libinput_device_config_accel_set_profile(dev, ic->accel_profile);
1077 }
1078 if (ic->click_method != INT_MIN) {
1079 sway_log(L_DEBUG, "apply_input_config(%s) click_set_method(%d)", ic->identifier, ic->click_method);
1080 libinput_device_config_click_set_method(dev, ic->click_method);
1081 }
1082 if (ic->drag_lock != INT_MIN) {
1083 sway_log(L_DEBUG, "apply_input_config(%s) tap_set_drag_lock_enabled(%d)", ic->identifier, ic->click_method);
1084 libinput_device_config_tap_set_drag_lock_enabled(dev, ic->drag_lock);
1085 }
1086 if (ic->dwt != INT_MIN) {
1087 sway_log(L_DEBUG, "apply_input_config(%s) dwt_set_enabled(%d)", ic->identifier, ic->dwt);
1088 libinput_device_config_dwt_set_enabled(dev, ic->dwt);
1089 }
1090 if (ic->left_handed != INT_MIN) {
1091 sway_log(L_DEBUG, "apply_input_config(%s) left_handed_set_enabled(%d)", ic->identifier, ic->left_handed);
1092 libinput_device_config_left_handed_set(dev, ic->left_handed);
1093 }
1094 if (ic->middle_emulation != INT_MIN) {
1095 sway_log(L_DEBUG, "apply_input_config(%s) middle_emulation_set_enabled(%d)", ic->identifier, ic->middle_emulation);
1096 libinput_device_config_middle_emulation_set_enabled(dev, ic->middle_emulation);
1097 }
1098 if (ic->natural_scroll != INT_MIN) {
1099 sway_log(L_DEBUG, "apply_input_config(%s) natural_scroll_set_enabled(%d)", ic->identifier, ic->natural_scroll);
1100 libinput_device_config_scroll_set_natural_scroll_enabled(dev, ic->natural_scroll);
1101 }
1102 if (ic->pointer_accel != FLT_MIN) {
1103 sway_log(L_DEBUG, "apply_input_config(%s) accel_set_speed(%f)", ic->identifier, ic->pointer_accel);
1104 libinput_device_config_accel_set_speed(dev, ic->pointer_accel);
1105 }
1106 if (ic->scroll_method != INT_MIN) {
1107 sway_log(L_DEBUG, "apply_input_config(%s) scroll_set_method(%d)", ic->identifier, ic->scroll_method);
1108 libinput_device_config_scroll_set_method(dev, ic->scroll_method);
1109 }
1110 if (ic->send_events != INT_MIN) {
1111 sway_log(L_DEBUG, "apply_input_config(%s) send_events_set_mode(%d)", ic->identifier, ic->send_events);
1112 libinput_device_config_send_events_set_mode(dev, ic->send_events);
1113 }
1114 if (ic->tap != INT_MIN) {
1115 sway_log(L_DEBUG, "apply_input_config(%s) tap_set_enabled(%d)", ic->identifier, ic->tap);
1116 libinput_device_config_tap_set_enabled(dev, ic->tap);
1117 }
1118}
1119
1120void apply_output_config(struct output_config *oc, swayc_t *output) {
1121 if (oc && oc->enabled == 0) {
1122 destroy_output(output);
1123 return;
1124 }
1125
1126 if (oc && oc->width > 0 && oc->height > 0) {
1127 output->width = oc->width;
1128 output->height = oc->height;
1129
1130 sway_log(L_DEBUG, "Set %s size to %ix%i (%d)", oc->name, oc->width, oc->height, oc->scale);
1131 struct wlc_size new_size = { .w = oc->width, .h = oc->height };
1132 wlc_output_set_resolution(output->handle, &new_size, (uint32_t)oc->scale);
1133 } else if (oc) {
1134 const struct wlc_size *new_size = wlc_output_get_resolution(output->handle);
1135 wlc_output_set_resolution(output->handle, new_size, (uint32_t)oc->scale);
1136 }
1137
1138 // Find position for it
1139 if (oc && oc->x != -1 && oc->y != -1) {
1140 sway_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
1141 output->x = oc->x;
1142 output->y = oc->y;
1143 } else {
1144 int x = 0;
1145 for (int i = 0; i < root_container.children->length; ++i) {
1146 swayc_t *c = root_container.children->items[i];
1147 if (c->type == C_OUTPUT) {
1148 if (c->width + c->x > x) {
1149 x = c->width + c->x;
1150 }
1151 }
1152 }
1153 output->x = x;
1154 }
1155
1156 if (!oc || !oc->background) {
1157 // Look for a * config for background
1158 int i = list_seq_find(config->output_configs, output_name_cmp, "*");
1159 if (i >= 0) {
1160 oc = config->output_configs->items[i];
1161 } else {
1162 oc = NULL;
1163 }
1164 }
1165
1166 int output_i;
1167 for (output_i = 0; output_i < root_container.children->length; ++output_i) {
1168 if (root_container.children->items[output_i] == output) {
1169 break;
1170 }
1171 }
1172
1173 if (oc && oc->background) {
1174 if (output->bg_pid != 0) {
1175 terminate_swaybg(output->bg_pid);
1176 }
1177
1178 sway_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background);
1179
1180 size_t bufsize = 12;
1181 char output_id[bufsize];
1182 snprintf(output_id, bufsize, "%d", output_i);
1183 output_id[bufsize-1] = 0;
1184
1185 char *const cmd[] = {
1186 "swaybg",
1187 output_id,
1188 oc->background,
1189 oc->background_option,
1190 NULL,
1191 };
1192
1193 output->bg_pid = fork();
1194 if (output->bg_pid == 0) {
1195 execvp(cmd[0], cmd);
1196 }
1197 }
1198}
1199
1200char *do_var_replacement(char *str) { 672char *do_var_replacement(char *str) {
1201 int i; 673 int i;
1202 char *find = str; 674 char *find = str;
@@ -1216,8 +688,9 @@ char *do_var_replacement(char *str) {
1216 int vvlen = strlen(var->value); 688 int vvlen = strlen(var->value);
1217 char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); 689 char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
1218 if (!newstr) { 690 if (!newstr) {
1219 sway_log(L_ERROR, 691 wlr_log(L_ERROR,
1220 "Unable to allocate replacement during variable expansion"); 692 "Unable to allocate replacement "
693 "during variable expansion");
1221 break; 694 break;
1222 } 695 }
1223 char *newptr = newstr; 696 char *newptr = newstr;
@@ -1247,197 +720,3 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
1247 const struct workspace_output *wsa = a, *wsb = b; 720 const struct workspace_output *wsa = a, *wsb = b;
1248 return lenient_strcmp(wsa->workspace, wsb->workspace); 721 return lenient_strcmp(wsa->workspace, wsb->workspace);
1249} 722}
1250
1251int sway_binding_cmp_keys(const void *a, const void *b) {
1252 const struct sway_binding *binda = a, *bindb = b;
1253
1254 // Count keys pressed for this binding. important so we check long before
1255 // short ones. for example mod+a+b before mod+a
1256 unsigned int moda = 0, modb = 0, i;
1257
1258 // Count how any modifiers are pressed
1259 for (i = 0; i < 8 * sizeof(binda->modifiers); ++i) {
1260 moda += (binda->modifiers & 1 << i) != 0;
1261 modb += (bindb->modifiers & 1 << i) != 0;
1262 }
1263 if (bindb->keys->length + modb != binda->keys->length + moda) {
1264 return (bindb->keys->length + modb) - (binda->keys->length + moda);
1265 }
1266
1267 // Otherwise compare keys
1268 if (binda->modifiers > bindb->modifiers) {
1269 return 1;
1270 } else if (binda->modifiers < bindb->modifiers) {
1271 return -1;
1272 }
1273 struct wlc_modifiers no_mods = { 0, 0 };
1274 for (int i = 0; i < binda->keys->length; i++) {
1275 xkb_keysym_t ka = *(xkb_keysym_t *)binda->keys->items[i],
1276 kb = *(xkb_keysym_t *)bindb->keys->items[i];
1277 if (binda->bindcode) {
1278 uint32_t *keycode = binda->keys->items[i];
1279 ka = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
1280 }
1281
1282 if (bindb->bindcode) {
1283 uint32_t *keycode = bindb->keys->items[i];
1284 kb = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
1285 }
1286
1287 if (ka > kb) {
1288 return 1;
1289 } else if (ka < kb) {
1290 return -1;
1291 }
1292 }
1293
1294 return 0;
1295}
1296
1297int sway_binding_cmp(const void *a, const void *b) {
1298 int cmp = 0;
1299 if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
1300 return cmp;
1301 }
1302 const struct sway_binding *binda = a, *bindb = b;
1303 return lenient_strcmp(binda->command, bindb->command);
1304}
1305
1306int sway_binding_cmp_qsort(const void *a, const void *b) {
1307 return sway_binding_cmp(*(void **)a, *(void **)b);
1308}
1309
1310void free_sway_binding(struct sway_binding *binding) {
1311 if (binding->keys) {
1312 for (int i = 0; i < binding->keys->length; i++) {
1313 free(binding->keys->items[i]);
1314 }
1315 list_free(binding->keys);
1316 }
1317 if (binding->command) {
1318 free(binding->command);
1319 }
1320 free(binding);
1321}
1322
1323int sway_mouse_binding_cmp_buttons(const void *a, const void *b) {
1324 const struct sway_mouse_binding *binda = a, *bindb = b;
1325 if (binda->button > bindb->button) {
1326 return 1;
1327 }
1328 if (binda->button < bindb->button) {
1329 return -1;
1330 }
1331 return 0;
1332}
1333
1334int sway_mouse_binding_cmp(const void *a, const void *b) {
1335 int cmp = 0;
1336 if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
1337 return cmp;
1338 }
1339 const struct sway_mouse_binding *binda = a, *bindb = b;
1340 return lenient_strcmp(binda->command, bindb->command);
1341}
1342
1343int sway_mouse_binding_cmp_qsort(const void *a, const void *b) {
1344 return sway_mouse_binding_cmp(*(void **)a, *(void **)b);
1345}
1346
1347void free_sway_mouse_binding(struct sway_mouse_binding *binding) {
1348 if (binding->command) {
1349 free(binding->command);
1350 }
1351 free(binding);
1352}
1353
1354struct sway_binding *sway_binding_dup(struct sway_binding *sb) {
1355 struct sway_binding *new_sb = malloc(sizeof(struct sway_binding));
1356 if (!new_sb) {
1357 return NULL;
1358 }
1359
1360 new_sb->order = sb->order;
1361 new_sb->modifiers = sb->modifiers;
1362 new_sb->command = strdup(sb->command);
1363
1364 new_sb->keys = create_list();
1365 int i;
1366 for (i = 0; i < sb->keys->length; ++i) {
1367 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
1368 if (!key) {
1369 free_sway_binding(new_sb);
1370 return NULL;
1371 }
1372 *key = *(xkb_keysym_t *)sb->keys->items[i];
1373 list_add(new_sb->keys, key);
1374 }
1375
1376 return new_sb;
1377}
1378
1379struct bar_config *default_bar_config(void) {
1380 struct bar_config *bar = NULL;
1381 bar = malloc(sizeof(struct bar_config));
1382 if (!bar) {
1383 return NULL;
1384 }
1385 if (!(bar->mode = strdup("dock"))) goto cleanup;
1386 if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
1387 bar->modifier = WLC_BIT_MOD_LOGO;
1388 bar->outputs = NULL;
1389 bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
1390 if (!(bar->bindings = create_list())) goto cleanup;
1391 if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
1392 bar->pango_markup = false;
1393 bar->swaybar_command = NULL;
1394 bar->font = NULL;
1395 bar->height = -1;
1396 bar->workspace_buttons = true;
1397 bar->wrap_scroll = false;
1398 bar->separator_symbol = NULL;
1399 bar->strip_workspace_numbers = false;
1400 bar->binding_mode_indicator = true;
1401#ifdef ENABLE_TRAY
1402 bar->tray_output = NULL;
1403 bar->icon_theme = NULL;
1404 bar->tray_padding = 2;
1405 bar->activate_button = 0x110; /* BTN_LEFT */
1406 bar->context_button = 0x111; /* BTN_RIGHT */
1407 bar->secondary_button = 0x112; /* BTN_MIDDLE */
1408#endif
1409 bar->verbose = false;
1410 bar->pid = 0;
1411 // set default colors
1412 if (!(bar->colors.background = strndup("#000000ff", 9))) goto cleanup;
1413 if (!(bar->colors.statusline = strndup("#ffffffff", 9))) goto cleanup;
1414 if (!(bar->colors.separator = strndup("#666666ff", 9))) goto cleanup;
1415 if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) goto cleanup;
1416 if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) goto cleanup;
1417 if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
1418 if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) goto cleanup;
1419 if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) goto cleanup;
1420 if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
1421 if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) goto cleanup;
1422 if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) goto cleanup;
1423 if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) goto cleanup;
1424 if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) goto cleanup;
1425 if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) goto cleanup;
1426 if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
1427 // if the following colors stay undefined, they fall back to background,
1428 // statusline, separator and urgent_workspace_*.
1429 bar->colors.focused_background = NULL;
1430 bar->colors.focused_statusline = NULL;
1431 bar->colors.focused_separator = NULL;
1432 bar->colors.binding_mode_border = NULL;
1433 bar->colors.binding_mode_bg = NULL;
1434 bar->colors.binding_mode_text = NULL;
1435
1436 list_add(config->bars, bar);
1437
1438 return bar;
1439
1440cleanup:
1441 free_bar(bar);
1442 return NULL;
1443}
diff --git a/sway/config/bar.c b/sway/config/bar.c
new file mode 100644
index 00000000..2913f059
--- /dev/null
+++ b/sway/config/bar.c
@@ -0,0 +1,240 @@
1#define _POSIX_C_SOURCE 200809L
2#define _XOPEN_SOURCE 700
3#include <stdio.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <wordexp.h>
8#include <sys/types.h>
9#include <sys/wait.h>
10#include <sys/stat.h>
11#include <signal.h>
12#include <strings.h>
13#include "sway/config.h"
14#include "stringop.h"
15#include "list.h"
16#include "log.h"
17
18static void terminate_swaybar(pid_t pid) {
19 wlr_log(L_DEBUG, "Terminating swaybar %d", pid);
20 int ret = kill(pid, SIGTERM);
21 if (ret != 0) {
22 wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid);
23 } else {
24 int status;
25 waitpid(pid, &status, 0);
26 }
27}
28
29void free_bar_config(struct bar_config *bar) {
30 if (!bar) {
31 return;
32 }
33 free(bar->mode);
34 free(bar->position);
35 free(bar->hidden_state);
36 free(bar->status_command);
37 free(bar->font);
38 free(bar->separator_symbol);
39 // TODO: Free mouse bindings
40 list_free(bar->bindings);
41 if (bar->outputs) {
42 free_flat_list(bar->outputs);
43 }
44 if (bar->pid != 0) {
45 terminate_swaybar(bar->pid);
46 }
47 free(bar->colors.background);
48 free(bar->colors.statusline);
49 free(bar->colors.separator);
50 free(bar->colors.focused_background);
51 free(bar->colors.focused_statusline);
52 free(bar->colors.focused_separator);
53 free(bar->colors.focused_workspace_border);
54 free(bar->colors.focused_workspace_bg);
55 free(bar->colors.focused_workspace_text);
56 free(bar->colors.active_workspace_border);
57 free(bar->colors.active_workspace_bg);
58 free(bar->colors.active_workspace_text);
59 free(bar->colors.inactive_workspace_border);
60 free(bar->colors.inactive_workspace_bg);
61 free(bar->colors.inactive_workspace_text);
62 free(bar->colors.urgent_workspace_border);
63 free(bar->colors.urgent_workspace_bg);
64 free(bar->colors.urgent_workspace_text);
65 free(bar->colors.binding_mode_border);
66 free(bar->colors.binding_mode_bg);
67 free(bar->colors.binding_mode_text);
68 free(bar);
69}
70
71struct bar_config *default_bar_config(void) {
72 struct bar_config *bar = NULL;
73 bar = malloc(sizeof(struct bar_config));
74 if (!bar) {
75 return NULL;
76 }
77 if (!(bar->mode = strdup("dock"))) goto cleanup;
78 if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
79 bar->outputs = NULL;
80 bar->position = strdup("bottom");
81 if (!(bar->bindings = create_list())) goto cleanup;
82 if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
83 bar->pango_markup = false;
84 bar->swaybar_command = NULL;
85 bar->font = NULL;
86 bar->height = -1;
87 bar->workspace_buttons = true;
88 bar->wrap_scroll = false;
89 bar->separator_symbol = NULL;
90 bar->strip_workspace_numbers = false;
91 bar->binding_mode_indicator = true;
92 bar->verbose = false;
93 bar->pid = 0;
94 // set default colors
95 if (!(bar->colors.background = strndup("#000000ff", 9))) {
96 goto cleanup;
97 }
98 if (!(bar->colors.statusline = strndup("#ffffffff", 9))) {
99 goto cleanup;
100 }
101 if (!(bar->colors.separator = strndup("#666666ff", 9))) {
102 goto cleanup;
103 }
104 if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) {
105 goto cleanup;
106 }
107 if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) {
108 goto cleanup;
109 }
110 if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) {
111 goto cleanup;
112 }
113 if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) {
114 goto cleanup;
115 }
116 if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) {
117 goto cleanup;
118 }
119 if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) {
120 goto cleanup;
121 }
122 if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) {
123 goto cleanup;
124 }
125 if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) {
126 goto cleanup;
127 }
128 if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) {
129 goto cleanup;
130 }
131 if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) {
132 goto cleanup;
133 }
134 if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) {
135 goto cleanup;
136 }
137 if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) {
138 goto cleanup;
139 }
140 // if the following colors stay undefined, they fall back to background,
141 // statusline, separator and urgent_workspace_*.
142 bar->colors.focused_background = NULL;
143 bar->colors.focused_statusline = NULL;
144 bar->colors.focused_separator = NULL;
145 bar->colors.binding_mode_border = NULL;
146 bar->colors.binding_mode_bg = NULL;
147 bar->colors.binding_mode_text = NULL;
148
149 list_add(config->bars, bar);
150 return bar;
151cleanup:
152 free_bar_config(bar);
153 return NULL;
154}
155
156void invoke_swaybar(struct bar_config *bar) {
157 // Pipe to communicate errors
158 int filedes[2];
159 if (pipe(filedes) == -1) {
160 wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
161 return;
162 }
163
164 bar->pid = fork();
165 if (bar->pid == 0) {
166 close(filedes[0]);
167
168 // run custom swaybar
169 size_t len = snprintf(NULL, 0, "%s -b %s",
170 bar->swaybar_command ? bar->swaybar_command : "swaybar",
171 bar->id);
172 char *command = malloc(len + 1);
173 if (!command) {
174 const char msg[] = "Unable to allocate swaybar command string";
175 size_t len = sizeof(msg);
176 if (write(filedes[1], &len, sizeof(int))) {};
177 if (write(filedes[1], msg, len)) {};
178 close(filedes[1]);
179 exit(1);
180 }
181 snprintf(command, len + 1, "%s -b %s",
182 bar->swaybar_command ? bar->swaybar_command : "swaybar",
183 bar->id);
184 char *const cmd[] = { "sh", "-c", command, NULL, };
185 close(filedes[1]);
186 execvp(cmd[0], cmd);
187 exit(1);
188 }
189 wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid);
190 close(filedes[0]);
191 ssize_t len;
192 if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
193 char *buf = malloc(len);
194 if(!buf) {
195 wlr_log(L_ERROR, "Cannot allocate error string");
196 return;
197 }
198 if (read(filedes[1], buf, len)) {
199 wlr_log(L_ERROR, "%s", buf);
200 }
201 free(buf);
202 }
203 close(filedes[1]);
204}
205
206static bool active_output(const char *name) {
207 struct sway_container *cont = NULL;
208 for (int i = 0; i < root_container.children->length; ++i) {
209 cont = root_container.children->items[i];
210 if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
211 return true;
212 }
213 }
214 return false;
215}
216
217void load_swaybars() {
218 for (int i = 0; i < config->bars->length; ++i) {
219 struct bar_config *bar = config->bars->items[i];
220 bool apply = false;
221 if (bar->outputs) {
222 for (int j = 0; j < bar->outputs->length; ++j) {
223 char *o = bar->outputs->items[j];
224 if (!strcmp(o, "*") || active_output(o)) {
225 apply = true;
226 break;
227 }
228 }
229 } else {
230 apply = true;
231 }
232 if (apply) {
233 if (bar->pid != 0) {
234 terminate_swaybar(bar->pid);
235 }
236 wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
237 invoke_swaybar(bar);
238 }
239 }
240}
diff --git a/sway/config/input.c b/sway/config/input.c
new file mode 100644
index 00000000..5e657c43
--- /dev/null
+++ b/sway/config/input.c
@@ -0,0 +1,119 @@
1#define _XOPEN_SOURCE 700
2#include <stdlib.h>
3#include <limits.h>
4#include <float.h>
5#include "sway/config.h"
6#include "log.h"
7
8struct input_config *new_input_config(const char* identifier) {
9 struct input_config *input = calloc(1, sizeof(struct input_config));
10 if (!input) {
11 wlr_log(L_DEBUG, "Unable to allocate input config");
12 return NULL;
13 }
14 wlr_log(L_DEBUG, "new_input_config(%s)", identifier);
15 if (!(input->identifier = strdup(identifier))) {
16 free(input);
17 wlr_log(L_DEBUG, "Unable to allocate input config");
18 return NULL;
19 }
20
21 input->tap = INT_MIN;
22 input->drag_lock = INT_MIN;
23 input->dwt = INT_MIN;
24 input->send_events = INT_MIN;
25 input->click_method = INT_MIN;
26 input->middle_emulation = INT_MIN;
27 input->natural_scroll = INT_MIN;
28 input->accel_profile = INT_MIN;
29 input->pointer_accel = FLT_MIN;
30 input->scroll_method = INT_MIN;
31 input->left_handed = INT_MIN;
32
33 return input;
34}
35
36void merge_input_config(struct input_config *dst, struct input_config *src) {
37 if (src->identifier) {
38 free(dst->identifier);
39 dst->identifier = strdup(src->identifier);
40 }
41 if (src->accel_profile != INT_MIN) {
42 dst->accel_profile = src->accel_profile;
43 }
44 if (src->click_method != INT_MIN) {
45 dst->click_method = src->click_method;
46 }
47 if (src->drag_lock != INT_MIN) {
48 dst->drag_lock = src->drag_lock;
49 }
50 if (src->dwt != INT_MIN) {
51 dst->dwt = src->dwt;
52 }
53 if (src->middle_emulation != INT_MIN) {
54 dst->middle_emulation = src->middle_emulation;
55 }
56 if (src->natural_scroll != INT_MIN) {
57 dst->natural_scroll = src->natural_scroll;
58 }
59 if (src->pointer_accel != FLT_MIN) {
60 dst->pointer_accel = src->pointer_accel;
61 }
62 if (src->scroll_method != INT_MIN) {
63 dst->scroll_method = src->scroll_method;
64 }
65 if (src->send_events != INT_MIN) {
66 dst->send_events = src->send_events;
67 }
68 if (src->tap != INT_MIN) {
69 dst->tap = src->tap;
70 }
71 if (src->xkb_layout) {
72 free(dst->xkb_layout);
73 dst->xkb_layout = strdup(src->xkb_layout);
74 }
75 if (src->xkb_model) {
76 free(dst->xkb_model);
77 dst->xkb_model = strdup(src->xkb_model);
78 }
79 if (src->xkb_options) {
80 free(dst->xkb_options);
81 dst->xkb_options = strdup(src->xkb_options);
82 }
83 if (src->xkb_rules) {
84 free(dst->xkb_rules);
85 dst->xkb_rules = strdup(src->xkb_rules);
86 }
87 if (src->xkb_variant) {
88 free(dst->xkb_variant);
89 dst->xkb_variant = strdup(src->xkb_variant);
90 }
91 if (src->mapped_output) {
92 free(dst->mapped_output);
93 dst->mapped_output = strdup(src->mapped_output);
94 }
95}
96
97struct input_config *copy_input_config(struct input_config *ic) {
98 struct input_config *copy = calloc(1, sizeof(struct input_config));
99 if (copy == NULL) {
100 wlr_log(L_ERROR, "could not allocate input config");
101 return NULL;
102 }
103 merge_input_config(copy, ic);
104 return copy;
105}
106
107void free_input_config(struct input_config *ic) {
108 if (!ic) {
109 return;
110 }
111 free(ic->identifier);
112 free(ic);
113}
114
115int input_identifier_cmp(const void *item, const void *data) {
116 const struct input_config *ic = item;
117 const char *identifier = data;
118 return strcmp(ic->identifier, identifier);
119}
diff --git a/sway/config/output.c b/sway/config/output.c
new file mode 100644
index 00000000..1c298d37
--- /dev/null
+++ b/sway/config/output.c
@@ -0,0 +1,213 @@
1#define _XOPEN_SOURCE 700
2#include <assert.h>
3#include <stdbool.h>
4#include <string.h>
5#include <signal.h>
6#include <sys/wait.h>
7#include <unistd.h>
8#include <wlr/types/wlr_output.h>
9#include <wlr/types/wlr_output_layout.h>
10#include "sway/config.h"
11#include "sway/output.h"
12#include "log.h"
13
14int output_name_cmp(const void *item, const void *data) {
15 const struct output_config *output = item;
16 const char *name = data;
17
18 return strcmp(output->name, name);
19}
20
21void output_get_identifier(char *identifier, size_t len,
22 struct sway_output *output) {
23 struct wlr_output *wlr_output = output->wlr_output;
24 snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model,
25 wlr_output->serial);
26}
27
28struct output_config *new_output_config(const char *name) {
29 struct output_config *oc = calloc(1, sizeof(struct output_config));
30 if (oc == NULL) {
31 return NULL;
32 }
33 oc->name = strdup(name);
34 if (oc->name == NULL) {
35 free(oc);
36 return NULL;
37 }
38 oc->enabled = -1;
39 oc->width = oc->height = -1;
40 oc->refresh_rate = -1;
41 oc->x = oc->y = -1;
42 oc->scale = -1;
43 oc->transform = -1;
44 return oc;
45}
46
47void merge_output_config(struct output_config *dst, struct output_config *src) {
48 if (src->name) {
49 free(dst->name);
50 dst->name = strdup(src->name);
51 }
52 if (src->enabled != -1) {
53 dst->enabled = src->enabled;
54 }
55 if (src->width != -1) {
56 dst->width = src->width;
57 }
58 if (src->height != -1) {
59 dst->height = src->height;
60 }
61 if (src->x != -1) {
62 dst->x = src->x;
63 }
64 if (src->y != -1) {
65 dst->y = src->y;
66 }
67 if (src->scale != -1) {
68 dst->scale = src->scale;
69 }
70 if (src->refresh_rate != -1) {
71 dst->refresh_rate = src->refresh_rate;
72 }
73 if (src->transform != -1) {
74 dst->transform = src->transform;
75 }
76 if (src->background) {
77 free(dst->background);
78 dst->background = strdup(src->background);
79 }
80 if (src->background_option) {
81 free(dst->background_option);
82 dst->background_option = strdup(src->background_option);
83 }
84}
85
86static void set_mode(struct wlr_output *output, int width, int height,
87 float refresh_rate) {
88 int mhz = (int)(refresh_rate * 1000);
89 if (wl_list_empty(&output->modes)) {
90 wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name);
91 wlr_output_set_custom_mode(output, width, height, mhz);
92 return;
93 }
94
95 struct wlr_output_mode *mode, *best = NULL;
96 wl_list_for_each(mode, &output->modes, link) {
97 if (mode->width == width && mode->height == height) {
98 if (mode->refresh == mhz) {
99 best = mode;
100 break;
101 }
102 best = mode;
103 }
104 }
105 if (!best) {
106 wlr_log(L_ERROR, "Configured mode for %s not available", output->name);
107 } else {
108 wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name);
109 wlr_output_set_mode(output, best);
110 }
111}
112
113void terminate_swaybg(pid_t pid) {
114 int ret = kill(pid, SIGTERM);
115 if (ret != 0) {
116 wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
117 } else {
118 int status;
119 waitpid(pid, &status, 0);
120 }
121}
122
123void apply_output_config(struct output_config *oc, struct sway_container *output) {
124 assert(output->type == C_OUTPUT);
125
126 struct wlr_output_layout *output_layout =
127 root_container.sway_root->output_layout;
128 struct wlr_output *wlr_output = output->sway_output->wlr_output;
129
130 if (oc && oc->enabled == 0) {
131 container_destroy(output);
132 wlr_output_layout_remove(root_container.sway_root->output_layout,
133 wlr_output);
134 return;
135 }
136
137 if (oc && oc->width > 0 && oc->height > 0) {
138 wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width,
139 oc->height, oc->refresh_rate);
140 set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate);
141 }
142 if (oc && oc->scale > 0) {
143 wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale);
144 wlr_output_set_scale(wlr_output, oc->scale);
145 }
146 if (oc && oc->transform >= 0) {
147 wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform);
148 wlr_output_set_transform(wlr_output, oc->transform);
149 }
150
151 // Find position for it
152 if (oc && (oc->x != -1 || oc->y != -1)) {
153 wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
154 wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y);
155 } else {
156 wlr_output_layout_add_auto(output_layout, wlr_output);
157 }
158
159 if (!oc || !oc->background) {
160 // Look for a * config for background
161 int i = list_seq_find(config->output_configs, output_name_cmp, "*");
162 if (i >= 0) {
163 oc = config->output_configs->items[i];
164 } else {
165 oc = NULL;
166 }
167 }
168
169 int output_i;
170 for (output_i = 0; output_i < root_container.children->length; ++output_i) {
171 if (root_container.children->items[output_i] == output) {
172 break;
173 }
174 }
175
176 if (oc && oc->background) {
177 if (output->sway_output->bg_pid != 0) {
178 terminate_swaybg(output->sway_output->bg_pid);
179 }
180
181 wlr_log(L_DEBUG, "Setting background for output %d to %s",
182 output_i, oc->background);
183
184 size_t len = snprintf(NULL, 0, "%s %d %s %s",
185 config->swaybg_command ? config->swaybg_command : "swaybg",
186 output_i, oc->background, oc->background_option);
187 char *command = malloc(len + 1);
188 if (!command) {
189 wlr_log(L_DEBUG, "Unable to allocate swaybg command");
190 return;
191 }
192 snprintf(command, len + 1, "%s %d %s %s",
193 config->swaybg_command ? config->swaybg_command : "swaybg",
194 output_i, oc->background, oc->background_option);
195 wlr_log(L_DEBUG, "-> %s", command);
196
197 char *const cmd[] = { "sh", "-c", command, NULL };
198 output->sway_output->bg_pid = fork();
199 if (output->sway_output->bg_pid == 0) {
200 execvp(cmd[0], cmd);
201 }
202 }
203}
204
205void free_output_config(struct output_config *oc) {
206 if (!oc) {
207 return;
208 }
209 free(oc->name);
210 free(oc->background);
211 free(oc->background_option);
212 free(oc);
213}
diff --git a/sway/config/seat.c b/sway/config/seat.c
new file mode 100644
index 00000000..bd8b45c8
--- /dev/null
+++ b/sway/config/seat.c
@@ -0,0 +1,146 @@
1#define _XOPEN_SOURCE 700
2#include <stdlib.h>
3#include <string.h>
4#include "sway/config.h"
5#include "log.h"
6
7struct seat_config *new_seat_config(const char* name) {
8 struct seat_config *seat = calloc(1, sizeof(struct seat_config));
9 if (!seat) {
10 wlr_log(L_DEBUG, "Unable to allocate seat config");
11 return NULL;
12 }
13
14 wlr_log(L_DEBUG, "new_seat_config(%s)", name);
15 seat->name = strdup(name);
16 if (!sway_assert(seat->name, "could not allocate name for seat")) {
17 free(seat);
18 return NULL;
19 }
20
21 seat->fallback = -1;
22 seat->attachments = create_list();
23 if (!sway_assert(seat->attachments,
24 "could not allocate seat attachments list")) {
25 free(seat->name);
26 free(seat);
27 return NULL;
28 }
29
30 return seat;
31}
32
33struct seat_attachment_config *seat_attachment_config_new() {
34 struct seat_attachment_config *attachment =
35 calloc(1, sizeof(struct seat_attachment_config));
36 if (!attachment) {
37 wlr_log(L_DEBUG, "cannot allocate attachment config");
38 return NULL;
39 }
40 return attachment;
41}
42
43static void seat_attachment_config_free(
44 struct seat_attachment_config *attachment) {
45 free(attachment->identifier);
46 free(attachment);
47 return;
48}
49
50static struct seat_attachment_config *seat_attachment_config_copy(
51 struct seat_attachment_config *attachment) {
52 struct seat_attachment_config *copy = seat_attachment_config_new();
53 if (!copy) {
54 return NULL;
55 }
56
57 copy->identifier = strdup(attachment->identifier);
58
59 return copy;
60}
61
62static void merge_seat_attachment_config(struct seat_attachment_config *dest,
63 struct seat_attachment_config *source) {
64 // nothing to merge yet, but there will be some day
65}
66
67void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
68 if (source->name) {
69 free(dest->name);
70 dest->name = strdup(source->name);
71 }
72
73 if (source->fallback != -1) {
74 dest->fallback = source->fallback;
75 }
76
77 for (int i = 0; i < source->attachments->length; ++i) {
78 struct seat_attachment_config *source_attachment =
79 source->attachments->items[i];
80 bool found = false;
81 for (int j = 0; j < dest->attachments->length; ++j) {
82 struct seat_attachment_config *dest_attachment =
83 dest->attachments->items[j];
84 if (strcmp(source_attachment->identifier,
85 dest_attachment->identifier) == 0) {
86 merge_seat_attachment_config(dest_attachment,
87 source_attachment);
88 found = true;
89 }
90 }
91
92 if (!found) {
93 struct seat_attachment_config *copy =
94 seat_attachment_config_copy(source_attachment);
95 if (copy) {
96 list_add(dest->attachments, copy);
97 }
98 }
99 }
100}
101
102struct seat_config *copy_seat_config(struct seat_config *seat) {
103 struct seat_config *copy = new_seat_config(seat->name);
104 if (copy == NULL) {
105 return NULL;
106 }
107
108 merge_seat_config(copy, seat);
109
110 return copy;
111}
112
113void free_seat_config(struct seat_config *seat) {
114 if (!seat) {
115 return;
116 }
117
118 free(seat->name);
119 for (int i = 0; i < seat->attachments->length; ++i) {
120 struct seat_attachment_config *attachment =
121 seat->attachments->items[i];
122 seat_attachment_config_free(attachment);
123 }
124
125 list_free(seat->attachments);
126 free(seat);
127}
128
129int seat_name_cmp(const void *item, const void *data) {
130 const struct seat_config *sc = item;
131 const char *name = data;
132 return strcmp(sc->name, name);
133}
134
135struct seat_attachment_config *seat_config_get_attachment(
136 struct seat_config *seat_config, char *identifier) {
137 for (int i = 0; i < seat_config->attachments->length; ++i) {
138 struct seat_attachment_config *attachment =
139 seat_config->attachments->items[i];
140 if (strcmp(attachment->identifier, identifier) == 0) {
141 return attachment;
142 }
143 }
144
145 return NULL;
146}
diff --git a/sway/container.c b/sway/container.c
deleted file mode 100644
index 829fde69..00000000
--- a/sway/container.c
+++ /dev/null
@@ -1,1016 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h>
3#include <stdlib.h>
4#include <stdbool.h>
5#include <strings.h>
6#include <string.h>
7#include "sway/config.h"
8#include "sway/container.h"
9#include "sway/workspace.h"
10#include "sway/focus.h"
11#include "sway/border.h"
12#include "sway/layout.h"
13#include "sway/input_state.h"
14#include "sway/ipc-server.h"
15#include "sway/output.h"
16#include "log.h"
17#include "stringop.h"
18
19#define ASSERT_NONNULL(PTR) \
20 sway_assert (PTR, #PTR "must be non-null")
21
22static swayc_t *new_swayc(enum swayc_types type) {
23 // next id starts at 1 because 0 is assigned to root_container in layout.c
24 static size_t next_id = 1;
25 swayc_t *c = calloc(1, sizeof(swayc_t));
26 if (!c) {
27 return NULL;
28 }
29 c->id = next_id++;
30 c->handle = -1;
31 c->gaps = -1;
32 c->layout = L_NONE;
33 c->workspace_layout = L_NONE;
34 c->type = type;
35 c->nb_master = 1;
36 c->nb_slave_groups = 1;
37 if (type != C_VIEW) {
38 c->children = create_list();
39 }
40 return c;
41}
42
43static void free_swayc(swayc_t *cont) {
44 if (!ASSERT_NONNULL(cont)) {
45 return;
46 }
47 if (cont->children) {
48 // remove children until there are no more, free_swayc calls
49 // remove_child, which removes child from this container
50 while (cont->children->length) {
51 free_swayc(cont->children->items[0]);
52 }
53 list_free(cont->children);
54 }
55 if (cont->unmanaged) {
56 list_free(cont->unmanaged);
57 }
58 if (cont->floating) {
59 while (cont->floating->length) {
60 free_swayc(cont->floating->items[0]);
61 }
62 list_free(cont->floating);
63 }
64 if (cont->marks) {
65 list_foreach(cont->marks, free);
66 list_free(cont->marks);
67 }
68 if (cont->parent) {
69 remove_child(cont);
70 }
71 if (cont->name) {
72 free(cont->name);
73 }
74 if (cont->class) {
75 free(cont->class);
76 }
77 if (cont->instance) {
78 free(cont->instance);
79 }
80 if (cont->app_id) {
81 free(cont->app_id);
82 }
83 if (cont->bg_pid != 0) {
84 terminate_swaybg(cont->bg_pid);
85 }
86 if (cont->border) {
87 if (cont->border->buffer) {
88 free(cont->border->buffer);
89 }
90 free(cont->border);
91 }
92 free(cont);
93}
94
95static void update_root_geometry() {
96 int width = 0;
97 int height = 0;
98 swayc_t *child;
99 int child_width;
100 int child_height;
101
102 for (int i = 0; i < root_container.children->length; ++i) {
103 child = root_container.children->items[i];
104 child_width = child->width + child->x;
105 child_height = child->height + child->y;
106 if (child_width > width) {
107 width = child_width;
108 }
109
110 if (child_height > height) {
111 height = child_height;
112 }
113 }
114
115 root_container.width = width;
116 root_container.height = height;
117}
118
119// New containers
120
121swayc_t *new_output(wlc_handle handle) {
122 struct wlc_size size;
123 output_get_scaled_size(handle, &size);
124 const char *name = wlc_output_get_name(handle);
125 // Find current outputs to see if this already exists
126 {
127 int i, len = root_container.children->length;
128 for (i = 0; i < len; ++i) {
129 swayc_t *op = root_container.children->items[i];
130 const char *op_name = op->name;
131 if (op_name && name && strcmp(op_name, name) == 0) {
132 sway_log(L_DEBUG, "restoring output %" PRIuPTR ":%s", handle, op_name);
133 return op;
134 }
135 }
136 }
137
138 sway_log(L_DEBUG, "New output %" PRIuPTR ":%s", handle, name);
139
140 struct output_config *oc = NULL, *all = NULL;
141 int i;
142 for (i = 0; i < config->output_configs->length; ++i) {
143 struct output_config *cur = config->output_configs->items[i];
144 if (strcasecmp(name, cur->name) == 0) {
145 sway_log(L_DEBUG, "Matched output config for %s", name);
146 oc = cur;
147 }
148 if (strcasecmp("*", cur->name) == 0) {
149 sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
150 all = cur;
151 }
152
153 if (oc && all) {
154 break;
155 }
156 }
157
158 if (!oc) {
159 oc = all;
160 }
161
162 if (oc && !oc->enabled) {
163 return NULL;
164 }
165
166 swayc_t *output = new_swayc(C_OUTPUT);
167 output->handle = handle;
168 output->name = name ? strdup(name) : NULL;
169 output->width = size.w;
170 output->height = size.h;
171 output->unmanaged = create_list();
172 output->bg_pid = 0;
173
174 apply_output_config(oc, output);
175 add_child(&root_container, output);
176 load_swaybars();
177
178 // Create workspace
179 char *ws_name = NULL;
180 swayc_t *ws = NULL;
181
182 if (name) {
183 for (i = 0; i < config->workspace_outputs->length; ++i) {
184 struct workspace_output *wso = config->workspace_outputs->items[i];
185 if (strcasecmp(wso->output, name) == 0) {
186 sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output);
187 // Check if any other workspaces are using this name
188 if ((ws = workspace_by_name(wso->workspace))) {
189 // if yes, move those to this output, because they should be here
190 move_workspace_to(ws, output);
191 } else if (!ws_name) {
192 // set a workspace name in case we need to create a default one
193 ws_name = strdup(wso->workspace);
194 }
195 }
196 }
197 }
198
199 if (output->children->length == 0) {
200 if (!ws_name) {
201 ws_name = workspace_next_name(output->name);
202 }
203 // create and initialize default workspace
204 sway_log(L_DEBUG, "Creating default workspace %s", ws_name);
205 ws = new_workspace(output, ws_name);
206 ws->is_focused = true;
207 } else {
208 sort_workspaces(output);
209 set_focused_container(output->children->items[0]);
210 }
211
212 free(ws_name);
213 update_root_geometry();
214 return output;
215}
216
217swayc_t *new_workspace(swayc_t *output, const char *name) {
218 if (!ASSERT_NONNULL(output)) {
219 return NULL;
220 }
221 sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle);
222 swayc_t *workspace = new_swayc(C_WORKSPACE);
223
224 workspace->prev_layout = L_NONE;
225 workspace->layout = default_layout(output);
226 workspace->workspace_layout = default_layout(output);
227
228 workspace->x = output->x;
229 workspace->y = output->y;
230 workspace->width = output->width;
231 workspace->height = output->height;
232 workspace->name = !name ? NULL : strdup(name);
233 workspace->visible = false;
234 workspace->floating = create_list();
235
236 add_child(output, workspace);
237 sort_workspaces(output);
238
239 return workspace;
240}
241
242swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
243 if (!ASSERT_NONNULL(child)
244 && !sway_assert(!child->is_floating, "cannot create container around floating window")) {
245 return NULL;
246 }
247 swayc_t *cont = new_swayc(C_CONTAINER);
248
249 sway_log(L_DEBUG, "creating container %p around %p", cont, child);
250
251 cont->prev_layout = L_NONE;
252 cont->layout = layout;
253 cont->width = child->width;
254 cont->height = child->height;
255 cont->x = child->x;
256 cont->y = child->y;
257 cont->visible = child->visible;
258 cont->cached_geometry = child->cached_geometry;
259 cont->gaps = child->gaps;
260
261 /* Container inherits all of workspaces children, layout and whatnot */
262 if (child->type == C_WORKSPACE) {
263 swayc_t *workspace = child;
264 // reorder focus
265 cont->focused = workspace->focused;
266 workspace->focused = cont;
267 // set all children focu to container
268 int i;
269 for (i = 0; i < workspace->children->length; ++i) {
270 ((swayc_t *)workspace->children->items[i])->parent = cont;
271 }
272 // Swap children
273 list_t *tmp_list = workspace->children;
274 workspace->children = cont->children;
275 cont->children = tmp_list;
276 // add container to workspace chidren
277 add_child(workspace, cont);
278 // give them proper layouts
279 cont->layout = workspace->workspace_layout;
280 cont->prev_layout = workspace->prev_layout;
281 /* TODO: might break shit in move_container!!! workspace->layout = layout; */
282 set_focused_container_for(workspace, get_focused_view(workspace));
283 } else { // Or is built around container
284 swayc_t *parent = replace_child(child, cont);
285 if (parent) {
286 add_child(cont, child);
287 }
288 }
289 return cont;
290}
291
292swayc_t *new_view(swayc_t *sibling, wlc_handle handle) {
293 if (!ASSERT_NONNULL(sibling)) {
294 return NULL;
295 }
296 const char *title = wlc_view_get_title(handle);
297 swayc_t *view = new_swayc(C_VIEW);
298 sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%s to container %p %d",
299 handle, title, sibling, sibling ? sibling->type : 0);
300 // Setup values
301 view->handle = handle;
302 view->name = title ? strdup(title) : NULL;
303 const char *class = wlc_view_get_class(handle);
304 view->class = class ? strdup(class) : NULL;
305 const char *instance = wlc_view_get_instance(handle);
306 view->instance = instance ? strdup(instance) : NULL;
307 const char *app_id = wlc_view_get_app_id(handle);
308 view->app_id = app_id ? strdup(app_id) : NULL;
309 view->visible = true;
310 view->is_focused = true;
311 view->sticky = false;
312 view->width = 0;
313 view->height = 0;
314 view->desired_width = -1;
315 view->desired_height = -1;
316 // setup border
317 view->border_type = config->border;
318 view->border_thickness = config->border_thickness;
319
320 view->is_floating = false;
321
322 if (sibling->type == C_WORKSPACE) {
323 // Case of focused workspace, just create as child of it
324 add_child(sibling, view);
325 } else {
326 // Regular case, create as sibling of current container
327 add_sibling(sibling, view);
328 }
329 return view;
330}
331
332swayc_t *new_floating_view(wlc_handle handle) {
333 if (swayc_active_workspace() == NULL) {
334 return NULL;
335 }
336 const char *title = wlc_view_get_title(handle);
337 swayc_t *view = new_swayc(C_VIEW);
338 sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%x:%s as a floating view",
339 handle, wlc_view_get_type(handle), title);
340 // Setup values
341 view->handle = handle;
342 view->name = title ? strdup(title) : NULL;
343 const char *class = wlc_view_get_class(handle);
344 view->class = class ? strdup(class) : NULL;
345 const char *instance = wlc_view_get_instance(handle);
346 view->instance = instance ? strdup(instance) : NULL;
347 const char *app_id = wlc_view_get_app_id(handle);
348 view->app_id = app_id ? strdup(app_id) : NULL;
349 view->visible = true;
350 view->sticky = false;
351
352 // Set the geometry of the floating view
353 const struct wlc_geometry *geometry = wlc_view_get_geometry(handle);
354
355 // give it requested geometry, but place in center if possible
356 // in top left otherwise
357 if (geometry->size.w != 0) {
358 view->x = (swayc_active_workspace()->width - geometry->size.w) / 2;
359 } else {
360 view->x = 0;
361 }
362 if (geometry->size.h != 0) {
363 view->y = (swayc_active_workspace()->height - geometry->size.h) / 2;
364 } else {
365 view->y = 0;
366 }
367
368 view->width = geometry->size.w;
369 view->height = geometry->size.h;
370
371 view->desired_width = view->width;
372 view->desired_height = view->height;
373
374 // setup border
375 view->border_type = config->floating_border;
376 view->border_thickness = config->floating_border_thickness;
377
378 view->is_floating = true;
379
380 // Case of focused workspace, just create as child of it
381 list_add(swayc_active_workspace()->floating, view);
382 view->parent = swayc_active_workspace();
383 if (swayc_active_workspace()->focused == NULL) {
384 set_focused_container_for(swayc_active_workspace(), view);
385 }
386 return view;
387}
388
389void floating_view_sane_size(swayc_t *view) {
390 // floating_minimum is used as sane value.
391 // floating_maximum has priority in case of conflict
392 // TODO: implement total_outputs_dimensions()
393 if (config->floating_minimum_height != -1 &&
394 view->desired_height < config->floating_minimum_height) {
395 view->desired_height = config->floating_minimum_height;
396 }
397 if (config->floating_minimum_width != -1 &&
398 view->desired_width < config->floating_minimum_width) {
399 view->desired_width = config->floating_minimum_width;
400 }
401
402 // if 0 do not resize, only enforce max value
403 if (config->floating_maximum_height == 0) {
404 // Missing total_outputs_dimensions() using swayc_active_workspace()
405 config->floating_maximum_height = swayc_active_workspace()->height;
406
407 } else if (config->floating_maximum_height != -1 &&
408 view->desired_height > config->floating_maximum_height) {
409 view->desired_height = config->floating_maximum_height;
410 }
411
412 // if 0 do not resize, only enforce max value
413 if (config->floating_maximum_width == 0) {
414 // Missing total_outputs_dimensions() using swayc_active_workspace()
415 config->floating_maximum_width = swayc_active_workspace()->width;
416
417 } else if (config->floating_maximum_width != -1 &&
418 view->desired_width > config->floating_maximum_width) {
419 view->desired_width = config->floating_maximum_width;
420 }
421
422 sway_log(L_DEBUG, "Sane values for view to %d x %d @ %.f, %.f",
423 view->desired_width, view->desired_height, view->x, view->y);
424
425 return;
426}
427
428
429// Destroy container
430
431swayc_t *destroy_output(swayc_t *output) {
432 if (!ASSERT_NONNULL(output)) {
433 return NULL;
434 }
435 if (output->children->length > 0) {
436 // TODO save workspaces when there are no outputs.
437 // TODO also check if there will ever be no outputs except for exiting
438 // program
439 if (root_container.children->length > 1) {
440 int p = root_container.children->items[0] == output;
441 // Move workspace from this output to another output
442 while (output->children->length) {
443 swayc_t *child = output->children->items[0];
444 remove_child(child);
445 add_child(root_container.children->items[p], child);
446 }
447 sort_workspaces(root_container.children->items[p]);
448 update_visibility(root_container.children->items[p]);
449 arrange_windows(root_container.children->items[p], -1, -1);
450 }
451 }
452 sway_log(L_DEBUG, "OUTPUT: Destroying output '%" PRIuPTR "'", output->handle);
453 free_swayc(output);
454 update_root_geometry();
455 return &root_container;
456}
457
458swayc_t *destroy_workspace(swayc_t *workspace) {
459 if (!ASSERT_NONNULL(workspace)) {
460 return NULL;
461 }
462
463 // Do not destroy this if it's the last workspace on this output
464 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
465 if (output && output->children->length == 1) {
466 return NULL;
467 }
468
469 swayc_t *parent = workspace->parent;
470 // destroy the WS if there are no children
471 if (workspace->children->length == 0 && workspace->floating->length == 0) {
472 sway_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
473 ipc_event_workspace(workspace, NULL, "empty");
474 } else {
475 // Move children to a different workspace on this output
476 swayc_t *new_workspace = NULL;
477 int i;
478 for(i = 0; i < output->children->length; i++) {
479 if(output->children->items[i] != workspace) {
480 break;
481 }
482 }
483 new_workspace = output->children->items[i];
484
485 sway_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
486 workspace->name, new_workspace->name);
487
488 for(i = 0; i < workspace->children->length; i++) {
489 move_container_to(workspace->children->items[i], new_workspace);
490 }
491
492 for(i = 0; i < workspace->floating->length; i++) {
493 move_container_to(workspace->floating->items[i], new_workspace);
494 }
495 }
496
497 free_swayc(workspace);
498 return parent;
499}
500
501swayc_t *destroy_container(swayc_t *container) {
502 if (!ASSERT_NONNULL(container)) {
503 return NULL;
504 }
505 while (container->children->length == 0 && container->type == C_CONTAINER) {
506 sway_log(L_DEBUG, "Container: Destroying container '%p'", container);
507 swayc_t *parent = container->parent;
508 free_swayc(container);
509 container = parent;
510 }
511 return container;
512}
513
514swayc_t *destroy_view(swayc_t *view) {
515 if (!ASSERT_NONNULL(view)) {
516 return NULL;
517 }
518 sway_log(L_DEBUG, "Destroying view '%p'", view);
519 swayc_t *parent = view->parent;
520 free_swayc(view);
521
522 // Destroy empty containers
523 if (parent && parent->type == C_CONTAINER) {
524 return destroy_container(parent);
525 }
526 return parent;
527}
528
529// Container lookup
530
531
532swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) {
533 if (!container->children) {
534 return NULL;
535 }
536 // Special case for checking floating stuff
537 int i;
538 if (container->type == C_WORKSPACE) {
539 for (i = 0; i < container->floating->length; ++i) {
540 swayc_t *child = container->floating->items[i];
541 if (test(child, data)) {
542 return child;
543 }
544 }
545 }
546 for (i = 0; i < container->children->length; ++i) {
547 swayc_t *child = container->children->items[i];
548 if (test(child, data)) {
549 return child;
550 } else {
551 swayc_t *res = swayc_by_test(child, test, data);
552 if (res) {
553 return res;
554 }
555 }
556 }
557 return NULL;
558}
559
560static bool test_name(swayc_t *view, void *data) {
561 if (!view || !view->name) {
562 return false;
563 }
564 return strcmp(view->name, data) == 0;
565}
566
567swayc_t *swayc_by_name(const char *name) {
568 return swayc_by_test(&root_container, test_name, (void *)name);
569}
570
571swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
572 if (!ASSERT_NONNULL(container)) {
573 return NULL;
574 }
575 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
576 return NULL;
577 }
578 do {
579 container = container->parent;
580 } while (container && container->type != type);
581 return container;
582}
583
584swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) {
585 if (!ASSERT_NONNULL(container)) {
586 return NULL;
587 }
588 if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
589 return NULL;
590 }
591 do {
592 container = container->parent;
593 } while (container && container->layout != layout);
594 return container;
595}
596
597swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types type) {
598 if (!ASSERT_NONNULL(container)) {
599 return NULL;
600 }
601 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
602 return NULL;
603 }
604 do {
605 container = container->focused;
606 } while (container && container->type != type);
607 return container;
608}
609
610swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) {
611 if (!ASSERT_NONNULL(container)) {
612 return NULL;
613 }
614 if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
615 return NULL;
616 }
617 do {
618 container = container->focused;
619 } while (container && container->layout != layout);
620 return container;
621}
622
623
624static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) {
625 if (!parent || !parent->children) {
626 return NULL;
627 }
628 int i, len;
629 swayc_t **child;
630 if (parent->type == C_WORKSPACE) {
631 len = parent->floating->length;
632 child = (swayc_t **)parent->floating->items;
633 for (i = 0; i < len; ++i, ++child) {
634 if ((*child)->handle == handle) {
635 return *child;
636 }
637 }
638 }
639
640 len = parent->children->length;
641 child = (swayc_t**)parent->children->items;
642 for (i = 0; i < len; ++i, ++child) {
643 if ((*child)->handle == handle) {
644 return *child;
645 } else {
646 swayc_t *res;
647 if ((res = _swayc_by_handle_helper(handle, *child))) {
648 return res;
649 }
650 }
651 }
652 return NULL;
653}
654
655swayc_t *swayc_by_handle(wlc_handle handle) {
656 return _swayc_by_handle_helper(handle, &root_container);
657}
658
659swayc_t *swayc_active_output(void) {
660 return root_container.focused;
661}
662
663swayc_t *swayc_active_workspace(void) {
664 return root_container.focused ? root_container.focused->focused : NULL;
665}
666
667swayc_t *swayc_active_workspace_for(swayc_t *cont) {
668 if (!cont) {
669 return NULL;
670 }
671 switch (cont->type) {
672 case C_ROOT:
673 cont = cont->focused;
674 /* Fallthrough */
675
676 case C_OUTPUT:
677 cont = cont ? cont->focused : NULL;
678 /* Fallthrough */
679
680 case C_WORKSPACE:
681 return cont;
682
683 default:
684 return swayc_parent_by_type(cont, C_WORKSPACE);
685 }
686}
687
688static bool pointer_test(swayc_t *view, void *_origin) {
689 const struct wlc_point *origin = _origin;
690 // Determine the output that the view is under
691 swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT);
692 if (origin->x >= view->x && origin->y >= view->y
693 && origin->x < view->x + view->width && origin->y < view->y + view->height
694 && view->visible && parent == root_container.focused) {
695 return true;
696 }
697 return false;
698}
699
700swayc_t *container_under_pointer(void) {
701 // root.output->workspace
702 if (!root_container.focused) {
703 return NULL;
704 }
705 swayc_t *lookup = root_container.focused;
706 // Case of empty workspace
707 if (lookup->children && !lookup->unmanaged) {
708 return NULL;
709 }
710 double x, y;
711 wlc_pointer_get_position_v2(&x, &y);
712 struct wlc_point origin = { .x = x, .y = y };
713
714 while (lookup && lookup->type != C_VIEW) {
715 int i;
716 int len;
717 for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) {
718 wlc_handle *handle = lookup->unmanaged->items[_i];
719 const struct wlc_geometry *geo = wlc_view_get_geometry(*handle);
720 if (origin.x >= geo->origin.x && origin.y >= geo->origin.y
721 && origin.x < geo->origin.x + (int)geo->size.w
722 && origin.y < geo->origin.y + (int)geo->size.h) {
723 // Hack: we force focus upon unmanaged views here
724 wlc_view_focus(*handle);
725 return NULL;
726 }
727 }
728 // if tabbed/stacked go directly to focused container, otherwise search
729 // children
730 if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) {
731 lookup = lookup->focused;
732 continue;
733 }
734 // if workspace, search floating
735 if (lookup->type == C_WORKSPACE) {
736 i = len = lookup->floating->length;
737 bool got_floating = false;
738 while (--i > -1) {
739 if (pointer_test(lookup->floating->items[i], &origin)) {
740 lookup = lookup->floating->items[i];
741 got_floating = true;
742 break;
743 }
744 }
745 if (got_floating) {
746 continue;
747 }
748 }
749 // search children
750 len = lookup->children->length;
751 for (i = 0; i < len; ++i) {
752 if (pointer_test(lookup->children->items[i], &origin)) {
753 lookup = lookup->children->items[i];
754 break;
755 }
756 }
757 // when border and titles are done, this could happen
758 if (i == len) {
759 break;
760 }
761 }
762 return lookup;
763}
764
765swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data) {
766 if (container->children == NULL || container->children->length == 0) {
767 return NULL;
768 }
769
770 swayc_t *con;
771 if (container->type == C_WORKSPACE) {
772 for (int i = 0; i < container->floating->length; ++i) {
773 con = container->floating->items[i];
774 if (f(con, data)) {
775 return con;
776 }
777 con = container_find(con, f, data);
778 if (con != NULL) {
779 return con;
780 }
781 }
782 }
783
784 for (int i = 0; i < container->children->length; ++i) {
785 con = container->children->items[i];
786 if (f(con, data)) {
787 return con;
788 }
789
790 con = container_find(con, f, data);
791 if (con != NULL) {
792 return con;
793 }
794 }
795
796 return NULL;
797}
798
799// Container information
800
801bool swayc_is_fullscreen(swayc_t *view) {
802 return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN);
803}
804
805bool swayc_is_active(swayc_t *view) {
806 return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED);
807}
808
809bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) {
810 while (child != &root_container) {
811 child = child->parent;
812 if (child == parent) {
813 return true;
814 }
815 }
816 return false;
817}
818
819bool swayc_is_child_of(swayc_t *child, swayc_t *parent) {
820 return swayc_is_parent_of(parent, child);
821}
822
823bool swayc_is_empty_workspace(swayc_t *container) {
824 return container->type == C_WORKSPACE && container->children->length == 0;
825}
826
827int swayc_gap(swayc_t *container) {
828 if (container->type == C_VIEW || container->type == C_CONTAINER) {
829 return container->gaps >= 0 ? container->gaps : config->gaps_inner;
830 } else if (container->type == C_WORKSPACE) {
831 int base = container->gaps >= 0 ? container->gaps : config->gaps_outer;
832 if (config->edge_gaps && !(config->smart_gaps && container->children->length == 1)) {
833 // the inner gap is created via a margin around each window which
834 // is half the gap size, so the workspace also needs half a gap
835 // size to make the outermost gap the same size (excluding the
836 // actual "outer gap" size which is handled independently)
837 return base + config->gaps_inner / 2;
838 } else if (config->smart_gaps && container->children->length == 1) {
839 return 0;
840 } else {
841 return base;
842 }
843 } else {
844 return 0;
845 }
846}
847
848// Mapping
849
850void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) {
851 if (container) {
852 int i;
853 if (container->children) {
854 for (i = 0; i < container->children->length; ++i) {
855 swayc_t *child = container->children->items[i];
856 container_map(child, f, data);
857 }
858 }
859 if (container->floating) {
860 for (i = 0; i < container->floating->length; ++i) {
861 swayc_t *child = container->floating->items[i];
862 container_map(child, f, data);
863 }
864 }
865 f(container, data);
866 }
867}
868
869void update_visibility_output(swayc_t *container, wlc_handle output) {
870 // Inherit visibility
871 swayc_t *parent = container->parent;
872 container->visible = parent->visible;
873 // special cases where visibility depends on focus
874 if (parent->type == C_OUTPUT || parent->layout == L_TABBED ||
875 parent->layout == L_STACKED) {
876 container->visible = parent->focused == container && parent->visible;
877 }
878 // Set visibility and output for view
879 if (container->type == C_VIEW) {
880 wlc_view_set_output(container->handle, output);
881 wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0);
882 }
883 // Update visibility for children
884 else {
885 if (container->children) {
886 int i, len = container->children->length;
887 for (i = 0; i < len; ++i) {
888 update_visibility_output(container->children->items[i], output);
889 }
890 }
891 if (container->floating) {
892 int i, len = container->floating->length;
893 for (i = 0; i < len; ++i) {
894 update_visibility_output(container->floating->items[i], output);
895 }
896 }
897 }
898}
899
900void update_visibility(swayc_t *container) {
901 if (!container) return;
902 switch (container->type) {
903 case C_ROOT:
904 container->visible = true;
905 if (container->children) {
906 int i, len = container->children->length;
907 for (i = 0; i < len; ++i) {
908 update_visibility(container->children->items[i]);
909 }
910 }
911 return;
912
913 case C_OUTPUT:
914 container->visible = true;
915 if (container->children) {
916 int i, len = container->children->length;
917 for (i = 0; i < len; ++i) {
918 update_visibility_output(container->children->items[i], container->handle);
919 }
920 }
921 return;
922
923 default:
924 {
925 swayc_t *op = swayc_parent_by_type(container, C_OUTPUT);
926 update_visibility_output(container, op->handle);
927 }
928 }
929}
930
931void set_gaps(swayc_t *view, void *_data) {
932 int *data = _data;
933 if (!ASSERT_NONNULL(view)) {
934 return;
935 }
936 if (view->type == C_WORKSPACE || view->type == C_VIEW) {
937 view->gaps = *data;
938 }
939}
940
941void add_gaps(swayc_t *view, void *_data) {
942 int *data = _data;
943 if (!ASSERT_NONNULL(view)) {
944 return;
945 }
946 if (view->type == C_WORKSPACE || view->type == C_VIEW) {
947 if ((view->gaps += *data) < 0) {
948 view->gaps = 0;
949 }
950 }
951}
952
953static void close_view(swayc_t *container, void *data) {
954 if (container->type == C_VIEW) {
955 wlc_view_close(container->handle);
956 }
957}
958
959void close_views(swayc_t *container) {
960 container_map(container, close_view, NULL);
961}
962
963swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view) {
964 swayc_t *parent = NULL;
965 if (!ASSERT_NONNULL(view)) {
966 return NULL;
967 }
968 while (view->type != C_WORKSPACE && view->parent && view->parent->type != C_WORKSPACE) {
969 view = view->parent;
970 if (view->layout == L_TABBED || view->layout == L_STACKED) {
971 parent = view;
972 }
973 }
974
975 return parent;
976}
977
978swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
979 if (!ASSERT_NONNULL(con)) {
980 return NULL;
981 }
982 if (con->parent && (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED)) {
983 return con->parent;
984 }
985 return NULL;
986}
987
988swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
989 // if layout change modifies the auto layout's major axis, swap width and height
990 // to preserve current ratios.
991 if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
992 enum swayc_layouts prev_major =
993 container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT
994 ? L_HORIZ : L_VERT;
995 enum swayc_layouts new_major =
996 layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT
997 ? L_HORIZ : L_VERT;
998 if (new_major != prev_major) {
999 for (int i = 0; i < container->children->length; ++i) {
1000 swayc_t *child = container->children->items[i];
1001 double h = child->height;
1002 child->height = child->width;
1003 child->width = h;
1004 }
1005 }
1006 }
1007 if (container->type == C_WORKSPACE) {
1008 container->workspace_layout = layout;
1009 if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
1010 container->layout = layout;
1011 }
1012 } else {
1013 container->layout = layout;
1014 }
1015 return container;
1016}
diff --git a/sway/criteria.c b/sway/criteria.c
index e8978ebe..22e9a49b 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -4,13 +4,15 @@
4#include <stdbool.h> 4#include <stdbool.h>
5#include <pcre.h> 5#include <pcre.h>
6#include "sway/criteria.h" 6#include "sway/criteria.h"
7#include "sway/container.h" 7#include "sway/tree/container.h"
8#include "sway/config.h" 8#include "sway/config.h"
9#include "sway/tree/view.h"
9#include "stringop.h" 10#include "stringop.h"
10#include "list.h" 11#include "list.h"
11#include "log.h" 12#include "log.h"
12 13
13enum criteria_type { // *must* keep in sync with criteria_strings[] 14enum criteria_type { // *must* keep in sync with criteria_strings[]
15 CRIT_APP_ID,
14 CRIT_CLASS, 16 CRIT_CLASS,
15 CRIT_CON_ID, 17 CRIT_CON_ID,
16 CRIT_CON_MARK, 18 CRIT_CON_MARK,
@@ -27,6 +29,7 @@ enum criteria_type { // *must* keep in sync with criteria_strings[]
27}; 29};
28 30
29static const char * const criteria_strings[CRIT_LAST] = { 31static const char * const criteria_strings[CRIT_LAST] = {
32 [CRIT_APP_ID] = "app_id",
30 [CRIT_CLASS] = "class", 33 [CRIT_CLASS] = "class",
31 [CRIT_CON_ID] = "con_id", 34 [CRIT_CON_ID] = "con_id",
32 [CRIT_CON_MARK] = "con_mark", 35 [CRIT_CON_MARK] = "con_mark",
@@ -100,8 +103,9 @@ static int countchr(char *str, char c) {
100// of buf. 103// of buf.
101// 104//
102// Returns error string or NULL if successful. 105// Returns error string or NULL if successful.
103static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) { 106static char *crit_tokens(int *argc, char ***buf,
104 sway_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str); 107 const char * const criteria_str) {
108 wlr_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
105 char *base = criteria_from(criteria_str); 109 char *base = criteria_from(criteria_str);
106 char *head = base; 110 char *head = base;
107 char *namep = head; // start of criteria name 111 char *namep = head; // start of criteria name
@@ -247,13 +251,13 @@ char *extract_crit_tokens(list_t *tokens, const char * const criteria) {
247 free_crit_token(token); 251 free_crit_token(token);
248 goto ect_cleanup; 252 goto ect_cleanup;
249 } else if (token->type == CRIT_URGENT || crit_is_focused(value)) { 253 } else if (token->type == CRIT_URGENT || crit_is_focused(value)) {
250 sway_log(L_DEBUG, "%s -> \"%s\"", name, value); 254 wlr_log(L_DEBUG, "%s -> \"%s\"", name, value);
251 list_add(tokens, token); 255 list_add(tokens, token);
252 } else if((error = generate_regex(&token->regex, value))) { 256 } else if((error = generate_regex(&token->regex, value))) {
253 free_crit_token(token); 257 free_crit_token(token);
254 goto ect_cleanup; 258 goto ect_cleanup;
255 } else { 259 } else {
256 sway_log(L_DEBUG, "%s -> /%s/", name, value); 260 wlr_log(L_DEBUG, "%s -> /%s/", name, value);
257 list_add(tokens, token); 261 list_add(tokens, token);
258 } 262 }
259 } 263 }
@@ -268,8 +272,8 @@ static int regex_cmp(const char *item, const pcre *regex) {
268} 272}
269 273
270// test a single view if it matches list of criteria tokens (all of them). 274// test a single view if it matches list of criteria tokens (all of them).
271static bool criteria_test(swayc_t *cont, list_t *tokens) { 275static bool criteria_test(struct sway_container *cont, list_t *tokens) {
272 if (cont->type != C_VIEW) { 276 if (cont->type != C_CONTAINER && cont->type != C_VIEW) {
273 return false; 277 return false;
274 } 278 }
275 int matches = 0; 279 int matches = 0;
@@ -277,94 +281,85 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
277 struct crit_token *crit = tokens->items[i]; 281 struct crit_token *crit = tokens->items[i];
278 switch (crit->type) { 282 switch (crit->type) {
279 case CRIT_CLASS: 283 case CRIT_CLASS:
280 if (!cont->class) { 284 {
281 // ignore 285 const char *class = view_get_class(cont->sway_view);
282 } else if (crit_is_focused(crit->raw)) { 286 if (!class) {
283 swayc_t *focused = get_focused_view(&root_container); 287 break;
284 if (focused->class && strcmp(cont->class, focused->class) == 0) { 288 }
289 if (crit->regex && regex_cmp(class, crit->regex) == 0) {
285 matches++; 290 matches++;
286 } 291 }
287 } else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) { 292 break;
288 matches++;
289 } 293 }
290 break; 294 case CRIT_CON_ID:
291 case CRIT_CON_ID: { 295 {
292 char *endptr; 296 char *endptr;
293 size_t crit_id = strtoul(crit->raw, &endptr, 10); 297 size_t crit_id = strtoul(crit->raw, &endptr, 10);
294 298
295 if (*endptr == 0 && cont->id == crit_id) { 299 if (*endptr == 0 && cont->id == crit_id) {
296 ++matches;
297 }
298 break;
299 }
300 case CRIT_CON_MARK:
301 if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
302 // Make sure it isn't matching the NUL string
303 if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
304 ++matches; 300 ++matches;
305 } 301 }
302 break;
306 } 303 }
304 case CRIT_CON_MARK:
305 // TODO
307 break; 306 break;
308 case CRIT_FLOATING: 307 case CRIT_FLOATING:
309 if (cont->is_floating) { 308 // TODO
310 matches++;
311 }
312 break; 309 break;
313 case CRIT_ID: 310 case CRIT_ID:
314 if (!cont->app_id) { 311 // TODO
315 // ignore
316 } else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) {
317 matches++;
318 }
319 break; 312 break;
313 case CRIT_APP_ID:
314 {
315 const char *app_id = view_get_app_id(cont->sway_view);
316 if (!app_id) {
317 break;
318 }
319
320 if (crit->regex && regex_cmp(app_id, crit->regex) == 0) {
321 matches++;
322 }
323 break;
324 }
320 case CRIT_INSTANCE: 325 case CRIT_INSTANCE:
321 if (!cont->instance) { 326 {
322 // ignore 327 const char *instance = view_get_instance(cont->sway_view);
323 } else if (crit_is_focused(crit->raw)) { 328 if (!instance) {
324 swayc_t *focused = get_focused_view(&root_container); 329 break;
325 if (focused->instance && strcmp(cont->instance, focused->instance) == 0) { 330 }
331
332 if (crit->regex && regex_cmp(instance, crit->regex) == 0) {
326 matches++; 333 matches++;
327 } 334 }
328 } else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) { 335 break;
329 matches++;
330 } 336 }
331 break;
332 case CRIT_TILING: 337 case CRIT_TILING:
333 if (!cont->is_floating) { 338 // TODO
334 matches++;
335 }
336 break; 339 break;
337 case CRIT_TITLE: 340 case CRIT_TITLE:
338 if (!cont->name) { 341 {
339 // ignore 342 const char *title = view_get_title(cont->sway_view);
340 } else if (crit_is_focused(crit->raw)) { 343 if (!title) {
341 swayc_t *focused = get_focused_view(&root_container); 344 break;
342 if (focused->name && strcmp(cont->name, focused->name) == 0) { 345 }
346
347 if (crit->regex && regex_cmp(title, crit->regex) == 0) {
343 matches++; 348 matches++;
344 } 349 }
345 } else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) { 350 break;
346 matches++;
347 } 351 }
348 break; 352 case CRIT_URGENT:
349 case CRIT_URGENT: // "latest" or "oldest" 353 // TODO "latest" or "oldest"
350 break; 354 break;
351 case CRIT_WINDOW_ROLE: 355 case CRIT_WINDOW_ROLE:
356 // TODO
352 break; 357 break;
353 case CRIT_WINDOW_TYPE: 358 case CRIT_WINDOW_TYPE:
354 // TODO wlc indeed exposes this information 359 // TODO
355 break; 360 break;
356 case CRIT_WORKSPACE: ; 361 case CRIT_WORKSPACE:
357 swayc_t *cont_ws = swayc_parent_by_type(cont, C_WORKSPACE); 362 // TODO
358 if (!cont_ws || !cont_ws->name) {
359 // ignore
360 } else if (crit_is_focused(crit->raw)) {
361 swayc_t *focused_ws = swayc_active_workspace();
362 if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) {
363 matches++;
364 }
365 } else if (crit->regex && regex_cmp(cont_ws->name, crit->regex) == 0) {
366 matches++;
367 }
368 break; 363 break;
369 default: 364 default:
370 sway_abort("Invalid criteria type (%i)", crit->type); 365 sway_abort("Invalid criteria type (%i)", crit->type);
@@ -403,7 +398,7 @@ void free_criteria(struct criteria *crit) {
403 free(crit); 398 free(crit);
404} 399}
405 400
406bool criteria_any(swayc_t *cont, list_t *criteria) { 401bool criteria_any(struct sway_container *cont, list_t *criteria) {
407 for (int i = 0; i < criteria->length; i++) { 402 for (int i = 0; i < criteria->length; i++) {
408 struct criteria *bc = criteria->items[i]; 403 struct criteria *bc = criteria->items[i];
409 if (criteria_test(cont, bc->tokens)) { 404 if (criteria_test(cont, bc->tokens)) {
@@ -413,7 +408,7 @@ bool criteria_any(swayc_t *cont, list_t *criteria) {
413 return false; 408 return false;
414} 409}
415 410
416list_t *criteria_for(swayc_t *cont) { 411list_t *criteria_for(struct sway_container *cont) {
417 list_t *criteria = config->criteria, *matches = create_list(); 412 list_t *criteria = config->criteria, *matches = create_list();
418 for (int i = 0; i < criteria->length; i++) { 413 for (int i = 0; i < criteria->length; i++) {
419 struct criteria *bc = criteria->items[i]; 414 struct criteria *bc = criteria->items[i];
@@ -429,23 +424,22 @@ struct list_tokens {
429 list_t *tokens; 424 list_t *tokens;
430}; 425};
431 426
432static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) { 427static void container_match_add(struct sway_container *container,
428 struct list_tokens *list_tokens) {
433 if (criteria_test(container, list_tokens->tokens)) { 429 if (criteria_test(container, list_tokens->tokens)) {
434 list_add(list_tokens->list, container); 430 list_add(list_tokens->list, container);
435 } 431 }
436} 432}
437 433
438list_t *container_for(list_t *tokens) { 434list_t *container_for_crit_tokens(list_t *tokens) {
439 struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens}; 435 struct list_tokens list_tokens =
436 (struct list_tokens){create_list(), tokens};
440 437
441 container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens); 438 container_for_each_descendant_dfs(&root_container,
442 439 (void (*)(struct sway_container *, void *))container_match_add,
443 for (int i = 0; i < scratchpad->length; ++i) { 440 &list_tokens);
444 swayc_t *c = scratchpad->items[i];
445 if (criteria_test(c, tokens)) {
446 list_add(list_tokens.list, c);
447 }
448 }
449 441
442 // TODO look in the scratchpad
443
450 return list_tokens.list; 444 return list_tokens.list;
451} 445}
diff --git a/sway/debug-tree.c b/sway/debug-tree.c
new file mode 100644
index 00000000..ae0a1869
--- /dev/null
+++ b/sway/debug-tree.c
@@ -0,0 +1,119 @@
1#include <pango/pangocairo.h>
2#include <wlr/backend.h>
3#include <wlr/render/wlr_texture.h>
4#include <wlr/util/log.h>
5#include "config.h"
6#include "sway/input/input-manager.h"
7#include "sway/input/seat.h"
8#include "sway/server.h"
9#include "sway/tree/container.h"
10#include "sway/tree/layout.h"
11#include "cairo.h"
12#include "config.h"
13#include "pango.h"
14
15static const char *layout_to_str(enum sway_container_layout layout) {
16 switch (layout) {
17 case L_HORIZ:
18 return "L_HORIZ";
19 case L_VERT:
20 return "L_VERT";
21 case L_STACKED:
22 return "L_STACKED";
23 case L_TABBED:
24 return "L_TABBED";
25 case L_FLOATING:
26 return "L_FLOATING";
27 case L_NONE:
28 default:
29 return "L_NONE";
30 }
31}
32
33static int draw_container(cairo_t *cairo, struct sway_container *container,
34 struct sway_container *focus, int x, int y) {
35 int text_width, text_height;
36 get_text_size(cairo, "monospace", &text_width, &text_height,
37 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
38 container_type_to_str(container->type), container->id, container->name,
39 layout_to_str(container->layout),
40 container->width, container->height, container->x, container->y);
41 cairo_save(cairo);
42 cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
43 cairo_set_source_u32(cairo, 0xFFFFFFE0);
44 cairo_fill(cairo);
45 int height = text_height;
46 if (container->children) {
47 for (int i = 0; i < container->children->length; ++i) {
48 struct sway_container *child = container->children->items[i];
49 if (child->parent == container) {
50 cairo_set_source_u32(cairo, 0x000000FF);
51 } else {
52 cairo_set_source_u32(cairo, 0xFF0000FF);
53 }
54 height += draw_container(cairo, child, focus, x + 10, y + height);
55 }
56 }
57 cairo_set_source_u32(cairo, 0xFFFFFFE0);
58 cairo_rectangle(cairo, x, y, 2, height);
59 cairo_fill(cairo);
60 cairo_restore(cairo);
61 cairo_move_to(cairo, x, y);
62 if (focus == container) {
63 cairo_set_source_u32(cairo, 0x0000FFFF);
64 }
65 pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
66 container_type_to_str(container->type), container->id, container->name,
67 layout_to_str(container->layout),
68 container->width, container->height, container->x, container->y);
69 return height;
70}
71
72bool enable_debug_tree = false;
73
74void update_debug_tree() {
75 if (!enable_debug_tree) {
76 return;
77 }
78
79 int width = 640, height = 480;
80 for (int i = 0; i < root_container.children->length; ++i) {
81 struct sway_container *container = root_container.children->items[i];
82 if (container->width > width) {
83 width = container->width;
84 }
85 if (container->height > height) {
86 height = container->height;
87 }
88 }
89 cairo_surface_t *surface =
90 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
91 cairo_t *cairo = cairo_create(surface);
92 PangoContext *pango = pango_cairo_create_context(cairo);
93
94 struct sway_seat *seat = NULL;
95 wl_list_for_each(seat, &input_manager->seats, link) {
96 break;
97 }
98
99 struct sway_container *focus = NULL;
100 if (seat != NULL) {
101 focus = seat_get_focus(seat);
102 }
103 cairo_set_source_u32(cairo, 0x000000FF);
104 draw_container(cairo, &root_container, focus, 0, 0);
105
106 cairo_surface_flush(surface);
107 struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
108 if (root_container.sway_root->debug_tree) {
109 wlr_texture_destroy(root_container.sway_root->debug_tree);
110 }
111 unsigned char *data = cairo_image_surface_get_data(surface);
112 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
113 struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
114 WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
115 root_container.sway_root->debug_tree = texture;
116 cairo_surface_destroy(surface);
117 g_object_unref(pango);
118 cairo_destroy(cairo);
119}
diff --git a/sway/debug_log.c b/sway/debug_log.c
deleted file mode 100644
index d1eafae8..00000000
--- a/sway/debug_log.c
+++ /dev/null
@@ -1,103 +0,0 @@
1#include "log.h"
2#include "sway.h"
3#include <stdarg.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <libgen.h>
7#include <fcntl.h>
8#include <unistd.h>
9#include <signal.h>
10#include <errno.h>
11#include <string.h>
12#include <stringop.h>
13#include "sway/workspace.h"
14
15/* XXX:DEBUG:XXX */
16static void container_log(const swayc_t *c, int depth) {
17 fprintf(stderr, "focus:%c",
18 c == get_focused_view(&root_container) ? 'K':
19 c == get_focused_container(&root_container) ? 'F' : // Focused
20 c == swayc_active_workspace() ? 'W' : // active workspace
21 c == &root_container ? 'R' : // root
22 'X');// not any others
23 for (int i = 6; i > depth; i--) { fprintf(stderr, " "); }
24 fprintf(stderr,"|(%p)",c);
25 fprintf(stderr,"(p:%-8p)",c->parent);
26 fprintf(stderr,"(f:%-8p)",c->focused);
27 fprintf(stderr,"(h:%2" PRIuPTR ")",c->handle);
28 fprintf(stderr,"Type:%-4s|",
29 c->type == C_ROOT ? "root" :
30 c->type == C_OUTPUT ? "op" :
31 c->type == C_WORKSPACE ? "ws" :
32 c->type == C_CONTAINER ? "cont" :
33 c->type == C_VIEW ? "view" : "?");
34 fprintf(stderr,"layout:%-5s|",
35 c->layout == L_NONE ? "-" :
36 c->layout == L_HORIZ ? "Horiz":
37 c->layout == L_VERT ? "Vert":
38 c->layout == L_STACKED ? "Stack":
39 c->layout == L_TABBED ? "Tab":
40 c->layout == L_FLOATING ? "Float":
41 c->layout == L_AUTO_LEFT ? "A_lft":
42 c->layout == L_AUTO_RIGHT ? "A_rgt":
43 c->layout == L_AUTO_TOP ? "A_top":
44 c->layout == L_AUTO_BOTTOM ? "A_bot":
45 "Unknown");
46 fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height);
47 fprintf(stderr, "x:%4.f|y:%4.f|", c->x, c->y);
48 fprintf(stderr, "g:%3d|",c->gaps);
49 fprintf(stderr, "vis:%c|", c->visible?'t':'f');
50 fprintf(stderr, "children:%2d|",c->children?c->children->length:0);
51 fprintf(stderr, "name:%.16s\n", c->name);
52}
53void layout_log(const swayc_t *c, int depth) {
54 if (L_DEBUG > get_log_level()) return;
55 int i, d;
56 int e = c->children ? c->children->length : 0;
57 container_log(c, depth);
58 if (e) {
59 for (i = 0; i < e; ++i) {
60 fputc('|',stderr);
61 for (d = 0; d < depth; ++d) fputc('-', stderr);
62 layout_log(c->children->items[i], depth + 1);
63 }
64 }
65 if (c->type == C_WORKSPACE) {
66 e = c->floating?c->floating->length:0;
67 if (e) {
68 for (i = 0; i < e; ++i) {
69 fputc('|',stderr);
70 for (d = 0; d < depth; ++d) fputc('=', stderr);
71 layout_log(c->floating->items[i], depth + 1);
72 }
73 }
74 }
75}
76
77const char *swayc_type_string(enum swayc_types type) {
78 return type == C_ROOT ? "ROOT" :
79 type == C_OUTPUT ? "OUTPUT" :
80 type == C_WORKSPACE ? "WORKSPACE" :
81 type == C_CONTAINER ? "CONTAINER" :
82 type == C_VIEW ? "VIEW" :
83 "UNKNOWN";
84}
85
86// Like sway_log, but also appends some info about given container to log output.
87void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) {
88 sway_assert(cont, "swayc_log: no container ...");
89 va_list args;
90 va_start(args, format);
91 char *txt = malloc(128);
92 vsprintf(txt, format, args);
93 va_end(args);
94
95 char *debug_txt = malloc(32);
96 snprintf(debug_txt, 32, "%s '%s'", swayc_type_string(cont->type), cont->name);
97
98 sway_log(verbosity, "%s (%s)", txt, debug_txt);
99 free(txt);
100 free(debug_txt);
101}
102
103/* XXX:DEBUG:XXX */
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
new file mode 100644
index 00000000..66f33151
--- /dev/null
+++ b/sway/desktop/desktop.c
@@ -0,0 +1,14 @@
1#include "sway/tree/container.h"
2#include "sway/desktop.h"
3#include "sway/output.h"
4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) {
7 for (int i = 0; i < root_container.children->length; ++i) {
8 struct sway_container *cont = root_container.children->items[i];
9 if (cont->type == C_OUTPUT) {
10 output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y,
11 surface, whole);
12 }
13 }
14}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
new file mode 100644
index 00000000..f841e5f1
--- /dev/null
+++ b/sway/desktop/layer_shell.c
@@ -0,0 +1,347 @@
1#include <stdbool.h>
2#include <stdlib.h>
3#include <string.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_box.h>
6#include <wlr/types/wlr_layer_shell.h>
7#include <wlr/types/wlr_output_damage.h>
8#include <wlr/types/wlr_output.h>
9#include <wlr/util/log.h>
10#include "sway/input/input-manager.h"
11#include "sway/input/seat.h"
12#include "sway/layers.h"
13#include "sway/output.h"
14#include "sway/server.h"
15#include "sway/tree/layout.h"
16
17static void apply_exclusive(struct wlr_box *usable_area,
18 uint32_t anchor, int32_t exclusive,
19 int32_t margin_top, int32_t margin_right,
20 int32_t margin_bottom, int32_t margin_left) {
21 if (exclusive <= 0) {
22 return;
23 }
24 struct {
25 uint32_t anchors;
26 int *positive_axis;
27 int *negative_axis;
28 int margin;
29 } edges[] = {
30 {
31 .anchors =
32 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
33 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
34 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
35 .positive_axis = &usable_area->y,
36 .negative_axis = &usable_area->height,
37 .margin = margin_top,
38 },
39 {
40 .anchors =
41 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
42 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
43 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
44 .positive_axis = NULL,
45 .negative_axis = &usable_area->height,
46 .margin = margin_bottom,
47 },
48 {
49 .anchors =
50 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
51 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
52 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
53 .positive_axis = &usable_area->x,
54 .negative_axis = &usable_area->width,
55 .margin = margin_left,
56 },
57 {
58 .anchors =
59 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
60 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
61 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
62 .positive_axis = NULL,
63 .negative_axis = &usable_area->width,
64 .margin = margin_right,
65 },
66 };
67 for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
68 if ((anchor & edges[i].anchors) == edges[i].anchors) {
69 if (edges[i].positive_axis) {
70 *edges[i].positive_axis += exclusive + edges[i].margin;
71 }
72 if (edges[i].negative_axis) {
73 *edges[i].negative_axis -= exclusive + edges[i].margin;
74 }
75 }
76 }
77}
78
79static void arrange_layer(struct sway_output *output, struct wl_list *list,
80 struct wlr_box *usable_area, bool exclusive) {
81 struct sway_layer_surface *sway_layer;
82 struct wlr_box full_area = { 0 };
83 wlr_output_effective_resolution(output->wlr_output,
84 &full_area.width, &full_area.height);
85 wl_list_for_each(sway_layer, list, link) {
86 struct wlr_layer_surface *layer = sway_layer->layer_surface;
87 struct wlr_layer_surface_state *state = &layer->current;
88 if (exclusive != (state->exclusive_zone > 0)) {
89 continue;
90 }
91 struct wlr_box bounds;
92 if (state->exclusive_zone == -1) {
93 bounds = full_area;
94 } else {
95 bounds = *usable_area;
96 }
97 struct wlr_box box = {
98 .width = state->desired_width,
99 .height = state->desired_height
100 };
101 // Horizontal axis
102 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
103 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
104 if ((state->anchor & both_horiz) && box.width == 0) {
105 box.x = bounds.x;
106 box.width = bounds.width;
107 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
108 box.x = bounds.x;
109 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
110 box.x = bounds.x + (bounds.width - box.width);
111 } else {
112 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
113 }
114 // Vertical axis
115 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
116 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
117 if ((state->anchor & both_vert) && box.height == 0) {
118 box.y = bounds.y;
119 box.height = bounds.height;
120 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
121 box.y = bounds.y;
122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
123 box.y = bounds.y + (bounds.height - box.height);
124 } else {
125 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
126 }
127 // Margin
128 if ((state->anchor & both_horiz) == both_horiz) {
129 box.x += state->margin.left;
130 box.width -= state->margin.left + state->margin.right;
131 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
132 box.x += state->margin.left;
133 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
134 box.x -= state->margin.right;
135 }
136 if ((state->anchor & both_vert) == both_vert) {
137 box.y += state->margin.top;
138 box.height -= state->margin.top + state->margin.bottom;
139 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
140 box.y += state->margin.top;
141 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
142 box.y -= state->margin.bottom;
143 }
144 if (box.width < 0 || box.height < 0) {
145 // TODO: Bubble up a protocol error?
146 wlr_layer_surface_close(layer);
147 continue;
148 }
149 // Apply
150 sway_layer->geo = box;
151 apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
152 state->margin.top, state->margin.right,
153 state->margin.bottom, state->margin.left);
154 wlr_layer_surface_configure(layer, box.width, box.height);
155 }
156}
157
158void arrange_layers(struct sway_output *output) {
159 struct wlr_box usable_area = { 0 };
160 wlr_output_effective_resolution(output->wlr_output,
161 &usable_area.width, &usable_area.height);
162
163 // Arrange exclusive surfaces from top->bottom
164 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
165 &usable_area, true);
166 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
167 &usable_area, true);
168 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
169 &usable_area, true);
170 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
171 &usable_area, true);
172
173 if (memcmp(&usable_area, &output->usable_area,
174 sizeof(struct wlr_box)) != 0) {
175 wlr_log(L_DEBUG, "Usable area changed, rearranging output");
176 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
177 arrange_windows(output->swayc, -1, -1);
178 }
179
180 // Arrange non-exlusive surfaces from top->bottom
181 usable_area.x = usable_area.y = 0;
182 wlr_output_effective_resolution(output->wlr_output,
183 &usable_area.width, &usable_area.height);
184 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
185 &usable_area, false);
186 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
187 &usable_area, false);
188 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
189 &usable_area, false);
190 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
191 &usable_area, false);
192
193 // Find topmost keyboard interactive layer, if such a layer exists
194 uint32_t layers_above_shell[] = {
195 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
196 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
197 };
198 size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
199 struct sway_layer_surface *layer, *topmost = NULL;
200 for (size_t i = 0; i < nlayers; ++i) {
201 wl_list_for_each_reverse(layer,
202 &output->layers[layers_above_shell[i]], link) {
203 if (layer->layer_surface->current.keyboard_interactive) {
204 topmost = layer;
205 break;
206 }
207 }
208 if (topmost != NULL) {
209 break;
210 }
211 }
212
213 struct sway_seat *seat;
214 wl_list_for_each(seat, &input_manager->seats, link) {
215 seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL);
216 }
217}
218
219static void handle_output_destroy(struct wl_listener *listener, void *data) {
220 struct sway_layer_surface *sway_layer =
221 wl_container_of(listener, sway_layer, output_destroy);
222 wl_list_remove(&sway_layer->output_destroy.link);
223 sway_layer->layer_surface->output = NULL;
224 wlr_layer_surface_close(sway_layer->layer_surface);
225}
226
227static void handle_surface_commit(struct wl_listener *listener, void *data) {
228 struct sway_layer_surface *layer =
229 wl_container_of(listener, layer, surface_commit);
230 struct wlr_layer_surface *layer_surface = layer->layer_surface;
231 struct wlr_output *wlr_output = layer_surface->output;
232 if (wlr_output == NULL) {
233 return;
234 }
235
236 struct sway_output *output = wlr_output->data;
237 struct wlr_box old_geo = layer->geo;
238 arrange_layers(output);
239 if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) {
240 output_damage_surface(output, old_geo.x, old_geo.y,
241 layer_surface->surface, true);
242 output_damage_surface(output, layer->geo.x, layer->geo.y,
243 layer_surface->surface, true);
244 } else {
245 output_damage_surface(output, layer->geo.x, layer->geo.y,
246 layer_surface->surface, false);
247 }
248}
249
250static void unmap(struct sway_layer_surface *sway_layer) {
251 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
252 if (wlr_output == NULL) {
253 return;
254 }
255 struct sway_output *output = wlr_output->data;
256 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
257 sway_layer->layer_surface->surface, true);
258}
259
260static void handle_destroy(struct wl_listener *listener, void *data) {
261 struct sway_layer_surface *sway_layer =
262 wl_container_of(listener, sway_layer, destroy);
263 wlr_log(L_DEBUG, "Layer surface destroyed (%s)",
264 sway_layer->layer_surface->namespace);
265 if (sway_layer->layer_surface->mapped) {
266 unmap(sway_layer);
267 }
268 wl_list_remove(&sway_layer->link);
269 wl_list_remove(&sway_layer->destroy.link);
270 wl_list_remove(&sway_layer->map.link);
271 wl_list_remove(&sway_layer->unmap.link);
272 wl_list_remove(&sway_layer->surface_commit.link);
273 if (sway_layer->layer_surface->output != NULL) {
274 struct sway_output *output = sway_layer->layer_surface->output->data;
275 arrange_layers(output);
276
277 wl_list_remove(&sway_layer->output_destroy.link);
278 }
279 free(sway_layer);
280}
281
282static void handle_map(struct wl_listener *listener, void *data) {
283 struct sway_layer_surface *sway_layer = wl_container_of(listener,
284 sway_layer, map);
285 struct sway_output *output = sway_layer->layer_surface->output->data;
286 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
287 sway_layer->layer_surface->surface, true);
288 // TODO: send enter to subsurfaces and popups
289 wlr_surface_send_enter(sway_layer->layer_surface->surface,
290 sway_layer->layer_surface->output);
291}
292
293static void handle_unmap(struct wl_listener *listener, void *data) {
294 struct sway_layer_surface *sway_layer = wl_container_of(
295 listener, sway_layer, unmap);
296 unmap(sway_layer);
297}
298
299void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
300 struct wlr_layer_surface *layer_surface = data;
301 struct sway_server *server =
302 wl_container_of(listener, server, layer_shell_surface);
303 wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
304 "size %dx%d margin %d,%d,%d,%d",
305 layer_surface->namespace, layer_surface->layer, layer_surface->layer,
306 layer_surface->client_pending.desired_width,
307 layer_surface->client_pending.desired_height,
308 layer_surface->client_pending.margin.top,
309 layer_surface->client_pending.margin.right,
310 layer_surface->client_pending.margin.bottom,
311 layer_surface->client_pending.margin.left);
312
313 struct sway_layer_surface *sway_layer =
314 calloc(1, sizeof(struct sway_layer_surface));
315 if (!sway_layer) {
316 return;
317 }
318
319 sway_layer->surface_commit.notify = handle_surface_commit;
320 wl_signal_add(&layer_surface->surface->events.commit,
321 &sway_layer->surface_commit);
322
323 sway_layer->output_destroy.notify = handle_output_destroy;
324 wl_signal_add(&layer_surface->output->events.destroy,
325 &sway_layer->output_destroy);
326
327 sway_layer->destroy.notify = handle_destroy;
328 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
329 sway_layer->map.notify = handle_map;
330 wl_signal_add(&layer_surface->events.map, &sway_layer->map);
331 sway_layer->unmap.notify = handle_unmap;
332 wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
333 // TODO: Listen for subsurfaces
334
335 sway_layer->layer_surface = layer_surface;
336 layer_surface->data = sway_layer;
337
338 struct sway_output *output = layer_surface->output->data;
339 wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->link);
340
341 // Temporarily set the layer's current state to client_pending
342 // So that we can easily arrange it
343 struct wlr_layer_surface_state old_state = layer_surface->current;
344 layer_surface->current = layer_surface->client_pending;
345 arrange_layers(output);
346 layer_surface->current = old_state;
347}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
new file mode 100644
index 00000000..1b3143d0
--- /dev/null
+++ b/sway/desktop/output.c
@@ -0,0 +1,579 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <stdlib.h>
4#include <strings.h>
5#include <time.h>
6#include <wayland-server.h>
7#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_matrix.h>
10#include <wlr/types/wlr_output_damage.h>
11#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_output.h>
13#include <wlr/types/wlr_surface.h>
14#include <wlr/types/wlr_wl_shell.h>
15#include <wlr/util/region.h>
16#include "log.h"
17#include "sway/input/input-manager.h"
18#include "sway/input/seat.h"
19#include "sway/layers.h"
20#include "sway/output.h"
21#include "sway/server.h"
22#include "sway/tree/container.h"
23#include "sway/tree/layout.h"
24#include "sway/tree/view.h"
25
26struct sway_container *output_by_name(const char *name) {
27 for (int i = 0; i < root_container.children->length; ++i) {
28 struct sway_container *output = root_container.children->items[i];
29 if (strcasecmp(output->name, name) == 0) {
30 return output;
31 }
32 }
33 return NULL;
34}
35
36/**
37 * Rotate a child's position relative to a parent. The parent size is (pw, ph),
38 * the child position is (*sx, *sy) and its size is (sw, sh).
39 */
40static void rotate_child_position(double *sx, double *sy, double sw, double sh,
41 double pw, double ph, float rotation) {
42 if (rotation == 0.0f) {
43 return;
44 }
45
46 // Coordinates relative to the center of the subsurface
47 double ox = *sx - pw/2 + sw/2,
48 oy = *sy - ph/2 + sh/2;
49 // Rotated coordinates
50 double rx = cos(-rotation)*ox - sin(-rotation)*oy,
51 ry = cos(-rotation)*oy + sin(-rotation)*ox;
52 *sx = rx + pw/2 - sw/2;
53 *sy = ry + ph/2 - sh/2;
54}
55
56/**
57 * Contains a surface's root geometry information. For instance, when rendering
58 * a popup, this will contain the parent view's position and size.
59 */
60struct root_geometry {
61 double x, y;
62 int width, height;
63 float rotation;
64};
65
66static bool get_surface_box(struct root_geometry *geo,
67 struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
68 struct wlr_box *surface_box) {
69 if (!wlr_surface_has_buffer(surface)) {
70 return false;
71 }
72
73 int sw = surface->current->width;
74 int sh = surface->current->height;
75
76 double _sx = sx, _sy = sy;
77 rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
78 geo->rotation);
79
80 struct wlr_box box = {
81 .x = geo->x + _sx,
82 .y = geo->y + _sy,
83 .width = sw,
84 .height = sh,
85 };
86 if (surface_box != NULL) {
87 memcpy(surface_box, &box, sizeof(struct wlr_box));
88 }
89
90 struct wlr_box rotated_box;
91 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
92
93 struct wlr_box output_box = {
94 .width = output->swayc->width,
95 .height = output->swayc->height,
96 };
97
98 struct wlr_box intersection;
99 return wlr_box_intersection(&output_box, &rotated_box, &intersection);
100}
101
102static void surface_for_each_surface(struct wlr_surface *surface,
103 double ox, double oy, struct root_geometry *geo,
104 wlr_surface_iterator_func_t iterator, void *user_data) {
105 geo->x = ox;
106 geo->y = oy;
107 geo->width = surface->current->width;
108 geo->height = surface->current->height;
109 geo->rotation = 0;
110
111 wlr_surface_for_each_surface(surface, iterator, user_data);
112}
113
114static void output_view_for_each_surface(struct sway_view *view,
115 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
116 void *user_data) {
117 geo->x = view->swayc->x;
118 geo->y = view->swayc->y;
119 geo->width = view->surface->current->width;
120 geo->height = view->surface->current->height;
121 geo->rotation = 0; // TODO
122
123 view_for_each_surface(view, iterator, user_data);
124}
125
126static void layer_for_each_surface(struct wl_list *layer_surfaces,
127 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
128 void *user_data) {
129 struct sway_layer_surface *layer_surface;
130 wl_list_for_each(layer_surface, layer_surfaces, link) {
131 struct wlr_layer_surface *wlr_layer_surface =
132 layer_surface->layer_surface;
133 surface_for_each_surface(wlr_layer_surface->surface,
134 layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
135 user_data);
136 }
137}
138
139static void unmanaged_for_each_surface(struct wl_list *unmanaged,
140 struct sway_output *output, struct root_geometry *geo,
141 wlr_surface_iterator_func_t iterator, void *user_data) {
142 struct sway_xwayland_unmanaged *unmanaged_surface;
143 wl_list_for_each(unmanaged_surface, unmanaged, link) {
144 struct wlr_xwayland_surface *xsurface =
145 unmanaged_surface->wlr_xwayland_surface;
146 double ox = unmanaged_surface->lx - output->swayc->x;
147 double oy = unmanaged_surface->ly - output->swayc->y;
148
149 surface_for_each_surface(xsurface->surface, ox, oy, geo,
150 iterator, user_data);
151 }
152}
153
154static void scale_box(struct wlr_box *box, float scale) {
155 box->x *= scale;
156 box->y *= scale;
157 box->width *= scale;
158 box->height *= scale;
159}
160
161struct render_data {
162 struct root_geometry root_geo;
163 struct sway_output *output;
164 float alpha;
165};
166
167static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
168 void *_data) {
169 struct render_data *data = _data;
170 struct wlr_output *wlr_output = data->output->wlr_output;
171 float rotation = data->root_geo.rotation;
172 float alpha = data->alpha;
173
174 if (!wlr_surface_has_buffer(surface)) {
175 return;
176 }
177
178 struct wlr_box box;
179 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
180 sx, sy, &box);
181 if (!intersects) {
182 return;
183 }
184
185 struct wlr_renderer *renderer =
186 wlr_backend_get_renderer(wlr_output->backend);
187 if (!sway_assert(renderer != NULL,
188 "expected the output backend to have a renderer")) {
189 return;
190 }
191
192 scale_box(&box, wlr_output->scale);
193
194 float matrix[9];
195 enum wl_output_transform transform =
196 wlr_output_transform_invert(surface->current->transform);
197 wlr_matrix_project_box(matrix, &box, transform, rotation,
198 wlr_output->transform_matrix);
199
200 wlr_render_texture_with_matrix(renderer, surface->texture,
201 matrix, alpha);
202}
203
204static void render_layer(struct sway_output *output,
205 struct wl_list *layer_surfaces) {
206 struct render_data data = { .output = output, .alpha = 1.0f };
207 layer_for_each_surface(layer_surfaces, &data.root_geo,
208 render_surface_iterator, &data);
209}
210
211static void render_unmanaged(struct sway_output *output,
212 struct wl_list *unmanaged) {
213 struct render_data data = { .output = output, .alpha = 1.0f };
214 unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
215 render_surface_iterator, &data);
216}
217
218static void render_container_iterator(struct sway_container *con,
219 void *_data) {
220 struct sway_output *output = _data;
221 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
222 return;
223 }
224 struct render_data data = { .output = output, .alpha = con->alpha };
225 output_view_for_each_surface(con->sway_view, &data.root_geo,
226 render_surface_iterator, &data);
227}
228
229static void render_container(struct sway_output *output,
230 struct sway_container *con) {
231 container_descendants(con, C_VIEW, render_container_iterator, output);
232}
233
234static struct sway_container *output_get_active_workspace(
235 struct sway_output *output) {
236 struct sway_seat *seat = input_manager_current_seat(input_manager);
237 struct sway_container *focus =
238 seat_get_focus_inactive(seat, output->swayc);
239 if (!focus) {
240 // We've never been to this output before
241 focus = output->swayc->children->items[0];
242 }
243 struct sway_container *workspace = focus;
244 if (workspace->type != C_WORKSPACE) {
245 workspace = container_parent(workspace, C_WORKSPACE);
246 }
247 return workspace;
248}
249
250static void render_output(struct sway_output *output, struct timespec *when,
251 pixman_region32_t *damage) {
252 struct wlr_output *wlr_output = output->wlr_output;
253
254 struct wlr_renderer *renderer =
255 wlr_backend_get_renderer(wlr_output->backend);
256 if (!sway_assert(renderer != NULL,
257 "expected the output backend to have a renderer")) {
258 return;
259 }
260
261 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
262
263 if (!pixman_region32_not_empty(damage)) {
264 // Output isn't damaged but needs buffer swap
265 goto renderer_end;
266 }
267
268 // TODO: don't damage the whole output
269 int width, height;
270 wlr_output_transformed_resolution(wlr_output, &width, &height);
271 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
272
273 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
274 wlr_renderer_clear(renderer, clear_color);
275
276 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
277 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
278
279 struct sway_container *workspace = output_get_active_workspace(output);
280 render_container(output, workspace);
281
282 render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged);
283
284 // TODO: consider revising this when fullscreen windows are supported
285 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
286 render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
287
288renderer_end:
289 if (root_container.sway_root->debug_tree) {
290 wlr_render_texture(renderer, root_container.sway_root->debug_tree,
291 wlr_output->transform_matrix, 0, 0, 1);
292 }
293
294 wlr_renderer_end(renderer);
295 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
296 return;
297 }
298 output->last_frame = *when;
299}
300
301struct send_frame_done_data {
302 struct root_geometry root_geo;
303 struct sway_output *output;
304 struct timespec *when;
305};
306
307static void send_frame_done_iterator(struct wlr_surface *surface,
308 int sx, int sy, void *_data) {
309 struct send_frame_done_data *data = _data;
310
311 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
312 sx, sy, NULL);
313 if (intersects) {
314 wlr_surface_send_frame_done(surface, data->when);
315 }
316}
317
318static void send_frame_done_layer(struct send_frame_done_data *data,
319 struct wl_list *layer_surfaces) {
320 layer_for_each_surface(layer_surfaces, &data->root_geo,
321 send_frame_done_iterator, data);
322}
323
324static void send_frame_done_unmanaged(struct send_frame_done_data *data,
325 struct wl_list *unmanaged) {
326 unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
327 send_frame_done_iterator, data);
328}
329
330static void send_frame_done_container_iterator(struct sway_container *con,
331 void *_data) {
332 struct send_frame_done_data *data = _data;
333 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
334 return;
335 }
336 output_view_for_each_surface(con->sway_view, &data->root_geo,
337 send_frame_done_iterator, data);
338}
339
340static void send_frame_done_container(struct send_frame_done_data *data,
341 struct sway_container *con) {
342 container_descendants(con, C_VIEW,
343 send_frame_done_container_iterator, data);
344}
345
346static void send_frame_done(struct sway_output *output, struct timespec *when) {
347 struct send_frame_done_data data = {
348 .output = output,
349 .when = when,
350 };
351
352 send_frame_done_layer(&data,
353 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
354 send_frame_done_layer(&data,
355 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
356
357 struct sway_container *workspace = output_get_active_workspace(output);
358 send_frame_done_container(&data, workspace);
359
360 send_frame_done_unmanaged(&data,
361 &root_container.sway_root->xwayland_unmanaged);
362
363 send_frame_done_layer(&data,
364 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
365 send_frame_done_layer(&data,
366 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
367}
368
369static void damage_handle_frame(struct wl_listener *listener, void *data) {
370 struct sway_output *output =
371 wl_container_of(listener, output, damage_frame);
372
373 if (!output->wlr_output->enabled) {
374 return;
375 }
376
377 struct timespec now;
378 clock_gettime(CLOCK_MONOTONIC, &now);
379
380 bool needs_swap;
381 pixman_region32_t damage;
382 pixman_region32_init(&damage);
383 if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
384 return;
385 }
386
387 if (needs_swap) {
388 render_output(output, &now, &damage);
389 }
390
391 pixman_region32_fini(&damage);
392
393 // Send frame done to all visible surfaces
394 send_frame_done(output, &now);
395}
396
397void output_damage_whole(struct sway_output *output) {
398 wlr_output_damage_add_whole(output->damage);
399}
400
401struct damage_data {
402 struct root_geometry root_geo;
403 struct sway_output *output;
404 bool whole;
405};
406
407static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
408 void *_data) {
409 struct damage_data *data = _data;
410 struct sway_output *output = data->output;
411 float rotation = data->root_geo.rotation;
412 bool whole = data->whole;
413
414 struct wlr_box box;
415 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
416 sx, sy, &box);
417 if (!intersects) {
418 return;
419 }
420
421 scale_box(&box, output->wlr_output->scale);
422
423 if (whole) {
424 wlr_box_rotated_bounds(&box, rotation, &box);
425 wlr_output_damage_add_box(output->damage, &box);
426 } else {
427 int center_x = box.x + box.width/2;
428 int center_y = box.y + box.height/2;
429
430 pixman_region32_t damage;
431 pixman_region32_init(&damage);
432 pixman_region32_copy(&damage, &surface->current->surface_damage);
433 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
434 if (ceil(output->wlr_output->scale) > surface->current->scale) {
435 // When scaling up a surface, it'll become blurry so we need to
436 // expand the damage region
437 wlr_region_expand(&damage, &damage,
438 ceil(output->wlr_output->scale) - surface->current->scale);
439 }
440 pixman_region32_translate(&damage, box.x, box.y);
441 wlr_region_rotated_bounds(&damage, &damage, rotation,
442 center_x, center_y);
443 wlr_output_damage_add(output->damage, &damage);
444 pixman_region32_fini(&damage);
445 }
446}
447
448void output_damage_surface(struct sway_output *output, double ox, double oy,
449 struct wlr_surface *surface, bool whole) {
450 struct damage_data data = {
451 .output = output,
452 .whole = whole,
453 };
454
455 surface_for_each_surface(surface, ox, oy, &data.root_geo,
456 damage_surface_iterator, &data);
457}
458
459void output_damage_view(struct sway_output *output, struct sway_view *view,
460 bool whole) {
461 if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
462 return;
463 }
464
465 struct damage_data data = {
466 .output = output,
467 .whole = whole,
468 };
469
470 output_view_for_each_surface(view, &data.root_geo,
471 damage_surface_iterator, &data);
472}
473
474static void output_damage_whole_container_iterator(struct sway_container *con,
475 void *data) {
476 struct sway_output *output = data;
477
478 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
479 return;
480 }
481
482 output_damage_view(output, con->sway_view, true);
483}
484
485void output_damage_whole_container(struct sway_output *output,
486 struct sway_container *con) {
487 float scale = output->wlr_output->scale;
488 struct wlr_box box = {
489 .x = con->x * scale,
490 .y = con->y * scale,
491 .width = con->width * scale,
492 .height = con->height * scale,
493 };
494 wlr_output_damage_add_box(output->damage, &box);
495
496 container_descendants(con, C_VIEW, output_damage_whole_container_iterator,
497 output);
498}
499
500static void damage_handle_destroy(struct wl_listener *listener, void *data) {
501 struct sway_output *output =
502 wl_container_of(listener, output, damage_destroy);
503 container_destroy(output->swayc);
504}
505
506static void handle_destroy(struct wl_listener *listener, void *data) {
507 struct sway_output *output = wl_container_of(listener, output, destroy);
508 container_destroy(output->swayc);
509}
510
511static void handle_mode(struct wl_listener *listener, void *data) {
512 struct sway_output *output = wl_container_of(listener, output, mode);
513 arrange_layers(output);
514 arrange_windows(output->swayc, -1, -1);
515}
516
517static void handle_transform(struct wl_listener *listener, void *data) {
518 struct sway_output *output = wl_container_of(listener, output, transform);
519 arrange_layers(output);
520 arrange_windows(output->swayc, -1, -1);
521}
522
523static void handle_scale(struct wl_listener *listener, void *data) {
524 struct sway_output *output = wl_container_of(listener, output, scale);
525 arrange_layers(output);
526 arrange_windows(output->swayc, -1, -1);
527}
528
529void handle_new_output(struct wl_listener *listener, void *data) {
530 struct sway_server *server = wl_container_of(listener, server, new_output);
531 struct wlr_output *wlr_output = data;
532 wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
533
534 struct sway_output *output = calloc(1, sizeof(struct sway_output));
535 if (!output) {
536 return;
537 }
538 output->wlr_output = wlr_output;
539 wlr_output->data = output;
540 output->server = server;
541
542 if (!wl_list_empty(&wlr_output->modes)) {
543 struct wlr_output_mode *mode =
544 wl_container_of(wlr_output->modes.prev, mode, link);
545 wlr_output_set_mode(wlr_output, mode);
546 }
547
548 output->damage = wlr_output_damage_create(wlr_output);
549
550 output->swayc = output_create(output);
551 if (!output->swayc) {
552 free(output);
553 return;
554 }
555
556 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
557 for (size_t i = 0; i < len; ++i) {
558 wl_list_init(&output->layers[i]);
559 }
560
561 input_manager_configure_xcursor(input_manager);
562
563 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
564 output->destroy.notify = handle_destroy;
565 wl_signal_add(&wlr_output->events.mode, &output->mode);
566 output->mode.notify = handle_mode;
567 wl_signal_add(&wlr_output->events.transform, &output->transform);
568 output->transform.notify = handle_transform;
569 wl_signal_add(&wlr_output->events.scale, &output->scale);
570 output->scale.notify = handle_scale;
571
572 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
573 output->damage_frame.notify = damage_handle_frame;
574 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
575 output->damage_destroy.notify = damage_handle_destroy;
576
577 arrange_layers(output);
578 arrange_windows(&root_container, -1, -1);
579}
diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c
new file mode 100644
index 00000000..b63c220c
--- /dev/null
+++ b/sway/desktop/wl_shell.c
@@ -0,0 +1,131 @@
1#define _POSIX_C_SOURCE 199309L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_wl_shell.h>
6#include "sway/tree/container.h"
7#include "sway/tree/layout.h"
8#include "sway/server.h"
9#include "sway/tree/view.h"
10#include "sway/input/seat.h"
11#include "sway/input/input-manager.h"
12#include "log.h"
13
14static struct sway_wl_shell_view *wl_shell_view_from_view(
15 struct sway_view *view) {
16 if (!sway_assert(view->type == SWAY_VIEW_WL_SHELL,
17 "Expected wl_shell view")) {
18 return NULL;
19 }
20 return (struct sway_wl_shell_view *)view;
21}
22
23static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
24 if (wl_shell_view_from_view(view) == NULL) {
25 return NULL;
26 }
27 switch (prop) {
28 case VIEW_PROP_TITLE:
29 return view->wlr_wl_shell_surface->title;
30 case VIEW_PROP_CLASS:
31 return view->wlr_wl_shell_surface->class;
32 default:
33 return NULL;
34 }
35}
36
37static void configure(struct sway_view *view, double ox, double oy, int width,
38 int height) {
39 struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
40 if (wl_shell_view == NULL) {
41 return;
42 }
43 view_update_position(view, ox, oy);
44 wl_shell_view->pending_width = width;
45 wl_shell_view->pending_height = height;
46 wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height);
47}
48
49static void _close(struct sway_view *view) {
50 if (wl_shell_view_from_view(view) == NULL) {
51 return;
52 }
53
54 wl_client_destroy(view->wlr_wl_shell_surface->client);
55}
56
57static void destroy(struct sway_view *view) {
58 struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
59 if (wl_shell_view == NULL) {
60 return;
61 }
62 wl_list_remove(&wl_shell_view->commit.link);
63 wl_list_remove(&wl_shell_view->destroy.link);
64 free(wl_shell_view);
65}
66
67static const struct sway_view_impl view_impl = {
68 .get_prop = get_prop,
69 .configure = configure,
70 .close = _close,
71 .destroy = destroy,
72};
73
74static void handle_commit(struct wl_listener *listener, void *data) {
75 struct sway_wl_shell_view *wl_shell_view =
76 wl_container_of(listener, wl_shell_view, commit);
77 struct sway_view *view = &wl_shell_view->view;
78 // NOTE: We intentionally discard the view's desired width here
79 // TODO: Let floating views do whatever
80 view_update_size(view, wl_shell_view->pending_width,
81 wl_shell_view->pending_height);
82 view_damage(view, false);
83}
84
85static void handle_destroy(struct wl_listener *listener, void *data) {
86 struct sway_wl_shell_view *wl_shell_view =
87 wl_container_of(listener, wl_shell_view, destroy);
88 view_destroy(&wl_shell_view->view);
89}
90
91void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
92 struct sway_server *server = wl_container_of(listener, server,
93 wl_shell_surface);
94 struct wlr_wl_shell_surface *shell_surface = data;
95
96 if (shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) {
97 // popups don't get views
98 wlr_log(L_DEBUG, "New wl_shell popup");
99 return;
100 }
101
102 // TODO: make transient windows floating
103
104 wlr_log(L_DEBUG, "New wl_shell toplevel title='%s' app_id='%s'",
105 shell_surface->title, shell_surface->class);
106 wlr_wl_shell_surface_ping(shell_surface);
107
108 struct sway_wl_shell_view *wl_shell_view =
109 calloc(1, sizeof(struct sway_wl_shell_view));
110 if (!sway_assert(wl_shell_view, "Failed to allocate view")) {
111 return;
112 }
113
114 view_init(&wl_shell_view->view, SWAY_VIEW_WL_SHELL, &view_impl);
115 wl_shell_view->view.wlr_wl_shell_surface = shell_surface;
116
117 // TODO:
118 // - Wire up listeners
119 // - Look up pid and open on appropriate workspace
120 // - Set new view to maximized so it behaves nicely
121 // - Criteria
122
123 wl_shell_view->commit.notify = handle_commit;
124 wl_signal_add(&shell_surface->surface->events.commit,
125 &wl_shell_view->commit);
126
127 wl_shell_view->destroy.notify = handle_destroy;
128 wl_signal_add(&shell_surface->events.destroy, &wl_shell_view->destroy);
129
130 view_map(&wl_shell_view->view, shell_surface->surface);
131}
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
new file mode 100644
index 00000000..e4703040
--- /dev/null
+++ b/sway/desktop/xdg_shell_v6.c
@@ -0,0 +1,249 @@
1#define _POSIX_C_SOURCE 199309L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_xdg_shell_v6.h>
6#include "sway/tree/container.h"
7#include "sway/tree/layout.h"
8#include "sway/server.h"
9#include "sway/tree/view.h"
10#include "sway/input/seat.h"
11#include "sway/input/input-manager.h"
12#include "log.h"
13
14static const struct sway_view_child_impl popup_impl;
15
16static void popup_destroy(struct sway_view_child *child) {
17 if (!sway_assert(child->impl == &popup_impl,
18 "Expected an xdg_shell_v6 popup")) {
19 return;
20 }
21 struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child;
22 wl_list_remove(&popup->new_popup.link);
23 wl_list_remove(&popup->unmap.link);
24 wl_list_remove(&popup->destroy.link);
25 free(popup);
26}
27
28static const struct sway_view_child_impl popup_impl = {
29 .destroy = popup_destroy,
30};
31
32static struct sway_xdg_popup_v6 *popup_create(
33 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view);
34
35static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
36 struct sway_xdg_popup_v6 *popup =
37 wl_container_of(listener, popup, new_popup);
38 struct wlr_xdg_popup_v6 *wlr_popup = data;
39 popup_create(wlr_popup, popup->child.view);
40}
41
42static void popup_handle_unmap(struct wl_listener *listener, void *data) {
43 struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, unmap);
44 view_child_destroy(&popup->child);
45}
46
47static void popup_handle_destroy(struct wl_listener *listener, void *data) {
48 struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, destroy);
49 view_child_destroy(&popup->child);
50}
51
52static struct sway_xdg_popup_v6 *popup_create(
53 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {
54 struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base;
55
56 struct sway_xdg_popup_v6 *popup =
57 calloc(1, sizeof(struct sway_xdg_popup_v6));
58 if (popup == NULL) {
59 return NULL;
60 }
61 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
62
63 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
64 popup->new_popup.notify = popup_handle_new_popup;
65 wl_signal_add(&xdg_surface->events.unmap, &popup->unmap);
66 popup->unmap.notify = popup_handle_unmap;
67 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
68 popup->destroy.notify = popup_handle_destroy;
69
70 return popup;
71}
72
73
74static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view(
75 struct sway_view *view) {
76 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6,
77 "Expected xdg_shell_v6 view")) {
78 return NULL;
79 }
80 return (struct sway_xdg_shell_v6_view *)view;
81}
82
83static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
84 if (xdg_shell_v6_view_from_view(view) == NULL) {
85 return NULL;
86 }
87 switch (prop) {
88 case VIEW_PROP_TITLE:
89 return view->wlr_xdg_surface_v6->toplevel->title;
90 case VIEW_PROP_APP_ID:
91 return view->wlr_xdg_surface_v6->toplevel->app_id;
92 default:
93 return NULL;
94 }
95}
96
97static void configure(struct sway_view *view, double ox, double oy, int width,
98 int height) {
99 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
100 xdg_shell_v6_view_from_view(view);
101 if (xdg_shell_v6_view == NULL) {
102 return;
103 }
104
105 view_update_position(view, ox, oy);
106 xdg_shell_v6_view->pending_width = width;
107 xdg_shell_v6_view->pending_height = height;
108 wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);
109}
110
111static void set_activated(struct sway_view *view, bool activated) {
112 if (xdg_shell_v6_view_from_view(view) == NULL) {
113 return;
114 }
115 struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
116 if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
117 wlr_xdg_toplevel_v6_set_activated(surface, activated);
118 }
119}
120
121static void for_each_surface(struct sway_view *view,
122 wlr_surface_iterator_func_t iterator, void *user_data) {
123 if (xdg_shell_v6_view_from_view(view) == NULL) {
124 return;
125 }
126 wlr_xdg_surface_v6_for_each_surface(view->wlr_xdg_surface_v6, iterator,
127 user_data);
128}
129
130static void _close(struct sway_view *view) {
131 if (xdg_shell_v6_view_from_view(view) == NULL) {
132 return;
133 }
134 struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
135 if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
136 wlr_xdg_surface_v6_send_close(surface);
137 }
138}
139
140static void destroy(struct sway_view *view) {
141 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
142 xdg_shell_v6_view_from_view(view);
143 if (xdg_shell_v6_view == NULL) {
144 return;
145 }
146 wl_list_remove(&xdg_shell_v6_view->destroy.link);
147 wl_list_remove(&xdg_shell_v6_view->map.link);
148 wl_list_remove(&xdg_shell_v6_view->unmap.link);
149 free(xdg_shell_v6_view);
150}
151
152static const struct sway_view_impl view_impl = {
153 .get_prop = get_prop,
154 .configure = configure,
155 .set_activated = set_activated,
156 .for_each_surface = for_each_surface,
157 .close = _close,
158 .destroy = destroy,
159};
160
161static void handle_commit(struct wl_listener *listener, void *data) {
162 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
163 wl_container_of(listener, xdg_shell_v6_view, commit);
164 struct sway_view *view = &xdg_shell_v6_view->view;
165 // NOTE: We intentionally discard the view's desired width here
166 // TODO: Store this for restoration when moving to floating plane
167 // TODO: Let floating views do whatever
168 view_update_size(view, xdg_shell_v6_view->pending_width,
169 xdg_shell_v6_view->pending_height);
170 view_damage(view, false);
171}
172
173static void handle_new_popup(struct wl_listener *listener, void *data) {
174 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
175 wl_container_of(listener, xdg_shell_v6_view, new_popup);
176 struct wlr_xdg_popup_v6 *wlr_popup = data;
177 popup_create(wlr_popup, &xdg_shell_v6_view->view);
178}
179
180static void handle_unmap(struct wl_listener *listener, void *data) {
181 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
182 wl_container_of(listener, xdg_shell_v6_view, unmap);
183
184 view_unmap(&xdg_shell_v6_view->view);
185
186 wl_list_remove(&xdg_shell_v6_view->commit.link);
187 wl_list_remove(&xdg_shell_v6_view->new_popup.link);
188}
189
190static void handle_map(struct wl_listener *listener, void *data) {
191 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
192 wl_container_of(listener, xdg_shell_v6_view, map);
193 struct sway_view *view = &xdg_shell_v6_view->view;
194 struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6;
195
196 view_map(view, view->wlr_xdg_surface_v6->surface);
197
198 xdg_shell_v6_view->commit.notify = handle_commit;
199 wl_signal_add(&xdg_surface->surface->events.commit,
200 &xdg_shell_v6_view->commit);
201
202 xdg_shell_v6_view->new_popup.notify = handle_new_popup;
203 wl_signal_add(&xdg_surface->events.new_popup,
204 &xdg_shell_v6_view->new_popup);
205}
206
207static void handle_destroy(struct wl_listener *listener, void *data) {
208 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
209 wl_container_of(listener, xdg_shell_v6_view, destroy);
210 view_destroy(&xdg_shell_v6_view->view);
211}
212
213void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
214 struct sway_server *server = wl_container_of(listener, server,
215 xdg_shell_v6_surface);
216 struct wlr_xdg_surface_v6 *xdg_surface = data;
217
218 if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
219 wlr_log(L_DEBUG, "New xdg_shell_v6 popup");
220 return;
221 }
222
223 wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
224 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
225 wlr_xdg_surface_v6_ping(xdg_surface);
226 wlr_xdg_toplevel_v6_set_maximized(xdg_surface, true);
227
228 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
229 calloc(1, sizeof(struct sway_xdg_shell_v6_view));
230 if (!sway_assert(xdg_shell_v6_view, "Failed to allocate view")) {
231 return;
232 }
233
234 view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
235 xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
236
237 // TODO:
238 // - Look up pid and open on appropriate workspace
239 // - Criteria
240
241 xdg_shell_v6_view->map.notify = handle_map;
242 wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
243
244 xdg_shell_v6_view->unmap.notify = handle_unmap;
245 wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_v6_view->unmap);
246
247 xdg_shell_v6_view->destroy.notify = handle_destroy;
248 wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy);
249}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
new file mode 100644
index 00000000..413dbf8b
--- /dev/null
+++ b/sway/desktop/xwayland.c
@@ -0,0 +1,310 @@
1#define _POSIX_C_SOURCE 199309L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <wayland-server.h>
5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/types/wlr_output.h>
7#include <wlr/xwayland.h>
8#include "log.h"
9#include "sway/desktop.h"
10#include "sway/input/input-manager.h"
11#include "sway/input/seat.h"
12#include "sway/output.h"
13#include "sway/server.h"
14#include "sway/tree/container.h"
15#include "sway/tree/layout.h"
16#include "sway/tree/view.h"
17
18static void unmanaged_handle_request_configure(struct wl_listener *listener,
19 void *data) {
20 struct sway_xwayland_unmanaged *surface =
21 wl_container_of(listener, surface, request_configure);
22 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
23 struct wlr_xwayland_surface_configure_event *ev = data;
24 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
25 ev->width, ev->height);
26}
27
28static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
29 struct sway_xwayland_unmanaged *surface =
30 wl_container_of(listener, surface, commit);
31 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
32
33 if (xsurface->x != surface->lx || xsurface->y != surface->ly) {
34 // Surface has moved
35 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
36 true);
37 surface->lx = xsurface->x;
38 surface->ly = xsurface->y;
39 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
40 true);
41 } else {
42 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y,
43 false);
44 }
45}
46
47static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
48 struct sway_xwayland_unmanaged *surface =
49 wl_container_of(listener, surface, map);
50 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
51
52 wl_list_insert(&root_container.sway_root->xwayland_unmanaged,
53 &surface->link);
54
55 wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
56 surface->commit.notify = unmanaged_handle_commit;
57
58 surface->lx = xsurface->x;
59 surface->ly = xsurface->y;
60 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
61
62 if (!wlr_xwayland_surface_is_unmanaged(xsurface)) {
63 struct sway_seat *seat = input_manager_current_seat(input_manager);
64 struct wlr_xwayland *xwayland = seat->input->server->xwayland;
65 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
66 seat_set_focus_surface(seat, xsurface->surface);
67 }
68
69 // TODO: we don't send surface enter/leave events to xwayland unmanaged
70 // surfaces, but xwayland doesn't support HiDPI anyway
71}
72
73static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
74 struct sway_xwayland_unmanaged *surface =
75 wl_container_of(listener, surface, unmap);
76 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
77 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true);
78 wl_list_remove(&surface->link);
79 wl_list_remove(&surface->commit.link);
80}
81
82static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
83 struct sway_xwayland_unmanaged *surface =
84 wl_container_of(listener, surface, destroy);
85 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
86 if (xsurface->mapped) {
87 unmanaged_handle_unmap(&surface->unmap, xsurface);
88 }
89 wl_list_remove(&surface->map.link);
90 wl_list_remove(&surface->unmap.link);
91 wl_list_remove(&surface->destroy.link);
92 free(surface);
93}
94
95static struct sway_xwayland_unmanaged *create_unmanaged(
96 struct wlr_xwayland_surface *xsurface) {
97 struct sway_xwayland_unmanaged *surface =
98 calloc(1, sizeof(struct sway_xwayland_unmanaged));
99 if (surface == NULL) {
100 wlr_log(L_ERROR, "Allocation failed");
101 return NULL;
102 }
103
104 surface->wlr_xwayland_surface = xsurface;
105
106 wl_signal_add(&xsurface->events.request_configure,
107 &surface->request_configure);
108 surface->request_configure.notify = unmanaged_handle_request_configure;
109 wl_signal_add(&xsurface->events.map, &surface->map);
110 surface->map.notify = unmanaged_handle_map;
111 wl_signal_add(&xsurface->events.unmap, &surface->unmap);
112 surface->unmap.notify = unmanaged_handle_unmap;
113 wl_signal_add(&xsurface->events.destroy, &surface->destroy);
114 surface->destroy.notify = unmanaged_handle_destroy;
115
116 unmanaged_handle_map(&surface->map, xsurface);
117
118 return surface;
119}
120
121
122static struct sway_xwayland_view *xwayland_view_from_view(
123 struct sway_view *view) {
124 if (!sway_assert(view->type == SWAY_VIEW_XWAYLAND,
125 "Expected xwayland view")) {
126 return NULL;
127 }
128 return (struct sway_xwayland_view *)view;
129}
130
131static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
132 if (xwayland_view_from_view(view) == NULL) {
133 return NULL;
134 }
135 switch (prop) {
136 case VIEW_PROP_TITLE:
137 return view->wlr_xwayland_surface->title;
138 case VIEW_PROP_CLASS:
139 return view->wlr_xwayland_surface->class;
140 default:
141 return NULL;
142 }
143}
144
145static void configure(struct sway_view *view, double ox, double oy, int width,
146 int height) {
147 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
148 if (xwayland_view == NULL) {
149 return;
150 }
151 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
152
153 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
154 if (!sway_assert(output, "view must be within tree to set position")) {
155 return;
156 }
157 struct sway_container *root = container_parent(output, C_ROOT);
158 if (!sway_assert(root, "output must be within tree to set position")) {
159 return;
160 }
161 struct wlr_output_layout *layout = root->sway_root->output_layout;
162 struct wlr_output_layout_output *loutput =
163 wlr_output_layout_get(layout, output->sway_output->wlr_output);
164 if (!sway_assert(loutput, "output must be within layout to set position")) {
165 return;
166 }
167
168 view_update_position(view, ox, oy);
169
170 xwayland_view->pending_width = width;
171 xwayland_view->pending_height = height;
172 wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y,
173 width, height);
174}
175
176static void set_activated(struct sway_view *view, bool activated) {
177 if (xwayland_view_from_view(view) == NULL) {
178 return;
179 }
180 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
181 wlr_xwayland_surface_activate(surface, activated);
182}
183
184static void _close(struct sway_view *view) {
185 if (xwayland_view_from_view(view) == NULL) {
186 return;
187 }
188 wlr_xwayland_surface_close(view->wlr_xwayland_surface);
189}
190
191static void destroy(struct sway_view *view) {
192 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
193 if (xwayland_view == NULL) {
194 return;
195 }
196 wl_list_remove(&xwayland_view->destroy.link);
197 wl_list_remove(&xwayland_view->request_configure.link);
198 wl_list_remove(&xwayland_view->map.link);
199 wl_list_remove(&xwayland_view->unmap.link);
200 free(xwayland_view);
201}
202
203static const struct sway_view_impl view_impl = {
204 .get_prop = get_prop,
205 .configure = configure,
206 .set_activated = set_activated,
207 .close = _close,
208 .destroy = destroy,
209};
210
211static void handle_commit(struct wl_listener *listener, void *data) {
212 struct sway_xwayland_view *xwayland_view =
213 wl_container_of(listener, xwayland_view, commit);
214 struct sway_view *view = &xwayland_view->view;
215 // NOTE: We intentionally discard the view's desired width here
216 // TODO: Let floating views do whatever
217 view_update_size(view, xwayland_view->pending_width,
218 xwayland_view->pending_height);
219 view_damage(view, false);
220}
221
222static void handle_unmap(struct wl_listener *listener, void *data) {
223 struct sway_xwayland_view *xwayland_view =
224 wl_container_of(listener, xwayland_view, unmap);
225 wl_list_remove(&xwayland_view->commit.link);
226 view_unmap(&xwayland_view->view);
227}
228
229static void handle_map(struct wl_listener *listener, void *data) {
230 struct sway_xwayland_view *xwayland_view =
231 wl_container_of(listener, xwayland_view, map);
232 struct wlr_xwayland_surface *xsurface = data;
233 struct sway_view *view = &xwayland_view->view;
234
235 // Wire up the commit listener here, because xwayland map/unmap can change
236 // the underlying wlr_surface
237 wl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit);
238 xwayland_view->commit.notify = handle_commit;
239
240 // Put it back into the tree
241 wlr_xwayland_surface_set_maximized(xsurface, true);
242 view_map(view, xsurface->surface);
243}
244
245static void handle_destroy(struct wl_listener *listener, void *data) {
246 struct sway_xwayland_view *xwayland_view =
247 wl_container_of(listener, xwayland_view, destroy);
248 struct sway_view *view = &xwayland_view->view;
249 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
250 if (xsurface->mapped) {
251 handle_unmap(&xwayland_view->unmap, xsurface);
252 }
253 view_destroy(&xwayland_view->view);
254}
255
256static void handle_request_configure(struct wl_listener *listener, void *data) {
257 struct sway_xwayland_view *xwayland_view =
258 wl_container_of(listener, xwayland_view, request_configure);
259 struct wlr_xwayland_surface_configure_event *ev = data;
260 struct sway_view *view = &xwayland_view->view;
261 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
262 // TODO: floating windows are allowed to move around like this, but make
263 // sure tiling windows always stay in place.
264 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
265 ev->width, ev->height);
266}
267
268void handle_xwayland_surface(struct wl_listener *listener, void *data) {
269 struct sway_server *server = wl_container_of(listener, server,
270 xwayland_surface);
271 struct wlr_xwayland_surface *xsurface = data;
272
273 if (wlr_xwayland_surface_is_unmanaged(xsurface) ||
274 xsurface->override_redirect) {
275 wlr_log(L_DEBUG, "New xwayland unmanaged surface");
276 create_unmanaged(xsurface);
277 return;
278 }
279
280 wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'",
281 xsurface->title, xsurface->class);
282
283 struct sway_xwayland_view *xwayland_view =
284 calloc(1, sizeof(struct sway_xwayland_view));
285 if (!sway_assert(xwayland_view, "Failed to allocate view")) {
286 return;
287 }
288
289 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
290 xwayland_view->view.wlr_xwayland_surface = xsurface;
291
292 // TODO:
293 // - Look up pid and open on appropriate workspace
294 // - Criteria
295
296 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
297 xwayland_view->destroy.notify = handle_destroy;
298
299 wl_signal_add(&xsurface->events.request_configure,
300 &xwayland_view->request_configure);
301 xwayland_view->request_configure.notify = handle_request_configure;
302
303 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap);
304 xwayland_view->unmap.notify = handle_unmap;
305
306 wl_signal_add(&xsurface->events.map, &xwayland_view->map);
307 xwayland_view->map.notify = handle_map;
308
309 handle_map(&xwayland_view->map, xsurface);
310}
diff --git a/sway/extensions.c b/sway/extensions.c
deleted file mode 100644
index 91746561..00000000
--- a/sway/extensions.c
+++ /dev/null
@@ -1,407 +0,0 @@
1#include <stdlib.h>
2#include <wlc/wlc.h>
3#include <wlc/wlc-wayland.h>
4#include <wlc/wlc-render.h>
5#include "wayland-desktop-shell-server-protocol.h"
6#include "wayland-swaylock-server-protocol.h"
7#include "wayland-gamma-control-server-protocol.h"
8#include "wayland-server-decoration-server-protocol.h"
9#include "sway/layout.h"
10#include "sway/input_state.h"
11#include "sway/extensions.h"
12#include "sway/security.h"
13#include "sway/ipc-server.h"
14#include "log.h"
15
16struct desktop_shell_state desktop_shell;
17struct decoration_state decoration_state;
18
19static struct panel_config *find_or_create_panel_config(struct wl_resource *resource) {
20 for (int i = 0; i < desktop_shell.panels->length; i++) {
21 struct panel_config *conf = desktop_shell.panels->items[i];
22 if (conf->wl_resource == resource) {
23 sway_log(L_DEBUG, "Found existing panel config for resource %p", resource);
24 return conf;
25 }
26 }
27 sway_log(L_DEBUG, "Creating panel config for resource %p", resource);
28 struct panel_config *config = calloc(1, sizeof(struct panel_config));
29 if (!config) {
30 sway_log(L_ERROR, "Unable to create panel config");
31 return NULL;
32 }
33 list_add(desktop_shell.panels, config);
34 config->wl_resource = resource;
35 return config;
36}
37
38void background_surface_destructor(struct wl_resource *resource) {
39 sway_log(L_DEBUG, "Background surface killed");
40 int i;
41 for (i = 0; i < desktop_shell.backgrounds->length; ++i) {
42 struct background_config *config = desktop_shell.backgrounds->items[i];
43 if (config->wl_surface_res == resource) {
44 list_del(desktop_shell.backgrounds, i);
45 break;
46 }
47 }
48}
49
50void panel_surface_destructor(struct wl_resource *resource) {
51 sway_log(L_DEBUG, "Panel surface killed");
52 int i;
53 for (i = 0; i < desktop_shell.panels->length; ++i) {
54 struct panel_config *config = desktop_shell.panels->items[i];
55 if (config->wl_surface_res == resource) {
56 list_del(desktop_shell.panels, i);
57 arrange_windows(&root_container, -1, -1);
58 break;
59 }
60 }
61}
62
63void lock_surface_destructor(struct wl_resource *resource) {
64 sway_log(L_DEBUG, "Lock surface killed");
65 int i;
66 for (i = 0; i < desktop_shell.lock_surfaces->length; ++i) {
67 struct wl_resource *surface = desktop_shell.lock_surfaces->items[i];
68 if (surface == resource) {
69 list_del(desktop_shell.lock_surfaces, i);
70 arrange_windows(&root_container, -1, -1);
71 break;
72 }
73 }
74 if (desktop_shell.lock_surfaces->length == 0) {
75 sway_log(L_DEBUG, "Desktop shell unlocked");
76 desktop_shell.is_locked = false;
77
78 // We need to now give focus back to the focus which we internally
79 // track, since when we lock sway we don't actually change our internal
80 // focus tracking.
81 swayc_t *focus = get_focused_container(swayc_active_workspace());
82 set_focused_container(focus);
83 wlc_view_focus(focus->handle);
84 }
85}
86
87static void set_background(struct wl_client *client, struct wl_resource *resource,
88 struct wl_resource *_output, struct wl_resource *surface) {
89 pid_t pid;
90 wl_client_get_credentials(client, &pid, NULL, NULL);
91 if (!(get_feature_policy_mask(pid) & FEATURE_BACKGROUND)) {
92 sway_log(L_INFO, "Denying background feature to %d", pid);
93 return;
94 }
95 wlc_handle output = wlc_handle_from_wl_output_resource(_output);
96 if (!output) {
97 return;
98 }
99 sway_log(L_DEBUG, "Setting surface %p as background for output %d", surface, (int)output);
100 struct background_config *config = malloc(sizeof(struct background_config));
101 if (!config) {
102 sway_log(L_ERROR, "Unable to allocate background config");
103 return;
104 }
105 config->client = client;
106 config->output = output;
107 config->surface = wlc_resource_from_wl_surface_resource(surface);
108 config->wl_surface_res = surface;
109 list_add(desktop_shell.backgrounds, config);
110 wl_resource_set_destructor(surface, background_surface_destructor);
111 arrange_windows(swayc_by_handle(output), -1, -1);
112 wlc_output_schedule_render(config->output);
113}
114
115static void set_panel(struct wl_client *client, struct wl_resource *resource,
116 struct wl_resource *_output, struct wl_resource *surface) {
117 pid_t pid;
118 wl_client_get_credentials(client, &pid, NULL, NULL);
119 if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
120 sway_log(L_INFO, "Denying panel feature to %d", pid);
121 return;
122 }
123 wlc_handle output = wlc_handle_from_wl_output_resource(_output);
124 if (!output) {
125 return;
126 }
127 sway_log(L_DEBUG, "Setting surface %p as panel for output %d (wl_resource: %p)", surface, (int)output, resource);
128 struct panel_config *config = find_or_create_panel_config(resource);
129 config->output = output;
130 config->client = client;
131 config->surface = wlc_resource_from_wl_surface_resource(surface);
132 config->wl_surface_res = surface;
133 wl_resource_set_destructor(surface, panel_surface_destructor);
134 arrange_windows(&root_container, -1, -1);
135 wlc_output_schedule_render(config->output);
136}
137
138static void desktop_set_lock_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) {
139 sway_log(L_ERROR, "desktop_set_lock_surface is not currently supported");
140}
141
142static void desktop_unlock(struct wl_client *client, struct wl_resource *resource) {
143 sway_log(L_ERROR, "desktop_unlock is not currently supported");
144}
145
146static void set_grab_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) {
147 sway_log(L_ERROR, "desktop_set_grab_surface is not currently supported");
148}
149
150static void desktop_ready(struct wl_client *client, struct wl_resource *resource) {
151 // nop
152}
153
154static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) {
155 pid_t pid;
156 wl_client_get_credentials(client, &pid, NULL, NULL);
157 if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
158 sway_log(L_INFO, "Denying panel feature to %d", pid);
159 return;
160 }
161 struct panel_config *config = find_or_create_panel_config(resource);
162 sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position);
163 config->panel_position = position;
164 arrange_windows(&root_container, -1, -1);
165}
166
167static struct desktop_shell_interface desktop_shell_implementation = {
168 .set_background = set_background,
169 .set_panel = set_panel,
170 .set_lock_surface = desktop_set_lock_surface,
171 .unlock = desktop_unlock,
172 .set_grab_surface = set_grab_surface,
173 .desktop_ready = desktop_ready,
174 .set_panel_position = set_panel_position
175};
176
177static void desktop_shell_bind(struct wl_client *client, void *data,
178 uint32_t version, uint32_t id) {
179 if (version > 3) {
180 // Unsupported version
181 return;
182 }
183
184 struct wl_resource *resource = wl_resource_create(client, &desktop_shell_interface, version, id);
185 if (!resource) {
186 wl_client_post_no_memory(client);
187 }
188
189 wl_resource_set_implementation(resource, &desktop_shell_implementation, NULL, NULL);
190}
191
192static void set_lock_surface(struct wl_client *client, struct wl_resource *resource,
193 struct wl_resource *_output, struct wl_resource *surface) {
194 pid_t pid;
195 wl_client_get_credentials(client, &pid, NULL, NULL);
196 if (!(get_feature_policy_mask(pid) & FEATURE_LOCK)) {
197 sway_log(L_INFO, "Denying lock feature to %d", pid);
198 return;
199 }
200 swayc_t *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output));
201 swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface));
202 sway_log(L_DEBUG, "Setting lock surface to %p", view);
203 if (view && output) {
204 swayc_t *workspace = output->focused;
205 if (!swayc_is_child_of(view, workspace)) {
206 move_container_to(view, workspace);
207 }
208 // make the view floating so it doesn't rearrange other siblings.
209 if (!view->is_floating) {
210 destroy_container(remove_child(view));
211 add_floating(workspace, view);
212 }
213 wlc_view_set_state(view->handle, WLC_BIT_FULLSCREEN, true);
214 wlc_view_bring_to_front(view->handle);
215 wlc_view_focus(view->handle);
216 desktop_shell.is_locked = true;
217 input_init();
218 arrange_windows(workspace, -1, -1);
219 list_add(desktop_shell.lock_surfaces, surface);
220 wl_resource_set_destructor(surface, lock_surface_destructor);
221 } else {
222 sway_log(L_ERROR, "Attempted to set lock surface to non-view");
223 }
224}
225
226static void unlock(struct wl_client *client, struct wl_resource *resource) {
227 sway_log(L_ERROR, "unlock is not currently supported");
228 // This isn't really necessary, we just unlock when the client exits.
229}
230
231static struct lock_interface swaylock_implementation = {
232 .set_lock_surface = set_lock_surface,
233 .unlock = unlock
234};
235
236static void swaylock_bind(struct wl_client *client, void *data,
237 uint32_t version, uint32_t id) {
238 if (version > 1) {
239 // Unsupported version
240 return;
241 }
242
243 struct wl_resource *resource = wl_resource_create(client, &lock_interface, version, id);
244 if (!resource) {
245 wl_client_post_no_memory(client);
246 }
247
248 wl_resource_set_implementation(resource, &swaylock_implementation, NULL, NULL);
249}
250
251static void gamma_control_destroy(struct wl_client *client, struct wl_resource *res) {
252 wl_resource_destroy(res);
253}
254
255static void gamma_control_set_gamma(struct wl_client *client,
256 struct wl_resource *res, struct wl_array *red,
257 struct wl_array *green, struct wl_array *blue) {
258 if (red->size != green->size || red->size != blue->size) {
259 wl_resource_post_error(res, GAMMA_CONTROL_ERROR_INVALID_GAMMA,
260 "The gamma ramps don't have the same size");
261 return;
262 }
263 uint16_t *r = (uint16_t *)red->data;
264 uint16_t *g = (uint16_t *)green->data;
265 uint16_t *b = (uint16_t *)blue->data;
266 wlc_handle output = wlc_handle_from_wl_output_resource(
267 wl_resource_get_user_data(res));
268 if (!output) {
269 return;
270 }
271 sway_log(L_DEBUG, "Setting gamma for output");
272 wlc_output_set_gamma(output, red->size / sizeof(uint16_t), r, g, b);
273}
274
275static void gamma_control_reset_gamma(struct wl_client *client,
276 struct wl_resource *resource) {
277 // This space intentionally left blank
278}
279
280static struct gamma_control_interface gamma_control_implementation = {
281 .destroy = gamma_control_destroy,
282 .set_gamma = gamma_control_set_gamma,
283 .reset_gamma = gamma_control_reset_gamma
284};
285
286static void gamma_control_manager_destroy(struct wl_client *client,
287 struct wl_resource *res) {
288 wl_resource_destroy(res);
289}
290
291static void gamma_control_manager_get(struct wl_client *client,
292 struct wl_resource *res, uint32_t id, struct wl_resource *_output) {
293 struct wl_resource *manager_res = wl_resource_create(client,
294 &gamma_control_interface, wl_resource_get_version(res), id);
295 wlc_handle output = wlc_handle_from_wl_output_resource(_output);
296 if (!output) {
297 return;
298 }
299 wl_resource_set_implementation(manager_res, &gamma_control_implementation,
300 _output, NULL);
301 gamma_control_send_gamma_size(manager_res, wlc_output_get_gamma_size(output));
302}
303
304static struct gamma_control_manager_interface gamma_manager_implementation = {
305 .destroy = gamma_control_manager_destroy,
306 .get_gamma_control = gamma_control_manager_get
307};
308
309static void gamma_control_manager_bind(struct wl_client *client, void *data,
310 uint32_t version, uint32_t id) {
311 if (version > 1) {
312 // Unsupported version
313 return;
314 }
315 struct wl_resource *resource = wl_resource_create(client,
316 &gamma_control_manager_interface, version, id);
317 if (!resource) {
318 wl_client_post_no_memory(client);
319 }
320 wl_resource_set_implementation(resource, &gamma_manager_implementation, NULL, NULL);
321}
322
323static void server_decoration_release(struct wl_client *client,
324 struct wl_resource *resource) {
325 wl_resource_destroy(resource);
326}
327
328void server_decoration_enable_csd(wlc_handle handle) {
329 swayc_t *view = swayc_by_handle(handle);
330 if (!view) {
331 sway_log(L_DEBUG, "view invalid");
332 return;
333 }
334 sway_log(L_DEBUG, "%s requested client side decorations", view->name);
335 view->border_type = B_NONE;
336 update_geometry(view);
337}
338
339static void server_decoration_request_mode(struct wl_client *client,
340 struct wl_resource *resource, uint32_t mode) {
341 sway_log(L_DEBUG, "Client requested server decoration mode %d", mode);
342 if (mode == ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER) {
343 return;
344 }
345 struct wl_resource *surface = wl_resource_get_user_data(resource);
346 if (!surface) {
347 sway_log(L_DEBUG, "surface invalid");
348 return;
349 }
350 wlc_handle handle = wlc_handle_from_wl_surface_resource(surface);
351 if (!handle) {
352 list_add(decoration_state.csd_resources, surface);
353 return;
354 }
355 server_decoration_enable_csd(handle);
356}
357
358static struct org_kde_kwin_server_decoration_interface server_decoration_implementation = {
359 .release = server_decoration_release,
360 .request_mode = server_decoration_request_mode,
361};
362
363static void server_decoration_manager_create(struct wl_client *client,
364 struct wl_resource *resource, uint32_t id, struct wl_resource *surface) {
365 sway_log(L_DEBUG, "Client requested server decoration manager");
366 struct wl_resource *manager = wl_resource_create(client,
367 &org_kde_kwin_server_decoration_interface, 1, id);
368 if (!manager) {
369 wl_client_post_no_memory(client);
370 }
371 wl_resource_set_implementation(manager, &server_decoration_implementation, surface, NULL);
372}
373
374// Jesus christ KDE, these names are whack as hell
375static struct org_kde_kwin_server_decoration_manager_interface server_decoration_manager_implementation = {
376 .create = server_decoration_manager_create,
377};
378
379static void server_decoration_manager_bind(struct wl_client *client, void *data,
380 uint32_t version, uint32_t id) {
381 if (version > 1) {
382 // Unsupported version
383 return;
384 }
385 struct wl_resource *resource = wl_resource_create(client,
386 &org_kde_kwin_server_decoration_manager_interface, version, id);
387 if (!resource) {
388 wl_client_post_no_memory(client);
389 }
390 wl_resource_set_implementation(resource, &server_decoration_manager_implementation, NULL, NULL);
391 org_kde_kwin_server_decoration_manager_send_default_mode(resource,
392 ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER);
393}
394
395void register_extensions(void) {
396 wl_global_create(wlc_get_wl_display(), &desktop_shell_interface, 3, NULL, desktop_shell_bind);
397 desktop_shell.backgrounds = create_list();
398 desktop_shell.panels = create_list();
399 desktop_shell.lock_surfaces = create_list();
400 desktop_shell.is_locked = false;
401 decoration_state.csd_resources = create_list();
402 wl_global_create(wlc_get_wl_display(), &lock_interface, 1, NULL, swaylock_bind);
403 wl_global_create(wlc_get_wl_display(), &gamma_control_manager_interface, 1,
404 NULL, gamma_control_manager_bind);
405 wl_global_create(wlc_get_wl_display(), &org_kde_kwin_server_decoration_manager_interface ,
406 1, NULL, server_decoration_manager_bind);
407}
diff --git a/sway/focus.c b/sway/focus.c
deleted file mode 100644
index e9b032f8..00000000
--- a/sway/focus.c
+++ /dev/null
@@ -1,277 +0,0 @@
1#include "stdbool.h"
2#include <wlc/wlc.h>
3#include "sway/focus.h"
4#include "sway/workspace.h"
5#include "sway/layout.h"
6#include "sway/config.h"
7#include "sway/extensions.h"
8#include "sway/input_state.h"
9#include "sway/ipc-server.h"
10#include "sway/border.h"
11#include "log.h"
12
13bool locked_container_focus = false;
14bool suspend_workspace_cleanup = false;
15
16// switches parent focus to c. will switch it accordingly
17static void update_focus(swayc_t *c) {
18 // Handle if focus switches
19 swayc_t *parent = c->parent;
20 if (!parent) return;
21 if (parent->focused != c) {
22 // Get previous focus
23 swayc_t *prev = parent->focused;
24 // Set new focus
25 parent->focused = c;
26
27 switch (c->type) {
28 // Shouldn't happen
29 case C_ROOT: return;
30
31 // Case where output changes
32 case C_OUTPUT:
33 wlc_output_focus(c->handle);
34 break;
35
36 // Case where workspace changes
37 case C_WORKSPACE:
38 if (prev) {
39 ipc_event_workspace(prev, c, "focus");
40
41 // if the old workspace has no children, destroy it
42 if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) {
43 destroy_workspace(prev);
44 } else {
45 // update visibility of old workspace
46 update_visibility(prev);
47 }
48 }
49 // Update visibility of newly focused workspace
50 update_visibility(c);
51 break;
52
53 default:
54 case C_VIEW:
55 case C_CONTAINER:
56 break;
57 }
58 }
59}
60
61bool move_focus(enum movement_direction direction) {
62 swayc_t *old_view = get_focused_container(&root_container);
63 swayc_t *new_view = get_swayc_in_direction(old_view, direction);
64 if (!new_view) {
65 return false;
66 } else if (new_view->type == C_ROOT) {
67 sway_log(L_DEBUG, "Not setting focus above the workspace level");
68 return false;
69 } else if (new_view->type == C_OUTPUT) {
70 return set_focused_container(swayc_active_workspace_for(new_view));
71 } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) {
72 return set_focused_container(new_view);
73 } else if (config->mouse_warping) {
74 swayc_t *old_op = old_view->type == C_OUTPUT ?
75 old_view : swayc_parent_by_type(old_view, C_OUTPUT);
76 swayc_t *focused = get_focused_view(new_view);
77 if (set_focused_container(focused)) {
78 if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) {
79 center_pointer_on(focused);
80 }
81 return true;
82 }
83 } else {
84 return set_focused_container(get_focused_view(new_view));
85 }
86 return false;
87}
88
89swayc_t *get_focused_container(swayc_t *parent) {
90 if (!parent) {
91 return swayc_active_workspace();
92 }
93 while (!parent->is_focused && parent->focused) {
94 parent = parent->focused;
95 }
96 return parent;
97}
98
99bool set_focused_container(swayc_t *c) {
100 if (locked_container_focus || !c || !c->parent) {
101 return false;
102 }
103
104 // current ("old") workspace for sending workspace change event later
105 swayc_t *old_ws = swayc_active_workspace();
106 // keep track of child count so we can determine if it gets destroyed
107 int old_ws_child_count = 0;
108 if (old_ws) {
109 old_ws_child_count = old_ws->children->length + old_ws->floating->length;
110 }
111
112 // current ("old") focused container
113 swayc_t *old_focus = get_focused_container(&root_container);
114 // if old_focus is a workspace, then it's the same workspace as
115 // old_ws, and we'll need to null its pointer too, since it will
116 // be destroyed in the update_focus() call
117 bool old_focus_was_ws = (old_focus->type == C_WORKSPACE);
118
119 // workspace of new focused container
120 swayc_t *workspace = swayc_active_workspace_for(c);
121
122 if (swayc_is_fullscreen(get_focused_container(workspace))) {
123 // if switching to a workspace with a fullscreen view,
124 // focus on the fullscreen view
125 c = get_focused_container(workspace);
126 }
127
128 swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle);
129
130 if (c->type == C_VIEW) {
131 // dispatch a window event
132 ipc_event_window(c, "focus");
133 }
134
135 // update the global pointer
136 current_focus = c;
137
138 // update container focus from here to root, making necessary changes along
139 // the way
140 swayc_t *p = c;
141 if (p->type != C_OUTPUT && p->type != C_ROOT) {
142 p->is_focused = true;
143 }
144 while (p != &root_container) {
145 update_focus(p);
146 p = p->parent;
147 p->is_focused = false;
148 }
149
150 if (old_focus_was_ws && old_ws_child_count == 0) {
151 // this workspace was destroyed in update_focus(), so null the pointers
152 old_focus = NULL;
153 old_ws = NULL;
154 }
155
156 if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) {
157 if (old_focus) {
158 if (old_focus->type == C_VIEW) {
159 wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false);
160 }
161 update_container_border(old_focus);
162 }
163 if (c->type == C_VIEW) {
164 wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true);
165 }
166 if (!desktop_shell.is_locked) {
167 // If the system is locked, we do everything _but_ actually setting
168 // focus. This includes making our internals think that this view is
169 // focused.
170 wlc_view_focus(c->handle);
171 }
172 if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) {
173 update_container_border(c);
174 }
175
176 swayc_t *parent = swayc_tabbed_stacked_ancestor(c);
177 if (parent != NULL) {
178 arrange_backgrounds();
179 arrange_windows(parent, -1, -1);
180 }
181 }
182
183 if (old_ws != workspace) {
184 // old_ws might be NULL here but that's ok
185 ipc_event_workspace(old_ws, workspace, "focus");
186 }
187
188 return true;
189}
190
191bool set_focused_container_for(swayc_t *a, swayc_t *c) {
192 if (locked_container_focus || !c) {
193 return false;
194 }
195 swayc_t *find = c;
196 while (find != a && (find = find->parent)) {
197 if (find == &root_container) {
198 return false;
199 }
200 }
201
202 // Get workspace for c, get that workspaces current focused container.
203 swayc_t *workspace = swayc_active_workspace_for(c);
204 swayc_t *focused = get_focused_view(workspace);
205 // if the workspace we are changing focus to has a fullscreen view return
206 if (swayc_is_fullscreen(focused) && c != focused) {
207 return false;
208 }
209
210 // Check if we are changing a parent container that will see change
211 bool effective = true;
212 while (find != &root_container) {
213 if (find->parent->focused != find) {
214 effective = false;
215 }
216 find = find->parent;
217 }
218 if (effective) {
219 // Go to set_focused_container
220 return set_focused_container(c);
221 }
222
223 sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR,
224 a, a->handle, c, c->handle);
225
226 c->is_focused = true;
227 swayc_t *p = c;
228 while (p != a) {
229 update_focus(p);
230 p = p->parent;
231 p->is_focused = false;
232 }
233 return true;
234}
235
236swayc_t *get_focused_view(swayc_t *parent) {
237 swayc_t *c = parent;
238 while (c && c->type != C_VIEW) {
239 if (c->type == C_WORKSPACE && c->focused == NULL) {
240 return c;
241 }
242 c = c->focused;
243 }
244 if (c == NULL) {
245 c = swayc_active_workspace_for(parent);
246 }
247 return c;
248}
249
250swayc_t *get_focused_float(swayc_t *ws) {
251 if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) {
252 ws = swayc_active_workspace();
253 }
254 if (ws->floating->length) {
255 return ws->floating->items[ws->floating->length - 1];
256 }
257 return NULL;
258}
259
260swayc_t *get_focused_view_include_floating(swayc_t *parent) {
261 swayc_t *c = parent;
262 swayc_t *f = NULL;
263
264 while (c && c->type != C_VIEW) {
265 if (c->type == C_WORKSPACE && c->focused == NULL) {
266 return ((f = get_focused_float(c))) ? f : c;
267 }
268
269 c = c->focused;
270 }
271
272 if (c == NULL) {
273 c = swayc_active_workspace_for(parent);
274 }
275
276 return c;
277}
diff --git a/sway/handlers.c b/sway/handlers.c
deleted file mode 100644
index 33e75d6b..00000000
--- a/sway/handlers.c
+++ /dev/null
@@ -1,1143 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <xkbcommon/xkbcommon.h>
3#include <strings.h>
4#include <stdlib.h>
5#include <stdbool.h>
6#include <libinput.h>
7#include <math.h>
8#include <wlc/wlc.h>
9#include <wlc/wlc-render.h>
10#include <wlc/wlc-wayland.h>
11#include <ctype.h>
12#include "sway/handlers.h"
13#include "sway/border.h"
14#include "sway/layout.h"
15#include "sway/config.h"
16#include "sway/commands.h"
17#include "sway/workspace.h"
18#include "sway/container.h"
19#include "sway/output.h"
20#include "sway/focus.h"
21#include "sway/input_state.h"
22#include "sway/extensions.h"
23#include "sway/criteria.h"
24#include "sway/ipc-server.h"
25#include "sway/input.h"
26#include "sway/security.h"
27#include "list.h"
28#include "stringop.h"
29#include "log.h"
30
31// Event should be sent to client
32#define EVENT_PASSTHROUGH false
33
34// Event handled by sway and should not be sent to client
35#define EVENT_HANDLED true
36
37static struct panel_config *if_panel_find_config(struct wl_client *client) {
38 int i;
39 for (i = 0; i < desktop_shell.panels->length; i++) {
40 struct panel_config *config = desktop_shell.panels->items[i];
41 if (config->client == client) {
42 return config;
43 }
44 }
45 return NULL;
46}
47
48static struct background_config *if_background_find_config(struct wl_client *client) {
49 int i;
50 for (i = 0; i < desktop_shell.backgrounds->length; i++) {
51 struct background_config *config = desktop_shell.backgrounds->items[i];
52 if (config->client == client) {
53 return config;
54 }
55 }
56 return NULL;
57}
58
59static struct wlc_geometry compute_panel_geometry(struct panel_config *config) {
60 struct wlc_size resolution;
61 output_get_scaled_size(config->output, &resolution);
62 const struct wlc_geometry *old = wlc_view_get_geometry(config->handle);
63 struct wlc_geometry new;
64
65 switch (config->panel_position) {
66 case DESKTOP_SHELL_PANEL_POSITION_TOP:
67 new.origin.x = 0;
68 new.origin.y = 0;
69 new.size.w = resolution.w;
70 new.size.h = old->size.h;
71 break;
72 case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
73 new.origin.x = 0;
74 new.origin.y = resolution.h - old->size.h;
75 new.size.w = resolution.w;
76 new.size.h = old->size.h;
77 break;
78 case DESKTOP_SHELL_PANEL_POSITION_LEFT:
79 new.origin.x = 0;
80 new.origin.y = 0;
81 new.size.w = old->size.w;
82 new.size.h = resolution.h;
83 break;
84 case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
85 new.origin.x = resolution.w - old->size.w;
86 new.origin.y = 0;
87 new.size.w = old->size.w;
88 new.size.h = resolution.h;
89 break;
90 }
91
92 return new;
93}
94
95static void update_panel_geometry(struct panel_config *config) {
96 struct wlc_geometry geometry = compute_panel_geometry(config);
97 wlc_view_set_geometry(config->handle, 0, &geometry);
98}
99
100static void update_panel_geometries(wlc_handle output) {
101 for (int i = 0; i < desktop_shell.panels->length; i++) {
102 struct panel_config *config = desktop_shell.panels->items[i];
103 if (config->output == output) {
104 update_panel_geometry(config);
105 }
106 }
107}
108
109static void update_background_geometry(struct background_config *config) {
110 struct wlc_geometry geometry = wlc_geometry_zero;
111 output_get_scaled_size(config->output, &geometry.size);
112 wlc_view_set_geometry(config->handle, 0, &geometry);
113}
114
115static void update_background_geometries(wlc_handle output) {
116 for (int i = 0; i < desktop_shell.backgrounds->length; i++) {
117 struct background_config *config = desktop_shell.backgrounds->items[i];
118 if (config->output == output) {
119 update_background_geometry(config);
120 }
121 }
122}
123
124/* Handles */
125
126static bool handle_input_created(struct libinput_device *device) {
127 const char *identifier = libinput_dev_unique_id(device);
128 if (!identifier) {
129 sway_log(L_ERROR, "Unable to allocate unique name for input device %p",
130 device);
131 return true;
132 }
133 sway_log(L_INFO, "Found input device (%s)", identifier);
134
135 list_add(input_devices, device);
136
137 struct input_config *ic = NULL;
138 int i;
139 for (i = 0; i < config->input_configs->length; ++i) {
140 struct input_config *cur = config->input_configs->items[i];
141 if (strcasecmp(identifier, cur->identifier) == 0) {
142 sway_log(L_DEBUG, "Matched input config for %s",
143 identifier);
144 ic = cur;
145 break;
146 }
147 if (strcasecmp("*", cur->identifier) == 0) {
148 sway_log(L_DEBUG, "Matched wildcard input config for %s",
149 identifier);
150 ic = cur;
151 break;
152 }
153 }
154
155 apply_input_config(ic, device);
156 return true;
157}
158
159static void handle_input_destroyed(struct libinput_device *device) {
160 int i;
161 list_t *list = input_devices;
162 for (i = 0; i < list->length; ++i) {
163 if(((struct libinput_device *)list->items[i]) == device) {
164 list_del(list, i);
165 break;
166 }
167 }
168}
169
170static bool handle_output_created(wlc_handle output) {
171 swayc_t *op = new_output(output);
172
173 // Visibility mask to be able to make view invisible
174 wlc_output_set_mask(output, VISIBLE);
175
176 if (!op) {
177 return false;
178 }
179
180 // Switch to workspace if we need to
181 if (swayc_active_workspace() == NULL) {
182 swayc_t *ws = op->children->items[0];
183 workspace_switch(ws);
184 }
185
186 // Fixes issues with backgrounds and wlc
187 wlc_handle prev = wlc_get_focused_output();
188 wlc_output_focus(output);
189 wlc_output_focus(prev);
190 return true;
191}
192
193static void handle_output_destroyed(wlc_handle output) {
194 int i;
195 list_t *list = root_container.children;
196 for (i = 0; i < list->length; ++i) {
197 if (((swayc_t *)list->items[i])->handle == output) {
198 break;
199 }
200 }
201 if (i < list->length) {
202 destroy_output(list->items[i]);
203 } else {
204 return;
205 }
206 if (list->length > 0) {
207 // switch to other outputs active workspace
208 workspace_switch(((swayc_t *)root_container.children->items[0])->focused);
209 }
210}
211
212static void handle_output_post_render(wlc_handle output) {
213 ipc_get_pixels(output);
214}
215
216static void handle_view_pre_render(wlc_handle view) {
217 render_view_borders(view);
218}
219
220static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) {
221 sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h);
222
223 swayc_t *c = swayc_by_handle(output);
224 if (!c) {
225 return;
226 }
227 c->width = to->w;
228 c->height = to->h;
229
230 update_panel_geometries(output);
231 update_background_geometries(output);
232
233 arrange_windows(&root_container, -1, -1);
234}
235
236static void handle_output_focused(wlc_handle output, bool focus) {
237 swayc_t *c = swayc_by_handle(output);
238 // if for some reason this output doesn't exist, create it.
239 if (!c) {
240 handle_output_created(output);
241 }
242 if (focus) {
243 set_focused_container(get_focused_container(c));
244 }
245}
246
247static void ws_cleanup() {
248 swayc_t *op, *ws;
249 int i = 0, j;
250 if (!root_container.children)
251 return;
252 while (i < root_container.children->length) {
253 op = root_container.children->items[i++];
254 if (!op->children)
255 continue;
256 j = 0;
257 while (j < op->children->length) {
258 ws = op->children->items[j++];
259 if (ws->children->length == 0 && ws->floating->length == 0 && ws != op->focused) {
260 if (destroy_workspace(ws)) {
261 j--;
262 }
263 }
264 }
265 }
266}
267
268static void positioner_place_window(wlc_handle handle) {
269 const struct wlc_geometry *anchor = wlc_view_positioner_get_anchor_rect(handle);
270 const struct wlc_size *sr = wlc_view_positioner_get_size(handle);
271 // a positioner is required to have a non-null anchor and a non-negative size
272 if (!anchor || !sr ||
273 sr->w <= 0 || sr->h <= 0 ||
274 anchor->size.w <= 0 || anchor->size.h <= 0) {
275 return;
276 }
277 const struct wlc_point offset = *wlc_view_positioner_get_offset(handle);
278 enum wlc_positioner_anchor_bit anchors = wlc_view_positioner_get_anchor(handle);
279 enum wlc_positioner_gravity_bit gravity = wlc_view_positioner_get_gravity(handle);
280 struct wlc_geometry geo = {
281 .origin = offset,
282 .size = *sr
283 };
284
285 if (anchors & WLC_BIT_ANCHOR_TOP) {
286 geo.origin.y += anchor->origin.y;
287 } else if (anchors & WLC_BIT_ANCHOR_BOTTOM) {
288 geo.origin.y += anchor->origin.y + anchor->size.h;
289 } else {
290 geo.origin.y += anchor->origin.y + anchor->size.h / 2;
291 }
292 if (anchors & WLC_BIT_ANCHOR_LEFT) {
293 geo.origin.x += anchor->origin.x;
294 } else if (anchors & WLC_BIT_ANCHOR_RIGHT) {
295 geo.origin.x += anchor->origin.x + anchor->size.w;
296 } else {
297 geo.origin.x += anchor->origin.x + anchor->size.w / 2;
298 }
299
300 if (gravity & WLC_BIT_GRAVITY_TOP) {
301 geo.origin.y -= geo.size.h;
302 } else if (gravity & WLC_BIT_GRAVITY_BOTTOM) {
303 /* default */
304 } else {
305 geo.origin.y -= geo.size.h / 2;
306 }
307 if (gravity & WLC_BIT_GRAVITY_LEFT) {
308 geo.origin.x -= geo.size.w;
309 } else if (gravity & WLC_BIT_GRAVITY_RIGHT) {
310 /* default */
311 } else {
312 geo.origin.x -= geo.size.w / 2;
313 }
314
315 sway_log(L_DEBUG, "xdg-positioner: placing window %" PRIuPTR " "
316 "sized (%u,%u) offset by (%d,%d), "
317 "anchor rectangle sized (%u,%u) at (%d,%d), "
318 "anchor edges: %s %s, gravity: %s %s",
319 handle,
320 sr->w, sr->h, offset.x, offset.y,
321 anchor->size.w, anchor->size.h, anchor->origin.x, anchor->origin.y,
322 anchors & WLC_BIT_ANCHOR_TOP ? "top" :
323 (anchors & WLC_BIT_ANCHOR_BOTTOM ? "bottom" : "middle"),
324 anchors & WLC_BIT_ANCHOR_LEFT ? "left" :
325 (anchors & WLC_BIT_ANCHOR_RIGHT ? "right" : "center"),
326 gravity & WLC_BIT_GRAVITY_TOP ? "top" :
327 (gravity & WLC_BIT_GRAVITY_BOTTOM ? "bottom" : "middle"),
328 gravity & WLC_BIT_GRAVITY_LEFT ? "left" :
329 (gravity & WLC_BIT_GRAVITY_RIGHT ? "right" : "center"));
330
331 wlc_handle parent = wlc_view_get_parent(handle);
332 if (parent) {
333 const struct wlc_geometry *pg = wlc_view_get_geometry(parent);
334 geo.origin.x += pg->origin.x;
335 geo.origin.y += pg->origin.y;
336 }
337 wlc_view_set_geometry(handle, 0, &geo);
338}
339
340static bool handle_view_created(wlc_handle handle) {
341 // if view is child of another view, the use that as focused container
342 wlc_handle parent = wlc_view_get_parent(handle);
343 swayc_t *focused = NULL;
344 swayc_t *newview = NULL;
345 swayc_t *current_ws = swayc_active_workspace();
346 bool return_to_workspace = false;
347 struct wl_client *client = wlc_view_get_wl_client(handle);
348 struct wl_resource *resource = wlc_surface_get_wl_resource(
349 wlc_view_get_surface(handle));
350 pid_t pid;
351 struct panel_config *panel_config = NULL;
352 struct background_config *background_config = NULL;
353
354 panel_config = if_panel_find_config(client);
355 if (panel_config) {
356 panel_config->handle = handle;
357 update_panel_geometry(panel_config);
358 wlc_view_set_mask(handle, VISIBLE);
359 wlc_view_set_output(handle, panel_config->output);
360 wlc_view_bring_to_front(handle);
361 arrange_windows(&root_container, -1, -1);
362 return true;
363 }
364
365 background_config = if_background_find_config(client);
366 if (background_config) {
367 background_config->handle = handle;
368 update_background_geometry(background_config);
369 wlc_view_set_mask(handle, VISIBLE);
370 wlc_view_set_output(handle, background_config->output);
371 wlc_view_send_to_back(handle);
372 return true;
373 }
374
375 // Get parent container, to add view in
376 if (parent) {
377 focused = swayc_by_handle(parent);
378 }
379
380 if (client) {
381 pid = wlc_view_get_pid(handle);
382
383 if (pid) {
384 // using newview as a temp storage location here,
385 // rather than adding yet another workspace var
386 newview = workspace_for_pid(pid);
387 if (newview) {
388 focused = get_focused_container(newview);
389 return_to_workspace = true;
390 }
391 newview = NULL;
392 }
393 }
394
395 swayc_t *prev_focus = get_focused_container(&root_container);
396
397 if (!focused || focused->type == C_OUTPUT) {
398 focused = prev_focus;
399 // Move focus from floating view
400 if (focused->is_floating) {
401 // To workspace if there are no children
402 if (focused->parent->children->length == 0) {
403 focused = focused->parent;
404 }
405 // TODO find a better way of doing this
406 // Or to focused container
407 else {
408 focused = get_focused_container(focused->parent->children->items[0]);
409 }
410 }
411 }
412
413 positioner_place_window(handle);
414
415 sway_log(L_DEBUG, "handle:%" PRIuPTR " type:%x state:%x parent:%" PRIuPTR " "
416 "mask:%d (x:%d y:%d w:%d h:%d) title:%s "
417 "class:%s appid:%s",
418 handle, wlc_view_get_type(handle), wlc_view_get_state(handle), parent,
419 wlc_view_get_mask(handle), wlc_view_get_geometry(handle)->origin.x,
420 wlc_view_get_geometry(handle)->origin.y,wlc_view_get_geometry(handle)->size.w,
421 wlc_view_get_geometry(handle)->size.h, wlc_view_get_title(handle),
422 wlc_view_get_class(handle), wlc_view_get_app_id(handle));
423
424 // TODO properly figure out how each window should be handled.
425 switch (wlc_view_get_type(handle)) {
426 // regular view created regularly
427 case 0:
428 if (parent) {
429 newview = new_floating_view(handle);
430 } else {
431 newview = new_view(focused, handle);
432 wlc_view_set_state(handle, WLC_BIT_MAXIMIZED, true);
433 }
434 break;
435
436 // Dmenu keeps viewfocus, but others with this flag don't, for now simulate
437 // dmenu
438 case WLC_BIT_OVERRIDE_REDIRECT:
439 wlc_view_focus(handle);
440 wlc_view_set_state(handle, WLC_BIT_ACTIVATED, true);
441 wlc_view_bring_to_front(handle);
442 break;
443
444 // Firefox popups have this flag set.
445 case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
446 wlc_view_bring_to_front(handle);
447 locked_container_focus = true;
448 break;
449
450 // Modals, get focus, popups do not
451 case WLC_BIT_MODAL:
452 wlc_view_focus(handle);
453 wlc_view_bring_to_front(handle);
454 newview = new_floating_view(handle);
455 /* fallthrough */
456 case WLC_BIT_POPUP:
457 wlc_view_bring_to_front(handle);
458 break;
459 }
460
461 // Prevent current ws from being destroyed, if empty
462 suspend_workspace_cleanup = true;
463
464 if (newview) {
465 ipc_event_window(newview, "new");
466 set_focused_container(newview);
467 wlc_view_set_mask(handle, VISIBLE);
468 swayc_t *output = swayc_parent_by_type(newview, C_OUTPUT);
469 arrange_windows(output, -1, -1);
470 // check if it matches for_window in config and execute if so
471 list_t *criteria = criteria_for(newview);
472 for (int i = 0; i < criteria->length; i++) {
473 struct criteria *crit = criteria->items[i];
474 sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'",
475 crit->crit_raw, newview, crit->cmdlist);
476 struct cmd_results *res = handle_command(crit->cmdlist, CONTEXT_CRITERIA);
477 if (res->status != CMD_SUCCESS) {
478 sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
479 }
480 free_cmd_results(res);
481 // view must be focused for commands to affect it, so always
482 // refocus in-between command lists
483 set_focused_container(newview);
484 }
485 swayc_t *workspace = swayc_parent_by_type(focused, C_WORKSPACE);
486 if (workspace && workspace->fullscreen) {
487 set_focused_container(workspace->fullscreen);
488 }
489 for (int i = 0; i < decoration_state.csd_resources->length; ++i) {
490 struct wl_resource *res = decoration_state.csd_resources->items[i];
491 if (res == resource) {
492 list_del(decoration_state.csd_resources, i);
493 server_decoration_enable_csd(handle);
494 break;
495 }
496 }
497 } else {
498 swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
499 wlc_handle *h = malloc(sizeof(wlc_handle));
500 if (!h) {
501 sway_log(L_ERROR, "Unable to allocate window handle, view handler bailing out");
502 return true;
503 }
504 *h = handle;
505 sway_log(L_DEBUG, "Adding unmanaged window %p to %p", h, output->unmanaged);
506 list_add(output->unmanaged, h);
507 wlc_view_set_mask(handle, VISIBLE);
508 }
509
510 if (return_to_workspace && current_ws != swayc_active_workspace()) {
511 // we were on one workspace, switched to another to add this view,
512 // now let's return to where we were
513 workspace_switch(current_ws);
514 set_focused_container(get_focused_container(current_ws));
515 }
516 if (prev_focus && prev_focus->type == C_VIEW
517 && newview && criteria_any(newview, config->no_focus)) {
518 // Restore focus
519 swayc_t *ws = swayc_parent_by_type(newview, C_WORKSPACE);
520 if (!ws || ws != newview->parent
521 || ws->children->length + ws->floating->length != 1) {
522 sway_log(L_DEBUG, "no_focus: restoring focus to %s", prev_focus->name);
523 set_focused_container(prev_focus);
524 }
525 }
526
527 suspend_workspace_cleanup = false;
528 ws_cleanup();
529 return true;
530}
531
532static void handle_view_destroyed(wlc_handle handle) {
533 sway_log(L_DEBUG, "Destroying window %" PRIuPTR, handle);
534 swayc_t *view = swayc_by_handle(handle);
535
536 // destroy views by type
537 switch (wlc_view_get_type(handle)) {
538 // regular view created regularly
539 case 0:
540 case WLC_BIT_MODAL:
541 case WLC_BIT_POPUP:
542 break;
543 // DMENU has this flag, and takes view_focus, but other things with this
544 // flag don't
545 case WLC_BIT_OVERRIDE_REDIRECT:
546 break;
547 case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
548 locked_container_focus = false;
549 break;
550 }
551
552 if (view) {
553 bool fullscreen = swayc_is_fullscreen(view);
554 remove_view_from_scratchpad(view);
555 swayc_t *parent = destroy_view(view), *iter = NULL;
556 if (parent) {
557 ipc_event_window(parent, "close");
558
559 // Destroy empty workspaces
560 if (parent->type == C_WORKSPACE &&
561 parent->children->length == 0 &&
562 parent->floating->length == 0 &&
563 swayc_active_workspace() != parent &&
564 !parent->visible) {
565 parent = destroy_workspace(parent);
566 }
567
568 if (fullscreen) {
569 iter = parent;
570 while (iter) {
571 if (iter->fullscreen) {
572 iter->fullscreen = NULL;
573 break;
574 }
575 iter = iter->parent;
576 }
577 }
578
579
580 arrange_windows(iter ? iter : parent, -1, -1);
581 }
582 } else {
583 // Is it unmanaged?
584 int i;
585 for (i = 0; i < root_container.children->length; ++i) {
586 swayc_t *output = root_container.children->items[i];
587 int j;
588 for (j = 0; j < output->unmanaged->length; ++j) {
589 wlc_handle *_handle = output->unmanaged->items[j];
590 if (*_handle == handle) {
591 list_del(output->unmanaged, j);
592 free(_handle);
593 break;
594 }
595 }
596 }
597 // Is it in the scratchpad?
598 for (i = 0; i < scratchpad->length; ++i) {
599 swayc_t *item = scratchpad->items[i];
600 if (item->handle == handle) {
601 list_del(scratchpad, i);
602 destroy_view(item);
603 break;
604 }
605 }
606 }
607 set_focused_container(get_focused_view(&root_container));
608}
609
610static void handle_view_focus(wlc_handle view, bool focus) {
611 return;
612}
613
614static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geometry *geometry) {
615 sway_log(L_DEBUG, "geometry request for %" PRIuPTR " %dx%d @ %d,%d", handle,
616 geometry->size.w, geometry->size.h, geometry->origin.x, geometry->origin.y);
617 // If the view is floating, then apply the geometry.
618 // Otherwise save the desired width/height for the view.
619 // This will not do anything for the time being as WLC improperly sends geometry requests
620 swayc_t *view = swayc_by_handle(handle);
621 if (view) {
622 view->desired_width = geometry->size.w;
623 view->desired_height = geometry->size.h;
624
625 if (view->is_floating) {
626 floating_view_sane_size(view);
627 view->width = view->desired_width;
628 view->height = view->desired_height;
629 view->x = geometry->origin.x;
630 view->y = geometry->origin.y;
631 update_geometry(view);
632 }
633 } else {
634 wlc_view_set_geometry(handle, 0, geometry);
635 }
636}
637
638static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) {
639 swayc_t *c = swayc_by_handle(view);
640 pid_t pid = wlc_view_get_pid(view);
641 switch (state) {
642 case WLC_BIT_FULLSCREEN:
643 if (!(get_feature_policy_mask(pid) & FEATURE_FULLSCREEN)) {
644 sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name);
645 break;
646 }
647 // i3 just lets it become fullscreen
648 wlc_view_set_state(view, state, toggle);
649 if (c) {
650 sway_log(L_DEBUG, "setting view %" PRIuPTR " %s, fullscreen %d", view, c->name, toggle);
651 arrange_windows(c->parent, -1, -1);
652 // Set it as focused window for that workspace if its going fullscreen
653 swayc_t *ws = swayc_parent_by_type(c, C_WORKSPACE);
654 if (toggle) {
655 // Set ws focus to c
656 set_focused_container_for(ws, c);
657 ws->fullscreen = c;
658 } else {
659 ws->fullscreen = NULL;
660 }
661 }
662 break;
663 case WLC_BIT_MAXIMIZED:
664 case WLC_BIT_RESIZING:
665 case WLC_BIT_MOVING:
666 break;
667 case WLC_BIT_ACTIVATED:
668 sway_log(L_DEBUG, "View %p requested to be activated", c);
669 break;
670 }
671 return;
672}
673
674static void handle_view_properties_updated(wlc_handle view, uint32_t mask) {
675 if (mask == WLC_BIT_PROPERTY_TITLE) {
676 swayc_t *c = swayc_by_handle(view);
677 if (!c) {
678 return;
679 }
680
681 // update window title
682 const char *new_name = wlc_view_get_title(view);
683
684 if (new_name) {
685 if (!c->name || strcmp(c->name, new_name) != 0) {
686 free(c->name);
687 c->name = strdup(new_name);
688 swayc_t *p = swayc_tabbed_stacked_ancestor(c);
689 if (p) {
690 // TODO: we only got the topmost tabbed/stacked container, update borders of all containers on the path
691 update_container_border(get_focused_view(p));
692 } else if (c->border_type == B_NORMAL) {
693 update_container_border(c);
694 }
695 ipc_event_window(c, "title");
696 }
697 }
698 }
699}
700
701static void handle_binding_command(struct sway_binding *binding) {
702 struct sway_binding *binding_copy = binding;
703 bool reload = false;
704 // if this is a reload command we need to make a duplicate of the
705 // binding since it will be gone after the reload has completed.
706 if (strcasecmp(binding->command, "reload") == 0) {
707 binding_copy = sway_binding_dup(binding);
708 if (!binding_copy) {
709 sway_log(L_ERROR, "Unable to duplicate binding during reload");
710 return;
711 }
712 reload = true;
713 }
714
715 struct cmd_results *res = handle_command(binding->command, CONTEXT_BINDING);
716 if (res->status != CMD_SUCCESS) {
717 sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
718 }
719 ipc_event_binding_keyboard(binding_copy);
720
721 if (reload) { // free the binding if we made a copy
722 free_sway_binding(binding_copy);
723 }
724
725 free_cmd_results(res);
726}
727
728static bool handle_bindsym(struct sway_binding *binding, uint32_t keysym, uint32_t keycode) {
729 int i;
730 for (i = 0; i < binding->keys->length; ++i) {
731 if (binding->bindcode) {
732 xkb_keycode_t *key = binding->keys->items[i];
733 if (keycode == *key) {
734 handle_binding_command(binding);
735 return true;
736 }
737 } else {
738 xkb_keysym_t *key = binding->keys->items[i];
739 if (keysym == *key) {
740 handle_binding_command(binding);
741 return true;
742 }
743 }
744 }
745
746 return false;
747}
748
749static bool valid_bindsym(struct sway_binding *binding) {
750 bool match = false;
751 int i;
752 for (i = 0; i < binding->keys->length; ++i) {
753 if (binding->bindcode) {
754 xkb_keycode_t *key = binding->keys->items[i];
755 if ((match = check_key(0, *key)) == false) {
756 break;
757 }
758 } else {
759 xkb_keysym_t *key = binding->keys->items[i];
760 if ((match = check_key(*key, 0)) == false) {
761 break;
762 }
763 }
764 }
765
766 return match;
767}
768
769static bool handle_bindsym_release(struct sway_binding *binding) {
770 if (binding->keys->length == 1) {
771 xkb_keysym_t *key = binding->keys->items[0];
772 if (check_released_key(*key)) {
773 handle_binding_command(binding);
774 return true;
775 }
776 }
777
778 return false;
779}
780
781static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
782 uint32_t key, enum wlc_key_state state) {
783
784 if (desktop_shell.is_locked) {
785 return EVENT_PASSTHROUGH;
786 }
787
788 // reset pointer mode on keypress
789 if (state == WLC_KEY_STATE_PRESSED && pointer_state.mode) {
790 pointer_mode_reset();
791 }
792
793 struct sway_mode *mode = config->current_mode;
794
795 struct wlc_modifiers no_mods = { 0, 0 };
796 uint32_t sym = tolower(wlc_keyboard_get_keysym_for_key(key, &no_mods));
797
798 int i;
799
800 if (state == WLC_KEY_STATE_PRESSED) {
801 press_key(sym, key);
802 } else { // WLC_KEY_STATE_RELEASED
803 release_key(sym, key);
804 }
805
806 // handle bar modifiers pressed/released
807 uint32_t modifier;
808 for (i = 0; i < config->active_bar_modifiers->length; ++i) {
809 modifier = *(uint32_t *)config->active_bar_modifiers->items[i];
810
811 switch (modifier_state_changed(modifiers->mods, modifier)) {
812 case MOD_STATE_PRESSED:
813 ipc_event_modifier(modifier, "pressed");
814 break;
815 case MOD_STATE_RELEASED:
816 ipc_event_modifier(modifier, "released");
817 break;
818 }
819 }
820 // update modifiers state
821 modifiers_state_update(modifiers->mods);
822
823 // handle bindings
824 list_t *candidates = create_list();
825 for (i = 0; i < mode->bindings->length; ++i) {
826 struct sway_binding *binding = mode->bindings->items[i];
827 if ((modifiers->mods ^ binding->modifiers) == 0) {
828 switch (state) {
829 case WLC_KEY_STATE_PRESSED:
830 if (!binding->release && valid_bindsym(binding)) {
831 list_add(candidates, binding);
832 }
833 break;
834 case WLC_KEY_STATE_RELEASED:
835 if (binding->release && handle_bindsym_release(binding)) {
836 list_free(candidates);
837 return EVENT_HANDLED;
838 }
839 break;
840 }
841 }
842 }
843
844 for (i = 0; i < candidates->length; ++i) {
845 struct sway_binding *binding = candidates->items[i];
846 if (state == WLC_KEY_STATE_PRESSED) {
847 if (!binding->release && handle_bindsym(binding, sym, key)) {
848 list_free(candidates);
849 return EVENT_HANDLED;
850 }
851 }
852 }
853
854 list_free(candidates);
855
856 swayc_t *focused = get_focused_container(&root_container);
857 if (focused->type == C_VIEW) {
858 pid_t pid = wlc_view_get_pid(focused->handle);
859 if (!(get_feature_policy_mask(pid) & FEATURE_KEYBOARD)) {
860 return EVENT_HANDLED;
861 }
862 }
863 return EVENT_PASSTHROUGH;
864}
865
866static bool handle_pointer_motion(wlc_handle handle, uint32_t time, double x, double y) {
867 if (desktop_shell.is_locked) {
868 return EVENT_PASSTHROUGH;
869 }
870
871 double new_x = x;
872 double new_y = y;
873 // Switch to adjacent output if touching output edge.
874 //
875 // Since this doesn't currently support moving windows between outputs we
876 // don't do the switch if the pointer is in a mode.
877 if (config->seamless_mouse && !pointer_state.mode &&
878 !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) {
879
880 swayc_t *output = swayc_active_output(), *adjacent = NULL;
881 struct wlc_point abs_pos = { .x = x + output->x, .y = y + output->y };
882 if (x <= 0) { // Left edge
883 if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) {
884 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
885 new_x = adjacent->width;
886 // adjust for differently aligned outputs (well, this is
887 // only correct when the two outputs have the same
888 // resolution or the same dpi I guess, it should take
889 // physical attributes into account)
890 new_y += (output->y - adjacent->y);
891 }
892 }
893 } else if (x >= output->width) { // Right edge
894 if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) {
895 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
896 new_x = 0;
897 new_y += (output->y - adjacent->y);
898 }
899 }
900 } else if (y <= 0) { // Top edge
901 if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) {
902 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
903 new_y = adjacent->height;
904 new_x += (output->x - adjacent->x);
905 }
906 }
907 } else if (y >= output->height) { // Bottom edge
908 if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) {
909 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
910 new_y = 0;
911 new_x += (output->x - adjacent->x);
912 }
913 }
914 }
915 }
916
917 pointer_position_set(new_x, new_y, false);
918
919 swayc_t *focused = get_focused_container(&root_container);
920 if (focused->type == C_VIEW) {
921 pid_t pid = wlc_view_get_pid(focused->handle);
922 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
923 return EVENT_HANDLED;
924 }
925 }
926
927 return EVENT_PASSTHROUGH;
928}
929
930static bool swayc_border_check(swayc_t *c, const void *_origin) {
931 const struct wlc_point *origin = _origin;
932 const struct wlc_geometry title_bar = c->title_bar_geometry;
933
934 if (c->border_type != B_NORMAL) {
935 return false;
936 }
937
938 if (origin->x >= title_bar.origin.x && origin->y >= title_bar.origin.y
939 && origin->x < title_bar.origin.x + (int32_t)title_bar.size.w
940 && origin->y < title_bar.origin.y + (int32_t)title_bar.size.h) {
941 return true;
942 }
943 return false;
944}
945
946static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
947 uint32_t button, enum wlc_button_state state, const struct wlc_point *origin) {
948
949 // Update view pointer is on
950 pointer_state.view = container_under_pointer();
951
952 struct sway_mode *mode = config->current_mode;
953 // handle bindings
954 for (int i = 0; i < mode->bindings->length; ++i) {
955 struct sway_binding *binding = mode->bindings->items[i];
956 if ((modifiers->mods ^ binding->modifiers) == 0) {
957 switch (state) {
958 case WLC_BUTTON_STATE_PRESSED:
959 if (!binding->release && handle_bindsym(binding, button, 0)) {
960 return EVENT_HANDLED;
961 }
962 break;
963 case WLC_BUTTON_STATE_RELEASED:
964 if (binding->release && handle_bindsym(binding, button, 0)) {
965 return EVENT_HANDLED;
966 }
967 break;
968 }
969 }
970 }
971
972 // Update pointer_state
973 switch (button) {
974 case M_LEFT_CLICK:
975 if (state == WLC_BUTTON_STATE_PRESSED) {
976 pointer_state.left.held = true;
977 pointer_state.left.x = origin->x;
978 pointer_state.left.y = origin->y;
979 pointer_state.left.view = pointer_state.view;
980 } else {
981 pointer_state.left.held = false;
982 }
983 break;
984
985 case M_RIGHT_CLICK:
986 if (state == WLC_BUTTON_STATE_PRESSED) {
987 pointer_state.right.held = true;
988 pointer_state.right.x = origin->x;
989 pointer_state.right.y = origin->y;
990 pointer_state.right.view = pointer_state.view;
991 } else {
992 pointer_state.right.held = false;
993 }
994 break;
995
996 case M_SCROLL_CLICK:
997 if (state == WLC_BUTTON_STATE_PRESSED) {
998 pointer_state.scroll.held = true;
999 pointer_state.scroll.x = origin->x;
1000 pointer_state.scroll.y = origin->y;
1001 pointer_state.scroll.view = pointer_state.view;
1002 } else {
1003 pointer_state.scroll.held = false;
1004 }
1005 break;
1006
1007 //TODO scrolling behavior
1008 case M_SCROLL_UP:
1009 case M_SCROLL_DOWN:
1010 break;
1011 }
1012
1013 // get focused window and check if to change focus on mouse click
1014 swayc_t *focused = get_focused_container(&root_container);
1015
1016 // don't change focus or mode if fullscreen
1017 if (swayc_is_fullscreen(focused)) {
1018 if (focused->type == C_VIEW) {
1019 pid_t pid = wlc_view_get_pid(focused->handle);
1020 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
1021 return EVENT_HANDLED;
1022 }
1023 }
1024 return EVENT_PASSTHROUGH;
1025 }
1026
1027 // set pointer mode only if floating mod has been set
1028 if (config->floating_mod) {
1029 pointer_mode_set(button, !(modifiers->mods ^ config->floating_mod));
1030 }
1031
1032 // Check whether to change focus
1033 swayc_t *pointer = pointer_state.view;
1034 if (pointer) {
1035 swayc_t *ws = swayc_parent_by_type(focused, C_WORKSPACE);
1036 if (ws != NULL) {
1037 swayc_t *find = container_find(ws, &swayc_border_check, origin);
1038 if (find != NULL) {
1039 set_focused_container(find);
1040 return EVENT_HANDLED;
1041 }
1042 }
1043
1044 if (focused != pointer) {
1045 set_focused_container(pointer_state.view);
1046 }
1047 // Send to front if floating
1048 if (pointer->is_floating) {
1049 int i;
1050 for (i = 0; i < pointer->parent->floating->length; i++) {
1051 if (pointer->parent->floating->items[i] == pointer) {
1052 list_del(pointer->parent->floating, i);
1053 list_add(pointer->parent->floating, pointer);
1054 break;
1055 }
1056 }
1057 wlc_view_bring_to_front(pointer->handle);
1058 }
1059 }
1060
1061 // Return if mode has been set
1062 if (pointer_state.mode) {
1063 return EVENT_HANDLED;
1064 }
1065
1066 if (focused->type == C_VIEW) {
1067 pid_t pid = wlc_view_get_pid(focused->handle);
1068 if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
1069 return EVENT_HANDLED;
1070 }
1071 }
1072
1073 // Always send mouse release
1074 if (state == WLC_BUTTON_STATE_RELEASED) {
1075 return EVENT_PASSTHROUGH;
1076 }
1077
1078 // Finally send click
1079 return EVENT_PASSTHROUGH;
1080}
1081
1082bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modifiers* modifiers,
1083 uint8_t axis_bits, double _amount[2]) {
1084 if (!(modifiers->mods ^ config->floating_mod)) {
1085 int x_amount = (int)_amount[0];
1086 int y_amount = (int)_amount[1];
1087
1088 if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) {
1089 handle_command(config->floating_scroll_up_cmd, CONTEXT_BINDING);
1090 return EVENT_HANDLED;
1091 } else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) {
1092 handle_command(config->floating_scroll_down_cmd, CONTEXT_BINDING);
1093 return EVENT_HANDLED;
1094 }
1095
1096 if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) {
1097 handle_command(config->floating_scroll_right_cmd, CONTEXT_BINDING);
1098 return EVENT_HANDLED;
1099 } else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) {
1100 handle_command(config->floating_scroll_left_cmd, CONTEXT_BINDING);
1101 return EVENT_HANDLED;
1102 }
1103 }
1104 return EVENT_PASSTHROUGH;
1105}
1106
1107static void handle_wlc_ready(void) {
1108 sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
1109 // Execute commands until there are none left
1110 config->active = true;
1111 while (config->cmd_queue->length) {
1112 char *line = config->cmd_queue->items[0];
1113 struct cmd_results *res = handle_command(line, CONTEXT_CONFIG);
1114 if (res->status != CMD_SUCCESS) {
1115 sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
1116 }
1117 free_cmd_results(res);
1118 free(line);
1119 list_del(config->cmd_queue, 0);
1120 }
1121}
1122
1123void register_wlc_handlers() {
1124 wlc_set_output_created_cb(handle_output_created);
1125 wlc_set_output_destroyed_cb(handle_output_destroyed);
1126 wlc_set_output_resolution_cb(handle_output_resolution_change);
1127 wlc_set_output_focus_cb(handle_output_focused);
1128 wlc_set_output_render_post_cb(handle_output_post_render);
1129 wlc_set_view_created_cb(handle_view_created);
1130 wlc_set_view_destroyed_cb(handle_view_destroyed);
1131 wlc_set_view_focus_cb(handle_view_focus);
1132 wlc_set_view_render_pre_cb(handle_view_pre_render);
1133 wlc_set_view_request_geometry_cb(handle_view_geometry_request);
1134 wlc_set_view_request_state_cb(handle_view_state_request);
1135 wlc_set_view_properties_updated_cb(handle_view_properties_updated);
1136 wlc_set_keyboard_key_cb(handle_key);
1137 wlc_set_pointer_motion_cb_v2(handle_pointer_motion);
1138 wlc_set_pointer_button_cb(handle_pointer_button);
1139 wlc_set_pointer_scroll_cb(handle_pointer_scroll);
1140 wlc_set_compositor_ready_cb(handle_wlc_ready);
1141 wlc_set_input_created_cb(handle_input_created);
1142 wlc_set_input_destroyed_cb(handle_input_destroyed);
1143}
diff --git a/sway/input.c b/sway/input.c
deleted file mode 100644
index 6263f79f..00000000
--- a/sway/input.c
+++ /dev/null
@@ -1,69 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <ctype.h>
3#include <float.h>
4#include <limits.h>
5#include <stdio.h>
6#include <string.h>
7#include <libinput.h>
8#include "sway/config.h"
9#include "sway/input.h"
10#include "list.h"
11#include "log.h"
12
13struct input_config *new_input_config(const char* identifier) {
14 struct input_config *input = calloc(1, sizeof(struct input_config));
15 if (!input) {
16 sway_log(L_DEBUG, "Unable to allocate input config");
17 return NULL;
18 }
19 sway_log(L_DEBUG, "new_input_config(%s)", identifier);
20 if (!(input->identifier = strdup(identifier))) {
21 free(input);
22 sway_log(L_DEBUG, "Unable to allocate input config");
23 return NULL;
24 }
25
26 input->tap = INT_MIN;
27 input->drag_lock = INT_MIN;
28 input->dwt = INT_MIN;
29 input->send_events = INT_MIN;
30 input->click_method = INT_MIN;
31 input->middle_emulation = INT_MIN;
32 input->natural_scroll = INT_MIN;
33 input->accel_profile = INT_MIN;
34 input->pointer_accel = FLT_MIN;
35 input->scroll_method = INT_MIN;
36 input->left_handed = INT_MIN;
37
38 return input;
39}
40
41char *libinput_dev_unique_id(struct libinput_device *device) {
42 int vendor = libinput_device_get_id_vendor(device);
43 int product = libinput_device_get_id_product(device);
44 char *name = strdup(libinput_device_get_name(device));
45
46 char *p = name;
47 for (; *p; ++p) {
48 if (*p == ' ') {
49 *p = '_';
50 }
51 }
52
53 sway_log(L_DEBUG, "rewritten name %s", name);
54
55 int len = strlen(name) + sizeof(char) * 6;
56 char *identifier = malloc(len);
57 if (!identifier) {
58 sway_log(L_ERROR, "Unable to allocate unique input device name");
59 return NULL;
60 }
61
62 const char *fmt = "%d:%d:%s";
63 snprintf(identifier, len, fmt, vendor, product, name);
64 free(name);
65 return identifier;
66}
67
68list_t *input_devices = NULL;
69struct input_config *current_input_config = NULL;
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
new file mode 100644
index 00000000..15a61cbf
--- /dev/null
+++ b/sway/input/cursor.c
@@ -0,0 +1,384 @@
1#define _XOPEN_SOURCE 700
2#ifdef __linux__
3#include <linux/input-event-codes.h>
4#elif __FreeBSD__
5#include <dev/evdev/input-event-codes.h>
6#endif
7#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_xcursor_manager.h>
9#include "list.h"
10#include "log.h"
11#include "sway/input/cursor.h"
12#include "sway/layers.h"
13#include "sway/output.h"
14#include "sway/tree/view.h"
15#include "wlr-layer-shell-unstable-v1-protocol.h"
16
17static struct wlr_surface *layer_surface_at(struct sway_output *output,
18 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
19 struct sway_layer_surface *sway_layer;
20 wl_list_for_each_reverse(sway_layer, layer, link) {
21 struct wlr_surface *wlr_surface =
22 sway_layer->layer_surface->surface;
23 double _sx = ox - sway_layer->geo.x;
24 double _sy = oy - sway_layer->geo.y;
25 // TODO: Test popups/subsurfaces
26 if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) {
27 *sx = _sx;
28 *sy = _sy;
29 return wlr_surface;
30 }
31 }
32 return NULL;
33}
34
35/**
36 * Returns the container at the cursor's position. If there is a surface at that
37 * location, it is stored in **surface (it may not be a view).
38 */
39static struct sway_container *container_at_cursor(struct sway_cursor *cursor,
40 struct wlr_surface **surface, double *sx, double *sy) {
41 // check for unmanaged views first
42 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
43 struct sway_xwayland_unmanaged *unmanaged_surface;
44 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
45 struct wlr_xwayland_surface *xsurface =
46 unmanaged_surface->wlr_xwayland_surface;
47
48 double _sx = cursor->cursor->x - unmanaged_surface->lx;
49 double _sy = cursor->cursor->y - unmanaged_surface->ly;
50 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) {
51 *surface = xsurface->surface;
52 *sx = _sx;
53 *sy = _sy;
54 return NULL;
55 }
56 }
57
58 // find the output the cursor is on
59 struct wlr_output_layout *output_layout =
60 root_container.sway_root->output_layout;
61 struct wlr_output *wlr_output =
62 wlr_output_layout_output_at(output_layout,
63 cursor->cursor->x, cursor->cursor->y);
64 if (wlr_output == NULL) {
65 return NULL;
66 }
67 struct sway_output *output = wlr_output->data;
68 double ox = cursor->cursor->x, oy = cursor->cursor->y;
69 wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy);
70
71 // find the focused workspace on the output for this seat
72 struct sway_container *ws =
73 seat_get_focus_inactive(cursor->seat, output->swayc);
74 if (ws && ws->type != C_WORKSPACE) {
75 ws = container_parent(ws, C_WORKSPACE);
76 }
77 if (!ws) {
78 return output->swayc;
79 }
80
81 if ((*surface = layer_surface_at(output,
82 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
83 ox, oy, sx, sy))) {
84 return ws;
85 }
86 if ((*surface = layer_surface_at(output,
87 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
88 ox, oy, sx, sy))) {
89 return ws;
90 }
91
92 struct sway_container *c;
93 if ((c = container_at(ws, cursor->cursor->x, cursor->cursor->y,
94 surface, sx, sy))) {
95 return c;
96 }
97
98 if ((*surface = layer_surface_at(output,
99 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
100 ox, oy, sx, sy))) {
101 return ws;
102 }
103 if ((*surface = layer_surface_at(output,
104 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
105 ox, oy, sx, sy))) {
106 return ws;
107 }
108
109 c = seat_get_focus_inactive(cursor->seat, output->swayc);
110 if (c) {
111 return c;
112 }
113 if (!c && output->swayc->children->length) {
114 c = output->swayc->children->items[0];
115 return c;
116 }
117
118 return output->swayc;
119}
120
121void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time) {
122 struct wlr_seat *seat = cursor->seat->wlr_seat;
123 struct wlr_surface *surface = NULL;
124 double sx, sy;
125 struct sway_container *c = container_at_cursor(cursor, &surface, &sx, &sy);
126 if (c && config->focus_follows_mouse) {
127 seat_set_focus_warp(cursor->seat, c, false);
128 }
129
130 // reset cursor if switching between clients
131 struct wl_client *client = NULL;
132 if (surface != NULL) {
133 client = wl_resource_get_client(surface->resource);
134 }
135 if (client != cursor->image_client) {
136 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
137 "left_ptr", cursor->cursor);
138 cursor->image_client = client;
139 }
140
141 // send pointer enter/leave
142 if (surface != NULL) {
143 if (seat_is_input_allowed(cursor->seat, surface)) {
144 wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
145 wlr_seat_pointer_notify_motion(seat, time, sx, sy);
146 }
147 } else {
148 wlr_seat_pointer_clear_focus(seat);
149 }
150}
151
152static void handle_cursor_motion(struct wl_listener *listener, void *data) {
153 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
154 struct wlr_event_pointer_motion *event = data;
155 wlr_cursor_move(cursor->cursor, event->device,
156 event->delta_x, event->delta_y);
157 cursor_send_pointer_motion(cursor, event->time_msec);
158}
159
160static void handle_cursor_motion_absolute(
161 struct wl_listener *listener, void *data) {
162 struct sway_cursor *cursor =
163 wl_container_of(listener, cursor, motion_absolute);
164 struct wlr_event_pointer_motion_absolute *event = data;
165 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
166 cursor_send_pointer_motion(cursor, event->time_msec);
167}
168
169void dispatch_cursor_button(struct sway_cursor *cursor,
170 uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
171 struct wlr_surface *surface = NULL;
172 double sx, sy;
173 struct sway_container *cont =
174 container_at_cursor(cursor, &surface, &sx, &sy);
175 if (surface && wlr_surface_is_layer_surface(surface)) {
176 struct wlr_layer_surface *layer =
177 wlr_layer_surface_from_wlr_surface(surface);
178 if (layer->current.keyboard_interactive) {
179 seat_set_focus_layer(cursor->seat, layer);
180 return;
181 }
182 }
183 // Avoid moving keyboard focus from a surface that accepts it to one
184 // that does not unless the change would move us to a new workspace.
185 //
186 // This prevents, for example, losing focus when clicking on swaybar.
187 if (surface && cont && cont->type != C_VIEW) {
188 struct sway_container *new_ws = cont;
189 if (new_ws && new_ws->type != C_WORKSPACE) {
190 new_ws = container_parent(new_ws, C_WORKSPACE);
191 }
192 struct sway_container *old_ws = seat_get_focus(cursor->seat);
193 if (old_ws && old_ws->type != C_WORKSPACE) {
194 old_ws = container_parent(old_ws, C_WORKSPACE);
195 }
196 if (new_ws != old_ws) {
197 seat_set_focus(cursor->seat, cont);
198 }
199 } else {
200 seat_set_focus(cursor->seat, cont);
201 }
202
203 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
204 time_msec, button, state);
205}
206
207static void handle_cursor_button(struct wl_listener *listener, void *data) {
208 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
209 struct wlr_event_pointer_button *event = data;
210 dispatch_cursor_button(cursor,
211 event->time_msec, event->button, event->state);
212}
213
214static void handle_cursor_axis(struct wl_listener *listener, void *data) {
215 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
216 struct wlr_event_pointer_axis *event = data;
217 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
218 event->orientation, event->delta);
219}
220
221static void handle_touch_down(struct wl_listener *listener, void *data) {
222 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
223 struct wlr_event_touch_down *event = data;
224 wlr_log(L_DEBUG, "TODO: handle touch down event: %p", event);
225}
226
227static void handle_touch_up(struct wl_listener *listener, void *data) {
228 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
229 struct wlr_event_touch_up *event = data;
230 wlr_log(L_DEBUG, "TODO: handle touch up event: %p", event);
231}
232
233static void handle_touch_motion(struct wl_listener *listener, void *data) {
234 struct sway_cursor *cursor =
235 wl_container_of(listener, cursor, touch_motion);
236 struct wlr_event_touch_motion *event = data;
237 wlr_log(L_DEBUG, "TODO: handle touch motion event: %p", event);
238}
239
240static void handle_tool_axis(struct wl_listener *listener, void *data) {
241 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
242 struct wlr_event_tablet_tool_axis *event = data;
243
244 if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
245 (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
246 wlr_cursor_warp_absolute(cursor->cursor, event->device,
247 event->x, event->y);
248 cursor_send_pointer_motion(cursor, event->time_msec);
249 } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
250 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
251 cursor_send_pointer_motion(cursor, event->time_msec);
252 } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
253 wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
254 cursor_send_pointer_motion(cursor, event->time_msec);
255 }
256}
257
258static void handle_tool_tip(struct wl_listener *listener, void *data) {
259 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
260 struct wlr_event_tablet_tool_tip *event = data;
261 dispatch_cursor_button(cursor, event->time_msec,
262 BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
263 WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED);
264}
265
266static void handle_tool_button(struct wl_listener *listener, void *data) {
267 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
268 struct wlr_event_tablet_tool_button *event = data;
269 // TODO: the user may want to configure which tool buttons are mapped to
270 // which simulated pointer buttons
271 switch (event->state) {
272 case WLR_BUTTON_PRESSED:
273 if (cursor->tool_buttons == 0) {
274 dispatch_cursor_button(cursor,
275 event->time_msec, BTN_RIGHT, event->state);
276 }
277 cursor->tool_buttons++;
278 break;
279 case WLR_BUTTON_RELEASED:
280 if (cursor->tool_buttons == 1) {
281 dispatch_cursor_button(cursor,
282 event->time_msec, BTN_RIGHT, event->state);
283 }
284 cursor->tool_buttons--;
285 break;
286 }
287}
288
289static void handle_request_set_cursor(struct wl_listener *listener,
290 void *data) {
291 struct sway_cursor *cursor =
292 wl_container_of(listener, cursor, request_set_cursor);
293 struct wlr_seat_pointer_request_set_cursor_event *event = data;
294
295 struct wl_client *focused_client = NULL;
296 struct wlr_surface *focused_surface =
297 cursor->seat->wlr_seat->pointer_state.focused_surface;
298 if (focused_surface != NULL) {
299 focused_client = wl_resource_get_client(focused_surface->resource);
300 }
301
302 // TODO: check cursor mode
303 if (focused_client == NULL ||
304 event->seat_client->client != focused_client) {
305 wlr_log(L_DEBUG, "denying request to set cursor from unfocused client");
306 return;
307 }
308
309 wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
310 event->hotspot_y);
311 cursor->image_client = focused_client;
312}
313
314void sway_cursor_destroy(struct sway_cursor *cursor) {
315 if (!cursor) {
316 return;
317 }
318
319 wlr_xcursor_manager_destroy(cursor->xcursor_manager);
320 wlr_cursor_destroy(cursor->cursor);
321 free(cursor);
322}
323
324struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
325 struct sway_cursor *cursor = calloc(1, sizeof(struct sway_cursor));
326 if (!sway_assert(cursor, "could not allocate sway cursor")) {
327 return NULL;
328 }
329
330 struct wlr_cursor *wlr_cursor = wlr_cursor_create();
331 if (!sway_assert(wlr_cursor, "could not allocate wlr cursor")) {
332 free(cursor);
333 return NULL;
334 }
335
336 cursor->seat = seat;
337 wlr_cursor_attach_output_layout(wlr_cursor,
338 root_container.sway_root->output_layout);
339
340 // input events
341 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
342 cursor->motion.notify = handle_cursor_motion;
343
344 wl_signal_add(&wlr_cursor->events.motion_absolute,
345 &cursor->motion_absolute);
346 cursor->motion_absolute.notify = handle_cursor_motion_absolute;
347
348 wl_signal_add(&wlr_cursor->events.button, &cursor->button);
349 cursor->button.notify = handle_cursor_button;
350
351 wl_signal_add(&wlr_cursor->events.axis, &cursor->axis);
352 cursor->axis.notify = handle_cursor_axis;
353
354 wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down);
355 cursor->touch_down.notify = handle_touch_down;
356
357 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
358 cursor->touch_up.notify = handle_touch_up;
359
360 wl_signal_add(&wlr_cursor->events.touch_motion,
361 &cursor->touch_motion);
362 cursor->touch_motion.notify = handle_touch_motion;
363
364 // TODO: tablet protocol support
365 // Note: We should emulate pointer events for clients that don't support the
366 // tablet protocol when the time comes
367 wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
368 &cursor->tool_axis);
369 cursor->tool_axis.notify = handle_tool_axis;
370
371 wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);
372 cursor->tool_tip.notify = handle_tool_tip;
373
374 wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button);
375 cursor->tool_button.notify = handle_tool_button;
376
377 wl_signal_add(&seat->wlr_seat->events.request_set_cursor,
378 &cursor->request_set_cursor);
379 cursor->request_set_cursor.notify = handle_request_set_cursor;
380
381 cursor->cursor = wlr_cursor;
382
383 return cursor;
384}
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
new file mode 100644
index 00000000..ae55d2a1
--- /dev/null
+++ b/sway/input/input-manager.c
@@ -0,0 +1,445 @@
1#define _XOPEN_SOURCE 700
2#include <ctype.h>
3#include <float.h>
4#include <limits.h>
5#include <stdio.h>
6#include <string.h>
7#include <libinput.h>
8#include <math.h>
9#include <wlr/backend/libinput.h>
10#include <wlr/types/wlr_input_inhibitor.h>
11#include "sway/config.h"
12#include "sway/input/input-manager.h"
13#include "sway/input/seat.h"
14#include "sway/server.h"
15#include "stringop.h"
16#include "list.h"
17#include "log.h"
18
19static const char *default_seat = "seat0";
20
21// TODO make me not global
22struct sway_input_manager *input_manager;
23
24struct input_config *current_input_config = NULL;
25struct seat_config *current_seat_config = NULL;
26
27struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) {
28 struct sway_seat *seat = config->handler_context.seat;
29 if (!seat) {
30 seat = input_manager_get_default_seat(input_manager);
31 }
32 return seat;
33}
34
35struct sway_seat *input_manager_get_seat(
36 struct sway_input_manager *input, const char *seat_name) {
37 struct sway_seat *seat = NULL;
38 wl_list_for_each(seat, &input->seats, link) {
39 if (strcmp(seat->wlr_seat->name, seat_name) == 0) {
40 return seat;
41 }
42 }
43
44 return seat_create(input, seat_name);
45}
46
47static char *get_device_identifier(struct wlr_input_device *device) {
48 int vendor = device->vendor;
49 int product = device->product;
50 char *name = strdup(device->name);
51 name = strip_whitespace(name);
52
53 char *p = name;
54 for (; *p; ++p) {
55 if (*p == ' ') {
56 *p = '_';
57 }
58 }
59
60 const char *fmt = "%d:%d:%s";
61 int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;
62 char *identifier = malloc(len);
63 if (!identifier) {
64 wlr_log(L_ERROR, "Unable to allocate unique input device name");
65 return NULL;
66 }
67
68 snprintf(identifier, len, fmt, vendor, product, name);
69 free(name);
70 return identifier;
71}
72
73static struct sway_input_device *input_sway_device_from_wlr(
74 struct sway_input_manager *input, struct wlr_input_device *device) {
75 struct sway_input_device *input_device = NULL;
76 wl_list_for_each(input_device, &input->devices, link) {
77 if (input_device->wlr_device == device) {
78 return input_device;
79 }
80 }
81 return NULL;
82}
83
84static bool input_has_seat_configuration(struct sway_input_manager *input) {
85 struct sway_seat *seat = NULL;
86 wl_list_for_each(seat, &input->seats, link) {
87 struct seat_config *seat_config = seat_get_config(seat);
88 if (seat_config) {
89 return true;
90 }
91 }
92
93 return false;
94}
95
96static void input_manager_libinput_config_pointer(
97 struct sway_input_device *input_device) {
98 struct wlr_input_device *wlr_device = input_device->wlr_device;
99 struct input_config *ic = input_device_get_config(input_device);
100 struct libinput_device *libinput_device;
101
102 if (!ic || !wlr_input_device_is_libinput(wlr_device)) {
103 return;
104 }
105
106 libinput_device = wlr_libinput_get_device_handle(wlr_device);
107 wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)",
108 ic->identifier);
109
110 if (ic->accel_profile != INT_MIN) {
111 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)",
112 ic->identifier, ic->accel_profile);
113 libinput_device_config_accel_set_profile(libinput_device,
114 ic->accel_profile);
115 }
116 if (ic->click_method != INT_MIN) {
117 wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)",
118 ic->identifier, ic->click_method);
119 libinput_device_config_click_set_method(libinput_device,
120 ic->click_method);
121 }
122 if (ic->drag_lock != INT_MIN) {
123 wlr_log(L_DEBUG,
124 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
125 ic->identifier, ic->click_method);
126 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
127 ic->drag_lock);
128 }
129 if (ic->dwt != INT_MIN) {
130 wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)",
131 ic->identifier, ic->dwt);
132 libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt);
133 }
134 if (ic->left_handed != INT_MIN) {
135 wlr_log(L_DEBUG,
136 "libinput_config_pointer(%s) left_handed_set_enabled(%d)",
137 ic->identifier, ic->left_handed);
138 libinput_device_config_left_handed_set(libinput_device,
139 ic->left_handed);
140 }
141 if (ic->middle_emulation != INT_MIN) {
142 wlr_log(L_DEBUG,
143 "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)",
144 ic->identifier, ic->middle_emulation);
145 libinput_device_config_middle_emulation_set_enabled(libinput_device,
146 ic->middle_emulation);
147 }
148 if (ic->natural_scroll != INT_MIN) {
149 wlr_log(L_DEBUG,
150 "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)",
151 ic->identifier, ic->natural_scroll);
152 libinput_device_config_scroll_set_natural_scroll_enabled(
153 libinput_device, ic->natural_scroll);
154 }
155 if (ic->pointer_accel != FLT_MIN) {
156 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)",
157 ic->identifier, ic->pointer_accel);
158 libinput_device_config_accel_set_speed(libinput_device,
159 ic->pointer_accel);
160 }
161 if (ic->scroll_method != INT_MIN) {
162 wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)",
163 ic->identifier, ic->scroll_method);
164 libinput_device_config_scroll_set_method(libinput_device,
165 ic->scroll_method);
166 }
167 if (ic->send_events != INT_MIN) {
168 wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)",
169 ic->identifier, ic->send_events);
170 libinput_device_config_send_events_set_mode(libinput_device,
171 ic->send_events);
172 }
173 if (ic->tap != INT_MIN) {
174 wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)",
175 ic->identifier, ic->tap);
176 libinput_device_config_tap_set_enabled(libinput_device, ic->tap);
177 }
178}
179
180static void handle_device_destroy(struct wl_listener *listener, void *data) {
181 struct wlr_input_device *device = data;
182
183 struct sway_input_device *input_device =
184 input_sway_device_from_wlr(input_manager, device);
185
186 if (!sway_assert(input_device, "could not find sway device")) {
187 return;
188 }
189
190 wlr_log(L_DEBUG, "removing device: '%s'",
191 input_device->identifier);
192
193 struct sway_seat *seat = NULL;
194 wl_list_for_each(seat, &input_manager->seats, link) {
195 seat_remove_device(seat, input_device);
196 }
197
198 wl_list_remove(&input_device->link);
199 wl_list_remove(&input_device->device_destroy.link);
200 free(input_device->identifier);
201 free(input_device);
202}
203
204static void handle_new_input(struct wl_listener *listener, void *data) {
205 struct sway_input_manager *input =
206 wl_container_of(listener, input, new_input);
207 struct wlr_input_device *device = data;
208
209 struct sway_input_device *input_device =
210 calloc(1, sizeof(struct sway_input_device));
211 if (!sway_assert(input_device, "could not allocate input device")) {
212 return;
213 }
214
215 input_device->wlr_device = device;
216 input_device->identifier = get_device_identifier(device);
217 wl_list_insert(&input->devices, &input_device->link);
218
219 wlr_log(L_DEBUG, "adding device: '%s'",
220 input_device->identifier);
221
222 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
223 input_manager_libinput_config_pointer(input_device);
224 }
225
226 struct sway_seat *seat = NULL;
227 if (!input_has_seat_configuration(input)) {
228 wlr_log(L_DEBUG, "no seat configuration, using default seat");
229 seat = input_manager_get_seat(input, default_seat);
230 seat_add_device(seat, input_device);
231 return;
232 }
233
234 bool added = false;
235 wl_list_for_each(seat, &input->seats, link) {
236 struct seat_config *seat_config = seat_get_config(seat);
237 bool has_attachment = seat_config &&
238 (seat_config_get_attachment(seat_config, input_device->identifier) ||
239 seat_config_get_attachment(seat_config, "*"));
240
241 if (has_attachment) {
242 seat_add_device(seat, input_device);
243 added = true;
244 }
245 }
246
247 if (!added) {
248 wl_list_for_each(seat, &input->seats, link) {
249 struct seat_config *seat_config = seat_get_config(seat);
250 if (seat_config && seat_config->fallback == 1) {
251 seat_add_device(seat, input_device);
252 added = true;
253 }
254 }
255 }
256
257 if (!added) {
258 wlr_log(L_DEBUG,
259 "device '%s' is not configured on any seats",
260 input_device->identifier);
261 }
262
263 wl_signal_add(&device->events.destroy, &input_device->device_destroy);
264 input_device->device_destroy.notify = handle_device_destroy;
265}
266
267static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
268 struct sway_input_manager *input_manager = wl_container_of(
269 listener, input_manager, inhibit_activate);
270 struct sway_seat *seat;
271 wl_list_for_each(seat, &input_manager->seats, link) {
272 seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
273 }
274}
275
276static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) {
277 struct sway_input_manager *input_manager = wl_container_of(
278 listener, input_manager, inhibit_deactivate);
279 struct sway_seat *seat;
280 wl_list_for_each(seat, &input_manager->seats, link) {
281 seat_set_exclusive_client(seat, NULL);
282 struct sway_container *previous = seat_get_focus(seat);
283 if (previous) {
284 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
285 container_type_to_str(previous->type), previous->name);
286 // Hack to get seat to re-focus the return value of get_focus
287 seat_set_focus(seat, previous->parent);
288 seat_set_focus(seat, previous);
289 }
290 }
291}
292
293struct sway_input_manager *input_manager_create(
294 struct sway_server *server) {
295 struct sway_input_manager *input =
296 calloc(1, sizeof(struct sway_input_manager));
297 if (!input) {
298 return NULL;
299 }
300 input->server = server;
301
302 wl_list_init(&input->devices);
303 wl_list_init(&input->seats);
304
305 // create the default seat
306 input_manager_get_seat(input, default_seat);
307
308 input->new_input.notify = handle_new_input;
309 wl_signal_add(&server->backend->events.new_input, &input->new_input);
310
311 input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
312 input->inhibit_activate.notify = handle_inhibit_activate;
313 wl_signal_add(&input->inhibit->events.activate,
314 &input->inhibit_activate);
315 input->inhibit_deactivate.notify = handle_inhibit_deactivate;
316 wl_signal_add(&input->inhibit->events.deactivate,
317 &input->inhibit_deactivate);
318
319 return input;
320}
321
322bool input_manager_has_focus(struct sway_input_manager *input,
323 struct sway_container *container) {
324 struct sway_seat *seat = NULL;
325 wl_list_for_each(seat, &input->seats, link) {
326 if (seat_get_focus(seat) == container) {
327 return true;
328 }
329 }
330
331 return false;
332}
333
334void input_manager_set_focus(struct sway_input_manager *input,
335 struct sway_container *container) {
336 struct sway_seat *seat;
337 wl_list_for_each(seat, &input->seats, link) {
338 seat_set_focus(seat, container);
339 }
340}
341
342void input_manager_apply_input_config(struct sway_input_manager *input,
343 struct input_config *input_config) {
344 struct sway_input_device *input_device = NULL;
345 wl_list_for_each(input_device, &input->devices, link) {
346 if (strcmp(input_device->identifier, input_config->identifier) == 0) {
347 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
348 input_manager_libinput_config_pointer(input_device);
349 }
350
351 struct sway_seat *seat = NULL;
352 wl_list_for_each(seat, &input->seats, link) {
353 seat_configure_device(seat, input_device);
354 }
355 }
356 }
357}
358
359void input_manager_apply_seat_config(struct sway_input_manager *input,
360 struct seat_config *seat_config) {
361 wlr_log(L_DEBUG, "applying new seat config for seat %s",
362 seat_config->name);
363 struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
364 if (!seat) {
365 return;
366 }
367
368 seat_apply_config(seat, seat_config);
369
370 // for every device, try to add it to a seat and if no seat has it
371 // attached, add it to the fallback seats.
372 struct sway_input_device *input_device = NULL;
373 wl_list_for_each(input_device, &input->devices, link) {
374 list_t *seat_list = create_list();
375 struct sway_seat *seat = NULL;
376 wl_list_for_each(seat, &input->seats, link) {
377 struct seat_config *seat_config = seat_get_config(seat);
378 if (!seat_config) {
379 continue;
380 }
381 if (seat_config_get_attachment(seat_config, "*") ||
382 seat_config_get_attachment(seat_config,
383 input_device->identifier)) {
384 list_add(seat_list, seat);
385 }
386 }
387
388 if (seat_list->length) {
389 wl_list_for_each(seat, &input->seats, link) {
390 bool attached = false;
391 for (int i = 0; i < seat_list->length; ++i) {
392 if (seat == seat_list->items[i]) {
393 attached = true;
394 break;
395 }
396 }
397 if (attached) {
398 seat_add_device(seat, input_device);
399 } else {
400 seat_remove_device(seat, input_device);
401 }
402 }
403 } else {
404 wl_list_for_each(seat, &input->seats, link) {
405 struct seat_config *seat_config = seat_get_config(seat);
406 if (seat_config && seat_config->fallback == 1) {
407 seat_add_device(seat, input_device);
408 } else {
409 seat_remove_device(seat, input_device);
410 }
411 }
412 }
413 list_free(seat_list);
414 }
415}
416
417void input_manager_configure_xcursor(struct sway_input_manager *input) {
418 struct sway_seat *seat = NULL;
419 wl_list_for_each(seat, &input->seats, link) {
420 seat_configure_xcursor(seat);
421 }
422}
423
424struct sway_seat *input_manager_get_default_seat(
425 struct sway_input_manager *input) {
426 struct sway_seat *seat = NULL;
427 wl_list_for_each(seat, &input->seats, link) {
428 if (strcmp(seat->wlr_seat->name, "seat0") == 0) {
429 return seat;
430 }
431 }
432 return seat;
433}
434
435struct input_config *input_device_get_config(struct sway_input_device *device) {
436 struct input_config *input_config = NULL;
437 for (int i = 0; i < config->input_configs->length; ++i) {
438 input_config = config->input_configs->items[i];
439 if (strcmp(input_config->identifier, device->identifier) == 0) {
440 return input_config;
441 }
442 }
443
444 return NULL;
445}
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
new file mode 100644
index 00000000..dbb0c359
--- /dev/null
+++ b/sway/input/keyboard.c
@@ -0,0 +1,504 @@
1#include <assert.h>
2#include <wlr/backend/multi.h>
3#include <wlr/backend/session.h>
4#include "sway/input/seat.h"
5#include "sway/input/keyboard.h"
6#include "sway/input/input-manager.h"
7#include "sway/commands.h"
8#include "log.h"
9
10static bool keysym_is_modifier(xkb_keysym_t keysym) {
11 switch (keysym) {
12 case XKB_KEY_Shift_L: case XKB_KEY_Shift_R:
13 case XKB_KEY_Control_L: case XKB_KEY_Control_R:
14 case XKB_KEY_Caps_Lock:
15 case XKB_KEY_Shift_Lock:
16 case XKB_KEY_Meta_L: case XKB_KEY_Meta_R:
17 case XKB_KEY_Alt_L: case XKB_KEY_Alt_R:
18 case XKB_KEY_Super_L: case XKB_KEY_Super_R:
19 case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R:
20 return true;
21 default:
22 return false;
23 }
24}
25
26static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) {
27 size_t n = 0;
28 for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
29 if (pressed_keysyms[i] != XKB_KEY_NoSymbol) {
30 ++n;
31 }
32 }
33 return n;
34}
35
36static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms,
37 xkb_keysym_t keysym) {
38 for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
39 if (pressed_keysyms[i] == keysym) {
40 return i;
41 }
42 }
43 return -1;
44}
45
46static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms,
47 xkb_keysym_t keysym) {
48 ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
49 if (i < 0) {
50 i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol);
51 if (i >= 0) {
52 pressed_keysyms[i] = keysym;
53 }
54 }
55}
56
57static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms,
58 xkb_keysym_t keysym) {
59 ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
60 if (i >= 0) {
61 pressed_keysyms[i] = XKB_KEY_NoSymbol;
62 }
63}
64
65static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms,
66 const xkb_keysym_t *keysyms, size_t keysyms_len,
67 enum wlr_key_state state) {
68 for (size_t i = 0; i < keysyms_len; ++i) {
69 if (keysym_is_modifier(keysyms[i])) {
70 continue;
71 }
72 if (state == WLR_KEY_PRESSED) {
73 pressed_keysyms_add(pressed_keysyms, keysyms[i]);
74 } else { // WLR_KEY_RELEASED
75 pressed_keysyms_remove(pressed_keysyms, keysyms[i]);
76 }
77 }
78}
79
80static bool binding_matches_key_state(struct sway_binding *binding,
81 enum wlr_key_state key_state) {
82 if (key_state == WLR_KEY_PRESSED && !binding->release) {
83 return true;
84 }
85 if (key_state == WLR_KEY_RELEASED && binding->release) {
86 return true;
87 }
88
89 return false;
90}
91
92static void keyboard_execute_command(struct sway_keyboard *keyboard,
93 struct sway_binding *binding) {
94 wlr_log(L_DEBUG, "running command for binding: %s",
95 binding->command);
96 config_clear_handler_context(config);
97 config->handler_context.seat = keyboard->seat_device->sway_seat;
98 struct cmd_results *results = execute_command(binding->command, NULL);
99 if (results->status != CMD_SUCCESS) {
100 wlr_log(L_DEBUG, "could not run command for binding: %s (%s)",
101 binding->command, results->error);
102 }
103 free_cmd_results(results);
104}
105
106/**
107 * Execute a built-in, hardcoded compositor binding. These are triggered from a
108 * single keysym.
109 *
110 * Returns true if the keysym was handled by a binding and false if the event
111 * should be propagated to clients.
112 */
113static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
114 xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) {
115 for (size_t i = 0; i < keysyms_len; ++i) {
116 xkb_keysym_t keysym = pressed_keysyms[i];
117 if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
118 keysym <= XKB_KEY_XF86Switch_VT_12) {
119 if (wlr_backend_is_multi(server.backend)) {
120 struct wlr_session *session =
121 wlr_multi_get_session(server.backend);
122 if (session) {
123 unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
124 wlr_session_change_vt(session, vt);
125 }
126 }
127 return true;
128 }
129 }
130
131 return false;
132}
133
134/**
135 * Execute keyboard bindings bound with `bindysm` for the given keyboard state.
136 *
137 * Returns true if the keysym was handled by a binding and false if the event
138 * should be propagated to clients.
139 */
140static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
141 xkb_keysym_t *pressed_keysyms, uint32_t modifiers,
142 enum wlr_key_state key_state) {
143 // configured bindings
144 int n = pressed_keysyms_length(pressed_keysyms);
145 list_t *keysym_bindings = config->current_mode->keysym_bindings;
146 for (int i = 0; i < keysym_bindings->length; ++i) {
147 struct sway_binding *binding = keysym_bindings->items[i];
148 if (!binding_matches_key_state(binding, key_state) ||
149 modifiers ^ binding->modifiers ||
150 n != binding->keys->length) {
151 continue;
152 }
153
154 bool match = true;
155 for (int j = 0; j < binding->keys->length; ++j) {
156 match =
157 pressed_keysyms_index(pressed_keysyms,
158 *(int*)binding->keys->items[j]) >= 0;
159
160 if (!match) {
161 break;
162 }
163 }
164
165 if (match) {
166 keyboard_execute_command(keyboard, binding);
167 return true;
168 }
169 }
170
171 return false;
172}
173
174static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
175 struct sway_binding *binding, struct wlr_event_keyboard_key *event) {
176 assert(binding->bindcode);
177
178 uint32_t keycode = event->keycode + 8;
179
180 if (!binding_matches_key_state(binding, event->state)) {
181 return false;
182 }
183
184 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
185 if (modifiers ^ binding->modifiers) {
186 return false;
187 }
188
189 // on release, the released key must be in the binding
190 if (event->state == WLR_KEY_RELEASED) {
191 bool found = false;
192 for (int i = 0; i < binding->keys->length; ++i) {
193 uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
194 if (binding_keycode == keycode) {
195 found = true;
196 break;
197 }
198 }
199 if (!found) {
200 return false;
201 }
202 }
203
204 // every keycode in the binding must be present in the pressed keys on the
205 // keyboard
206 for (int i = 0; i < binding->keys->length; ++i) {
207 uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
208 if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) {
209 continue;
210 }
211
212 bool found = false;
213 for (size_t j = 0; j < keyboard->num_keycodes; ++j) {
214 xkb_keycode_t keycode = keyboard->keycodes[j] + 8;
215 if (keycode == binding_keycode) {
216 found = true;
217 break;
218 }
219 }
220
221 if (!found) {
222 return false;
223 }
224 }
225
226 // every keycode pressed on the keyboard must be present within the binding
227 // keys (unless it is a modifier)
228 for (size_t i = 0; i < keyboard->num_keycodes; ++i) {
229 xkb_keycode_t keycode = keyboard->keycodes[i] + 8;
230 bool found = false;
231 for (int j = 0; j < binding->keys->length; ++j) {
232 uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8;
233 if (binding_keycode == keycode) {
234 found = true;
235 break;
236 }
237 }
238
239 if (!found) {
240 if (!binding->modifiers) {
241 return false;
242 }
243
244 // check if it is a modifier, which we know matched from the check
245 // above
246 const xkb_keysym_t *keysyms;
247 int num_keysyms =
248 xkb_state_key_get_syms(keyboard->xkb_state,
249 keycode, &keysyms);
250 if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) {
251 return false;
252 }
253 }
254 }
255
256 return true;
257}
258
259/**
260 * Execute keyboard bindings bound with `bindcode` for the given keyboard state.
261 *
262 * Returns true if the keysym was handled by a binding and false if the event
263 * should be propagated to clients.
264 */
265static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard,
266 struct wlr_event_keyboard_key *event) {
267 struct wlr_keyboard *wlr_keyboard =
268 keyboard->seat_device->input_device->wlr_device->keyboard;
269 list_t *keycode_bindings = config->current_mode->keycode_bindings;
270 for (int i = 0; i < keycode_bindings->length; ++i) {
271 struct sway_binding *binding = keycode_bindings->items[i];
272 if (binding_matches_keycodes(wlr_keyboard, binding, event)) {
273 keyboard_execute_command(keyboard, binding);
274 return true;
275 }
276 }
277
278 return false;
279}
280
281/**
282 * Get keysyms and modifiers from the keyboard as xkb sees them.
283 *
284 * This uses the xkb keysyms translation based on pressed modifiers and clears
285 * the consumed modifiers from the list of modifiers passed to keybind
286 * detection.
287 *
288 * On US layout, pressing Alt+Shift+2 will trigger Alt+@.
289 */
290static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
291 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
292 uint32_t *modifiers) {
293 struct wlr_input_device *device =
294 keyboard->seat_device->input_device->wlr_device;
295 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
296 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
297 device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
298 *modifiers = *modifiers & ~consumed;
299
300 return xkb_state_key_get_syms(device->keyboard->xkb_state,
301 keycode, keysyms);
302}
303
304/**
305 * Get keysyms and modifiers from the keyboard as if modifiers didn't change
306 * keysyms.
307 *
308 * This avoids the xkb keysym translation based on modifiers considered pressed
309 * in the state.
310 *
311 * This will trigger keybinds such as Alt+Shift+2.
312 */
313static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
314 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
315 uint32_t *modifiers) {
316 struct wlr_input_device *device =
317 keyboard->seat_device->input_device->wlr_device;
318 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
319
320 xkb_layout_index_t layout_index = xkb_state_key_get_layout(
321 device->keyboard->xkb_state, keycode);
322 return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap,
323 keycode, layout_index, 0, keysyms);
324}
325
326static void handle_keyboard_key(struct wl_listener *listener, void *data) {
327 struct sway_keyboard *keyboard =
328 wl_container_of(listener, keyboard, keyboard_key);
329 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
330 struct wlr_input_device *wlr_device =
331 keyboard->seat_device->input_device->wlr_device;
332 struct wlr_event_keyboard_key *event = data;
333
334 xkb_keycode_t keycode = event->keycode + 8;
335 bool handled = false;
336
337 // handle keycodes
338 handled = keyboard_execute_bindcode(keyboard, event);
339
340 // handle translated keysyms
341 if (!handled && event->state == WLR_KEY_RELEASED) {
342 handled = keyboard_execute_bindsym(keyboard,
343 keyboard->pressed_keysyms_translated,
344 keyboard->modifiers_translated,
345 event->state);
346 }
347 const xkb_keysym_t *translated_keysyms;
348 size_t translated_keysyms_len =
349 keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms,
350 &keyboard->modifiers_translated);
351 pressed_keysyms_update(keyboard->pressed_keysyms_translated,
352 translated_keysyms, translated_keysyms_len, event->state);
353 if (!handled && event->state == WLR_KEY_PRESSED) {
354 handled = keyboard_execute_bindsym(keyboard,
355 keyboard->pressed_keysyms_translated,
356 keyboard->modifiers_translated,
357 event->state);
358 }
359
360 // Handle raw keysyms
361 if (!handled && event->state == WLR_KEY_RELEASED) {
362 handled = keyboard_execute_bindsym(keyboard,
363 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
364 event->state);
365 }
366 const xkb_keysym_t *raw_keysyms;
367 size_t raw_keysyms_len =
368 keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw);
369 pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms,
370 raw_keysyms_len, event->state);
371 if (!handled && event->state == WLR_KEY_PRESSED) {
372 handled = keyboard_execute_bindsym(keyboard,
373 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
374 event->state);
375 }
376
377 // Compositor bindings
378 if (!handled && event->state == WLR_KEY_PRESSED) {
379 handled =
380 keyboard_execute_compositor_binding(keyboard,
381 keyboard->pressed_keysyms_translated,
382 keyboard->modifiers_translated,
383 translated_keysyms_len);
384 }
385 if (!handled && event->state == WLR_KEY_PRESSED) {
386 handled =
387 keyboard_execute_compositor_binding(keyboard,
388 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
389 raw_keysyms_len);
390 }
391
392 if (!handled || event->state == WLR_KEY_RELEASED) {
393 wlr_seat_set_keyboard(wlr_seat, wlr_device);
394 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
395 event->keycode, event->state);
396 }
397}
398
399static void handle_keyboard_modifiers(struct wl_listener *listener,
400 void *data) {
401 struct sway_keyboard *keyboard =
402 wl_container_of(listener, keyboard, keyboard_modifiers);
403 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
404 struct wlr_input_device *wlr_device =
405 keyboard->seat_device->input_device->wlr_device;
406 wlr_seat_set_keyboard(wlr_seat, wlr_device);
407 wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers);
408}
409
410struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
411 struct sway_seat_device *device) {
412 struct sway_keyboard *keyboard =
413 calloc(1, sizeof(struct sway_keyboard));
414 if (!sway_assert(keyboard, "could not allocate sway keyboard")) {
415 return NULL;
416 }
417
418 keyboard->seat_device = device;
419 device->keyboard = keyboard;
420
421 wl_list_init(&keyboard->keyboard_key.link);
422 wl_list_init(&keyboard->keyboard_modifiers.link);
423
424 return keyboard;
425}
426
427void sway_keyboard_configure(struct sway_keyboard *keyboard) {
428 struct xkb_rule_names rules;
429 memset(&rules, 0, sizeof(rules));
430 struct input_config *input_config =
431 input_device_get_config(keyboard->seat_device->input_device);
432 struct wlr_input_device *wlr_device =
433 keyboard->seat_device->input_device->wlr_device;
434
435 if (input_config && input_config->xkb_layout) {
436 rules.layout = input_config->xkb_layout;
437 } else {
438 rules.layout = getenv("XKB_DEFAULT_LAYOUT");
439 }
440 if (input_config && input_config->xkb_model) {
441 rules.model = input_config->xkb_model;
442 } else {
443 rules.model = getenv("XKB_DEFAULT_MODEL");
444 }
445
446 if (input_config && input_config->xkb_options) {
447 rules.options = input_config->xkb_options;
448 } else {
449 rules.options = getenv("XKB_DEFAULT_OPTIONS");
450 }
451
452 if (input_config && input_config->xkb_rules) {
453 rules.rules = input_config->xkb_rules;
454 } else {
455 rules.rules = getenv("XKB_DEFAULT_RULES");
456 }
457
458 if (input_config && input_config->xkb_variant) {
459 rules.variant = input_config->xkb_variant;
460 } else {
461 rules.variant = getenv("XKB_DEFAULT_VARIANT");
462 }
463
464 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
465 if (!sway_assert(context, "cannot create XKB context")) {
466 return;
467 }
468
469 struct xkb_keymap *keymap =
470 xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
471
472 if (!keymap) {
473 wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist");
474 xkb_context_unref(context);
475 return;
476 }
477
478 xkb_keymap_unref(keyboard->keymap);
479 keyboard->keymap = keymap;
480 wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
481
482 wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600);
483 xkb_context_unref(context);
484 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
485 wlr_seat_set_keyboard(seat, wlr_device);
486
487 wl_list_remove(&keyboard->keyboard_key.link);
488 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);
489 keyboard->keyboard_key.notify = handle_keyboard_key;
490
491 wl_list_remove(&keyboard->keyboard_modifiers.link);
492 wl_signal_add( &wlr_device->keyboard->events.modifiers,
493 &keyboard->keyboard_modifiers);
494 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
495}
496
497void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
498 if (!keyboard) {
499 return;
500 }
501 wl_list_remove(&keyboard->keyboard_key.link);
502 wl_list_remove(&keyboard->keyboard_modifiers.link);
503 free(keyboard);
504}
diff --git a/sway/input/seat.c b/sway/input/seat.c
new file mode 100644
index 00000000..467e5087
--- /dev/null
+++ b/sway/input/seat.c
@@ -0,0 +1,675 @@
1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 199309L
3#include <assert.h>
4#include <strings.h>
5#include <time.h>
6#include <wlr/types/wlr_cursor.h>
7#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_xcursor_manager.h>
9#include "sway/debug.h"
10#include "sway/tree/container.h"
11#include "sway/tree/workspace.h"
12#include "sway/input/seat.h"
13#include "sway/input/cursor.h"
14#include "sway/input/input-manager.h"
15#include "sway/input/keyboard.h"
16#include "sway/ipc-server.h"
17#include "sway/layers.h"
18#include "sway/output.h"
19#include "sway/tree/container.h"
20#include "sway/tree/view.h"
21#include "log.h"
22
23static void seat_device_destroy(struct sway_seat_device *seat_device) {
24 if (!seat_device) {
25 return;
26 }
27
28 sway_keyboard_destroy(seat_device->keyboard);
29 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
30 seat_device->input_device->wlr_device);
31 wl_list_remove(&seat_device->link);
32 free(seat_device);
33}
34
35void seat_destroy(struct sway_seat *seat) {
36 struct sway_seat_device *seat_device, *next;
37 wl_list_for_each_safe(seat_device, next, &seat->devices, link) {
38 seat_device_destroy(seat_device);
39 }
40 sway_cursor_destroy(seat->cursor);
41 wl_list_remove(&seat->link);
42 wlr_seat_destroy(seat->wlr_seat);
43}
44
45static struct sway_seat_container *seat_container_from_container(
46 struct sway_seat *seat, struct sway_container *con);
47
48static void seat_container_destroy(struct sway_seat_container *seat_con) {
49 struct sway_container *con = seat_con->container;
50 struct sway_container *child = NULL;
51
52 if (con->children != NULL) {
53 for (int i = 0; i < con->children->length; ++i) {
54 child = con->children->items[i];
55 struct sway_seat_container *seat_child =
56 seat_container_from_container(seat_con->seat, child);
57 seat_container_destroy(seat_child);
58 }
59 }
60
61 wl_list_remove(&seat_con->destroy.link);
62 wl_list_remove(&seat_con->link);
63 free(seat_con);
64}
65
66static void seat_send_focus(struct sway_seat *seat,
67 struct sway_container *con) {
68 if (con->type != C_VIEW) {
69 return;
70 }
71 struct sway_view *view = con->sway_view;
72 if (view->type == SWAY_VIEW_XWAYLAND) {
73 struct wlr_xwayland *xwayland =
74 seat->input->server->xwayland;
75 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
76 }
77 view_set_activated(view, true);
78 struct wlr_keyboard *keyboard =
79 wlr_seat_get_keyboard(seat->wlr_seat);
80 if (keyboard) {
81 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
82 view->surface, keyboard->keycodes,
83 keyboard->num_keycodes, &keyboard->modifiers);
84 } else {
85 wlr_seat_keyboard_notify_enter(
86 seat->wlr_seat, view->surface, NULL, 0, NULL);
87 }
88}
89
90static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
91 struct sway_container *container, enum sway_container_type type) {
92 if (container->type == C_VIEW || container->children->length == 0) {
93 return container;
94 }
95
96 struct sway_seat_container *current = NULL;
97 wl_list_for_each(current, &seat->focus_stack, link) {
98 if (current->container->type != type && type != C_TYPES) {
99 continue;
100 }
101
102 if (container_has_child(container, current->container)) {
103 return current->container;
104 }
105 }
106
107 return NULL;
108}
109
110void seat_focus_inactive_children_for_each(struct sway_seat *seat,
111 struct sway_container *container,
112 void (*f)(struct sway_container *container, void *data), void *data) {
113 struct sway_seat_container *current = NULL;
114 wl_list_for_each(current, &seat->focus_stack, link) {
115 if (current->container->parent == NULL) {
116 continue;
117 }
118 if (current->container->parent == container) {
119 f(current->container, data);
120 }
121 }
122}
123
124struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
125 struct sway_container *container) {
126 return seat_get_focus_by_type(seat, container, C_VIEW);
127}
128
129static void handle_seat_container_destroy(struct wl_listener *listener,
130 void *data) {
131 struct sway_seat_container *seat_con =
132 wl_container_of(listener, seat_con, destroy);
133 struct sway_seat *seat = seat_con->seat;
134 struct sway_container *con = seat_con->container;
135 struct sway_container *parent = con->parent;
136 struct sway_container *focus = seat_get_focus(seat);
137
138 bool set_focus =
139 focus != NULL &&
140 (focus == con || container_has_child(con, focus)) &&
141 con->type != C_WORKSPACE;
142
143 seat_container_destroy(seat_con);
144
145 if (set_focus) {
146 struct sway_container *next_focus = NULL;
147 while (next_focus == NULL) {
148 next_focus = seat_get_focus_by_type(seat, parent, C_VIEW);
149
150 if (next_focus == NULL && parent->type == C_WORKSPACE) {
151 next_focus = parent;
152 break;
153 }
154
155 parent = parent->parent;
156 }
157
158 // the structure change might have caused it to move up to the top of
159 // the focus stack without sending focus notifications to the view
160 if (seat_get_focus(seat) == next_focus) {
161 seat_send_focus(seat, next_focus);
162 } else {
163 seat_set_focus(seat, next_focus);
164 }
165 }
166}
167
168static struct sway_seat_container *seat_container_from_container(
169 struct sway_seat *seat, struct sway_container *con) {
170 if (con->type == C_ROOT || con->type == C_OUTPUT) {
171 // these don't get seat containers ever
172 return NULL;
173 }
174
175 struct sway_seat_container *seat_con = NULL;
176 wl_list_for_each(seat_con, &seat->focus_stack, link) {
177 if (seat_con->container == con) {
178 return seat_con;
179 }
180 }
181
182 seat_con = calloc(1, sizeof(struct sway_seat_container));
183 if (seat_con == NULL) {
184 wlr_log(L_ERROR, "could not allocate seat container");
185 return NULL;
186 }
187
188 seat_con->container = con;
189 seat_con->seat = seat;
190 wl_list_insert(seat->focus_stack.prev, &seat_con->link);
191 wl_signal_add(&con->events.destroy, &seat_con->destroy);
192 seat_con->destroy.notify = handle_seat_container_destroy;
193
194 return seat_con;
195}
196
197static void handle_new_container(struct wl_listener *listener, void *data) {
198 struct sway_seat *seat = wl_container_of(listener, seat, new_container);
199 struct sway_container *con = data;
200 seat_container_from_container(seat, con);
201}
202
203static void collect_focus_iter(struct sway_container *con, void *data) {
204 struct sway_seat *seat = data;
205 if (con->type > C_WORKSPACE) {
206 return;
207 }
208 struct sway_seat_container *seat_con =
209 seat_container_from_container(seat, con);
210 if (!seat_con) {
211 return;
212 }
213 wl_list_remove(&seat_con->link);
214 wl_list_insert(&seat->focus_stack, &seat_con->link);
215}
216
217struct sway_seat *seat_create(struct sway_input_manager *input,
218 const char *seat_name) {
219 struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
220 if (!seat) {
221 return NULL;
222 }
223
224 seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name);
225 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
226 free(seat);
227 return NULL;
228 }
229
230 seat->cursor = sway_cursor_create(seat);
231 if (!seat->cursor) {
232 wlr_seat_destroy(seat->wlr_seat);
233 free(seat);
234 return NULL;
235 }
236
237 // init the focus stack
238 wl_list_init(&seat->focus_stack);
239
240 container_for_each_descendant_dfs(&root_container,
241 collect_focus_iter, seat);
242
243 wl_signal_add(&root_container.sway_root->events.new_container,
244 &seat->new_container);
245 seat->new_container.notify = handle_new_container;
246
247 seat->input = input;
248 wl_list_init(&seat->devices);
249
250 wlr_seat_set_capabilities(seat->wlr_seat,
251 WL_SEAT_CAPABILITY_KEYBOARD |
252 WL_SEAT_CAPABILITY_POINTER |
253 WL_SEAT_CAPABILITY_TOUCH);
254
255 seat_configure_xcursor(seat);
256
257 wl_list_insert(&input->seats, &seat->link);
258
259 return seat;
260}
261
262static void seat_apply_input_config(struct sway_seat *seat,
263 struct sway_seat_device *sway_device) {
264 struct input_config *ic = input_device_get_config(
265 sway_device->input_device);
266 if (!ic) {
267 return;
268 }
269 wlr_log(L_DEBUG, "Applying input config to %s",
270 sway_device->input_device->identifier);
271 if (ic->mapped_output) {
272 struct sway_container *output = NULL;
273 for (int i = 0; i < root_container.children->length; ++i) {
274 struct sway_container *_output = root_container.children->items[i];
275 if (strcasecmp(_output->name, ic->mapped_output) == 0) {
276 output = _output;
277 break;
278 }
279 }
280 if (output) {
281 wlr_cursor_map_input_to_output(seat->cursor->cursor,
282 sway_device->input_device->wlr_device,
283 output->sway_output->wlr_output);
284 wlr_log(L_DEBUG, "Mapped to output %s", output->name);
285 }
286 }
287}
288
289static void seat_configure_pointer(struct sway_seat *seat,
290 struct sway_seat_device *sway_device) {
291 wlr_cursor_attach_input_device(seat->cursor->cursor,
292 sway_device->input_device->wlr_device);
293 seat_apply_input_config(seat, sway_device);
294}
295
296static void seat_configure_keyboard(struct sway_seat *seat,
297 struct sway_seat_device *seat_device) {
298 if (!seat_device->keyboard) {
299 sway_keyboard_create(seat, seat_device);
300 }
301 struct wlr_keyboard *wlr_keyboard =
302 seat_device->input_device->wlr_device->keyboard;
303 sway_keyboard_configure(seat_device->keyboard);
304 wlr_seat_set_keyboard(seat->wlr_seat,
305 seat_device->input_device->wlr_device);
306 struct sway_container *focus = seat_get_focus(seat);
307 if (focus && focus->type == C_VIEW) {
308 // force notify reenter to pick up the new configuration
309 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
310 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
311 focus->sway_view->surface, wlr_keyboard->keycodes,
312 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
313 }
314}
315
316static void seat_configure_tablet_tool(struct sway_seat *seat,
317 struct sway_seat_device *sway_device) {
318 wlr_cursor_attach_input_device(seat->cursor->cursor,
319 sway_device->input_device->wlr_device);
320 seat_apply_input_config(seat, sway_device);
321}
322
323static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
324 struct sway_input_device *input_device) {
325 struct sway_seat_device *seat_device = NULL;
326 wl_list_for_each(seat_device, &seat->devices, link) {
327 if (seat_device->input_device == input_device) {
328 return seat_device;
329 }
330 }
331
332 return NULL;
333}
334
335void seat_configure_device(struct sway_seat *seat,
336 struct sway_input_device *input_device) {
337 struct sway_seat_device *seat_device =
338 seat_get_device(seat, input_device);
339 if (!seat_device) {
340 return;
341 }
342
343 switch (input_device->wlr_device->type) {
344 case WLR_INPUT_DEVICE_POINTER:
345 seat_configure_pointer(seat, seat_device);
346 break;
347 case WLR_INPUT_DEVICE_KEYBOARD:
348 seat_configure_keyboard(seat, seat_device);
349 break;
350 case WLR_INPUT_DEVICE_TABLET_TOOL:
351 seat_configure_tablet_tool(seat, seat_device);
352 break;
353 case WLR_INPUT_DEVICE_TABLET_PAD:
354 case WLR_INPUT_DEVICE_TOUCH:
355 wlr_log(L_DEBUG, "TODO: configure other devices");
356 break;
357 }
358}
359
360void seat_add_device(struct sway_seat *seat,
361 struct sway_input_device *input_device) {
362 if (seat_get_device(seat, input_device)) {
363 seat_configure_device(seat, input_device);
364 return;
365 }
366
367 struct sway_seat_device *seat_device =
368 calloc(1, sizeof(struct sway_seat_device));
369 if (!seat_device) {
370 wlr_log(L_DEBUG, "could not allocate seat device");
371 return;
372 }
373
374 wlr_log(L_DEBUG, "adding device %s to seat %s",
375 input_device->identifier, seat->wlr_seat->name);
376
377 seat_device->sway_seat = seat;
378 seat_device->input_device = input_device;
379 wl_list_insert(&seat->devices, &seat_device->link);
380
381 seat_configure_device(seat, input_device);
382}
383
384void seat_remove_device(struct sway_seat *seat,
385 struct sway_input_device *input_device) {
386 struct sway_seat_device *seat_device =
387 seat_get_device(seat, input_device);
388
389 if (!seat_device) {
390 return;
391 }
392
393 wlr_log(L_DEBUG, "removing device %s from seat %s",
394 input_device->identifier, seat->wlr_seat->name);
395
396 seat_device_destroy(seat_device);
397}
398
399void seat_configure_xcursor(struct sway_seat *seat) {
400 // TODO configure theme and size
401 const char *cursor_theme = NULL;
402
403 if (!seat->cursor->xcursor_manager) {
404 seat->cursor->xcursor_manager =
405 wlr_xcursor_manager_create(cursor_theme, 24);
406 if (sway_assert(seat->cursor->xcursor_manager,
407 "Cannot create XCursor manager for theme %s",
408 cursor_theme)) {
409 return;
410 }
411 }
412
413 for (int i = 0; i < root_container.children->length; ++i) {
414 struct sway_container *output_container =
415 root_container.children->items[i];
416 struct wlr_output *output =
417 output_container->sway_output->wlr_output;
418 bool result =
419 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
420 output->scale);
421
422 sway_assert(!result,
423 "Cannot load xcursor theme for output '%s' with scale %f",
424 // TODO: Fractional scaling
425 output->name, (double)output->scale);
426 }
427
428 wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
429 "left_ptr", seat->cursor->cursor);
430 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
431 seat->cursor->cursor->y);
432}
433
434bool seat_is_input_allowed(struct sway_seat *seat,
435 struct wlr_surface *surface) {
436 struct wl_client *client = wl_resource_get_client(surface->resource);
437 return !seat->exclusive_client || seat->exclusive_client == client;
438}
439
440void seat_set_focus_warp(struct sway_seat *seat,
441 struct sway_container *container, bool warp) {
442 if (seat->focused_layer) {
443 return;
444 }
445
446 struct sway_container *last_focus = seat_get_focus(seat);
447 if (container && last_focus == container) {
448 return;
449 }
450
451 if (container) {
452 struct sway_seat_container *seat_con =
453 seat_container_from_container(seat, container);
454 if (seat_con == NULL) {
455 return;
456 }
457
458 // put all the anscestors of this container on top of the focus stack
459 struct sway_seat_container *parent =
460 seat_container_from_container(seat,
461 seat_con->container->parent);
462 while (parent) {
463 wl_list_remove(&parent->link);
464 wl_list_insert(&seat->focus_stack, &parent->link);
465
466 parent =
467 seat_container_from_container(seat,
468 parent->container->parent);
469 }
470
471 wl_list_remove(&seat_con->link);
472 wl_list_insert(&seat->focus_stack, &seat_con->link);
473
474 if (container->type == C_VIEW && !seat_is_input_allowed(
475 seat, container->sway_view->surface)) {
476 wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited");
477 return;
478 }
479
480 if (container->type == C_VIEW) {
481 seat_send_focus(seat, container);
482 }
483 }
484
485 if (last_focus) {
486 struct sway_container *last_ws = last_focus;
487 if (last_ws && last_ws->type != C_WORKSPACE) {
488 last_ws = container_parent(last_ws, C_WORKSPACE);
489 }
490 if (last_ws) {
491 ipc_event_workspace(last_ws, container, "focus");
492 if (!workspace_is_visible(last_ws)
493 && last_ws->children->length == 0) {
494 container_destroy(last_ws);
495 }
496 }
497
498 if (config->mouse_warping && warp) {
499 struct sway_container *last_output = last_focus;
500 if (last_output && last_output->type != C_OUTPUT) {
501 last_output = container_parent(last_output, C_OUTPUT);
502 }
503 struct sway_container *new_output = container;
504 if (new_output && new_output->type != C_OUTPUT) {
505 new_output = container_parent(new_output, C_OUTPUT);
506 }
507 if (new_output && last_output && new_output != last_output) {
508 double x = new_output->x + container->x +
509 container->width / 2.0;
510 double y = new_output->y + container->y +
511 container->height / 2.0;
512 struct wlr_output *wlr_output =
513 new_output->sway_output->wlr_output;
514 if (!wlr_output_layout_contains_point(
515 root_container.sway_root->output_layout,
516 wlr_output, seat->cursor->cursor->x,
517 seat->cursor->cursor->y)) {
518 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
519 struct timespec now;
520 clock_gettime(CLOCK_MONOTONIC, &now);
521 cursor_send_pointer_motion(seat->cursor, now.tv_nsec / 1000);
522 }
523 }
524 }
525 }
526
527 if (last_focus && last_focus->type == C_VIEW &&
528 !input_manager_has_focus(seat->input, last_focus)) {
529 struct sway_view *view = last_focus->sway_view;
530 view_set_activated(view, false);
531 }
532
533 seat->has_focus = (container != NULL);
534
535 update_debug_tree();
536}
537
538void seat_set_focus(struct sway_seat *seat,
539 struct sway_container *container) {
540 seat_set_focus_warp(seat, container, true);
541}
542
543void seat_set_focus_surface(struct sway_seat *seat,
544 struct wlr_surface *surface) {
545 if (seat->focused_layer != NULL) {
546 return;
547 }
548 if (seat->has_focus) {
549 struct sway_container *focus = seat_get_focus(seat);
550 if (focus->type == C_VIEW) {
551 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
552 view_set_activated(focus->sway_view, false);
553 }
554 seat->has_focus = false;
555 }
556 struct wlr_keyboard *keyboard =
557 wlr_seat_get_keyboard(seat->wlr_seat);
558 if (keyboard) {
559 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,
560 keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
561 } else {
562 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL);
563 }
564}
565
566void seat_set_focus_layer(struct sway_seat *seat,
567 struct wlr_layer_surface *layer) {
568 if (!layer && seat->focused_layer) {
569 seat->focused_layer = NULL;
570 struct sway_container *previous = seat_get_focus(seat);
571 if (previous) {
572 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
573 container_type_to_str(previous->type), previous->name);
574 // Hack to get seat to re-focus the return value of get_focus
575 seat_set_focus(seat, previous->parent);
576 seat_set_focus(seat, previous);
577 }
578 return;
579 } else if (!layer || seat->focused_layer == layer) {
580 return;
581 }
582 seat_set_focus_surface(seat, layer->surface);
583 if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
584 seat->focused_layer = layer;
585 }
586}
587
588void seat_set_exclusive_client(struct sway_seat *seat,
589 struct wl_client *client) {
590 if (!client) {
591 seat->exclusive_client = client;
592 // Triggers a refocus of the topmost surface layer if necessary
593 // TODO: Make layer surface focus per-output based on cursor position
594 for (int i = 0; i < root_container.children->length; ++i) {
595 struct sway_container *output = root_container.children->items[i];
596 if (!sway_assert(output->type == C_OUTPUT,
597 "root container has non-output child")) {
598 continue;
599 }
600 arrange_layers(output->sway_output);
601 }
602 return;
603 }
604 if (seat->focused_layer) {
605 if (wl_resource_get_client(seat->focused_layer->resource) != client) {
606 seat_set_focus_layer(seat, NULL);
607 }
608 }
609 if (seat->has_focus) {
610 struct sway_container *focus = seat_get_focus(seat);
611 if (focus->type == C_VIEW && wl_resource_get_client(
612 focus->sway_view->surface->resource) != client) {
613 seat_set_focus(seat, NULL);
614 }
615 }
616 if (seat->wlr_seat->pointer_state.focused_client) {
617 if (seat->wlr_seat->pointer_state.focused_client->client != client) {
618 wlr_seat_pointer_clear_focus(seat->wlr_seat);
619 }
620 }
621 struct timespec now;
622 clock_gettime(CLOCK_MONOTONIC, &now);
623 struct wlr_touch_point *point;
624 wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) {
625 if (point->client->client != client) {
626 wlr_seat_touch_point_clear_focus(seat->wlr_seat,
627 now.tv_nsec / 1000, point->touch_id);
628 }
629 }
630 seat->exclusive_client = client;
631}
632
633struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
634 struct sway_container *container) {
635 return seat_get_focus_by_type(seat, container, C_TYPES);
636}
637
638struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
639 if (!seat->has_focus) {
640 return NULL;
641 }
642 return seat_get_focus_inactive(seat, &root_container);
643}
644
645struct sway_container *seat_get_focus(struct sway_seat *seat) {
646 if (!seat->has_focus) {
647 return NULL;
648 }
649 return seat_get_focus_inactive(seat, &root_container);
650}
651
652void seat_apply_config(struct sway_seat *seat,
653 struct seat_config *seat_config) {
654 struct sway_seat_device *seat_device = NULL;
655
656 if (!seat_config) {
657 return;
658 }
659
660 wl_list_for_each(seat_device, &seat->devices, link) {
661 seat_configure_device(seat, seat_device->input_device);
662 }
663}
664
665struct seat_config *seat_get_config(struct sway_seat *seat) {
666 struct seat_config *seat_config = NULL;
667 for (int i = 0; i < config->seat_configs->length; ++i ) {
668 seat_config = config->seat_configs->items[i];
669 if (strcmp(seat->wlr_seat->name, seat_config->name) == 0) {
670 return seat_config;
671 }
672 }
673
674 return NULL;
675}
diff --git a/sway/input_state.c b/sway/input_state.c
deleted file mode 100644
index 04aafd37..00000000
--- a/sway/input_state.c
+++ /dev/null
@@ -1,490 +0,0 @@
1#include <string.h>
2#include <stdbool.h>
3#include <ctype.h>
4#include "sway/input_state.h"
5#include "sway/config.h"
6#include "log.h"
7
8#define KEY_STATE_MAX_LENGTH 64
9
10struct key_state {
11 /*
12 * Aims to store state regardless of modifiers.
13 * If you press a key, then hold shift, then release the key, we'll
14 * get two different key syms, but the same key code. This handles
15 * that scenario and makes sure we can use the right bindings.
16 */
17 uint32_t key_sym;
18 uint32_t alt_sym;
19 uint32_t key_code;
20};
21
22static struct key_state key_state_array[KEY_STATE_MAX_LENGTH];
23
24static struct key_state last_released;
25
26static uint32_t modifiers_state;
27
28void input_init(void) {
29 int i;
30 for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
31 struct key_state none = { 0, 0, 0 };
32 key_state_array[i] = none;
33 }
34
35 struct key_state none = { 0, 0, 0 };
36 last_released = none;
37
38 modifiers_state = 0;
39}
40
41uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod) {
42 if ((new_state & mod) != 0) { // pressed
43 if ((modifiers_state & mod) != 0) { // already pressed
44 return MOD_STATE_UNCHANGED;
45 } else { // pressed
46 return MOD_STATE_PRESSED;
47 }
48 } else { // not pressed
49 if ((modifiers_state & mod) != 0) { // released
50 return MOD_STATE_RELEASED;
51 } else { // already released
52 return MOD_STATE_UNCHANGED;
53 }
54 }
55}
56
57void modifiers_state_update(uint32_t new_state) {
58 modifiers_state = new_state;
59}
60
61static uint8_t find_key(uint32_t key_sym, uint32_t key_code, bool update) {
62 int i;
63 for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
64 if (0 == key_sym && 0 == key_code && key_state_array[i].key_sym == 0) {
65 break;
66 }
67 if (key_sym != 0 && (key_state_array[i].key_sym == key_sym
68 || key_state_array[i].alt_sym == key_sym)) {
69 break;
70 }
71 if (update && key_state_array[i].key_code == key_code) {
72 key_state_array[i].alt_sym = key_sym;
73 break;
74 }
75 if (key_sym == 0 && key_code != 0 && key_state_array[i].key_code == key_code) {
76 break;
77 }
78 }
79 return i;
80}
81
82bool check_key(uint32_t key_sym, uint32_t key_code) {
83 return find_key(key_sym, key_code, false) < KEY_STATE_MAX_LENGTH;
84}
85
86bool check_released_key(uint32_t key_sym) {
87 return (key_sym != 0
88 && (last_released.key_sym == key_sym
89 || last_released.alt_sym == key_sym));
90}
91
92void press_key(uint32_t key_sym, uint32_t key_code) {
93 if (key_code == 0) {
94 return;
95 }
96 // Check if key exists
97 if (!check_key(key_sym, key_code)) {
98 // Check that we don't exceed buffer length
99 int insert = find_key(0, 0, true);
100 if (insert < KEY_STATE_MAX_LENGTH) {
101 key_state_array[insert].key_sym = key_sym;
102 key_state_array[insert].key_code = key_code;
103 }
104 }
105}
106
107void release_key(uint32_t key_sym, uint32_t key_code) {
108 uint8_t index = find_key(key_sym, key_code, true);
109 if (index < KEY_STATE_MAX_LENGTH) {
110 last_released.key_sym = key_state_array[index].key_sym;
111 last_released.alt_sym = key_state_array[index].alt_sym;
112 last_released.key_code = key_state_array[index].key_code;
113 struct key_state none = { 0, 0, 0 };
114 key_state_array[index] = none;
115 }
116}
117
118// Pointer state and mode
119
120struct pointer_state pointer_state;
121
122static struct mode_state {
123 // initial view state
124 double x, y, w, h;
125 swayc_t *ptr;
126 // Containers used for resizing horizontally
127 struct {
128 double w;
129 swayc_t *ptr;
130 struct {
131 double w;
132 swayc_t *ptr;
133 } parent;
134 } horiz;
135 // Containers used for resizing vertically
136 struct {
137 double h;
138 swayc_t *ptr;
139 struct {
140 double h;
141 swayc_t *ptr;
142 } parent;
143 } vert;
144} initial;
145
146static struct {
147 bool left;
148 bool top;
149} lock;
150
151// initial set/unset
152
153static void set_initial_view(swayc_t *view) {
154 initial.ptr = view;
155 initial.x = view->x;
156 initial.y = view->y;
157 initial.w = view->width;
158 initial.h = view->height;
159}
160
161static void set_initial_sibling(void) {
162 bool reset = true;
163 swayc_t *ws = swayc_active_workspace_for(initial.ptr);
164 if ((initial.horiz.ptr = get_swayc_in_direction_under(initial.ptr,
165 lock.left ? MOVE_RIGHT: MOVE_LEFT, ws))) {
166 initial.horiz.w = initial.horiz.ptr->width;
167 initial.horiz.parent.ptr = get_swayc_in_direction_under(initial.horiz.ptr,
168 lock.left ? MOVE_LEFT : MOVE_RIGHT, ws);
169 initial.horiz.parent.w = initial.horiz.parent.ptr->width;
170 reset = false;
171 }
172 if ((initial.vert.ptr = get_swayc_in_direction_under(initial.ptr,
173 lock.top ? MOVE_DOWN: MOVE_UP, ws))) {
174 initial.vert.h = initial.vert.ptr->height;
175 initial.vert.parent.ptr = get_swayc_in_direction_under(initial.vert.ptr,
176 lock.top ? MOVE_UP : MOVE_DOWN, ws);
177 initial.vert.parent.h = initial.vert.parent.ptr->height;
178 reset = false;
179 }
180 // If nothing will change just undo the mode
181 if (reset) {
182 pointer_state.mode = 0;
183 }
184}
185
186static void reset_initial_view(void) {
187 initial.ptr->x = initial.x;
188 initial.ptr->y = initial.y;
189 initial.ptr->width = initial.w;
190 initial.ptr->height = initial.h;
191 arrange_windows(initial.ptr, -1, -1);
192 pointer_state.mode = 0;
193}
194
195static void reset_initial_sibling(void) {
196 initial.horiz.ptr->width = initial.horiz.w;
197 initial.horiz.parent.ptr->width = initial.horiz.parent.w;
198 initial.vert.ptr->height = initial.vert.h;
199 initial.vert.parent.ptr->height = initial.vert.parent.h;
200 arrange_windows(initial.horiz.ptr->parent, -1, -1);
201 arrange_windows(initial.vert.ptr->parent, -1, -1);
202 pointer_state.mode = 0;
203}
204
205void pointer_position_set(double new_x, double new_y, bool force_focus) {
206 double x, y;
207 wlc_pointer_get_position_v2(&x, &y);
208 pointer_state.delta.x = new_x - x;
209 pointer_state.delta.y = new_y - y;
210
211 wlc_pointer_set_position_v2(new_x, new_y);
212
213 // Update view under pointer
214 swayc_t *prev_view = pointer_state.view;
215 pointer_state.view = container_under_pointer();
216
217 // If pointer is in a mode, update it
218 if (pointer_state.mode) {
219 pointer_mode_update();
220 // Otherwise change focus if config is set
221 } else if (force_focus || (prev_view != pointer_state.view && config->focus_follows_mouse)) {
222 if (pointer_state.view && pointer_state.view->type == C_VIEW) {
223 set_focused_container(pointer_state.view);
224 }
225 }
226}
227
228void center_pointer_on(swayc_t *view) {
229 pointer_position_set(view->x + view->width/2, view->y + view->height/2, true);
230}
231
232// Mode set left/right click
233
234static void pointer_mode_set_dragging(void) {
235 switch (config->dragging_key) {
236 case M_LEFT_CLICK:
237 set_initial_view(pointer_state.left.view);
238 break;
239 case M_RIGHT_CLICK:
240 set_initial_view(pointer_state.right.view);
241 break;
242 }
243
244 if (initial.ptr->is_floating) {
245 pointer_state.mode = M_DRAGGING | M_FLOATING;
246 } else {
247 pointer_state.mode = M_DRAGGING | M_TILING;
248 // unset mode if we can't drag tile
249 if (initial.ptr->parent->type == C_WORKSPACE &&
250 initial.ptr->parent->children->length == 1) {
251 pointer_state.mode = 0;
252 }
253 }
254}
255
256static void pointer_mode_set_resizing(void) {
257 switch (config->resizing_key) {
258 case M_LEFT_CLICK:
259 set_initial_view(pointer_state.left.view);
260 break;
261 case M_RIGHT_CLICK:
262 set_initial_view(pointer_state.right.view);
263 break;
264 }
265 // Setup locking information
266 int midway_x = initial.ptr->x + initial.ptr->width/2;
267 int midway_y = initial.ptr->y + initial.ptr->height/2;
268
269 double x, y;
270 wlc_pointer_get_position_v2(&x, &y);
271 lock.left = x > midway_x;
272 lock.top = y > midway_y;
273
274 if (initial.ptr->is_floating) {
275 pointer_state.mode = M_RESIZING | M_FLOATING;
276 } else {
277 pointer_state.mode = M_RESIZING | M_TILING;
278 set_initial_sibling();
279 }
280}
281
282// Mode set/update/reset
283
284void pointer_mode_set(uint32_t button, bool condition) {
285 // switch on drag/resize mode
286 switch (pointer_state.mode & (M_DRAGGING | M_RESIZING)) {
287 case M_DRAGGING:
288 // end drag mode when 'dragging' click is unpressed
289 if (config->dragging_key == M_LEFT_CLICK && !pointer_state.left.held) {
290 pointer_state.mode = 0;
291 } else if (config->dragging_key == M_RIGHT_CLICK && !pointer_state.right.held) {
292 pointer_state.mode = 0;
293 }
294 break;
295
296 case M_RESIZING:
297 // end resize mode when 'resizing' click is unpressed
298 if (config->resizing_key == M_LEFT_CLICK && !pointer_state.left.held) {
299 pointer_state.mode = 0;
300 } else if (config->resizing_key == M_RIGHT_CLICK && !pointer_state.right.held) {
301 pointer_state.mode = 0;
302 }
303 break;
304
305 // No mode case
306 default:
307 // return if failed condition, or no view
308 if (!condition || !pointer_state.view) {
309 break;
310 }
311
312 // Set mode depending on current button press
313 switch (button) {
314 // Start left-click mode
315 case M_LEFT_CLICK:
316 // if button release don't do anything
317 if (pointer_state.left.held) {
318 if (config->dragging_key == M_LEFT_CLICK) {
319 pointer_mode_set_dragging();
320 } else if (config->resizing_key == M_LEFT_CLICK) {
321 pointer_mode_set_resizing();
322 }
323 }
324 break;
325
326 // Start right-click mode
327 case M_RIGHT_CLICK:
328 // if button release don't do anyhting
329 if (pointer_state.right.held) {
330 if (config->dragging_key == M_RIGHT_CLICK) {
331 pointer_mode_set_dragging();
332 } else if (config->resizing_key == M_RIGHT_CLICK) {
333 pointer_mode_set_resizing();
334 }
335 }
336 break;
337 }
338 }
339}
340
341void pointer_mode_update(void) {
342 if (initial.ptr->type != C_VIEW) {
343 pointer_state.mode = 0;
344 return;
345 }
346 double x, y;
347 wlc_pointer_get_position_v2(&x, &y);
348 int dx = x;
349 int dy = y;
350
351 switch (pointer_state.mode) {
352 case M_FLOATING | M_DRAGGING:
353 // Update position
354 switch (config->dragging_key) {
355 case M_LEFT_CLICK:
356 dx -= pointer_state.left.x;
357 dy -= pointer_state.left.y;
358 break;
359 case M_RIGHT_CLICK:
360 dx -= pointer_state.right.x;
361 dy -= pointer_state.right.y;
362 break;
363 }
364
365 if (initial.x + dx != initial.ptr->x) {
366 initial.ptr->x = initial.x + dx;
367 }
368 if (initial.y + dy != initial.ptr->y) {
369 initial.ptr->y = initial.y + dy;
370 }
371 update_geometry(initial.ptr);
372 break;
373
374 case M_FLOATING | M_RESIZING:
375 switch (config->resizing_key) {
376 case M_LEFT_CLICK:
377 dx -= pointer_state.left.x;
378 dy -= pointer_state.left.y;
379 initial.ptr = pointer_state.left.view;
380 break;
381 case M_RIGHT_CLICK:
382 dx -= pointer_state.right.x;
383 dy -= pointer_state.right.y;
384 initial.ptr = pointer_state.right.view;
385 break;
386 }
387
388 if (lock.left) {
389 if (initial.w + dx > min_sane_w) {
390 initial.ptr->width = initial.w + dx;
391 }
392 } else { // lock.right
393 if (initial.w - dx > min_sane_w) {
394 initial.ptr->width = initial.w - dx;
395 initial.ptr->x = initial.x + dx;
396 }
397 }
398 if (lock.top) {
399 if (initial.h + dy > min_sane_h) {
400 initial.ptr->height = initial.h + dy;
401 }
402 } else { // lock.bottom
403 if (initial.h - dy > min_sane_h) {
404 initial.ptr->height = initial.h - dy;
405 initial.ptr->y = initial.y + dy;
406 }
407 }
408 update_geometry(initial.ptr);
409 break;
410
411 case M_TILING | M_DRAGGING:
412 // swap current view under pointer with dragged view
413 if (pointer_state.view && pointer_state.view->type == C_VIEW
414 && pointer_state.view != initial.ptr
415 && !pointer_state.view->is_floating) {
416 // Swap them around
417 swap_container(pointer_state.view, initial.ptr);
418 swap_geometry(pointer_state.view, initial.ptr);
419 update_geometry(pointer_state.view);
420 update_geometry(initial.ptr);
421 // Set focus back to initial view
422 set_focused_container(initial.ptr);
423 // Arrange the windows
424 arrange_windows(&root_container, -1, -1);
425 }
426 break;
427
428 case M_TILING | M_RESIZING:
429 switch (config->resizing_key) {
430 case M_LEFT_CLICK:
431 dx -= pointer_state.left.x;
432 dy -= pointer_state.left.y;
433 break;
434 case M_RIGHT_CLICK:
435 dx -= pointer_state.right.x;
436 dy -= pointer_state.right.y;
437 break;
438 }
439
440 // resize if we can
441 if (initial.horiz.ptr) {
442 if (lock.left) {
443 // Check whether its fine to resize
444 if (initial.w + dx > min_sane_w && initial.horiz.w - dx > min_sane_w) {
445 initial.horiz.ptr->width = initial.horiz.w - dx;
446 initial.horiz.parent.ptr->width = initial.horiz.parent.w + dx;
447 }
448 } else { // lock.right
449 if (initial.w - dx > min_sane_w && initial.horiz.w + dx > min_sane_w) {
450 initial.horiz.ptr->width = initial.horiz.w + dx;
451 initial.horiz.parent.ptr->width = initial.horiz.parent.w - dx;
452 }
453 }
454 arrange_windows(initial.horiz.ptr->parent, -1, -1);
455 }
456 if (initial.vert.ptr) {
457 if (lock.top) {
458 if (initial.h + dy > min_sane_h && initial.vert.h - dy > min_sane_h) {
459 initial.vert.ptr->height = initial.vert.h - dy;
460 initial.vert.parent.ptr->height = initial.vert.parent.h + dy;
461 }
462 } else { // lock.bottom
463 if (initial.h - dy > min_sane_h && initial.vert.h + dy > min_sane_h) {
464 initial.vert.ptr->height = initial.vert.h + dy;
465 initial.vert.parent.ptr->height = initial.vert.parent.h - dy;
466 }
467 }
468 arrange_windows(initial.vert.ptr->parent, -1, -1);
469 }
470 default:
471 return;
472 }
473}
474
475void pointer_mode_reset(void) {
476 switch (pointer_state.mode) {
477 case M_FLOATING | M_RESIZING:
478 case M_FLOATING | M_DRAGGING:
479 reset_initial_view();
480 break;
481
482 case M_TILING | M_RESIZING:
483 (void) reset_initial_sibling;
484 break;
485
486 case M_TILING | M_DRAGGING:
487 default:
488 break;
489 }
490}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 6ab63c75..6158fc29 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -1,247 +1,213 @@
1#include <json-c/json.h> 1#include <json-c/json.h>
2#include <stdio.h>
2#include <ctype.h> 3#include <ctype.h>
3#include <string.h> 4#include "log.h"
4#include <stdint.h>
5#include <libinput.h>
6#include "sway/container.h"
7#include "sway/input.h"
8#include "sway/ipc-json.h" 5#include "sway/ipc-json.h"
9#include "util.h" 6#include "sway/tree/container.h"
10 7#include "sway/output.h"
11static json_object *ipc_json_create_rect(swayc_t *c) { 8#include "sway/input/input-manager.h"
12 json_object *rect = json_object_new_object(); 9#include "sway/input/seat.h"
10#include <wlr/types/wlr_box.h>
11#include <wlr/types/wlr_output.h>
12#include "wlr-layer-shell-unstable-v1-protocol.h"
13
14static const char *ipc_json_layout_description(enum sway_container_layout l) {
15 switch (l) {
16 case L_VERT:
17 return "splitv";
18 case L_HORIZ:
19 return "splith";
20 case L_TABBED:
21 return "tabbed";
22 case L_STACKED:
23 return "stacked";
24 case L_FLOATING:
25 return "floating";
26 case L_NONE:
27 break;
28 }
29 return "none";
30}
13 31
14 json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x)); 32json_object *ipc_json_get_version() {
15 json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y)); 33 int major = 0, minor = 0, patch = 0;
34 json_object *version = json_object_new_object();
16 35
17 struct wlc_size size; 36 sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch);
18 if (c->type == C_OUTPUT) {
19 size = *wlc_output_get_resolution(c->handle);
20 } else {
21 size.w = c->width;
22 size.h = c->height;
23 }
24 37
25 json_object_object_add(rect, "width", json_object_new_int((int32_t)size.w)); 38 json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION));
26 json_object_object_add(rect, "height", json_object_new_int((int32_t)size.h)); 39 json_object_object_add(version, "variant", json_object_new_string("sway"));
40 json_object_object_add(version, "major", json_object_new_int(major));
41 json_object_object_add(version, "minor", json_object_new_int(minor));
42 json_object_object_add(version, "patch", json_object_new_int(patch));
27 43
28 return rect; 44 return version;
29} 45}
30 46
31static json_object *ipc_json_create_rect_from_geometry(struct wlc_geometry g) { 47static json_object *ipc_json_create_rect(struct sway_container *c) {
32 json_object *rect = json_object_new_object(); 48 json_object *rect = json_object_new_object();
33 49
34 json_object_object_add(rect, "x", json_object_new_int(g.origin.x)); 50 json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x));
35 json_object_object_add(rect, "y", json_object_new_int(g.origin.y)); 51 json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y));
36 json_object_object_add(rect, "width", json_object_new_int(g.size.w)); 52 json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width));
37 json_object_object_add(rect, "height", json_object_new_int(g.size.h)); 53 json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height));
38 54
39 return rect; 55 return rect;
40} 56}
41 57
42static const char *ipc_json_border_description(swayc_t *c) { 58static void ipc_json_describe_root(struct sway_container *root, json_object *object) {
43 const char *border; 59 json_object_object_add(object, "type", json_object_new_string("root"));
44 60 json_object_object_add(object, "layout", json_object_new_string("splith"));
45 switch (c->border_type) {
46 case B_PIXEL:
47 border = "1pixel";
48 break;
49
50 case B_NORMAL:
51 border = "normal";
52 break;
53
54 case B_NONE: // fallthrough
55 default:
56 border = "none";
57 break;
58 }
59
60 return border;
61} 61}
62 62
63static const char *ipc_json_layout_description(enum swayc_layouts l) { 63static const char *ipc_json_get_output_transform(enum wl_output_transform transform) {
64 const char *layout; 64 switch (transform) {
65 65 case WL_OUTPUT_TRANSFORM_NORMAL:
66 switch (l) { 66 return "normal";
67 case L_VERT: 67 case WL_OUTPUT_TRANSFORM_90:
68 layout = "splitv"; 68 return "90";
69 break; 69 case WL_OUTPUT_TRANSFORM_180:
70 70 return "180";
71 case L_HORIZ: 71 case WL_OUTPUT_TRANSFORM_270:
72 layout = "splith"; 72 return "270";
73 break; 73 case WL_OUTPUT_TRANSFORM_FLIPPED:
74 74 return "flipped";
75 case L_TABBED: 75 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
76 layout = "tabbed"; 76 return "flipped-90";
77 break; 77 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
78 78 return "flipped-180";
79 case L_STACKED: 79 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
80 layout = "stacked"; 80 return "flipped-270";
81 break;
82
83 case L_FLOATING:
84 layout = "floating";
85 break;
86
87 case L_NONE: // fallthrough
88 case L_LAYOUTS: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
89 default:
90 layout = "null";
91 break;
92 } 81 }
93 82 return NULL;
94 return layout;
95} 83}
96 84
97static float ipc_json_child_percentage(swayc_t *c) { 85static void ipc_json_describe_output(struct sway_container *container, json_object *object) {
98 float percent = 0; 86 struct wlr_output *wlr_output = container->sway_output->wlr_output;
99 swayc_t *parent = c->parent; 87 json_object_object_add(object, "type",
100 88 json_object_new_string("output"));
101 if (parent) { 89 json_object_object_add(object, "active",
102 switch (parent->layout) { 90 json_object_new_boolean(true));
103 case L_VERT: 91 json_object_object_add(object, "primary",
104 percent = c->height / parent->height; 92 json_object_new_boolean(false));
105 break; 93 json_object_object_add(object, "layout",
106 94 json_object_new_string("output"));
107 case L_HORIZ: 95 json_object_object_add(object, "make",
108 percent = c->width / parent->width; 96 json_object_new_string(wlr_output->make));
109 break; 97 json_object_object_add(object, "model",
110 98 json_object_new_string(wlr_output->model));
111 case L_STACKED: // fallthrough 99 json_object_object_add(object, "serial",
112 case L_TABBED: // fallthrough 100 json_object_new_string(wlr_output->serial));
113 percent = 1.0; 101 json_object_object_add(object, "scale",
114 break; 102 json_object_new_double(wlr_output->scale));
115 103 json_object_object_add(object, "refresh",
116 default: 104 json_object_new_int(wlr_output->refresh));
117 break; 105 json_object_object_add(object, "transform",
106 json_object_new_string(
107 ipc_json_get_output_transform(wlr_output->transform)));
108
109 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
110 const char *ws = NULL;
111 if (seat) {
112 struct sway_container *focus =
113 seat_get_focus_inactive(seat, container);
114 if (focus && focus->type != C_WORKSPACE) {
115 focus = container_parent(focus, C_WORKSPACE);
116 }
117 if (focus) {
118 ws = focus->name;
118 } 119 }
119 } 120 }
121 json_object_object_add(object, "current_workspace",
122 json_object_new_string(ws));
123
124 json_object *modes_array = json_object_new_array();
125 struct wlr_output_mode *mode;
126 wl_list_for_each(mode, &wlr_output->modes, link) {
127 json_object *mode_object = json_object_new_object();
128 json_object_object_add(mode_object, "width",
129 json_object_new_int(mode->width));
130 json_object_object_add(mode_object, "height",
131 json_object_new_int(mode->height));
132 json_object_object_add(mode_object, "refresh",
133 json_object_new_int(mode->refresh));
134 json_object_array_add(modes_array, mode_object);
135 }
120 136
121 return percent; 137 json_object_object_add(object, "modes", modes_array);
122}
123
124static void ipc_json_describe_output(swayc_t *output, json_object *object) {
125 uint32_t scale = wlc_output_get_scale(output->handle);
126 json_object_object_add(object, "active", json_object_new_boolean(true));
127 json_object_object_add(object, "primary", json_object_new_boolean(false));
128 json_object_object_add(object, "layout", json_object_new_string("output")); 138 json_object_object_add(object, "layout", json_object_new_string("output"));
129 json_object_object_add(object, "type", json_object_new_string("output"));
130 json_object_object_add(object, "current_workspace",
131 (output->focused) ? json_object_new_string(output->focused->name) : NULL);
132 json_object_object_add(object, "scale", json_object_new_int(scale));
133} 139}
134 140
135static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) { 141static void ipc_json_describe_workspace(struct sway_container *workspace,
136 int num = (isdigit(workspace->name[0])) ? atoi(workspace->name) : -1; 142 json_object *object) {
137 const char *layout = ipc_json_layout_description(workspace->workspace_layout); 143 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1;
138 144
139 json_object_object_add(object, "num", json_object_new_int(num)); 145 json_object_object_add(object, "num", json_object_new_int(num));
140 json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL); 146 json_object_object_add(object, "output", workspace->parent ?
147 json_object_new_string(workspace->parent->name) : NULL);
141 json_object_object_add(object, "type", json_object_new_string("workspace")); 148 json_object_object_add(object, "type", json_object_new_string("workspace"));
142 json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); 149 json_object_object_add(object, "urgent", json_object_new_boolean(false));
143}
144 150
145// window is in the scratchpad ? changed : none 151 const char *layout = ipc_json_layout_description(workspace->workspace_layout);
146static const char *ipc_json_get_scratchpad_state(swayc_t *c) { 152 json_object_object_add(object, "layout", json_object_new_string(layout));
147 int i;
148 for (i = 0; i < scratchpad->length; i++) {
149 if (scratchpad->items[i] == c) {
150 return "changed";
151 }
152 }
153 return "none"; // we ignore the fresh value
154} 153}
155 154
156static void ipc_json_describe_view(swayc_t *c, json_object *object) { 155static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
157 json_object *props = json_object_new_object(); 156 json_object_object_add(object, "name",
158 json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con")); 157 c->name ? json_object_new_string(c->name) : NULL);
159 158 json_object_object_add(object, "type", json_object_new_string("con"));
160 wlc_handle parent = wlc_view_get_parent(c->handle);
161 json_object_object_add(object, "scratchpad_state",
162 json_object_new_string(ipc_json_get_scratchpad_state(c)));
163
164 json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
165
166 json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) :
167 c->app_id ? json_object_new_string(c->app_id) : NULL);
168 json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) :
169 c->app_id ? json_object_new_string(c->app_id) : NULL);
170 json_object_object_add(props, "title", (c->name) ? json_object_new_string(c->name) : NULL);
171 json_object_object_add(props, "transient_for", parent ? json_object_new_int(parent) : NULL);
172 json_object_object_add(object, "window_properties", props);
173
174 json_object_object_add(object, "fullscreen_mode",
175 json_object_new_int(swayc_is_fullscreen(c) ? 1 : 0));
176 json_object_object_add(object, "sticky", json_object_new_boolean(c->sticky));
177 json_object_object_add(object, "floating", json_object_new_string(
178 c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause
179
180 json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL);
181 159
182 if (c->parent) { 160 if (c->parent) {
183 const char *layout = (c->parent->type == C_CONTAINER) ? 161 enum sway_container_layout layout = (c->parent->type == C_CONTAINER) ?
184 ipc_json_layout_description(c->parent->layout) : "none"; 162 c->parent->layout : c->layout;
185 const char *last_layout = (c->parent->type == C_CONTAINER) ? 163
186 ipc_json_layout_description(c->parent->prev_layout) : "none";
187 json_object_object_add(object, "layout", 164 json_object_object_add(object, "layout",
188 (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); 165 json_object_new_string(ipc_json_layout_description(layout)));
189 json_object_object_add(object, "last_split_layout",
190 (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout));
191 json_object_object_add(object, "workspace_layout",
192 json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout)));
193 } 166 }
194} 167}
195 168
196static void ipc_json_describe_root(swayc_t *c, json_object *object) { 169static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
197 json_object_object_add(object, "type", json_object_new_string("root")); 170 json_object *focus = data;
198 json_object_object_add(object, "layout", json_object_new_string("splith")); 171 json_object_array_add(focus, json_object_new_int(c->id));
199} 172}
200 173
201json_object *ipc_json_describe_container(swayc_t *c) { 174json_object *ipc_json_describe_container(struct sway_container *c) {
202 float percent = ipc_json_child_percentage(c);
203
204 if (!(sway_assert(c, "Container must not be null."))) { 175 if (!(sway_assert(c, "Container must not be null."))) {
205 return NULL; 176 return NULL;
206 } 177 }
207 178
179 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
180 bool focused = seat_get_focus(seat) == c;
181
208 json_object *object = json_object_new_object(); 182 json_object *object = json_object_new_object();
209 183
210 json_object_object_add(object, "id", json_object_new_int((int)c->id)); 184 json_object_object_add(object, "id", json_object_new_int((int)c->id));
211 json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL); 185 json_object_object_add(object, "name",
186 c->name ? json_object_new_string(c->name) : NULL);
212 json_object_object_add(object, "rect", ipc_json_create_rect(c)); 187 json_object_object_add(object, "rect", ipc_json_create_rect(c));
213 json_object_object_add(object, "visible", json_object_new_boolean(c->visible)); 188 json_object_object_add(object, "focused",
214 json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus)); 189 json_object_new_boolean(focused));
215 190
216 json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c))); 191 json_object *focus = json_object_new_array();
217 json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry)); 192 seat_focus_inactive_children_for_each(seat, c,
218 json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry)); 193 focus_inactive_children_iterator, focus);
219 json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry)); 194 json_object_object_add(object, "focus", focus);
220 json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL);
221 json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat
222 // TODO: make urgency actually work once Sway supports it
223 json_object_object_add(object, "urgent", json_object_new_boolean(false));
224 json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness));
225 195
226 switch (c->type) { 196 switch (c->type) {
227 case C_ROOT: 197 case C_ROOT:
228 ipc_json_describe_root(c, object); 198 ipc_json_describe_root(c, object);
229 break; 199 break;
230
231 case C_OUTPUT: 200 case C_OUTPUT:
232 ipc_json_describe_output(c, object); 201 ipc_json_describe_output(c, object);
233 break; 202 break;
234 203 case C_CONTAINER:
235 case C_CONTAINER: // fallthrough
236 case C_VIEW: 204 case C_VIEW:
237 ipc_json_describe_view(c, object); 205 ipc_json_describe_view(c, object);
238 break; 206 break;
239
240 case C_WORKSPACE: 207 case C_WORKSPACE:
241 ipc_json_describe_workspace(c, object); 208 ipc_json_describe_workspace(c, object);
242 break; 209 break;
243 210 case C_TYPES:
244 case C_TYPES: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
245 default: 211 default:
246 break; 212 break;
247 } 213 }
@@ -249,80 +215,56 @@ json_object *ipc_json_describe_container(swayc_t *c) {
249 return object; 215 return object;
250} 216}
251 217
252json_object *ipc_json_describe_input(struct libinput_device *device) { 218json_object *ipc_json_describe_container_recursive(struct sway_container *c) {
253 char* identifier = libinput_dev_unique_id(device); 219 json_object *object = ipc_json_describe_container(c);
254 int vendor = libinput_device_get_id_vendor(device); 220 int i;
255 int product = libinput_device_get_id_product(device);
256 const char *name = libinput_device_get_name(device);
257 double width = -1, height = -1;
258 int has_size = libinput_device_get_size(device, &width, &height);
259
260 json_object *device_object = json_object_new_object();
261 json_object_object_add(device_object,"identifier",
262 identifier ? json_object_new_string(identifier) : NULL);
263 json_object_object_add(device_object,
264 "vendor", json_object_new_int(vendor));
265 json_object_object_add(device_object,
266 "product", json_object_new_int(product));
267 json_object_object_add(device_object,
268 "name", json_object_new_string(name));
269 if (has_size == 0) {
270 json_object *size_object = json_object_new_object();
271 json_object_object_add(size_object,
272 "width", json_object_new_double(width));
273 json_object_object_add(size_object,
274 "height", json_object_new_double(height));
275 } else {
276 json_object_object_add(device_object, "size", NULL);
277 }
278 221
279 struct { 222 json_object *children = json_object_new_array();
280 enum libinput_device_capability cap; 223 if (c->type != C_VIEW && c->children) {
281 const char *name; 224 for (i = 0; i < c->children->length; ++i) {
282 // If anyone feels like implementing device-specific IPC output, 225 json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
283 // be my guest
284 json_object *(*describe)(struct libinput_device *);
285 } caps[] = {
286 { LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard", NULL },
287 { LIBINPUT_DEVICE_CAP_POINTER, "pointer", NULL },
288 { LIBINPUT_DEVICE_CAP_TOUCH, "touch", NULL },
289 { LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet_tool", NULL },
290 { LIBINPUT_DEVICE_CAP_TABLET_PAD, "tablet_pad", NULL },
291 { LIBINPUT_DEVICE_CAP_GESTURE, "gesture", NULL },
292#ifdef LIBINPUT_DEVICE_CAP_SWITCH // libinput 1.7.0+
293 { LIBINPUT_DEVICE_CAP_SWITCH, "switch", NULL },
294#endif
295 };
296
297 json_object *_caps = json_object_new_array();
298 for (size_t i = 0; i < sizeof(caps) / sizeof(caps[0]); ++i) {
299 if (libinput_device_has_capability(device, caps[i].cap)) {
300 json_object_array_add(_caps, json_object_new_string(caps[i].name));
301 if (caps[i].describe) {
302 json_object *desc = caps[i].describe(device);
303 json_object_object_add(device_object, caps[i].name, desc);
304 }
305 } 226 }
306 } 227 }
307 json_object_object_add(device_object, "capabilities", _caps); 228 json_object_object_add(object, "nodes", children);
308 229
309 free(identifier); 230 return object;
310 return device_object;
311} 231}
312 232
313json_object *ipc_json_get_version() { 233static const char *describe_device_type(struct sway_input_device *device) {
314 int major = 0, minor = 0, patch = 0; 234 switch (device->wlr_device->type) {
315 json_object *version = json_object_new_object(); 235 case WLR_INPUT_DEVICE_POINTER:
236 return "pointer";
237 case WLR_INPUT_DEVICE_KEYBOARD:
238 return "keyboard";
239 case WLR_INPUT_DEVICE_TOUCH:
240 return "touch";
241 case WLR_INPUT_DEVICE_TABLET_TOOL:
242 return "tablet_tool";
243 case WLR_INPUT_DEVICE_TABLET_PAD:
244 return "tablet_pad";
245 }
246 return "unknown";
247}
316 248
317 sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch); 249json_object *ipc_json_describe_input(struct sway_input_device *device) {
250 if (!(sway_assert(device, "Device must not be null"))) {
251 return NULL;
252 }
318 253
319 json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION)); 254 json_object *object = json_object_new_object();
320 json_object_object_add(version, "variant", json_object_new_string("sway"));
321 json_object_object_add(version, "major", json_object_new_int(major));
322 json_object_object_add(version, "minor", json_object_new_int(minor));
323 json_object_object_add(version, "patch", json_object_new_int(patch));
324 255
325 return version; 256 json_object_object_add(object, "identifier",
257 json_object_new_string(device->identifier));
258 json_object_object_add(object, "name",
259 json_object_new_string(device->wlr_device->name));
260 json_object_object_add(object, "vendor",
261 json_object_new_int(device->wlr_device->vendor));
262 json_object_object_add(object, "product",
263 json_object_new_int(device->wlr_device->product));
264 json_object_object_add(object, "type",
265 json_object_new_string(describe_device_type(device)));
266
267 return object;
326} 268}
327 269
328json_object *ipc_json_describe_bar_config(struct bar_config *bar) { 270json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
@@ -332,107 +274,116 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
332 274
333 json_object *json = json_object_new_object(); 275 json_object *json = json_object_new_object();
334 json_object_object_add(json, "id", json_object_new_string(bar->id)); 276 json_object_object_add(json, "id", json_object_new_string(bar->id));
335#ifdef ENABLE_TRAY
336 if (bar->tray_output) {
337 json_object_object_add(json, "tray_output", json_object_new_string(bar->tray_output));
338 } else {
339 json_object_object_add(json, "tray_output", NULL);
340 }
341 if (bar->icon_theme) {
342 json_object_object_add(json, "icon_theme", json_object_new_string(bar->icon_theme));
343 } else {
344 json_object_object_add(json, "icon_theme", NULL);
345 }
346 json_object_object_add(json, "tray_padding", json_object_new_int(bar->tray_padding));
347 json_object_object_add(json, "activate_button", json_object_new_int(bar->activate_button));
348 json_object_object_add(json, "context_button", json_object_new_int(bar->context_button));
349 json_object_object_add(json, "secondary_button", json_object_new_int(bar->secondary_button));
350#endif
351 json_object_object_add(json, "mode", json_object_new_string(bar->mode)); 277 json_object_object_add(json, "mode", json_object_new_string(bar->mode));
352 json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state)); 278 json_object_object_add(json, "hidden_state",
353 json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier))); 279 json_object_new_string(bar->hidden_state));
354 switch (bar->position) { 280 json_object_object_add(json, "position",
355 case DESKTOP_SHELL_PANEL_POSITION_TOP: 281 json_object_new_string(bar->position));
356 json_object_object_add(json, "position", json_object_new_string("top")); 282 json_object_object_add(json, "status_command",
357 break; 283 json_object_new_string(bar->status_command));
358 case DESKTOP_SHELL_PANEL_POSITION_BOTTOM: 284 json_object_object_add(json, "font",
359 json_object_object_add(json, "position", json_object_new_string("bottom")); 285 json_object_new_string((bar->font) ? bar->font : config->font));
360 break;
361 case DESKTOP_SHELL_PANEL_POSITION_LEFT:
362 json_object_object_add(json, "position", json_object_new_string("left"));
363 break;
364 case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
365 json_object_object_add(json, "position", json_object_new_string("right"));
366 break;
367 }
368 json_object_object_add(json, "status_command", json_object_new_string(bar->status_command));
369 json_object_object_add(json, "font", json_object_new_string((bar->font) ? bar->font : config->font));
370 if (bar->separator_symbol) { 286 if (bar->separator_symbol) {
371 json_object_object_add(json, "separator_symbol", json_object_new_string(bar->separator_symbol)); 287 json_object_object_add(json, "separator_symbol",
288 json_object_new_string(bar->separator_symbol));
372 } 289 }
373 json_object_object_add(json, "bar_height", json_object_new_int(bar->height)); 290 json_object_object_add(json, "bar_height",
374 json_object_object_add(json, "wrap_scroll", json_object_new_boolean(bar->wrap_scroll)); 291 json_object_new_int(bar->height));
375 json_object_object_add(json, "workspace_buttons", json_object_new_boolean(bar->workspace_buttons)); 292 json_object_object_add(json, "wrap_scroll",
376 json_object_object_add(json, "strip_workspace_numbers", json_object_new_boolean(bar->strip_workspace_numbers)); 293 json_object_new_boolean(bar->wrap_scroll));
377 json_object_object_add(json, "binding_mode_indicator", json_object_new_boolean(bar->binding_mode_indicator)); 294 json_object_object_add(json, "workspace_buttons",
378 json_object_object_add(json, "verbose", json_object_new_boolean(bar->verbose)); 295 json_object_new_boolean(bar->workspace_buttons));
379 json_object_object_add(json, "pango_markup", json_object_new_boolean(bar->pango_markup)); 296 json_object_object_add(json, "strip_workspace_numbers",
297 json_object_new_boolean(bar->strip_workspace_numbers));
298 json_object_object_add(json, "binding_mode_indicator",
299 json_object_new_boolean(bar->binding_mode_indicator));
300 json_object_object_add(json, "verbose",
301 json_object_new_boolean(bar->verbose));
302 json_object_object_add(json, "pango_markup",
303 json_object_new_boolean(bar->pango_markup));
380 304
381 json_object *colors = json_object_new_object(); 305 json_object *colors = json_object_new_object();
382 json_object_object_add(colors, "background", json_object_new_string(bar->colors.background)); 306 json_object_object_add(colors, "background",
383 json_object_object_add(colors, "statusline", json_object_new_string(bar->colors.statusline)); 307 json_object_new_string(bar->colors.background));
384 json_object_object_add(colors, "separator", json_object_new_string(bar->colors.separator)); 308 json_object_object_add(colors, "statusline",
309 json_object_new_string(bar->colors.statusline));
310 json_object_object_add(colors, "separator",
311 json_object_new_string(bar->colors.separator));
385 312
386 if (bar->colors.focused_background) { 313 if (bar->colors.focused_background) {
387 json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.focused_background)); 314 json_object_object_add(colors, "focused_background",
315 json_object_new_string(bar->colors.focused_background));
388 } else { 316 } else {
389 json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.background)); 317 json_object_object_add(colors, "focused_background",
318 json_object_new_string(bar->colors.background));
390 } 319 }
391 320
392 if (bar->colors.focused_statusline) { 321 if (bar->colors.focused_statusline) {
393 json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.focused_statusline)); 322 json_object_object_add(colors, "focused_statusline",
323 json_object_new_string(bar->colors.focused_statusline));
394 } else { 324 } else {
395 json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.statusline)); 325 json_object_object_add(colors, "focused_statusline",
326 json_object_new_string(bar->colors.statusline));
396 } 327 }
397 328
398 if (bar->colors.focused_separator) { 329 if (bar->colors.focused_separator) {
399 json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.focused_separator)); 330 json_object_object_add(colors, "focused_separator",
331 json_object_new_string(bar->colors.focused_separator));
400 } else { 332 } else {
401 json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.separator)); 333 json_object_object_add(colors, "focused_separator",
334 json_object_new_string(bar->colors.separator));
402 } 335 }
403 336
404 json_object_object_add(colors, "focused_workspace_border", json_object_new_string(bar->colors.focused_workspace_border)); 337 json_object_object_add(colors, "focused_workspace_border",
405 json_object_object_add(colors, "focused_workspace_bg", json_object_new_string(bar->colors.focused_workspace_bg)); 338 json_object_new_string(bar->colors.focused_workspace_border));
406 json_object_object_add(colors, "focused_workspace_text", json_object_new_string(bar->colors.focused_workspace_text)); 339 json_object_object_add(colors, "focused_workspace_bg",
407 340 json_object_new_string(bar->colors.focused_workspace_bg));
408 json_object_object_add(colors, "inactive_workspace_border", json_object_new_string(bar->colors.inactive_workspace_border)); 341 json_object_object_add(colors, "focused_workspace_text",
409 json_object_object_add(colors, "inactive_workspace_bg", json_object_new_string(bar->colors.inactive_workspace_bg)); 342 json_object_new_string(bar->colors.focused_workspace_text));
410 json_object_object_add(colors, "inactive_workspace_text", json_object_new_string(bar->colors.inactive_workspace_text)); 343
411 344 json_object_object_add(colors, "inactive_workspace_border",
412 json_object_object_add(colors, "active_workspace_border", json_object_new_string(bar->colors.active_workspace_border)); 345 json_object_new_string(bar->colors.inactive_workspace_border));
413 json_object_object_add(colors, "active_workspace_bg", json_object_new_string(bar->colors.active_workspace_bg)); 346 json_object_object_add(colors, "inactive_workspace_bg",
414 json_object_object_add(colors, "active_workspace_text", json_object_new_string(bar->colors.active_workspace_text)); 347 json_object_new_string(bar->colors.inactive_workspace_bg));
415 348 json_object_object_add(colors, "inactive_workspace_text",
416 json_object_object_add(colors, "urgent_workspace_border", json_object_new_string(bar->colors.urgent_workspace_border)); 349 json_object_new_string(bar->colors.inactive_workspace_text));
417 json_object_object_add(colors, "urgent_workspace_bg", json_object_new_string(bar->colors.urgent_workspace_bg)); 350
418 json_object_object_add(colors, "urgent_workspace_text", json_object_new_string(bar->colors.urgent_workspace_text)); 351 json_object_object_add(colors, "active_workspace_border",
352 json_object_new_string(bar->colors.active_workspace_border));
353 json_object_object_add(colors, "active_workspace_bg",
354 json_object_new_string(bar->colors.active_workspace_bg));
355 json_object_object_add(colors, "active_workspace_text",
356 json_object_new_string(bar->colors.active_workspace_text));
357
358 json_object_object_add(colors, "urgent_workspace_border",
359 json_object_new_string(bar->colors.urgent_workspace_border));
360 json_object_object_add(colors, "urgent_workspace_bg",
361 json_object_new_string(bar->colors.urgent_workspace_bg));
362 json_object_object_add(colors, "urgent_workspace_text",
363 json_object_new_string(bar->colors.urgent_workspace_text));
419 364
420 if (bar->colors.binding_mode_border) { 365 if (bar->colors.binding_mode_border) {
421 json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.binding_mode_border)); 366 json_object_object_add(colors, "binding_mode_border",
367 json_object_new_string(bar->colors.binding_mode_border));
422 } else { 368 } else {
423 json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.urgent_workspace_border)); 369 json_object_object_add(colors, "binding_mode_border",
370 json_object_new_string(bar->colors.urgent_workspace_border));
424 } 371 }
425 372
426 if (bar->colors.binding_mode_bg) { 373 if (bar->colors.binding_mode_bg) {
427 json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.binding_mode_bg)); 374 json_object_object_add(colors, "binding_mode_bg",
375 json_object_new_string(bar->colors.binding_mode_bg));
428 } else { 376 } else {
429 json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.urgent_workspace_bg)); 377 json_object_object_add(colors, "binding_mode_bg",
378 json_object_new_string(bar->colors.urgent_workspace_bg));
430 } 379 }
431 380
432 if (bar->colors.binding_mode_text) { 381 if (bar->colors.binding_mode_text) {
433 json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.binding_mode_text)); 382 json_object_object_add(colors, "binding_mode_text",
383 json_object_new_string(bar->colors.binding_mode_text));
434 } else { 384 } else {
435 json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.urgent_workspace_text)); 385 json_object_object_add(colors, "binding_mode_text",
386 json_object_new_string(bar->colors.urgent_workspace_text));
436 } 387 }
437 388
438 json_object_object_add(json, "colors", colors); 389 json_object_object_add(json, "colors", colors);
@@ -440,75 +391,11 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
440 // Add outputs if defined 391 // Add outputs if defined
441 if (bar->outputs && bar->outputs->length > 0) { 392 if (bar->outputs && bar->outputs->length > 0) {
442 json_object *outputs = json_object_new_array(); 393 json_object *outputs = json_object_new_array();
443 int i; 394 for (int i = 0; i < bar->outputs->length; ++i) {
444 for (i = 0; i < bar->outputs->length; ++i) {
445 const char *name = bar->outputs->items[i]; 395 const char *name = bar->outputs->items[i];
446 json_object_array_add(outputs, json_object_new_string(name)); 396 json_object_array_add(outputs, json_object_new_string(name));
447 } 397 }
448 json_object_object_add(json, "outputs", outputs); 398 json_object_object_add(json, "outputs", outputs);
449 } 399 }
450
451 return json; 400 return json;
452} 401}
453
454json_object *ipc_json_describe_container_recursive(swayc_t *c) {
455 json_object *object = ipc_json_describe_container(c);
456 int i;
457
458 json_object *floating = json_object_new_array();
459 if (c->type != C_VIEW && c->floating) {
460 for (i = 0; i < c->floating->length; ++i) {
461 swayc_t *item = c->floating->items[i];
462 json_object_array_add(floating, ipc_json_describe_container_recursive(item));
463 }
464 }
465 json_object_object_add(object, "floating_nodes", floating);
466
467 json_object *children = json_object_new_array();
468 if (c->type != C_VIEW && c->children) {
469 for (i = 0; i < c->children->length; ++i) {
470 json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
471 }
472 }
473 json_object_object_add(object, "nodes", children);
474
475 json_object *focus = json_object_new_array();
476 if (c->type != C_VIEW) {
477 if (c->focused) {
478 json_object_array_add(focus, json_object_new_double(c->focused->id));
479 }
480 if (c->floating) {
481 for (i = 0; i < c->floating->length; ++i) {
482 swayc_t *item = c->floating->items[i];
483 if (item == c->focused) {
484 continue;
485 }
486
487 json_object_array_add(focus, json_object_new_double(item->id));
488 }
489 }
490 if (c->children) {
491 for (i = 0; i < c->children->length; ++i) {
492 swayc_t *item = c->children->items[i];
493 if (item == c->focused) {
494 continue;
495 }
496
497 json_object_array_add(focus, json_object_new_double(item->id));
498 }
499 }
500 }
501 json_object_object_add(object, "focus", focus);
502
503 if (c->type == C_ROOT) {
504 json_object *scratchpad_json = json_object_new_array();
505 if (scratchpad->length > 0) {
506 for (i = 0; i < scratchpad->length; ++i) {
507 json_object_array_add(scratchpad_json, ipc_json_describe_container_recursive(scratchpad->items[i]));
508 }
509 }
510 json_object_object_add(object, "scratchpad", scratchpad_json);
511 }
512
513 return object;
514}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index b560b930..045802e1 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,51 +1,41 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2
3#ifndef __FreeBSD__ 2#ifndef __FreeBSD__
4// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) 3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
5#define _XOPEN_SOURCE 700 4#define _XOPEN_SOURCE 700
6#endif 5#endif
7 6#include <assert.h>
8#include <errno.h> 7#include <errno.h>
8#include <fcntl.h>
9#include <json-c/json.h>
10#include <stdbool.h>
11#include <stdint.h>
12#include <stdlib.h>
9#include <string.h> 13#include <string.h>
10#include <sys/socket.h> 14#include <sys/socket.h>
15#include <sys/ioctl.h>
11#include <sys/un.h> 16#include <sys/un.h>
12#include <stdbool.h>
13#include <wlc/wlc-render.h>
14#include <unistd.h> 17#include <unistd.h>
15#include <stdlib.h> 18#include <wayland-server.h>
16#include <sys/ioctl.h> 19#include "sway/commands.h"
17#include <fcntl.h>
18#include <json-c/json.h>
19#include <list.h>
20#include <libinput.h>
21#ifdef __linux__
22struct ucred {
23 pid_t pid;
24 uid_t uid;
25 gid_t gid;
26};
27#endif
28#include "sway/ipc-json.h" 20#include "sway/ipc-json.h"
29#include "sway/ipc-server.h" 21#include "sway/ipc-server.h"
30#include "sway/security.h" 22#include "sway/server.h"
31#include "sway/config.h" 23#include "sway/input/input-manager.h"
32#include "sway/commands.h" 24#include "sway/input/seat.h"
33#include "sway/input.h"
34#include "stringop.h"
35#include "log.h"
36#include "list.h" 25#include "list.h"
37#include "util.h" 26#include "log.h"
38 27
39static int ipc_socket = -1; 28static int ipc_socket = -1;
40static struct wlc_event_source *ipc_event_source = NULL; 29static struct wl_event_source *ipc_event_source = NULL;
41static struct sockaddr_un *ipc_sockaddr = NULL; 30static struct sockaddr_un *ipc_sockaddr = NULL;
42static list_t *ipc_client_list = NULL; 31static list_t *ipc_client_list = NULL;
43 32
44static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; 33static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
45 34
46struct ipc_client { 35struct ipc_client {
47 struct wlc_event_source *event_source; 36 struct wl_event_source *event_source;
48 struct wlc_event_source *writable_event_source; 37 struct wl_event_source *writable_event_source;
38 struct sway_server *server;
49 int fd; 39 int fd;
50 uint32_t payload_length; 40 uint32_t payload_length;
51 uint32_t security_policy; 41 uint32_t security_policy;
@@ -56,27 +46,6 @@ struct ipc_client {
56 char *write_buffer; 46 char *write_buffer;
57}; 47};
58 48
59static list_t *ipc_get_pixel_requests = NULL;
60
61struct get_pixels_request {
62 struct ipc_client *client;
63 wlc_handle output;
64 struct wlc_geometry geo;
65};
66
67struct get_clipboard_request {
68 struct ipc_client *client;
69 json_object *json;
70 int fd;
71 struct wlc_event_source *fd_event_source;
72 struct wlc_event_source *timer_event_source;
73 char *type;
74 unsigned int *pending;
75 char *buf;
76 size_t buf_size;
77 size_t buf_position;
78};
79
80struct sockaddr_un *ipc_user_sockaddr(void); 49struct sockaddr_un *ipc_user_sockaddr(void);
81int ipc_handle_connection(int fd, uint32_t mask, void *data); 50int ipc_handle_connection(int fd, uint32_t mask, void *data);
82int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); 51int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
@@ -84,11 +53,8 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
84void ipc_client_disconnect(struct ipc_client *client); 53void ipc_client_disconnect(struct ipc_client *client);
85void ipc_client_handle_command(struct ipc_client *client); 54void ipc_client_handle_command(struct ipc_client *client);
86bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); 55bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
87void ipc_get_workspaces_callback(swayc_t *workspace, void *data);
88void ipc_get_outputs_callback(swayc_t *container, void *data);
89static void ipc_get_marks_callback(swayc_t *container, void *data);
90 56
91void ipc_init(void) { 57void ipc_init(struct sway_server *server) {
92 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 58 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
93 if (ipc_socket == -1) { 59 if (ipc_socket == -1) {
94 sway_abort("Unable to create IPC socket"); 60 sway_abort("Unable to create IPC socket");
@@ -116,14 +82,14 @@ void ipc_init(void) {
116 setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1); 82 setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1);
117 83
118 ipc_client_list = create_list(); 84 ipc_client_list = create_list();
119 ipc_get_pixel_requests = create_list();
120 85
121 ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); 86 ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket,
87 WL_EVENT_READABLE, ipc_handle_connection, server);
122} 88}
123 89
124void ipc_terminate(void) { 90void ipc_terminate(void) {
125 if (ipc_event_source) { 91 if (ipc_event_source) {
126 wlc_event_source_remove(ipc_event_source); 92 wl_event_source_remove(ipc_event_source);
127 } 93 }
128 close(ipc_socket); 94 close(ipc_socket);
129 unlink(ipc_sockaddr->sun_path); 95 unlink(ipc_sockaddr->sun_path);
@@ -157,100 +123,82 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
157 return ipc_sockaddr; 123 return ipc_sockaddr;
158} 124}
159 125
160static pid_t get_client_pid(int client_fd) {
161// FreeBSD supports getting uid/gid, but not pid
162#ifdef __linux__
163 struct ucred ucred;
164 socklen_t len = sizeof(struct ucred);
165
166 if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
167 return -1;
168 }
169
170 return ucred.pid;
171#else
172 return -1;
173#endif
174}
175
176int ipc_handle_connection(int fd, uint32_t mask, void *data) { 126int ipc_handle_connection(int fd, uint32_t mask, void *data) {
177 (void) fd; (void) data; 127 (void) fd;
178 sway_log(L_DEBUG, "Event on IPC listening socket"); 128 struct sway_server *server = data;
179 assert(mask == WLC_EVENT_READABLE); 129 wlr_log(L_DEBUG, "Event on IPC listening socket");
130 assert(mask == WL_EVENT_READABLE);
180 131
181 int client_fd = accept(ipc_socket, NULL, NULL); 132 int client_fd = accept(ipc_socket, NULL, NULL);
182 if (client_fd == -1) { 133 if (client_fd == -1) {
183 sway_log_errno(L_ERROR, "Unable to accept IPC client connection"); 134 wlr_log_errno(L_ERROR, "Unable to accept IPC client connection");
184 return 0; 135 return 0;
185 } 136 }
186 137
187 int flags; 138 int flags;
188 if ((flags = fcntl(client_fd, F_GETFD)) == -1 139 if ((flags = fcntl(client_fd, F_GETFD)) == -1
189 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { 140 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
190 sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); 141 wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
191 close(client_fd); 142 close(client_fd);
192 return 0; 143 return 0;
193 } 144 }
194 if ((flags = fcntl(client_fd, F_GETFL)) == -1 145 if ((flags = fcntl(client_fd, F_GETFL)) == -1
195 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { 146 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
196 sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); 147 wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
197 close(client_fd); 148 close(client_fd);
198 return 0; 149 return 0;
199 } 150 }
200 151
201 struct ipc_client* client = malloc(sizeof(struct ipc_client)); 152 struct ipc_client *client = malloc(sizeof(struct ipc_client));
202 if (!client) { 153 if (!client) {
203 sway_log(L_ERROR, "Unable to allocate ipc client"); 154 wlr_log(L_ERROR, "Unable to allocate ipc client");
204 close(client_fd); 155 close(client_fd);
205 return 0; 156 return 0;
206 } 157 }
158 client->server = server;
207 client->payload_length = 0; 159 client->payload_length = 0;
208 client->fd = client_fd; 160 client->fd = client_fd;
209 client->subscribed_events = 0; 161 client->subscribed_events = 0;
210 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); 162 client->event_source = wl_event_loop_add_fd(server->wl_event_loop,
163 client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client);
211 client->writable_event_source = NULL; 164 client->writable_event_source = NULL;
212 165
213 client->write_buffer_size = 128; 166 client->write_buffer_size = 128;
214 client->write_buffer_len = 0; 167 client->write_buffer_len = 0;
215 client->write_buffer = malloc(client->write_buffer_size); 168 client->write_buffer = malloc(client->write_buffer_size);
216 if (!client->write_buffer) { 169 if (!client->write_buffer) {
217 sway_log(L_ERROR, "Unable to allocate ipc client write buffer"); 170 wlr_log(L_ERROR, "Unable to allocate ipc client write buffer");
218 close(client_fd); 171 close(client_fd);
219 return 0; 172 return 0;
220 } 173 }
221 174
222 pid_t pid = get_client_pid(client->fd); 175 wlr_log(L_DEBUG, "New client: fd %d", client_fd);
223 client->security_policy = get_ipc_policy_mask(pid);
224
225 sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid);
226
227 list_add(ipc_client_list, client); 176 list_add(ipc_client_list, client);
228
229 return 0; 177 return 0;
230} 178}
231 179
232static const int ipc_header_size = sizeof(ipc_magic)+8; 180static const int ipc_header_size = sizeof(ipc_magic) + 8;
233 181
234int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { 182int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
235 struct ipc_client *client = data; 183 struct ipc_client *client = data;
236 184
237 if (mask & WLC_EVENT_ERROR) { 185 if (mask & WL_EVENT_ERROR) {
238 sway_log(L_ERROR, "IPC Client socket error, removing client"); 186 wlr_log(L_ERROR, "IPC Client socket error, removing client");
239 ipc_client_disconnect(client); 187 ipc_client_disconnect(client);
240 return 0; 188 return 0;
241 } 189 }
242 190
243 if (mask & WLC_EVENT_HANGUP) { 191 if (mask & WL_EVENT_HANGUP) {
244 sway_log(L_DEBUG, "Client %d hung up", client->fd); 192 wlr_log(L_DEBUG, "Client %d hung up", client->fd);
245 ipc_client_disconnect(client); 193 ipc_client_disconnect(client);
246 return 0; 194 return 0;
247 } 195 }
248 196
249 sway_log(L_DEBUG, "Client %d readable", client->fd); 197 wlr_log(L_DEBUG, "Client %d readable", client->fd);
250 198
251 int read_available; 199 int read_available;
252 if (ioctl(client_fd, FIONREAD, &read_available) == -1) { 200 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
253 sway_log_errno(L_INFO, "Unable to read IPC socket buffer size"); 201 wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size");
254 ipc_client_disconnect(client); 202 ipc_client_disconnect(client);
255 return 0; 203 return 0;
256 } 204 }
@@ -272,13 +220,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
272 // Should be fully available, because read_available >= ipc_header_size 220 // Should be fully available, because read_available >= ipc_header_size
273 ssize_t received = recv(client_fd, buf, ipc_header_size, 0); 221 ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
274 if (received == -1) { 222 if (received == -1) {
275 sway_log_errno(L_INFO, "Unable to receive header from IPC client"); 223 wlr_log_errno(L_INFO, "Unable to receive header from IPC client");
276 ipc_client_disconnect(client); 224 ipc_client_disconnect(client);
277 return 0; 225 return 0;
278 } 226 }
279 227
280 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { 228 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
281 sway_log(L_DEBUG, "IPC header check failed"); 229 wlr_log(L_DEBUG, "IPC header check failed");
282 ipc_client_disconnect(client); 230 ipc_client_disconnect(client);
283 return 0; 231 return 0;
284 } 232 }
@@ -293,17 +241,110 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
293 return 0; 241 return 0;
294} 242}
295 243
244static bool ipc_has_event_listeners(enum ipc_command_type event) {
245 for (int i = 0; i < ipc_client_list->length; i++) {
246 struct ipc_client *client = ipc_client_list->items[i];
247 if ((client->subscribed_events & event_mask(event)) == 0) {
248 return true;
249 }
250 }
251 return false;
252}
253
254static void ipc_send_event(const char *json_string, enum ipc_command_type event) {
255 struct ipc_client *client;
256 for (int i = 0; i < ipc_client_list->length; i++) {
257 client = ipc_client_list->items[i];
258 if ((client->subscribed_events & event_mask(event)) == 0) {
259 continue;
260 }
261 client->current_command = event;
262 if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
263 wlr_log_errno(L_INFO, "Unable to send reply to IPC client");
264 ipc_client_disconnect(client);
265 }
266 }
267}
268
269void ipc_event_workspace(struct sway_container *old,
270 struct sway_container *new, const char *change) {
271 if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
272 return;
273 }
274 wlr_log(L_DEBUG, "Sending workspace::%s event", change);
275 json_object *obj = json_object_new_object();
276 json_object_object_add(obj, "change", json_object_new_string(change));
277 if (strcmp("focus", change) == 0) {
278 if (old) {
279 json_object_object_add(obj, "old",
280 ipc_json_describe_container_recursive(old));
281 } else {
282 json_object_object_add(obj, "old", NULL);
283 }
284 }
285
286 if (new) {
287 json_object_object_add(obj, "current",
288 ipc_json_describe_container_recursive(new));
289 } else {
290 json_object_object_add(obj, "current", NULL);
291 }
292
293 const char *json_string = json_object_to_json_string(obj);
294 ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
295 json_object_put(obj);
296}
297
298void ipc_event_window(struct sway_container *window, const char *change) {
299 if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) {
300 return;
301 }
302 wlr_log(L_DEBUG, "Sending window::%s event", change);
303 json_object *obj = json_object_new_object();
304 json_object_object_add(obj, "change", json_object_new_string(change));
305 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
306
307 const char *json_string = json_object_to_json_string(obj);
308 ipc_send_event(json_string, IPC_EVENT_WINDOW);
309 json_object_put(obj);
310}
311
312void ipc_event_barconfig_update(struct bar_config *bar) {
313 if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) {
314 return;
315 }
316 wlr_log(L_DEBUG, "Sending barconfig_update event");
317 json_object *json = ipc_json_describe_bar_config(bar);
318
319 const char *json_string = json_object_to_json_string(json);
320 ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
321 json_object_put(json);
322}
323
324void ipc_event_mode(const char *mode) {
325 if (!ipc_has_event_listeners(IPC_EVENT_MODE)) {
326 return;
327 }
328 wlr_log(L_DEBUG, "Sending mode::%s event", mode);
329 json_object *obj = json_object_new_object();
330 json_object_object_add(obj, "change", json_object_new_string(mode));
331
332 const char *json_string = json_object_to_json_string(obj);
333 ipc_send_event(json_string, IPC_EVENT_MODE);
334 json_object_put(obj);
335}
336
296int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { 337int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
297 struct ipc_client *client = data; 338 struct ipc_client *client = data;
298 339
299 if (mask & WLC_EVENT_ERROR) { 340 if (mask & WL_EVENT_ERROR) {
300 sway_log(L_ERROR, "IPC Client socket error, removing client"); 341 wlr_log(L_ERROR, "IPC Client socket error, removing client");
301 ipc_client_disconnect(client); 342 ipc_client_disconnect(client);
302 return 0; 343 return 0;
303 } 344 }
304 345
305 if (mask & WLC_EVENT_HANGUP) { 346 if (mask & WL_EVENT_HANGUP) {
306 sway_log(L_DEBUG, "Client %d hung up", client->fd); 347 wlr_log(L_DEBUG, "Client %d hung up", client->fd);
307 ipc_client_disconnect(client); 348 ipc_client_disconnect(client);
308 return 0; 349 return 0;
309 } 350 }
@@ -312,14 +353,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
312 return 0; 353 return 0;
313 } 354 }
314 355
315 sway_log(L_DEBUG, "Client %d writable", client->fd); 356 wlr_log(L_DEBUG, "Client %d writable", client->fd);
316 357
317 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); 358 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
318 359
319 if (written == -1 && errno == EAGAIN) { 360 if (written == -1 && errno == EAGAIN) {
320 return 0; 361 return 0;
321 } else if (written == -1) { 362 } else if (written == -1) {
322 sway_log_errno(L_INFO, "Unable to send data from queue to IPC client"); 363 wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client");
323 ipc_client_disconnect(client); 364 ipc_client_disconnect(client);
324 return 0; 365 return 0;
325 } 366 }
@@ -328,7 +369,7 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
328 client->write_buffer_len -= written; 369 client->write_buffer_len -= written;
329 370
330 if (client->write_buffer_len == 0 && client->writable_event_source) { 371 if (client->write_buffer_len == 0 && client->writable_event_source) {
331 wlc_event_source_remove(client->writable_event_source); 372 wl_event_source_remove(client->writable_event_source);
332 client->writable_event_source = NULL; 373 client->writable_event_source = NULL;
333 } 374 }
334 375
@@ -344,332 +385,48 @@ void ipc_client_disconnect(struct ipc_client *client) {
344 shutdown(client->fd, SHUT_RDWR); 385 shutdown(client->fd, SHUT_RDWR);
345 } 386 }
346 387
347 sway_log(L_INFO, "IPC Client %d disconnected", client->fd); 388 wlr_log(L_INFO, "IPC Client %d disconnected", client->fd);
348 wlc_event_source_remove(client->event_source); 389 wl_event_source_remove(client->event_source);
349 if (client->writable_event_source) { 390 if (client->writable_event_source) {
350 wlc_event_source_remove(client->writable_event_source); 391 wl_event_source_remove(client->writable_event_source);
351 } 392 }
352 int i = 0; 393 int i = 0;
353 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; 394 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) {
395 i++;
396 }
354 list_del(ipc_client_list, i); 397 list_del(ipc_client_list, i);
355 free(client->write_buffer); 398 free(client->write_buffer);
356 close(client->fd); 399 close(client->fd);
357 free(client); 400 free(client);
358} 401}
359 402
360bool output_by_name_test(swayc_t *view, void *data) { 403static void ipc_get_workspaces_callback(struct sway_container *workspace,
361 char *name = (char *)data; 404 void *data) {
362 if (view->type != C_OUTPUT) { 405 if (workspace->type != C_WORKSPACE) {
363 return false;
364 }
365 return !strcmp(name, view->name);
366}
367
368void ipc_get_pixels(wlc_handle output) {
369 if (ipc_get_pixel_requests->length == 0) {
370 return; 406 return;
371 } 407 }
372 408 json_object *workspace_json = ipc_json_describe_container(workspace);
373 list_t *unhandled = create_list(); 409 // override the default focused indicator because
374 410 // it's set differently for the get_workspaces reply
375 struct get_pixels_request *req; 411 struct sway_seat *seat =
376 int i; 412 input_manager_get_default_seat(input_manager);
377 for (i = 0; i < ipc_get_pixel_requests->length; ++i) { 413 struct sway_container *focused_ws = seat_get_focus(seat);
378 req = ipc_get_pixel_requests->items[i]; 414 if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) {
379 if (req->output != output) { 415 focused_ws = container_parent(focused_ws, C_WORKSPACE);
380 list_add(unhandled, req); 416 }
381 continue; 417 bool focused = workspace == focused_ws;
382 } 418 json_object_object_del(workspace_json, "focused");
383 419 json_object_object_add(workspace_json, "focused",
384 const struct wlc_size *size = &req->geo.size; 420 json_object_new_boolean(focused));
385 struct wlc_geometry g_out; 421 json_object_array_add((json_object *)data, workspace_json);
386 char response_header[9]; 422
387 memset(response_header, 0, sizeof(response_header)); 423 focused_ws = seat_get_focus_inactive(seat, workspace->parent);
388 char *data = malloc(sizeof(response_header) + size->w * size->h * 4); 424 if (focused_ws->type != C_WORKSPACE) {
389 if (!data) { 425 focused_ws = container_parent(focused_ws, C_WORKSPACE);
390 sway_log(L_ERROR, "Unable to allocate pixels for get_pixels"); 426 }
391 ipc_client_disconnect(req->client); 427 bool visible = workspace == focused_ws;
392 free(req); 428 json_object_object_add(workspace_json, "visible",
393 continue; 429 json_object_new_boolean(visible));
394 }
395 wlc_pixels_read(WLC_RGBA8888, &req->geo, &g_out, data + sizeof(response_header));
396
397 response_header[0] = 1;
398 uint32_t *_size = (uint32_t *)(response_header + 1);
399 _size[0] = g_out.size.w;
400 _size[1] = g_out.size.h;
401 size_t len = sizeof(response_header) + (g_out.size.w * g_out.size.h * 4);
402 memcpy(data, response_header, sizeof(response_header));
403 ipc_send_reply(req->client, data, len);
404 free(data);
405 // free the request since it has been handled
406 free(req);
407 }
408
409 // free old list of pixel requests and set new list to all unhandled
410 // requests (request for another output).
411 list_free(ipc_get_pixel_requests);
412 ipc_get_pixel_requests = unhandled;
413}
414
415static bool is_text_target(const char *target) {
416 return (strncmp(target, "text/", 5) == 0
417 || strcmp(target, "UTF8_STRING") == 0
418 || strcmp(target, "STRING") == 0
419 || strcmp(target, "TEXT") == 0
420 || strcmp(target, "COMPOUND_TEXT") == 0);
421}
422
423static void release_clipboard_request(struct get_clipboard_request *req) {
424 if (--(*req->pending) == 0) {
425 const char *str = json_object_to_json_string(req->json);
426 ipc_send_reply(req->client, str, (uint32_t)strlen(str));
427 json_object_put(req->json);
428 }
429
430 free(req->type);
431 free(req->buf);
432 wlc_event_source_remove(req->fd_event_source);
433 wlc_event_source_remove(req->timer_event_source);
434 close(req->fd);
435 free(req);
436}
437
438static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) {
439 assert(data);
440 struct get_clipboard_request *req = (struct get_clipboard_request *)data;
441
442 if (mask & WLC_EVENT_ERROR) {
443 sway_log(L_ERROR, "Selection data fd error");
444 goto error;
445 }
446
447 if (mask & WLC_EVENT_READABLE) {
448 static const unsigned int max_size = 8192 * 1024;
449 int amt = 0;
450
451 do {
452 int size = req->buf_size - req->buf_position;
453 int amt = read(fd, req->buf + req->buf_position, size - 1);
454 if (amt < 0) {
455 if (errno == EAGAIN) {
456 return 0;
457 }
458
459 sway_log_errno(L_INFO, "Failed to read from clipboard data fd");
460 goto release;
461 }
462
463 req->buf_position += amt;
464 if (req->buf_position >= req->buf_size - 1) {
465 if (req->buf_size >= max_size) {
466 sway_log(L_ERROR, "get_clipbard: selection data too large");
467 goto error;
468 }
469 char *next = realloc(req->buf, req->buf_size *= 2);
470 if (!next) {
471 sway_log_errno(L_ERROR, "get_clipboard: realloc data buffer failed");
472 goto error;
473 }
474
475 req->buf = next;
476 }
477 } while(amt != 0);
478
479 req->buf[req->buf_position] = '\0';
480
481 json_object *obj = json_object_new_object();
482 json_object_object_add(obj, "success", json_object_new_boolean(true));
483 if (is_text_target(req->type)) {
484 json_object_object_add(obj, "content", json_object_new_string(req->buf));
485 json_object_object_add(req->json, req->type, obj);
486 } else {
487 size_t outlen;
488 char *b64 = b64_encode(req->buf, req->buf_position, &outlen);
489 json_object_object_add(obj, "content", json_object_new_string(b64));
490 free(b64);
491
492 char *type = malloc(strlen(req->type) + 8);
493 strcat(type, ";base64");
494 json_object_object_add(req->json, type, obj);
495 free(type);
496 }
497 }
498
499 goto release;
500
501error:;
502 json_object *obj = json_object_new_object();
503 json_object_object_add(obj, "success", json_object_new_boolean(false));
504 json_object_object_add(obj, "error",
505 json_object_new_string("Failed to retrieve data"));
506 json_object_object_add(req->json, req->type, obj);
507
508release:
509 release_clipboard_request(req);
510 return 0;
511}
512
513static int ipc_selection_timer_cb(void *data) {
514 assert(data);
515 struct get_clipboard_request *req = (struct get_clipboard_request *)data;
516
517 sway_log(L_INFO, "get_clipbard: timeout for type %s", req->type);
518 json_object *obj = json_object_new_object();
519 json_object_object_add(obj, "success", json_object_new_boolean(false));
520 json_object_object_add(obj, "error", json_object_new_string("Timeout"));
521 json_object_object_add(req->json, req->type, obj);
522
523 release_clipboard_request(req);
524 return 0;
525}
526
527// greedy wildcard (only "*") matching
528bool mime_type_matches(const char *mime_type, const char *pattern) {
529 const char *wildcard = NULL;
530 while (*mime_type && *pattern) {
531 if (*pattern == '*' && !wildcard) {
532 wildcard = pattern;
533 ++pattern;
534 }
535
536 if (*mime_type != *pattern) {
537 if (!wildcard)
538 return false;
539
540 pattern = wildcard;
541 ++mime_type;
542 continue;
543 }
544
545 ++mime_type;
546 ++pattern;
547 }
548
549 while (*pattern == '*') {
550 ++pattern;
551 }
552
553 return (*mime_type == *pattern);
554}
555
556void ipc_get_clipboard(struct ipc_client *client, char *buf) {
557 size_t size;
558 const char **types = wlc_get_selection_types(&size);
559 if (client->payload_length == 0) {
560 json_object *obj = json_object_new_array();
561 for (size_t i = 0; i < size; ++i) {
562 json_object_array_add(obj, json_object_new_string(types[i]));
563 }
564
565 const char *str = json_object_to_json_string(obj);
566 ipc_send_reply(client, str, strlen(str));
567 json_object_put(obj);
568 return;
569 }
570
571 unescape_string(buf);
572 strip_quotes(buf);
573 list_t *requested = split_string(buf, " ");
574 json_object *json = json_object_new_object();
575 unsigned int *pending = malloc(sizeof(unsigned int));
576 *pending = 0;
577
578 for (size_t l = 0; l < (size_t) requested->length; ++l) {
579 const char *pattern = requested->items[l];
580 bool found = false;
581 for (size_t i = 0; i < size; ++i) {
582 if (!mime_type_matches(types[i], pattern)) {
583 continue;
584 }
585
586 found = true;
587
588 struct get_clipboard_request *req = malloc(sizeof(*req));
589 if (!req) {
590 sway_log(L_ERROR, "get_clipboard: request malloc failed");
591 goto data_error;
592 }
593
594 int pipes[2];
595 if (pipe(pipes) == -1) {
596 sway_log_errno(L_ERROR, "get_clipboard: pipe call failed");
597 free(req);
598 goto data_error;
599 }
600
601 fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
602 fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
603
604 if (!wlc_get_selection_data(types[i], pipes[1])) {
605 close(pipes[0]);
606 close(pipes[1]);
607 free(req);
608 sway_log(L_ERROR, "get_clipboard: failed to retrieve "
609 "selection data");
610 goto data_error;
611 }
612
613 if (!(req->buf = malloc(512))) {
614 close(pipes[0]);
615 close(pipes[1]);
616 free(req);
617 sway_log_errno(L_ERROR, "get_clipboard: buf malloc failed");
618 goto data_error;
619 }
620
621 (*pending)++;
622
623 req->client = client;
624 req->type = strdup(types[i]);
625 req->json = json;
626 req->pending = pending;
627 req->buf_position = 0;
628 req->buf_size = 512;
629 req->fd = pipes[0];
630 req->timer_event_source = wlc_event_loop_add_timer(ipc_selection_timer_cb, req);
631 req->fd_event_source = wlc_event_loop_add_fd(pipes[0],
632 WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP,
633 &ipc_selection_data_cb, req);
634
635 wlc_event_source_timer_update(req->timer_event_source, 30000);
636
637 // NOTE: remove this goto to enable retrieving multiple
638 // targets at once. The whole implementation is already
639 // made for it. The only reason it was disabled
640 // at the time of writing is that neither wlc's xselection
641 // implementation nor (apparently) gtk on wayland supports
642 // multiple send requests at the same time which makes
643 // every request except the last one fail (and therefore
644 // return empty data)
645 goto cleanup;
646 }
647
648 if (!found) {
649 sway_log(L_INFO, "Invalid clipboard type %s requested", pattern);
650 }
651 }
652
653 if (*pending == 0) {
654 static const char *error_empty = "{ \"success\": false, \"error\": "
655 "\"No matching types found\" }";
656 ipc_send_reply(client, error_empty, (uint32_t)strlen(error_empty));
657 free(json);
658 free(pending);
659 }
660
661 goto cleanup;
662
663data_error:;
664 static const char *error_json = "{ \"success\": false, \"error\": "
665 "\"Failed to create clipboard data request\" }";
666 ipc_send_reply(client, error_json, (uint32_t)strlen(error_json));
667 free(json);
668 free(pending);
669
670cleanup:
671 list_free(requested);
672 free(types);
673} 430}
674 431
675void ipc_client_handle_command(struct ipc_client *client) { 432void ipc_client_handle_command(struct ipc_client *client) {
@@ -679,7 +436,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
679 436
680 char *buf = malloc(client->payload_length + 1); 437 char *buf = malloc(client->payload_length + 1);
681 if (!buf) { 438 if (!buf) {
682 sway_log_errno(L_INFO, "Unable to allocate IPC payload"); 439 wlr_log_errno(L_INFO, "Unable to allocate IPC payload");
683 ipc_client_disconnect(client); 440 ipc_client_disconnect(client);
684 return; 441 return;
685 } 442 }
@@ -688,7 +445,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
688 ssize_t received = recv(client->fd, buf, client->payload_length, 0); 445 ssize_t received = recv(client->fd, buf, client->payload_length, 0);
689 if (received == -1) 446 if (received == -1)
690 { 447 {
691 sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); 448 wlr_log_errno(L_INFO, "Unable to receive payload from IPC client");
692 ipc_client_disconnect(client); 449 ipc_client_disconnect(client);
693 free(buf); 450 free(buf);
694 return; 451 return;
@@ -701,10 +458,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
701 switch (client->current_command) { 458 switch (client->current_command) {
702 case IPC_COMMAND: 459 case IPC_COMMAND:
703 { 460 {
704 if (!(client->security_policy & IPC_FEATURE_COMMAND)) { 461 config_clear_handler_context(config);
705 goto exit_denied; 462 struct cmd_results *results = execute_command(buf, NULL);
706 }
707 struct cmd_results *results = handle_command(buf, CONTEXT_IPC);
708 const char *json = cmd_results_to_json(results); 463 const char *json = cmd_results_to_json(results);
709 char reply[256]; 464 char reply[256];
710 int length = snprintf(reply, sizeof(reply), "%s", json); 465 int length = snprintf(reply, sizeof(reply), "%s", json);
@@ -713,18 +468,45 @@ void ipc_client_handle_command(struct ipc_client *client) {
713 goto exit_cleanup; 468 goto exit_cleanup;
714 } 469 }
715 470
471 case IPC_GET_OUTPUTS:
472 {
473 json_object *outputs = json_object_new_array();
474 for (int i = 0; i < root_container.children->length; ++i) {
475 struct sway_container *container = root_container.children->items[i];
476 if (container->type == C_OUTPUT) {
477 json_object_array_add(outputs,
478 ipc_json_describe_container(container));
479 }
480 }
481 const char *json_string = json_object_to_json_string(outputs);
482 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
483 json_object_put(outputs); // free
484 goto exit_cleanup;
485 }
486
487 case IPC_GET_WORKSPACES:
488 {
489 json_object *workspaces = json_object_new_array();
490 container_for_each_descendant_dfs(&root_container,
491 ipc_get_workspaces_callback, workspaces);
492 const char *json_string = json_object_to_json_string(workspaces);
493 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
494 json_object_put(workspaces); // free
495 goto exit_cleanup;
496 }
497
716 case IPC_SUBSCRIBE: 498 case IPC_SUBSCRIBE:
717 { 499 {
718 // TODO: Check if they're permitted to use these events 500 // TODO: Check if they're permitted to use these events
719 struct json_object *request = json_tokener_parse(buf); 501 struct json_object *request = json_tokener_parse(buf);
720 if (request == NULL) { 502 if (request == NULL) {
721 ipc_send_reply(client, "{\"success\": false}", 18); 503 ipc_send_reply(client, "{\"success\": false}", 18);
722 sway_log_errno(L_INFO, "Failed to read request"); 504 wlr_log_errno(L_INFO, "Failed to read request");
723 goto exit_cleanup; 505 goto exit_cleanup;
724 } 506 }
725 507
726 // parse requested event types 508 // parse requested event types
727 for (int i = 0; i < json_object_array_length(request); i++) { 509 for (size_t i = 0; i < json_object_array_length(request); i++) {
728 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); 510 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
729 if (strcmp(event_type, "workspace") == 0) { 511 if (strcmp(event_type, "workspace") == 0) {
730 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); 512 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
@@ -741,86 +523,39 @@ void ipc_client_handle_command(struct ipc_client *client) {
741 } else { 523 } else {
742 ipc_send_reply(client, "{\"success\": false}", 18); 524 ipc_send_reply(client, "{\"success\": false}", 18);
743 json_object_put(request); 525 json_object_put(request);
744 sway_log_errno(L_INFO, "Failed to parse request"); 526 wlr_log_errno(L_INFO, "Failed to parse request");
745 goto exit_cleanup; 527 goto exit_cleanup;
746 } 528 }
747 } 529 }
748 530
749 json_object_put(request); 531 json_object_put(request);
750
751 ipc_send_reply(client, "{\"success\": true}", 17); 532 ipc_send_reply(client, "{\"success\": true}", 17);
752 goto exit_cleanup; 533 goto exit_cleanup;
753 } 534 }
754 535
755 case IPC_GET_WORKSPACES:
756 {
757 if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) {
758 goto exit_denied;
759 }
760 json_object *workspaces = json_object_new_array();
761 container_map(&root_container, ipc_get_workspaces_callback, workspaces);
762 const char *json_string = json_object_to_json_string(workspaces);
763 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
764 json_object_put(workspaces); // free
765 goto exit_cleanup;
766 }
767
768 case IPC_GET_INPUTS: 536 case IPC_GET_INPUTS:
769 { 537 {
770 if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) {
771 goto exit_denied;
772 }
773 json_object *inputs = json_object_new_array(); 538 json_object *inputs = json_object_new_array();
774 if (input_devices) { 539 struct sway_input_device *device = NULL;
775 for(int i = 0; i<input_devices->length; i++) { 540 wl_list_for_each(device, &input_manager->devices, link) {
776 struct libinput_device *device = input_devices->items[i]; 541 json_object_array_add(inputs, ipc_json_describe_input(device));
777 json_object_array_add(inputs, ipc_json_describe_input(device));
778 }
779 } 542 }
780 const char *json_string = json_object_to_json_string(inputs); 543 const char *json_string = json_object_to_json_string(inputs);
781 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); 544 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
782 json_object_put(inputs); 545 json_object_put(inputs); // free
783 goto exit_cleanup;
784 }
785
786 case IPC_GET_OUTPUTS:
787 {
788 if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) {
789 goto exit_denied;
790 }
791 json_object *outputs = json_object_new_array();
792 container_map(&root_container, ipc_get_outputs_callback, outputs);
793 const char *json_string = json_object_to_json_string(outputs);
794 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
795 json_object_put(outputs); // free
796 goto exit_cleanup; 546 goto exit_cleanup;
797 } 547 }
798 548
799 case IPC_GET_TREE: 549 case IPC_GET_TREE:
800 { 550 {
801 if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { 551 json_object *tree =
802 goto exit_denied; 552 ipc_json_describe_container_recursive(&root_container);
803 }
804 json_object *tree = ipc_json_describe_container_recursive(&root_container);
805 const char *json_string = json_object_to_json_string(tree); 553 const char *json_string = json_object_to_json_string(tree);
806 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); 554 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
807 json_object_put(tree); 555 json_object_put(tree);
808 goto exit_cleanup; 556 goto exit_cleanup;
809 } 557 }
810 558
811 case IPC_GET_MARKS:
812 {
813 if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) {
814 goto exit_denied;
815 }
816 json_object *marks = json_object_new_array();
817 container_map(&root_container, ipc_get_marks_callback, marks);
818 const char *json_string = json_object_to_json_string(marks);
819 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
820 json_object_put(marks);
821 goto exit_cleanup;
822 }
823
824 case IPC_GET_VERSION: 559 case IPC_GET_VERSION:
825 { 560 {
826 json_object *version = ipc_json_get_version(); 561 json_object *version = ipc_json_get_version();
@@ -830,62 +565,12 @@ void ipc_client_handle_command(struct ipc_client *client) {
830 goto exit_cleanup; 565 goto exit_cleanup;
831 } 566 }
832 567
833 case IPC_SWAY_GET_PIXELS:
834 {
835 char response_header[9];
836 memset(response_header, 0, sizeof(response_header));
837
838 json_object *obj = json_tokener_parse(buf);
839 json_object *o, *x, *y, *w, *h;
840
841 json_object_object_get_ex(obj, "output", &o);
842 json_object_object_get_ex(obj, "x", &x);
843 json_object_object_get_ex(obj, "y", &y);
844 json_object_object_get_ex(obj, "w", &w);
845 json_object_object_get_ex(obj, "h", &h);
846
847 struct wlc_geometry g = {
848 .origin = {
849 .x = json_object_get_int(x),
850 .y = json_object_get_int(y)
851 },
852 .size = {
853 .w = json_object_get_int(w),
854 .h = json_object_get_int(h)
855 }
856 };
857
858 swayc_t *output = swayc_by_test(&root_container, output_by_name_test, (void *)json_object_get_string(o));
859 json_object_put(obj);
860
861 if (!output) {
862 sway_log(L_ERROR, "IPC GET_PIXELS request with unknown output name");
863 ipc_send_reply(client, response_header, sizeof(response_header));
864 goto exit_cleanup;
865 }
866 struct get_pixels_request *req = malloc(sizeof(struct get_pixels_request));
867 if (!req) {
868 sway_log(L_ERROR, "Unable to allocate get_pixels request");
869 goto exit_cleanup;
870 }
871 req->client = client;
872 req->output = output->handle;
873 req->geo = g;
874 list_add(ipc_get_pixel_requests, req);
875 wlc_output_schedule_render(output->handle);
876 goto exit_cleanup;
877 }
878
879 case IPC_GET_BAR_CONFIG: 568 case IPC_GET_BAR_CONFIG:
880 { 569 {
881 if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
882 goto exit_denied;
883 }
884 if (!buf[0]) { 570 if (!buf[0]) {
885 // Send list of configured bar IDs 571 // Send list of configured bar IDs
886 json_object *bars = json_object_new_array(); 572 json_object *bars = json_object_new_array();
887 int i; 573 for (int i = 0; i < config->bars->length; ++i) {
888 for (i = 0; i < config->bars->length; ++i) {
889 struct bar_config *bar = config->bars->items[i]; 574 struct bar_config *bar = config->bars->items[i];
890 json_object_array_add(bars, json_object_new_string(bar->id)); 575 json_object_array_add(bars, json_object_new_string(bar->id));
891 } 576 }
@@ -895,8 +580,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
895 } else { 580 } else {
896 // Send particular bar's details 581 // Send particular bar's details
897 struct bar_config *bar = NULL; 582 struct bar_config *bar = NULL;
898 int i; 583 for (int i = 0; i < config->bars->length; ++i) {
899 for (i = 0; i < config->bars->length; ++i) {
900 bar = config->bars->items[i]; 584 bar = config->bars->items[i];
901 if (strcmp(buf, bar->id) == 0) { 585 if (strcmp(buf, bar->id) == 0) {
902 break; 586 break;
@@ -916,24 +600,13 @@ void ipc_client_handle_command(struct ipc_client *client) {
916 goto exit_cleanup; 600 goto exit_cleanup;
917 } 601 }
918 602
919 case IPC_GET_CLIPBOARD:
920 {
921 if (!(client->security_policy & IPC_FEATURE_GET_CLIPBOARD)) {
922 goto exit_denied;
923 }
924
925 ipc_get_clipboard(client, buf);
926 goto exit_cleanup;
927 }
928
929 default: 603 default:
930 sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); 604 wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command);
931 goto exit_cleanup; 605 goto exit_cleanup;
932 } 606 }
933 607
934exit_denied:
935 ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); 608 ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
936 sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); 609 wlr_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
937 610
938exit_cleanup: 611exit_cleanup:
939 client->payload_length = 0; 612 client->payload_length = 0;
@@ -956,16 +629,15 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
956 client->write_buffer_size *= 2; 629 client->write_buffer_size *= 2;
957 } 630 }
958 631
959 // TODO: reduce the limit back to 4 MB when screenshooter is implemented 632 if (client->write_buffer_size > 4e6) { // 4 MB
960 if (client->write_buffer_size > (1 << 28)) { // 256 MB 633 wlr_log(L_ERROR, "Client write buffer too big, disconnecting client");
961 sway_log(L_ERROR, "Client write buffer too big, disconnecting client");
962 ipc_client_disconnect(client); 634 ipc_client_disconnect(client);
963 return false; 635 return false;
964 } 636 }
965 637
966 char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); 638 char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
967 if (!new_buffer) { 639 if (!new_buffer) {
968 sway_log(L_ERROR, "Unable to reallocate ipc client write buffer"); 640 wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer");
969 ipc_client_disconnect(client); 641 ipc_client_disconnect(client);
970 return false; 642 return false;
971 } 643 }
@@ -977,212 +649,11 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
977 client->write_buffer_len += payload_length; 649 client->write_buffer_len += payload_length;
978 650
979 if (!client->writable_event_source) { 651 if (!client->writable_event_source) {
980 client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client); 652 client->writable_event_source = wl_event_loop_add_fd(
653 server.wl_event_loop, client->fd, WL_EVENT_WRITABLE,
654 ipc_client_handle_writable, client);
981 } 655 }
982 656
983 sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); 657 wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
984
985 return true; 658 return true;
986} 659}
987
988void ipc_get_workspaces_callback(swayc_t *workspace, void *data) {
989 if (workspace->type == C_WORKSPACE) {
990 json_object *workspace_json = ipc_json_describe_container(workspace);
991 // override the default focused indicator because
992 // it's set differently for the get_workspaces reply
993 bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace;
994 json_object_object_del(workspace_json, "focused");
995 json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused));
996 json_object_array_add((json_object *)data, workspace_json);
997 }
998}
999
1000void ipc_get_outputs_callback(swayc_t *container, void *data) {
1001 if (container->type == C_OUTPUT) {
1002 json_object_array_add((json_object *)data, ipc_json_describe_container(container));
1003 }
1004}
1005
1006static void ipc_get_marks_callback(swayc_t *container, void *data) {
1007 json_object *object = (json_object *)data;
1008 if (container->marks) {
1009 for (int i = 0; i < container->marks->length; ++i) {
1010 char *mark = (char *)container->marks->items[i];
1011 json_object_array_add(object, json_object_new_string(mark));
1012 }
1013 }
1014}
1015
1016void ipc_send_event(const char *json_string, enum ipc_command_type event) {
1017 static struct {
1018 enum ipc_command_type event;
1019 enum ipc_feature feature;
1020 } security_mappings[] = {
1021 { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE },
1022 { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT },
1023 { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE },
1024 { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW },
1025 { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING },
1026 { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT }
1027 };
1028
1029 uint32_t security_mask = 0;
1030 for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) {
1031 if (security_mappings[i].event == event) {
1032 security_mask = security_mappings[i].feature;
1033 break;
1034 }
1035 }
1036
1037 int i;
1038 struct ipc_client *client;
1039 for (i = 0; i < ipc_client_list->length; i++) {
1040 client = ipc_client_list->items[i];
1041 if (!(client->security_policy & security_mask)) {
1042 continue;
1043 }
1044 if ((client->subscribed_events & event_mask(event)) == 0) {
1045 continue;
1046 }
1047 client->current_command = event;
1048 if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
1049 sway_log_errno(L_INFO, "Unable to send reply to IPC client");
1050 ipc_client_disconnect(client);
1051 }
1052 }
1053}
1054
1055void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) {
1056 sway_log(L_DEBUG, "Sending workspace::%s event", change);
1057 json_object *obj = json_object_new_object();
1058 json_object_object_add(obj, "change", json_object_new_string(change));
1059 if (strcmp("focus", change) == 0) {
1060 if (old) {
1061 json_object_object_add(obj, "old", ipc_json_describe_container_recursive(old));
1062 } else {
1063 json_object_object_add(obj, "old", NULL);
1064 }
1065 }
1066
1067 if (new) {
1068 json_object_object_add(obj, "current", ipc_json_describe_container_recursive(new));
1069 } else {
1070 json_object_object_add(obj, "current", NULL);
1071 }
1072
1073 const char *json_string = json_object_to_json_string(obj);
1074 ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
1075
1076 json_object_put(obj); // free
1077}
1078
1079void ipc_event_window(swayc_t *window, const char *change) {
1080 sway_log(L_DEBUG, "Sending window::%s event", change);
1081 json_object *obj = json_object_new_object();
1082 json_object_object_add(obj, "change", json_object_new_string(change));
1083 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
1084
1085 const char *json_string = json_object_to_json_string(obj);
1086 ipc_send_event(json_string, IPC_EVENT_WINDOW);
1087
1088 json_object_put(obj); // free
1089}
1090
1091void ipc_event_barconfig_update(struct bar_config *bar) {
1092 sway_log(L_DEBUG, "Sending barconfig_update event");
1093 json_object *json = ipc_json_describe_bar_config(bar);
1094 const char *json_string = json_object_to_json_string(json);
1095 ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
1096
1097 json_object_put(json); // free
1098}
1099
1100void ipc_event_mode(const char *mode) {
1101 sway_log(L_DEBUG, "Sending mode::%s event", mode);
1102 json_object *obj = json_object_new_object();
1103 json_object_object_add(obj, "change", json_object_new_string(mode));
1104
1105 const char *json_string = json_object_to_json_string(obj);
1106 ipc_send_event(json_string, IPC_EVENT_MODE);
1107
1108 json_object_put(obj); // free
1109}
1110
1111void ipc_event_modifier(uint32_t modifier, const char *state) {
1112 sway_log(L_DEBUG, "Sending modifier::%s event", state);
1113 json_object *obj = json_object_new_object();
1114 json_object_object_add(obj, "change", json_object_new_string(state));
1115
1116 const char *modifier_name = get_modifier_name_by_mask(modifier);
1117 json_object_object_add(obj, "modifier", json_object_new_string(modifier_name));
1118
1119 const char *json_string = json_object_to_json_string(obj);
1120 ipc_send_event(json_string, IPC_EVENT_MODIFIER);
1121
1122 json_object_put(obj); // free
1123}
1124
1125static void ipc_event_binding(json_object *sb_obj) {
1126 sway_log(L_DEBUG, "Sending binding::run event");
1127 json_object *obj = json_object_new_object();
1128 json_object_object_add(obj, "change", json_object_new_string("run"));
1129 json_object_object_add(obj, "binding", sb_obj);
1130
1131 const char *json_string = json_object_to_json_string(obj);
1132 ipc_send_event(json_string, IPC_EVENT_BINDING);
1133
1134 json_object_put(obj); // free
1135}
1136
1137void ipc_event_binding_keyboard(struct sway_binding *sb) {
1138 json_object *sb_obj = json_object_new_object();
1139 json_object_object_add(sb_obj, "command", json_object_new_string(sb->command));
1140
1141 const char *names[10];
1142
1143 int len = get_modifier_names(names, sb->modifiers);
1144 int i;
1145 json_object *modifiers = json_object_new_array();
1146 for (i = 0; i < len; ++i) {
1147 json_object_array_add(modifiers, json_object_new_string(names[i]));
1148 }
1149
1150 json_object_object_add(sb_obj, "event_state_mask", modifiers);
1151
1152 json_object *input_codes = json_object_new_array();
1153 int input_code = 0;
1154 json_object *symbols = json_object_new_array();
1155 json_object *symbol = NULL;
1156
1157 if (sb->bindcode) { // bindcode: populate input_codes
1158 uint32_t keycode;
1159 for (i = 0; i < sb->keys->length; ++i) {
1160 keycode = *(uint32_t *)sb->keys->items[i];
1161 json_object_array_add(input_codes, json_object_new_int(keycode));
1162 if (i == 0) {
1163 input_code = keycode;
1164 }
1165 }
1166 } else { // bindsym: populate symbols
1167 uint32_t keysym;
1168 char buffer[64];
1169 for (i = 0; i < sb->keys->length; ++i) {
1170 keysym = *(uint32_t *)sb->keys->items[i];
1171 if (xkb_keysym_get_name(keysym, buffer, 64) > 0) {
1172 json_object *str = json_object_new_string(buffer);
1173 json_object_array_add(symbols, str);
1174 if (i == 0) {
1175 symbol = str;
1176 }
1177 }
1178 }
1179 }
1180
1181 json_object_object_add(sb_obj, "input_codes", input_codes);
1182 json_object_object_add(sb_obj, "input_code", json_object_new_int(input_code));
1183 json_object_object_add(sb_obj, "symbols", symbols);
1184 json_object_object_add(sb_obj, "symbol", symbol);
1185 json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard"));
1186
1187 ipc_event_binding(sb_obj);
1188}
diff --git a/sway/layout.c b/sway/layout.c
deleted file mode 100644
index 69291daf..00000000
--- a/sway/layout.c
+++ /dev/null
@@ -1,1770 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdbool.h>
4#include <math.h>
5#include <wlc/wlc.h>
6#include "sway/extensions.h"
7#include "sway/config.h"
8#include "sway/container.h"
9#include "sway/workspace.h"
10#include "sway/focus.h"
11#include "sway/output.h"
12#include "sway/ipc-server.h"
13#include "sway/border.h"
14#include "sway/layout.h"
15#include "list.h"
16#include "log.h"
17
18swayc_t root_container;
19swayc_t *current_focus;
20list_t *scratchpad;
21
22int min_sane_h = 60;
23int min_sane_w = 100;
24
25void init_layout(void) {
26 root_container.id = 0; // normally assigned in new_swayc()
27 root_container.type = C_ROOT;
28 root_container.layout = L_NONE;
29 root_container.name = strdup("root");
30 root_container.children = create_list();
31 root_container.handle = -1;
32 root_container.visible = true;
33 current_focus = &root_container;
34 scratchpad = create_list();
35}
36
37int index_child(const swayc_t *child) {
38 swayc_t *parent = child->parent;
39 int i, len;
40 if (!child->is_floating) {
41 len = parent->children->length;
42 for (i = 0; i < len; ++i) {
43 if (parent->children->items[i] == child) {
44 break;
45 }
46 }
47 } else {
48 len = parent->floating->length;
49 for (i = 0; i < len; ++i) {
50 if (parent->floating->items[i] == child) {
51 break;
52 }
53 }
54 }
55 if (!sway_assert(i < len, "Stray container")) {
56 return -1;
57 }
58 return i;
59}
60
61void add_child(swayc_t *parent, swayc_t *child) {
62 sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
63 child->width, child->height, parent, parent->type, parent->width, parent->height);
64 list_add(parent->children, child);
65 child->parent = parent;
66 // set focus for this container
67 if (!parent->focused) {
68 parent->focused = child;
69 }
70 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
71 child = new_container(child, parent->workspace_layout);
72 }
73}
74
75static double *get_height(swayc_t *cont) {
76 return &cont->height;
77}
78
79static double *get_width(swayc_t *cont) {
80 return &cont->width;
81}
82
83void insert_child(swayc_t *parent, swayc_t *child, int index) {
84 if (index > parent->children->length) {
85 index = parent->children->length;
86 }
87 if (index < 0) {
88 index = 0;
89 }
90 list_insert(parent->children, index, child);
91 child->parent = parent;
92 if (!parent->focused) {
93 parent->focused = child;
94 }
95 if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
96 child = new_container(child, parent->workspace_layout);
97 }
98 if (is_auto_layout(parent->layout)) {
99 /* go through each group, adjust the size of the first child of each group */
100 double *(*get_maj_dim)(swayc_t *cont);
101 double *(*get_min_dim)(swayc_t *cont);
102 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
103 get_maj_dim = get_width;
104 get_min_dim = get_height;
105 } else {
106 get_maj_dim = get_height;
107 get_min_dim = get_width;
108 }
109 for (int i = index; i < parent->children->length;) {
110 int start = auto_group_start_index(parent, i);
111 int end = auto_group_end_index(parent, i);
112 swayc_t *first = parent->children->items[start];
113 if (start + 1 < parent->children->length) {
114 /* preserve the group's dimension along major axis */
115 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
116 } else {
117 /* new group, let the apply_layout handle it */
118 first->height = first->width = 0;
119 break;
120 }
121 double remaining = *get_min_dim(parent);
122 for (int j = end - 1; j > start; --j) {
123 swayc_t *sibling = parent->children->items[j];
124 if (sibling == child) {
125 /* the inserted child won't yet have its minor
126 dimension set */
127 remaining -= *get_min_dim(parent) / (end - start);
128 } else {
129 remaining -= *get_min_dim(sibling);
130 }
131 }
132 *get_min_dim(first) = remaining;
133 i = end;
134 }
135 }
136}
137
138void add_floating(swayc_t *ws, swayc_t *child) {
139 sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
140 child->width, child->height, ws, ws->type, ws->width, ws->height);
141 if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) {
142 return;
143 }
144 list_add(ws->floating, child);
145 child->parent = ws;
146 child->is_floating = true;
147 if (!ws->focused) {
148 ws->focused = child;
149 }
150 ipc_event_window(child, "floating");
151}
152
153swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
154 swayc_t *parent = fixed->parent;
155 if (fixed->is_floating) {
156 if (active->is_floating) {
157 int i = index_child(fixed);
158 list_insert(parent->floating, i + 1, active);
159 } else {
160 list_add(parent->children, active);
161 }
162 } else {
163 if (active->is_floating) {
164 list_add(parent->floating, active);
165 } else {
166 int i = index_child(fixed);
167 if (is_auto_layout(parent->layout)) {
168 list_add(parent->children, active);
169 } else {
170 list_insert(parent->children, i + 1, active);
171 }
172 }
173 }
174 active->parent = parent;
175 // focus new child
176 parent->focused = active;
177 return active->parent;
178}
179
180swayc_t *replace_child(swayc_t *child, swayc_t *new_child) {
181 swayc_t *parent = child->parent;
182 if (parent == NULL) {
183 return NULL;
184 }
185 int i = index_child(child);
186 if (child->is_floating) {
187 parent->floating->items[i] = new_child;
188 } else {
189 parent->children->items[i] = new_child;
190 }
191 // Set parent and focus for new_child
192 new_child->parent = child->parent;
193 if (child->parent->focused == child) {
194 child->parent->focused = new_child;
195 }
196 child->parent = NULL;
197
198 // Set geometry for new child
199 new_child->x = child->x;
200 new_child->y = child->y;
201 new_child->width = child->width;
202 new_child->height = child->height;
203
204 // reset geometry for child
205 child->width = 0;
206 child->height = 0;
207
208 // deactivate child
209 if (child->type == C_VIEW) {
210 wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
211 }
212 return parent;
213}
214
215swayc_t *remove_child(swayc_t *child) {
216 int i;
217 swayc_t *parent = child->parent;
218 if (child->is_floating) {
219 // Special case for floating views
220 for (i = 0; i < parent->floating->length; ++i) {
221 if (parent->floating->items[i] == child) {
222 list_del(parent->floating, i);
223 break;
224 }
225 }
226 i = 0;
227 } else {
228 for (i = 0; i < parent->children->length; ++i) {
229 if (parent->children->items[i] == child) {
230 list_del(parent->children, i);
231 break;
232 }
233 }
234 if (is_auto_layout(parent->layout) && parent->children->length) {
235 /* go through each group, adjust the size of the last child of each group */
236 double *(*get_maj_dim)(swayc_t *cont);
237 double *(*get_min_dim)(swayc_t *cont);
238 if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
239 get_maj_dim = get_width;
240 get_min_dim = get_height;
241 } else {
242 get_maj_dim = get_height;
243 get_min_dim = get_width;
244 }
245 for (int j = parent->children->length - 1; j >= i;) {
246 int start = auto_group_start_index(parent, j);
247 int end = auto_group_end_index(parent, j);
248 swayc_t *first = parent->children->items[start];
249 if (i == start) {
250 /* removed element was first child in the current group,
251 use its size along the major axis */
252 *get_maj_dim(first) = *get_maj_dim(child);
253 } else if (start > i) {
254 /* preserve the group's dimension along major axis */
255 *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
256 }
257 if (end != parent->children->length) {
258 double remaining = *get_min_dim(parent);
259 for (int k = start; k < end - 1; ++k) {
260 swayc_t *sibling = parent->children->items[k];
261 remaining -= *get_min_dim(sibling);
262 }
263 /* last element of the group gets remaining size, elements
264 that don't change groups keep their ratio */
265 *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
266 } /* else last group, let apply_layout handle it */
267 j = start - 1;
268 }
269 }
270 }
271 // Set focused to new container
272 if (parent->focused == child) {
273 if (parent->children->length > 0) {
274 parent->focused = parent->children->items[i ? i-1:0];
275 } else if (parent->floating && parent->floating->length) {
276 parent->focused = parent->floating->items[parent->floating->length - 1];
277 } else {
278 parent->focused = NULL;
279 }
280 }
281 child->parent = NULL;
282 // deactivate view
283 if (child->type == C_VIEW) {
284 wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
285 }
286 return parent;
287}
288
289void swap_container(swayc_t *a, swayc_t *b) {
290 if (!sway_assert(a&&b, "parameters must be non null") ||
291 !sway_assert(a->parent && b->parent, "containers must have parents")) {
292 return;
293 }
294 size_t a_index = index_child(a);
295 size_t b_index = index_child(b);
296 swayc_t *a_parent = a->parent;
297 swayc_t *b_parent = b->parent;
298 // Swap the pointers
299 if (a->is_floating) {
300 a_parent->floating->items[a_index] = b;
301 } else {
302 a_parent->children->items[a_index] = b;
303 }
304 if (b->is_floating) {
305 b_parent->floating->items[b_index] = a;
306 } else {
307 b_parent->children->items[b_index] = a;
308 }
309 a->parent = b_parent;
310 b->parent = a_parent;
311 if (a_parent->focused == a) {
312 a_parent->focused = b;
313 }
314 // don't want to double switch
315 if (b_parent->focused == b && a_parent != b_parent) {
316 b_parent->focused = a;
317 }
318}
319
320void swap_geometry(swayc_t *a, swayc_t *b) {
321 double x = a->x;
322 double y = a->y;
323 double w = a->width;
324 double h = a->height;
325 a->x = b->x;
326 a->y = b->y;
327 a->width = b->width;
328 a->height = b->height;
329 b->x = x;
330 b->y = y;
331 b->width = w;
332 b->height = h;
333}
334
335static void swap_children(swayc_t *container, int a, int b) {
336 if (a >= 0 && b >= 0 && a < container->children->length
337 && b < container->children->length
338 && a != b) {
339 swayc_t *pa = (swayc_t *)container->children->items[a];
340 swayc_t *pb = (swayc_t *)container->children->items[b];
341 container->children->items[a] = container->children->items[b];
342 container->children->items[b] = pa;
343 if (is_auto_layout(container->layout)) {
344 size_t ga = auto_group_index(container, a);
345 size_t gb = auto_group_index(container, b);
346 if (ga != gb) {
347 swap_geometry(pa, pb);
348 }
349 }
350 }
351}
352
353void move_container(swayc_t *container, enum movement_direction dir, int move_amt) {
354 enum swayc_layouts layout = L_NONE;
355 swayc_t *parent = container->parent;
356 if (container->is_floating) {
357 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
358 switch(dir) {
359 case MOVE_LEFT:
360 container->x = MAX(0, container->x - move_amt);
361 break;
362 case MOVE_RIGHT:
363 container->x = MIN(output->width - container->width, container->x + move_amt);
364 break;
365 case MOVE_UP:
366 container->y = MAX(0, container->y - move_amt);
367 break;
368 case MOVE_DOWN:
369 container->y = MIN(output->height - container->height, container->y + move_amt);
370 break;
371 default:
372 break;
373 }
374 update_geometry(container);
375 return;
376 }
377 if (container->type != C_VIEW && container->type != C_CONTAINER) {
378 return;
379 }
380 if (dir == MOVE_UP || dir == MOVE_DOWN) {
381 layout = L_VERT;
382 } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
383 layout = L_HORIZ;
384 } else if (dir == MOVE_FIRST) {
385 // swap first child in auto layout with currently focused child
386 if (is_auto_layout(parent->layout)) {
387 int focused_idx = index_child(container);
388 swayc_t *first = parent->children->items[0];
389 if (focused_idx > 0) {
390 list_swap(parent->children, 0, focused_idx);
391 swap_geometry(first, container);
392 }
393 arrange_windows(parent->parent, -1, -1);
394 ipc_event_window(container, "move");
395 set_focused_container_for(parent->parent, container);
396 }
397 return;
398 } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
399 return;
400 }
401 swayc_t *child = container;
402 bool ascended = false;
403
404 // View is wrapped in intermediate container which is needed for displaying
405 // the titlebar. Moving only the view outside of its parent container would just
406 // wrap it again under worspace. There would effectively be no movement,
407 // just a change of wrapping container.
408 if (child->type == C_VIEW &&
409 parent->type == C_CONTAINER &&
410 parent->children->length == 1 &&
411 parent->parent->type == C_WORKSPACE) {
412 child = parent;
413 parent = parent->parent;
414 }
415
416 while (true) {
417 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
418 container,parent,child);
419 if (parent->layout == layout
420 || (layout == L_NONE && (parent->type == C_CONTAINER || parent->type == C_WORKSPACE)) /* accept any layout for next/prev direction */
421 || (parent->layout == L_TABBED && layout == L_HORIZ)
422 || (parent->layout == L_STACKED && layout == L_VERT)
423 || is_auto_layout(parent->layout)) {
424 int diff;
425 // If it has ascended (parent has moved up), no container is removed
426 // so insert it at index, or index+1.
427 // if it has not, the moved container is removed, so it needs to be
428 // inserted at index-1, or index+1
429 if (ascended) {
430 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
431 } else {
432 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
433 }
434 int idx = index_child(child);
435 int desired = idx + diff;
436 if (dir == MOVE_NEXT || dir == MOVE_PREV) {
437 // Next/Prev always wrap.
438 if (desired < 0) {
439 desired += parent->children->length;
440 } else if (desired >= parent->children->length) {
441 desired = 0;
442 }
443 }
444 // when it has ascended, legal insertion position is 0:len
445 // when it has not, legal insertion position is 0:len-1
446 if (desired >= 0 && desired - ascended < parent->children->length) {
447 if (!ascended) {
448 child = parent->children->items[desired];
449 // Move container into sibling container
450 if (child->type == C_CONTAINER) {
451 parent = child;
452 // Insert it in first/last if matching layout, otherwise
453 // insert it next to focused container
454 if (parent->layout == layout
455 || (parent->layout == L_TABBED && layout == L_HORIZ)
456 || (parent->layout == L_STACKED && layout == L_VERT)
457 || is_auto_layout(parent->layout)) {
458 desired = (diff < 0) * parent->children->length;
459 } else {
460 desired = index_child(child->focused) + 1;
461 }
462 //reset geometry
463 container->width = container->height = 0;
464 }
465 }
466 if (container->parent == parent) {
467 swap_children(parent, idx, desired);
468 } else {
469 swayc_t *old_parent = remove_child(container);
470 insert_child(parent, container, desired);
471 destroy_container(old_parent);
472 sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
473 }
474 break;
475 }
476 }
477 // Change parent layout if we need to
478 if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
479 /* swayc_change_layout(parent, layout); */
480 parent->layout = layout;
481 continue;
482 }
483 if (parent->type == C_WORKSPACE) {
484 // If moving to an adjacent output we need a starting position (since this
485 // output might border to multiple outputs).
486 struct wlc_point abs_pos;
487 get_absolute_center_position(container, &abs_pos);
488
489 swayc_t *output = swayc_adjacent_output(parent->parent, dir, &abs_pos, true);
490
491 if (output) {
492 sway_log(L_DEBUG, "Moving between outputs");
493 swayc_t *old_parent = remove_child(container);
494 destroy_container(old_parent);
495
496 swayc_t *dest = output->focused;
497 switch (dir) {
498 case MOVE_LEFT:
499 case MOVE_UP:
500 // reset container geometry
501 container->width = container->height = 0;
502 add_child(dest, container);
503 break;
504 case MOVE_RIGHT:
505 case MOVE_DOWN:
506 // reset container geometry
507 container->width = container->height = 0;
508 insert_child(dest, container, 0);
509 break;
510 default:
511 break;
512 }
513 // arrange new workspace
514 arrange_windows(dest, -1, -1);
515 set_focused_container(container);
516 break;
517 }
518
519 // We simply cannot move any further.
520 if (parent->layout == layout) {
521 break;
522 }
523 // Create container around workspace to insert child into
524 parent = new_container(parent, layout);
525 // Previous line set the resulting container's layout to
526 // workspace_layout. It should have been just layout.
527 parent->layout = parent->parent->layout;
528 }
529 ascended = true;
530 child = parent;
531 parent = child->parent;
532 }
533 arrange_windows(parent->parent, -1, -1);
534 ipc_event_window(container, "move");
535 set_focused_container_for(parent->parent, container);
536}
537
538void move_container_to(swayc_t* container, swayc_t* destination) {
539 if (container == destination || swayc_is_parent_of(container, destination)) {
540 return;
541 }
542 swayc_t *parent = remove_child(container);
543 // Send to new destination
544 if (container->is_floating) {
545 swayc_t *ws = swayc_active_workspace_for(destination);
546 add_floating(ws, container);
547
548 // If the workspace only has one child after adding one, it
549 // means that the workspace was just initialized.
550 if (ws->children->length + ws->floating->length == 1) {
551 ipc_event_workspace(NULL, ws, "init");
552 }
553 } else if (destination->type == C_WORKSPACE) {
554 // reset container geometry
555 container->width = container->height = 0;
556 add_child(destination, container);
557
558 // If the workspace only has one child after adding one, it
559 // means that the workspace was just initialized.
560 if (destination->children->length + destination->floating->length == 1) {
561 ipc_event_workspace(NULL, destination, "init");
562 }
563 } else {
564 // reset container geometry
565 container->width = container->height = 0;
566 add_sibling(destination, container);
567 }
568 // Destroy old container if we need to
569 parent = destroy_container(parent);
570 // Refocus
571 swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT);
572 swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT);
573 set_focused_container(get_focused_view(op1));
574 arrange_windows(op1, -1, -1);
575 update_visibility(op1);
576 if (op1 != op2) {
577 set_focused_container(get_focused_view(op2));
578 arrange_windows(op2, -1, -1);
579 update_visibility(op2);
580 }
581}
582
583void move_workspace_to(swayc_t* workspace, swayc_t* destination) {
584 if (workspace == destination || swayc_is_parent_of(workspace, destination)) {
585 return;
586 }
587 swayc_t *src_op = remove_child(workspace);
588 // reset container geometry
589 workspace->width = workspace->height = 0;
590 add_child(destination, workspace);
591 sort_workspaces(destination);
592 // Refocus destination (change to new workspace)
593 set_focused_container(get_focused_view(workspace));
594 arrange_windows(destination, -1, -1);
595 update_visibility(destination);
596
597 // make sure source output has a workspace
598 if (src_op->children->length == 0) {
599 char *ws_name = workspace_next_name(src_op->name);
600 swayc_t *ws = new_workspace(src_op, ws_name);
601 ws->is_focused = true;
602 free(ws_name);
603 }
604 set_focused_container(get_focused_view(src_op));
605 update_visibility(src_op);
606}
607
608static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
609 const struct wlc_size *res, int left, int right, int top, int bottom) {
610
611 g->size.w += left + right;
612 if (g->origin.x - left < 0) {
613 g->size.w += g->origin.x - left;
614 } else if (g->origin.x + g->size.w - right > res->w) {
615 g->size.w = res->w - g->origin.x + right;
616 }
617
618 g->size.h += top + bottom;
619 if (g->origin.y - top < 0) {
620 g->size.h += g->origin.y - top;
621 } else if (g->origin.y + g->size.h - top > res->h) {
622 g->size.h = res->h - g->origin.y + top;
623 }
624
625 g->origin.x = MIN((uint32_t)MAX(g->origin.x - left, 0), res->w);
626 g->origin.y = MIN((uint32_t)MAX(g->origin.y - top, 0), res->h);
627
628}
629
630static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) {
631 struct wlc_geometry g = *geometry;
632 c->actual_geometry = g;
633
634 swayc_t *output = swayc_parent_by_type(c, C_OUTPUT);
635 struct wlc_size res;
636 output_get_scaled_size(output->handle, &res);
637
638 switch (c->border_type) {
639 case B_NONE:
640 break;
641 case B_PIXEL:
642 adjust_border_geometry(c, &g, &res, c->border_thickness,
643 c->border_thickness, c->border_thickness, c->border_thickness);
644 break;
645 case B_NORMAL:
646 {
647 int title_bar_height = config->font_height + 4; // borders + padding
648
649 adjust_border_geometry(c, &g, &res, c->border_thickness,
650 c->border_thickness, title_bar_height, c->border_thickness);
651
652 struct wlc_geometry title_bar = {
653 .origin = {
654 .x = c->actual_geometry.origin.x - c->border_thickness,
655 .y = c->actual_geometry.origin.y - title_bar_height
656 },
657 .size = {
658 .w = c->actual_geometry.size.w + (2 * c->border_thickness),
659 .h = title_bar_height
660 }
661 };
662 c->title_bar_geometry = title_bar;
663 break;
664 }
665 }
666
667 c->border_geometry = g;
668 *geometry = c->actual_geometry;
669
670 update_container_border(c);
671}
672
673void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
674 switch (parent->layout) {
675 case L_TABBED:
676 case L_STACKED:
677 if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
678 // cache current geometry for all non-float children
679 int i;
680 for (i = 0; i < parent->children->length; ++i) {
681 swayc_t *child = parent->children->items[i];
682 child->cached_geometry.origin.x = child->x;
683 child->cached_geometry.origin.y = child->y;
684 child->cached_geometry.size.w = child->width;
685 child->cached_geometry.size.h = child->height;
686 }
687 }
688 break;
689 default:
690 if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
691 // recover cached geometry for all non-float children
692 int i;
693 for (i = 0; i < parent->children->length; ++i) {
694 swayc_t *child = parent->children->items[i];
695 // only recoverer cached geometry if non-zero
696 if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
697 child->x = child->cached_geometry.origin.x;
698 child->y = child->cached_geometry.origin.y;
699 child->width = child->cached_geometry.size.w;
700 child->height = child->cached_geometry.size.h;
701 }
702 }
703 }
704 break;
705 }
706}
707
708static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
709 swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
710 swayc_t *op = ws->parent;
711 int gap = container->is_floating ? 0 : swayc_gap(container);
712 if (gap % 2 != 0) {
713 // because gaps are implemented as "half sized margins" it's currently
714 // not possible to align views properly with odd sized gaps.
715 gap -= 1;
716 }
717
718 g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
719 g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
720 g->size.w = container->width > gap ? container->width - gap : 1;
721 g->size.h = container->height > gap ? container->height - gap : 1;
722
723 if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
724 // Remove gap against the workspace edges. Because a pixel is not
725 // divisable, depending on gap size and the number of siblings our view
726 // might be at the workspace edge without being exactly so (thus test
727 // with gap, and align correctly).
728 if (container->x - gap <= ws->x) {
729 g->origin.x = ws->x;
730 g->size.w = container->width - gap/2;
731 }
732 if (container->y - gap <= ws->y) {
733 g->origin.y = ws->y;
734 g->size.h = container->height - gap/2;
735 }
736 if (container->x + container->width + gap >= ws->x + ws->width) {
737 g->size.w = ws->x + ws->width - g->origin.x;
738 }
739 if (container->y + container->height + gap >= ws->y + ws->height) {
740 g->size.h = ws->y + ws->height - g->origin.y;
741 }
742 }
743
744 return gap;
745}
746
747void update_geometry(swayc_t *container) {
748 if (container->type != C_VIEW && container->type != C_CONTAINER) {
749 return;
750 }
751
752 swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
753 swayc_t *op = workspace->parent;
754 swayc_t *parent = container->parent;
755
756 struct wlc_geometry geometry = {
757 .origin = {
758 .x = container->x < op->width ? container->x : op->width-1,
759 .y = container->y < op->height ? container->y : op->height-1
760 },
761 .size = {
762 .w = container->width,
763 .h = container->height,
764 }
765 };
766
767 int gap = 0;
768
769 // apply inner gaps to non-tabbed/stacked containers
770 swayc_t *p = swayc_tabbed_stacked_ancestor(container);
771 if (p == NULL) {
772 gap = update_gap_geometry(container, &geometry);
773 }
774
775 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
776 struct wlc_size size;
777 output_get_scaled_size(output->handle, &size);
778
779 if (swayc_is_fullscreen(container)) {
780 geometry.origin.x = 0;
781 geometry.origin.y = 0;
782 geometry.size.w = size.w;
783 geometry.size.h = size.h;
784 if (op->focused == workspace) {
785 wlc_view_bring_to_front(container->handle);
786 }
787
788 container->border_geometry = wlc_geometry_zero;
789 container->title_bar_geometry = wlc_geometry_zero;
790 border_clear(container->border);
791 } else if (container->is_floating) { // allocate border for floating window
792 update_border_geometry_floating(container, &geometry);
793 } else if (!container->is_floating) { // allocate border for titled window
794 container->border_geometry = geometry;
795
796 int border_top = container->border_thickness;
797 int border_bottom = container->border_thickness;
798 int border_left = container->border_thickness;
799 int border_right = container->border_thickness;
800
801 // handle hide_edge_borders
802 if (config->hide_edge_borders != E_NONE && (gap <= 0 || (config->smart_gaps && workspace->children->length == 1))) {
803 if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) {
804 if (geometry.origin.x == workspace->x) {
805 border_left = 0;
806 }
807
808 if (geometry.origin.x + geometry.size.w == workspace->x + workspace->width) {
809 border_right = 0;
810 }
811 }
812
813 if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) {
814 if (geometry.origin.y == workspace->y || should_hide_top_border(container, geometry.origin.y)) {
815 border_top = 0;
816 }
817
818 if (geometry.origin.y + geometry.size.h == workspace->y + workspace->height) {
819 border_bottom = 0;
820 }
821 }
822
823 if (config->hide_edge_borders == E_SMART && workspace->children->length == 1) {
824 border_top = 0;
825 border_bottom = 0;
826 border_left = 0;
827 border_right = 0;
828 }
829 }
830
831 int title_bar_height = config->font_height + 4; //borders + padding
832
833 if (parent->layout == L_TABBED && parent->children->length > 1) {
834 int i, x = 0, w, l, r;
835 l = parent->children->length;
836 w = geometry.size.w / l;
837 r = geometry.size.w % l;
838 for (i = 0; i < parent->children->length; ++i) {
839 swayc_t *view = parent->children->items[i];
840 if (view == container) {
841 x = w * i;
842 if (i == l - 1) {
843 w += r;
844 }
845 break;
846 }
847 }
848
849 struct wlc_geometry title_bar = {
850 .origin = {
851 .x = container->border_geometry.origin.x + x,
852 .y = container->border_geometry.origin.y
853 },
854 .size = {
855 .w = w,
856 .h = title_bar_height
857 }
858 };
859 geometry.origin.x += border_left;
860 geometry.origin.y += title_bar.size.h;
861 geometry.size.w -= (border_left + border_right);
862 geometry.size.h -= (border_bottom + title_bar.size.h);
863 container->title_bar_geometry = title_bar;
864 } else if (parent->layout == L_STACKED && parent->children->length > 1) {
865 int i, y = 0;
866 for (i = 0; i < parent->children->length; ++i) {
867 swayc_t *view = parent->children->items[i];
868 if (view == container) {
869 y = title_bar_height * i;
870 }
871 }
872
873 struct wlc_geometry title_bar = {
874 .origin = {
875 .x = container->border_geometry.origin.x,
876 .y = container->border_geometry.origin.y + y
877 },
878 .size = {
879 .w = container->border_geometry.size.w,
880 .h = title_bar_height
881 }
882 };
883 title_bar_height = title_bar_height * parent->children->length;
884 geometry.origin.x += border_left;
885 geometry.origin.y += title_bar_height;
886 geometry.size.w -= (border_left + border_right);
887 geometry.size.h -= (border_bottom + title_bar_height);
888 container->title_bar_geometry = title_bar;
889 } else {
890 switch (container->border_type) {
891 case B_NONE:
892 break;
893 case B_PIXEL:
894 geometry.origin.x += border_left;
895 geometry.origin.y += border_top;
896 geometry.size.w -= (border_left + border_right);
897 geometry.size.h -= (border_top + border_bottom);
898 break;
899 case B_NORMAL:
900 {
901 struct wlc_geometry title_bar = {
902 .origin = {
903 .x = container->border_geometry.origin.x,
904 .y = container->border_geometry.origin.y
905 },
906 .size = {
907 .w = container->border_geometry.size.w,
908 .h = title_bar_height
909 }
910 };
911 geometry.origin.x += border_left;
912 geometry.origin.y += title_bar.size.h;
913 geometry.size.w -= (border_left + border_right);
914 geometry.size.h -= (border_bottom + title_bar.size.h);
915 container->title_bar_geometry = title_bar;
916 break;
917 }
918 }
919 }
920
921 container->actual_geometry = geometry;
922
923 if (container->type == C_VIEW) {
924 update_container_border(container);
925 }
926 }
927
928 if (container->type == C_VIEW) {
929 wlc_view_set_geometry(container->handle, 0, &geometry);
930 }
931}
932
933/**
934 * Layout application prototypes
935 */
936static void apply_horiz_layout(swayc_t *container, const double x,
937 const double y, const double width,
938 const double height, const int start,
939 const int end);
940static void apply_vert_layout(swayc_t *container, const double x,
941 const double y, const double width,
942 const double height, const int start,
943 const int end);
944static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
945 double y, double width,
946 double height);
947
948static void apply_auto_layout(swayc_t *container, const double x, const double y,
949 const double width, const double height,
950 enum swayc_layouts group_layout,
951 bool master_first);
952
953static void arrange_windows_r(swayc_t *container, double width, double height) {
954 int i;
955 if (width == -1 || height == -1) {
956 swayc_log(L_DEBUG, container, "Arranging layout for %p", container);
957 width = container->width;
958 height = container->height;
959 }
960 // pixels are indivisible. if we don't round the pixels, then the view
961 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
962 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
963 width = floor(width);
964 height = floor(height);
965
966 sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
967 container->name, container->width, container->height, container->x,
968 container->y);
969
970 double x = 0, y = 0;
971 switch (container->type) {
972 case C_ROOT:
973 for (i = 0; i < container->children->length; ++i) {
974 swayc_t *output = container->children->items[i];
975 sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y);
976 arrange_windows_r(output, -1, -1);
977 }
978 return;
979 case C_OUTPUT:
980 {
981 struct wlc_size resolution;
982 output_get_scaled_size(container->handle, &resolution);
983 width = resolution.w; height = resolution.h;
984 // output must have correct size due to e.g. seamless mouse,
985 // but a workspace might be smaller depending on panels.
986 container->width = width;
987 container->height = height;
988 }
989 // arrange all workspaces:
990 for (i = 0; i < container->children->length; ++i) {
991 swayc_t *child = container->children->items[i];
992 arrange_windows_r(child, -1, -1);
993 }
994 // Bring all unmanaged views to the front
995 for (i = 0; i < container->unmanaged->length; ++i) {
996 wlc_handle *handle = container->unmanaged->items[i];
997 wlc_view_bring_to_front(*handle);
998 }
999 return;
1000 case C_WORKSPACE:
1001 {
1002 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
1003 width = output->width, height = output->height;
1004 for (i = 0; i < desktop_shell.panels->length; ++i) {
1005 struct panel_config *config = desktop_shell.panels->items[i];
1006 if (config->output == output->handle) {
1007 struct wlc_size size = *wlc_surface_get_size(config->surface);
1008 sway_log(L_DEBUG, "-> Found panel for this workspace: %ux%u, position: %u", size.w, size.h, config->panel_position);
1009 switch (config->panel_position) {
1010 case DESKTOP_SHELL_PANEL_POSITION_TOP:
1011 y += size.h; height -= size.h;
1012 break;
1013 case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
1014 height -= size.h;
1015 break;
1016 case DESKTOP_SHELL_PANEL_POSITION_LEFT:
1017 x += size.w; width -= size.w;
1018 break;
1019 case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
1020 width -= size.w;
1021 break;
1022 }
1023 }
1024 }
1025 int gap = swayc_gap(container);
1026 x = container->x = x + gap;
1027 y = container->y = y + gap;
1028 width = container->width = width - gap * 2;
1029 height = container->height = height - gap * 2;
1030 sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
1031 }
1032 // children are properly handled below
1033 break;
1034 case C_VIEW:
1035 {
1036 container->width = width;
1037 container->height = height;
1038 update_geometry(container);
1039 sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", container->width,
1040 container->height, container->x, container->y);
1041 }
1042 return;
1043 default:
1044 container->width = width;
1045 container->height = height;
1046 x = container->x;
1047 y = container->y;
1048
1049 // add gaps to top level tapped/stacked container
1050 if (container->parent->type == C_WORKSPACE &&
1051 (container->layout == L_TABBED || container->layout == L_STACKED)) {
1052 update_geometry(container);
1053 width = container->border_geometry.size.w;
1054 height = container->border_geometry.size.h;
1055 x = container->border_geometry.origin.x;
1056 y = container->border_geometry.origin.y;
1057 }
1058
1059 // update container size if it's a direct child in a tabbed/stacked layout
1060 // if parent is a workspace, its actual_geometry won't be initialized
1061 if (swayc_tabbed_stacked_parent(container) != NULL &&
1062 container->parent->type != C_WORKSPACE) {
1063 // Use parent actual_geometry as a base for calculating
1064 // container geometry
1065 container->width = container->parent->actual_geometry.size.w;
1066 container->height = container->parent->actual_geometry.size.h;
1067 container->x = container->parent->actual_geometry.origin.x;
1068 container->y = container->parent->actual_geometry.origin.y;
1069
1070 update_geometry(container);
1071 width = container->width = container->actual_geometry.size.w;
1072 height = container->height = container->actual_geometry.size.h;
1073 x = container->x = container->actual_geometry.origin.x;
1074 y = container->y = container->actual_geometry.origin.y;
1075 }
1076
1077 break;
1078 }
1079
1080 switch (container->layout) {
1081 case L_HORIZ:
1082 default:
1083 apply_horiz_layout(container, x, y, width, height, 0,
1084 container->children->length);
1085 break;
1086 case L_VERT:
1087 apply_vert_layout(container, x, y, width, height, 0,
1088 container->children->length);
1089 break;
1090 case L_TABBED:
1091 case L_STACKED:
1092 apply_tabbed_or_stacked_layout(container, x, y, width, height);
1093 break;
1094 case L_AUTO_LEFT:
1095 apply_auto_layout(container, x, y, width, height, L_VERT, true);
1096 break;
1097 case L_AUTO_RIGHT:
1098 apply_auto_layout(container, x, y, width, height, L_VERT, false);
1099 break;
1100 case L_AUTO_TOP:
1101 apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
1102 break;
1103 case L_AUTO_BOTTOM:
1104 apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
1105 break;
1106 }
1107
1108 // Arrage floating layouts for workspaces last
1109 if (container->type == C_WORKSPACE) {
1110 for (int i = 0; i < container->floating->length; ++i) {
1111 swayc_t *view = container->floating->items[i];
1112 if (view->type == C_VIEW) {
1113 update_geometry(view);
1114 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
1115 view->width, view->height, view->x, view->y);
1116 if (swayc_is_fullscreen(view)) {
1117 wlc_view_bring_to_front(view->handle);
1118 } else if (!container->focused ||
1119 !swayc_is_fullscreen(container->focused)) {
1120 wlc_view_bring_to_front(view->handle);
1121 }
1122 }
1123 }
1124 }
1125}
1126
1127void apply_horiz_layout(swayc_t *container, const double x, const double y,
1128 const double width, const double height,
1129 const int start, const int end) {
1130 double scale = 0;
1131 // Calculate total width
1132 for (int i = start; i < end; ++i) {
1133 double *old_width = &((swayc_t *)container->children->items[i])->width;
1134 if (*old_width <= 0) {
1135 if (end - start > 1) {
1136 *old_width = width / (end - start - 1);
1137 } else {
1138 *old_width = width;
1139 }
1140 }
1141 scale += *old_width;
1142 }
1143 scale = width / scale;
1144
1145 // Resize windows
1146 double child_x = x;
1147 if (scale > 0.1) {
1148 sway_log(L_DEBUG, "Arranging %p horizontally", container);
1149 swayc_t *focused = NULL;
1150 for (int i = start; i < end; ++i) {
1151 swayc_t *child = container->children->items[i];
1152 sway_log(L_DEBUG,
1153 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1154 child->type, width, scale);
1155 child->x = child_x;
1156 child->y = y;
1157
1158 if (child == container->focused) {
1159 focused = child;
1160 }
1161
1162 if (i == end - 1) {
1163 double remaining_width = x + width - child_x;
1164 arrange_windows_r(child, remaining_width, height);
1165 } else {
1166 arrange_windows_r(child, child->width * scale, height);
1167 }
1168 child_x += child->width;
1169 }
1170
1171 // update focused view border last because it may
1172 // depend on the title bar geometry of its siblings.
1173 if (focused && container->children->length > 1) {
1174 update_container_border(focused);
1175 }
1176 }
1177}
1178
1179void apply_vert_layout(swayc_t *container, const double x, const double y,
1180 const double width, const double height, const int start,
1181 const int end) {
1182 int i;
1183 double scale = 0;
1184 // Calculate total height
1185 for (i = start; i < end; ++i) {
1186 double *old_height = &((swayc_t *)container->children->items[i])->height;
1187 if (*old_height <= 0) {
1188 if (end - start > 1) {
1189 *old_height = height / (end - start - 1);
1190 } else {
1191 *old_height = height;
1192 }
1193 }
1194 scale += *old_height;
1195 }
1196 scale = height / scale;
1197
1198 // Resize
1199 double child_y = y;
1200 if (scale > 0.1) {
1201 sway_log(L_DEBUG, "Arranging %p vertically", container);
1202 swayc_t *focused = NULL;
1203 for (i = start; i < end; ++i) {
1204 swayc_t *child = container->children->items[i];
1205 sway_log(L_DEBUG,
1206 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1207 child->type, height, scale);
1208 child->x = x;
1209 child->y = child_y;
1210
1211 if (child == container->focused) {
1212 focused = child;
1213 }
1214
1215 if (i == end - 1) {
1216 double remaining_height = y + height - child_y;
1217 arrange_windows_r(child, width, remaining_height);
1218 } else {
1219 arrange_windows_r(child, width, child->height * scale);
1220 }
1221 child_y += child->height;
1222 }
1223
1224 // update focused view border last because it may
1225 // depend on the title bar geometry of its siblings.
1226 if (focused && container->children->length > 1) {
1227 update_container_border(focused);
1228 }
1229 }
1230}
1231
1232void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
1233 double width, double height) {
1234 int i;
1235 swayc_t *focused = NULL;
1236 for (i = 0; i < container->children->length; ++i) {
1237 swayc_t *child = container->children->items[i];
1238 child->x = x;
1239 child->y = y;
1240 if (child == container->focused) {
1241 focused = child;
1242 } else {
1243 arrange_windows_r(child, width, height);
1244 }
1245 }
1246
1247 if (focused) {
1248 arrange_windows_r(focused, width, height);
1249 }
1250}
1251
1252void apply_auto_layout(swayc_t *container, const double x, const double y,
1253 const double width, const double height,
1254 enum swayc_layouts group_layout,
1255 bool master_first) {
1256 // Auto layout "container" in width x height @ x, y
1257 // using "group_layout" for each of the groups in the container.
1258 // There is one "master" group, plus container->nb_slave_groups.
1259 // Each group is layed out side by side following the "major" axis.
1260 // The direction of the layout used for groups is the "minor" axis.
1261 // Example:
1262 //
1263 // ---- major axis -->
1264 // +---------+-----------+
1265 // | | | |
1266 // | master | slave 1 | |
1267 // | +-----------+ | minor axis (direction of group_layout)
1268 // | | | |
1269 // | | slave 2 | V
1270 // +---------+-----------+
1271 //
1272 // container with three children (one master and two slaves) and
1273 // a single slave group (containing slave 1 and 2). The master
1274 // group and slave group are layed out using L_VERT.
1275
1276 size_t nb_groups = auto_group_count(container);
1277
1278 // the target dimension of the container along the "major" axis, each
1279 // group in the container will be layed out using "group_layout" along
1280 // the "minor" axis.
1281 double dim_maj;
1282 double pos_maj;
1283
1284 // x and y coords for the next group to be laid out.
1285 const double *group_x, *group_y;
1286
1287 // pos of the next group to layout along the major axis
1288 double pos;
1289
1290 // size of the next group along the major axis.
1291 double group_dim;
1292
1293 // height and width of next group to be laid out.
1294 const double *group_h, *group_w;
1295
1296 switch (group_layout) {
1297 default:
1298 sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
1299 group_layout, __func__);
1300 /* fall through */
1301 case L_VERT:
1302 dim_maj = width;
1303 pos_maj = x;
1304
1305 group_x = &pos;
1306 group_y = &y;
1307 group_w = &group_dim;
1308 group_h = &height;
1309 break;
1310 case L_HORIZ:
1311 dim_maj = height;
1312 pos_maj = y;
1313
1314 group_x = &x;
1315 group_y = &pos;
1316 group_w = &width;
1317 group_h = &group_dim;
1318 break;
1319 }
1320
1321 /* Determine the dimension of each of the groups in the layout.
1322 * Dimension will be width for a VERT layout and height for a HORIZ
1323 * layout. */
1324 double old_group_dim[nb_groups];
1325 double old_dim = 0;
1326 for (size_t group = 0; group < nb_groups; ++group) {
1327 int idx;
1328 if (auto_group_bounds(container, group, &idx, NULL)) {
1329 swayc_t *child = container->children->items[idx];
1330 double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
1331 if (*dim <= 0) {
1332 // New child with uninitialized dimension
1333 *dim = dim_maj;
1334 if (nb_groups > 1) {
1335 // child gets a dimension proportional to existing groups,
1336 // it will be later scaled based on to the available size
1337 // in the major axis.
1338 *dim /= (nb_groups - 1);
1339 }
1340 }
1341 old_dim += *dim;
1342 old_group_dim[group] = *dim;
1343 }
1344 }
1345 double scale = dim_maj / old_dim;
1346
1347 /* Apply layout to each group */
1348 pos = pos_maj;
1349
1350 for (size_t group = 0; group < nb_groups; ++group) {
1351 int start, end; // index of first (inclusive) and last (exclusive) child in the group
1352 if (auto_group_bounds(container, group, &start, &end)) {
1353 // adjusted size of the group
1354 group_dim = old_group_dim[group] * scale;
1355 if (group == nb_groups - 1) {
1356 group_dim = pos_maj + dim_maj - pos; // remaining width
1357 }
1358 sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
1359 container, group, start, end, *group_w, *group_h, *group_x, *group_y);
1360 switch (group_layout) {
1361 default:
1362 case L_VERT:
1363 apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1364 break;
1365 case L_HORIZ:
1366 apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1367 break;
1368 }
1369
1370 /* update position for next group */
1371 pos += group_dim;
1372 }
1373 }
1374}
1375
1376void arrange_windows(swayc_t *container, double width, double height) {
1377 update_visibility(container);
1378 arrange_windows_r(container, width, height);
1379 layout_log(&root_container, 0);
1380}
1381
1382void arrange_backgrounds(void) {
1383 struct background_config *bg;
1384 for (int i = 0; i < desktop_shell.backgrounds->length; ++i) {
1385 bg = desktop_shell.backgrounds->items[i];
1386 wlc_view_send_to_back(bg->handle);
1387 }
1388}
1389
1390/**
1391 * Get swayc in the direction of newly entered output.
1392 */
1393static swayc_t *get_swayc_in_output_direction(swayc_t *output, enum movement_direction dir) {
1394 if (!output) {
1395 return NULL;
1396 }
1397
1398 swayc_t *ws = swayc_focus_by_type(output, C_WORKSPACE);
1399 if (ws && ws->children->length > 0) {
1400 switch (dir) {
1401 case MOVE_LEFT:
1402 // get most right child of new output
1403 return ws->children->items[ws->children->length-1];
1404 case MOVE_RIGHT:
1405 // get most left child of new output
1406 return ws->children->items[0];
1407 case MOVE_UP:
1408 case MOVE_DOWN:
1409 {
1410 swayc_t *focused_view = swayc_focus_by_type(ws, C_VIEW);
1411 if (focused_view && focused_view->parent) {
1412 swayc_t *parent = focused_view->parent;
1413 if (parent->layout == L_VERT) {
1414 if (dir == MOVE_UP) {
1415 // get child furthest down on new output
1416 return parent->children->items[parent->children->length-1];
1417 } else if (dir == MOVE_DOWN) {
1418 // get child furthest up on new output
1419 return parent->children->items[0];
1420 }
1421 }
1422 return focused_view;
1423 }
1424 break;
1425 }
1426 default:
1427 break;
1428 }
1429 }
1430
1431 return output;
1432}
1433
1434swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit) {
1435 if (dir == MOVE_CHILD) {
1436 return container->focused;
1437 }
1438
1439 swayc_t *parent = container->parent;
1440 if (dir == MOVE_PARENT) {
1441 if (parent->type == C_OUTPUT) {
1442 return NULL;
1443 } else {
1444 return parent;
1445 }
1446 }
1447
1448 if (dir == MOVE_PREV || dir == MOVE_NEXT) {
1449 int focused_idx = index_child(container);
1450 if (focused_idx == -1) {
1451 return NULL;
1452 } else {
1453 int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
1454 parent->children->length;
1455 if (desired < 0) {
1456 desired += parent->children->length;
1457 }
1458 return parent->children->items[desired];
1459 }
1460 }
1461
1462 // If moving to an adjacent output we need a starting position (since this
1463 // output might border to multiple outputs).
1464 struct wlc_point abs_pos;
1465 get_absolute_center_position(container, &abs_pos);
1466
1467 if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
1468 sway_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
1469 container = swayc_parent_by_type(container, C_OUTPUT);
1470 get_absolute_center_position(container, &abs_pos);
1471 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1472 return get_swayc_in_output_direction(output, dir);
1473 }
1474
1475 if (container->type == C_WORKSPACE && container->fullscreen) {
1476 sway_log(L_DEBUG, "Moving to fullscreen view");
1477 return container->fullscreen;
1478 }
1479
1480 swayc_t *wrap_candidate = NULL;
1481 while (true) {
1482 // Test if we can even make a difference here
1483 bool can_move = false;
1484 int desired;
1485 int idx = index_child(container);
1486 if (parent->type == C_ROOT) {
1487 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1488 if (!output || output == container) {
1489 return wrap_candidate;
1490 }
1491 sway_log(L_DEBUG, "Moving between outputs");
1492 return get_swayc_in_output_direction(output, dir);
1493 } else {
1494 if (is_auto_layout(parent->layout)) {
1495 bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
1496 ? dir == MOVE_LEFT || dir == MOVE_RIGHT
1497 : dir == MOVE_DOWN || dir == MOVE_UP;
1498 size_t gidx = auto_group_index(parent, idx);
1499 if (is_major) {
1500 size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1501 can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
1502 } else {
1503 desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1504 int start, end;
1505 can_move = auto_group_bounds(parent, gidx, &start, &end)
1506 && desired >= start && desired < end;
1507 }
1508 } else {
1509 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
1510 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
1511 can_move = true;
1512 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
1513 }
1514 } else {
1515 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
1516 can_move = true;
1517 desired = idx + (dir == MOVE_UP ? -1 : 1);
1518 }
1519 }
1520 }
1521 }
1522
1523 if (can_move) {
1524 if (container->is_floating) {
1525 if (desired < 0) {
1526 wrap_candidate = parent->floating->items[parent->floating->length-1];
1527 } else if (desired >= parent->floating->length){
1528 wrap_candidate = parent->floating->items[0];
1529 } else {
1530 wrap_candidate = parent->floating->items[desired];
1531 }
1532 if (wrap_candidate) {
1533 wlc_view_bring_to_front(wrap_candidate->handle);
1534 }
1535 return wrap_candidate;
1536 } else if (desired < 0 || desired >= parent->children->length) {
1537 can_move = false;
1538 int len = parent->children->length;
1539 if (!wrap_candidate && len > 1) {
1540 if (desired < 0) {
1541 wrap_candidate = parent->children->items[len-1];
1542 } else {
1543 wrap_candidate = parent->children->items[0];
1544 }
1545 if (config->force_focus_wrapping) {
1546 return wrap_candidate;
1547 }
1548 }
1549 } else {
1550 sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
1551 idx, container, dir, desired, parent->children->items[desired]);
1552 return parent->children->items[desired];
1553 }
1554 }
1555 if (!can_move) {
1556 container = parent;
1557 parent = parent->parent;
1558 if (!parent || container == limit) {
1559 // wrapping is the last chance
1560 return wrap_candidate;
1561 }
1562 }
1563 }
1564}
1565
1566swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) {
1567 return get_swayc_in_direction_under(container, dir, NULL);
1568}
1569
1570void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge) {
1571 int i;
1572 bool layout_match = true;
1573 sway_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
1574 if (edge == WLC_RESIZE_EDGE_LEFT || edge == WLC_RESIZE_EDGE_RIGHT) {
1575 container->width += amount;
1576 layout_match = container->layout == L_HORIZ;
1577 } else if (edge == WLC_RESIZE_EDGE_TOP || edge == WLC_RESIZE_EDGE_BOTTOM) {
1578 container->height += amount;
1579 layout_match = container->layout == L_VERT;
1580 }
1581 if (container->type == C_VIEW) {
1582 update_geometry(container);
1583 return;
1584 }
1585 if (layout_match) {
1586 for (i = 0; i < container->children->length; i++) {
1587 recursive_resize(container->children->items[i], amount/container->children->length, edge);
1588 }
1589 } else {
1590 for (i = 0; i < container->children->length; i++) {
1591 recursive_resize(container->children->items[i], amount, edge);
1592 }
1593 }
1594}
1595
1596enum swayc_layouts default_layout(swayc_t *output) {
1597 if (config->default_layout != L_NONE) {
1598 return config->default_layout;
1599 } else if (config->default_orientation != L_NONE) {
1600 return config->default_orientation;
1601 } else if (output->width >= output->height) {
1602 return L_HORIZ;
1603 } else {
1604 return L_VERT;
1605 }
1606}
1607
1608bool is_auto_layout(enum swayc_layouts layout) {
1609 return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
1610}
1611
1612/**
1613 * Return the number of master elements in a container
1614 */
1615static inline size_t auto_master_count(const swayc_t *container) {
1616 sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
1617 container, container->children->length);
1618 return MIN(container->nb_master, (size_t)container->children->length);
1619}
1620
1621/**
1622 * Return the number of children in the slave groups. This corresponds to the children
1623 * that are not members of the master group.
1624 */
1625static inline size_t auto_slave_count(const swayc_t *container) {
1626 return container->children->length - auto_master_count(container);
1627}
1628
1629/**
1630 * Return the number of slave groups in the container.
1631 */
1632size_t auto_slave_group_count(const swayc_t *container) {
1633 return MIN(container->nb_slave_groups, auto_slave_count(container));
1634}
1635
1636/**
1637 * Return the combined number of master and slave groups in the container.
1638 */
1639size_t auto_group_count(const swayc_t *container) {
1640 return auto_slave_group_count(container)
1641 + (container->children->length && container->nb_master ? 1 : 0);
1642}
1643
1644/**
1645 * given the index of a container's child, return the index of the first child of the group
1646 * which index is a member of.
1647 */
1648int auto_group_start_index(const swayc_t *container, int index) {
1649 if (index < 0 || ! is_auto_layout(container->layout)
1650 || (size_t)index < container->nb_master) {
1651 return 0;
1652 } else {
1653 size_t nb_slaves = auto_slave_count(container);
1654 size_t nb_slave_grp = auto_slave_group_count(container);
1655 size_t grp_sz = nb_slaves / nb_slave_grp;
1656 size_t remainder = nb_slaves % nb_slave_grp;
1657 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1658 int start_idx;
1659 if (index < idx2) {
1660 start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
1661 } else {
1662 start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
1663 }
1664 return MIN(start_idx, container->children->length);
1665 }
1666}
1667
1668/**
1669 * given the index of a container's child, return the index of the first child of the group
1670 * that follows the one which index is a member of.
1671 * This makes the function usable to walk through the groups in a container.
1672 */
1673int auto_group_end_index(const swayc_t *container, int index) {
1674 if (index < 0 || ! is_auto_layout(container->layout)) {
1675 return container->children->length;
1676 } else {
1677 int nxt_idx;
1678 if ((size_t)index < container->nb_master) {
1679 nxt_idx = auto_master_count(container);
1680 } else {
1681 size_t nb_slaves = auto_slave_count(container);
1682 size_t nb_slave_grp = auto_slave_group_count(container);
1683 size_t grp_sz = nb_slaves / nb_slave_grp;
1684 size_t remainder = nb_slaves % nb_slave_grp;
1685 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1686 if (index < idx2) {
1687 nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
1688 } else {
1689 nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
1690 }
1691 }
1692 return MIN(nxt_idx, container->children->length);
1693 }
1694}
1695
1696/**
1697 * return the index of the Group containing <index>th child of <container>.
1698 * The index is the order of the group along the container's major axis (starting at 0).
1699 */
1700size_t auto_group_index(const swayc_t *container, int index) {
1701 if (index < 0) {
1702 return 0;
1703 }
1704 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1705 size_t nb_slaves = auto_slave_count(container);
1706 if ((size_t)index < container->nb_master) {
1707 if (master_first || nb_slaves <= 0) {
1708 return 0;
1709 } else {
1710 return auto_slave_group_count(container);
1711 }
1712 } else {
1713 size_t nb_slave_grp = auto_slave_group_count(container);
1714 size_t grp_sz = nb_slaves / nb_slave_grp;
1715 size_t remainder = nb_slaves % nb_slave_grp;
1716 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1717 size_t grp_idx;
1718 if (index < idx2) {
1719 grp_idx = (index - container->nb_master) / grp_sz;
1720 } else {
1721 grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
1722 }
1723 return grp_idx + (master_first && container-> nb_master ? 1 : 0);
1724 }
1725}
1726
1727/**
1728 * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
1729 * an auto layout.
1730 * If the bounds of the given group can be calculated, they are returned in the start/end
1731 * parameters (int pointers) and the return value will be true.
1732 * The indexes are passed by reference and can be NULL.
1733 */
1734bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
1735 size_t nb_grp = auto_group_count(container);
1736 if (group_index >= nb_grp) {
1737 return false;
1738 }
1739 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1740 size_t nb_master = auto_master_count(container);
1741 size_t nb_slave_grp = auto_slave_group_count(container);
1742 int g_start, g_end;
1743 if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
1744 g_start = 0;
1745 g_end = nb_master;
1746 } else {
1747 size_t nb_slaves = auto_slave_count(container);
1748 size_t grp_sz = nb_slaves / nb_slave_grp;
1749 size_t remainder = nb_slaves % nb_slave_grp;
1750 size_t g0 = master_first && container->nb_master ? 1 : 0;
1751 size_t g1 = g0 + nb_slave_grp - remainder;
1752 if (group_index < g1) {
1753 g_start = container->nb_master + (group_index - g0) * grp_sz;
1754 g_end = g_start + grp_sz;
1755 } else {
1756 size_t g2 = group_index - g1;
1757 g_start = container->nb_master
1758 + (nb_slave_grp - remainder) * grp_sz
1759 + g2 * (grp_sz + 1);
1760 g_end = g_start + grp_sz + 1;
1761 }
1762 }
1763 if (start) {
1764 *start = g_start;
1765 }
1766 if (end) {
1767 *end = g_end;
1768 }
1769 return true;
1770}
diff --git a/sway/main.c b/sway/main.c
index cc9147b8..efb674b6 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,58 +1,46 @@
1#define _XOPEN_SOURCE 700 1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 200112L 2#define _POSIX_C_SOURCE 200112L
3#include <stdio.h> 3#include <getopt.h>
4#include <stdlib.h> 4#include <signal.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <wlc/wlc.h> 6#include <stdlib.h>
7#include <sys/wait.h> 7#include <stdio.h>
8#include <sys/types.h> 8#include <string.h>
9#include <sys/stat.h> 9#include <sys/stat.h>
10#include <sys/types.h>
11#include <sys/wait.h>
10#include <sys/un.h> 12#include <sys/un.h>
11#include <signal.h>
12#include <unistd.h> 13#include <unistd.h>
13#include <getopt.h>
14#ifdef __linux__ 14#ifdef __linux__
15#include <sys/capability.h> 15#include <sys/capability.h>
16#include <sys/prctl.h> 16#include <sys/prctl.h>
17#endif 17#endif
18#include "sway/extensions.h" 18#include <wlr/util/log.h>
19#include "sway/layout.h"
20#include "sway/config.h" 19#include "sway/config.h"
21#include "sway/security.h" 20#include "sway/debug.h"
22#include "sway/handlers.h" 21#include "sway/server.h"
23#include "sway/input.h" 22#include "sway/tree/layout.h"
24#include "sway/ipc-server.h" 23#include "sway/ipc-server.h"
25#include "ipc-client.h" 24#include "ipc-client.h"
26#include "readline.h" 25#include "readline.h"
27#include "stringop.h" 26#include "stringop.h"
28#include "sway.h"
29#include "log.h"
30#include "util.h" 27#include "util.h"
31 28
32static bool terminate_request = false; 29static bool terminate_request = false;
33static int exit_value = 0; 30static int exit_value = 0;
31struct sway_server server;
34 32
35void sway_terminate(int exit_code) { 33void sway_terminate(int exit_code) {
36 terminate_request = true; 34 terminate_request = true;
37 exit_value = exit_code; 35 exit_value = exit_code;
38 wlc_terminate(); 36 wl_display_terminate(server.wl_display);
39} 37}
40 38
41void sig_handler(int signal) { 39void sig_handler(int signal) {
42 close_views(&root_container); 40 //close_views(&root_container);
43 sway_terminate(EXIT_SUCCESS); 41 sway_terminate(EXIT_SUCCESS);
44} 42}
45 43
46static void wlc_log_handler(enum wlc_log_type type, const char *str) {
47 if (type == WLC_LOG_ERROR) {
48 sway_log(L_ERROR, "[wlc] %s", str);
49 } else if (type == WLC_LOG_WARN) {
50 sway_log(L_INFO, "[wlc] %s", str);
51 } else {
52 sway_log(L_DEBUG, "[wlc] %s", str);
53 }
54}
55
56void detect_raspi() { 44void detect_raspi() {
57 bool raspi = false; 45 bool raspi = false;
58 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); 46 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
@@ -98,23 +86,16 @@ void detect_proprietary() {
98 if (!f) { 86 if (!f) {
99 return; 87 return;
100 } 88 }
101 bool nvidia = false, nvidia_modeset = false, nvidia_uvm = false, nvidia_drm = false;
102 while (!feof(f)) { 89 while (!feof(f)) {
103 char *line; 90 char *line;
104 if (!(line = read_line(f))) { 91 if (!(line = read_line(f))) {
105 break; 92 break;
106 } 93 }
107 if (strstr(line, "nvidia")) { 94 if (strstr(line, "nvidia")) {
108 nvidia = true; 95 fprintf(stderr, "\x1B[1;31mWarning: Proprietary Nvidia drivers are "
109 } 96 "NOT supported. Use Nouveau.\x1B[0m\n");
110 if (strstr(line, "nvidia_modeset")) { 97 free(line);
111 nvidia_modeset = true; 98 break;
112 }
113 if (strstr(line, "nvidia_uvm")) {
114 nvidia_uvm = true;
115 }
116 if (strstr(line, "nvidia_drm")) {
117 nvidia_drm = true;
118 } 99 }
119 if (strstr(line, "fglrx")) { 100 if (strstr(line, "fglrx")) {
120 fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do " 101 fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do "
@@ -125,52 +106,6 @@ void detect_proprietary() {
125 free(line); 106 free(line);
126 } 107 }
127 fclose(f); 108 fclose(f);
128 if (nvidia) {
129 fprintf(stderr, "\x1B[1;31mWarning: Proprietary nvidia driver support "
130 "is considered experimental. Nouveau is strongly recommended."
131 "\x1B[0m\n");
132 if (!nvidia_modeset || !nvidia_uvm || !nvidia_drm) {
133 fprintf(stderr, "\x1B[1;31mWarning: You do not have all of the "
134 "necessary kernel modules loaded for nvidia support. "
135 "You need nvidia, nvidia_modeset, nvidia_uvm, and nvidia_drm."
136 "\x1B[0m\n");
137 }
138#ifdef __linux__
139 f = fopen("/sys/module/nvidia_drm/parameters/modeset", "r");
140 if (f) {
141 char *line = read_line(f);
142 if (line && strstr(line, "Y")) {
143 // nvidia-drm.modeset is set to 0
144 fprintf(stderr, "\x1B[1;31mWarning: You must load "
145 "nvidia-drm with the modeset option on to use "
146 "the proprietary driver. Consider adding "
147 "nvidia-drm.modeset=1 to your kernel command line "
148 "parameters.\x1B[0m\n");
149 }
150 fclose(f);
151 free(line);
152 } else {
153 // nvidia-drm.modeset is not set
154 fprintf(stderr, "\x1B[1;31mWarning: You must load "
155 "nvidia-drm with the modeset option on to use "
156 "the proprietary driver. Consider adding "
157 "nvidia-drm.modeset=1 to your kernel command line "
158 "parameters.\x1B[0m\n");
159 }
160#else
161 f = fopen("/proc/cmdline", "r");
162 if (f) {
163 char *line = read_line(f);
164 if (line && !strstr(line, "nvidia-drm.modeset=1")) {
165 fprintf(stderr, "\x1B[1;31mWarning: You must add "
166 "nvidia-drm.modeset=1 to your kernel command line to use "
167 "the proprietary driver.\x1B[0m\n");
168 }
169 fclose(f);
170 free(line);
171 }
172#endif
173 }
174} 109}
175 110
176void run_as_ipc_client(char *command, char *socket_path) { 111void run_as_ipc_client(char *command, char *socket_path) {
@@ -184,27 +119,15 @@ void run_as_ipc_client(char *command, char *socket_path) {
184static void log_env() { 119static void log_env() {
185 const char *log_vars[] = { 120 const char *log_vars[] = {
186 "PATH", 121 "PATH",
187 "LD_LOAD_PATH", 122 "LD_LIBRARY_PATH",
188 "LD_PRELOAD_PATH", 123 "LD_PRELOAD_PATH",
189 "LD_LIBRARY_PATH", 124 "LD_LIBRARY_PATH",
190 "SWAY_CURSOR_THEME", 125 "SWAY_CURSOR_THEME",
191 "SWAY_CURSOR_SIZE", 126 "SWAY_CURSOR_SIZE",
192 "SWAYSOCK", 127 "SWAYSOCK"
193 "WLC_DRM_DEVICE",
194 "WLC_SHM",
195 "WLC_OUTPUTS",
196 "WLC_XWAYLAND",
197 "WLC_LIBINPUT",
198 "WLC_REPEAT_DELAY",
199 "WLC_REPEAT_RATE",
200 "XKB_DEFAULT_RULES",
201 "XKB_DEFAULT_MODEL",
202 "XKB_DEFAULT_LAYOUT",
203 "XKB_DEFAULT_VARIANT",
204 "XKB_DEFAULT_OPTIONS",
205 }; 128 };
206 for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { 129 for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) {
207 sway_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); 130 wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));
208 } 131 }
209} 132}
210 133
@@ -219,14 +142,14 @@ static void log_distro() {
219 for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { 142 for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) {
220 FILE *f = fopen(paths[i], "r"); 143 FILE *f = fopen(paths[i], "r");
221 if (f) { 144 if (f) {
222 sway_log(L_INFO, "Contents of %s:", paths[i]); 145 wlr_log(L_INFO, "Contents of %s:", paths[i]);
223 while (!feof(f)) { 146 while (!feof(f)) {
224 char *line; 147 char *line;
225 if (!(line = read_line(f))) { 148 if (!(line = read_line(f))) {
226 break; 149 break;
227 } 150 }
228 if (*line) { 151 if (*line) {
229 sway_log(L_INFO, "%s", line); 152 wlr_log(L_INFO, "%s", line);
230 } 153 }
231 free(line); 154 free(line);
232 } 155 }
@@ -236,9 +159,10 @@ static void log_distro() {
236} 159}
237 160
238static void log_kernel() { 161static void log_kernel() {
162 return;
239 FILE *f = popen("uname -a", "r"); 163 FILE *f = popen("uname -a", "r");
240 if (!f) { 164 if (!f) {
241 sway_log(L_INFO, "Unable to determine kernel version"); 165 wlr_log(L_INFO, "Unable to determine kernel version");
242 return; 166 return;
243 } 167 }
244 while (!feof(f)) { 168 while (!feof(f)) {
@@ -247,7 +171,7 @@ static void log_kernel() {
247 break; 171 break;
248 } 172 }
249 if (*line) { 173 if (*line) {
250 sway_log(L_INFO, "%s", line); 174 wlr_log(L_INFO, "%s", line);
251 } 175 }
252 free(line); 176 free(line);
253 } 177 }
@@ -258,14 +182,14 @@ static void security_sanity_check() {
258 // TODO: Notify users visually if this has issues 182 // TODO: Notify users visually if this has issues
259 struct stat s; 183 struct stat s;
260 if (stat("/proc", &s)) { 184 if (stat("/proc", &s)) {
261 sway_log(L_ERROR, 185 wlr_log(L_ERROR,
262 "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); 186 "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!");
263 } 187 }
264#ifdef __linux__ 188#ifdef __linux__
265 cap_flag_value_t v; 189 cap_flag_value_t v;
266 cap_t cap = cap_get_proc(); 190 cap_t cap = cap_get_proc();
267 if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { 191 if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) {
268 sway_log(L_ERROR, 192 wlr_log(L_ERROR,
269 "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); 193 "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users.");
270 } 194 }
271 if (cap) { 195 if (cap) {
@@ -281,13 +205,13 @@ static void executable_sanity_check() {
281 stat(exe, &sb); 205 stat(exe, &sb);
282 // We assume that cap_get_file returning NULL implies ENODATA 206 // We assume that cap_get_file returning NULL implies ENODATA
283 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { 207 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) {
284 sway_log(L_ERROR, 208 wlr_log(L_ERROR,
285 "sway executable has both the s(g)uid bit AND file caps set."); 209 "sway executable has both the s(g)uid bit AND file caps set.");
286 sway_log(L_ERROR, 210 wlr_log(L_ERROR,
287 "This is strongly discouraged (and completely broken)."); 211 "This is strongly discouraged (and completely broken).");
288 sway_log(L_ERROR, 212 wlr_log(L_ERROR,
289 "Please clear one of them (either the suid bit, or the file caps)."); 213 "Please clear one of them (either the suid bit, or the file caps).");
290 sway_log(L_ERROR, 214 wlr_log(L_ERROR,
291 "If unsure, strip the file caps."); 215 "If unsure, strip the file caps.");
292 exit(EXIT_FAILURE); 216 exit(EXIT_FAILURE);
293 } 217 }
@@ -295,6 +219,37 @@ static void executable_sanity_check() {
295#endif 219#endif
296} 220}
297 221
222static void drop_permissions(bool keep_caps) {
223 if (getuid() != geteuid() || getgid() != getegid()) {
224 if (setgid(getgid()) != 0) {
225 wlr_log(L_ERROR, "Unable to drop root");
226 exit(EXIT_FAILURE);
227 }
228 if (setuid(getuid()) != 0) {
229 wlr_log(L_ERROR, "Unable to drop root");
230 exit(EXIT_FAILURE);
231 }
232 }
233 if (setuid(0) != -1) {
234 wlr_log(L_ERROR, "Root privileges can be restored.");
235 exit(EXIT_FAILURE);
236 }
237#ifdef __linux__
238 if (keep_caps) {
239 // Drop every cap except CAP_SYS_PTRACE
240 cap_t caps = cap_init();
241 cap_value_t keep = CAP_SYS_PTRACE;
242 wlr_log(L_INFO, "Dropping extra capabilities");
243 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
244 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
245 cap_set_proc(caps)) {
246 wlr_log(L_ERROR, "Failed to drop extra capabilities");
247 exit(EXIT_FAILURE);
248 }
249 }
250#endif
251}
252
298int main(int argc, char **argv) { 253int main(int argc, char **argv) {
299 static int verbose = 0, debug = 0, validate = 0; 254 static int verbose = 0, debug = 0, validate = 0;
300 255
@@ -334,7 +289,7 @@ int main(int argc, char **argv) {
334 int c; 289 int c;
335 while (1) { 290 while (1) {
336 int option_index = 0; 291 int option_index = 0;
337 c = getopt_long(argc, argv, "hCdvVc:", long_options, &option_index); 292 c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index);
338 if (c == -1) { 293 if (c == -1) {
339 break; 294 break;
340 } 295 }
@@ -352,6 +307,9 @@ int main(int argc, char **argv) {
352 case 'd': // debug 307 case 'd': // debug
353 debug = 1; 308 debug = 1;
354 break; 309 break;
310 case 'D': // extended debug options
311 enable_debug_tree = true;
312 break;
355 case 'v': // version 313 case 'v': // version
356 fprintf(stdout, "sway version " SWAY_VERSION "\n"); 314 fprintf(stdout, "sway version " SWAY_VERSION "\n");
357 exit(EXIT_SUCCESS); 315 exit(EXIT_SUCCESS);
@@ -374,37 +332,24 @@ int main(int argc, char **argv) {
374 } 332 }
375 } 333 }
376 334
377 // we need to setup logging before wlc_init in case it fails. 335 // TODO: switch logging over to wlroots?
378 if (debug) { 336 if (debug) {
379 init_log(L_DEBUG); 337 wlr_log_init(L_DEBUG, NULL);
380 } else if (verbose || validate) { 338 } else if (verbose || validate) {
381 init_log(L_INFO); 339 wlr_log_init(L_INFO, NULL);
382 } else { 340 } else {
383 init_log(L_ERROR); 341 wlr_log_init(L_ERROR, NULL);
384 } 342 }
385 343
386 if (optind < argc) { // Behave as IPC client 344 if (optind < argc) { // Behave as IPC client
387 if(optind != 1) { 345 if(optind != 1) {
388 sway_log(L_ERROR, "Don't use options with the IPC client"); 346 wlr_log(L_ERROR, "Don't use options with the IPC client");
389 exit(EXIT_FAILURE);
390 }
391 if (getuid() != geteuid() || getgid() != getegid()) {
392 if (setgid(getgid()) != 0) {
393 sway_log(L_ERROR, "Unable to drop root");
394 exit(EXIT_FAILURE);
395 }
396 if (setuid(getuid()) != 0) {
397 sway_log(L_ERROR, "Unable to drop root");
398 exit(EXIT_FAILURE);
399 }
400 }
401 if (setuid(0) != -1) {
402 sway_log(L_ERROR, "Root privileges can be restored.");
403 exit(EXIT_FAILURE); 347 exit(EXIT_FAILURE);
404 } 348 }
349 drop_permissions(false);
405 char *socket_path = getenv("SWAYSOCK"); 350 char *socket_path = getenv("SWAYSOCK");
406 if (!socket_path) { 351 if (!socket_path) {
407 sway_log(L_ERROR, "Unable to retrieve socket path"); 352 wlr_log(L_ERROR, "Unable to retrieve socket path");
408 exit(EXIT_FAILURE); 353 exit(EXIT_FAILURE);
409 } 354 }
410 char *command = join_args(argv + optind, argc - optind); 355 char *command = join_args(argv + optind, argc - optind);
@@ -413,49 +358,25 @@ int main(int argc, char **argv) {
413 } 358 }
414 359
415 executable_sanity_check(); 360 executable_sanity_check();
416#ifdef __linux__
417 bool suid = false; 361 bool suid = false;
362#ifdef __linux__
418 if (getuid() != geteuid() || getgid() != getegid()) { 363 if (getuid() != geteuid() || getgid() != getegid()) {
419 // Retain capabilities after setuid() 364 // Retain capabilities after setuid()
420 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { 365 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
421 sway_log(L_ERROR, "Cannot keep caps after setuid()"); 366 wlr_log(L_ERROR, "Cannot keep caps after setuid()");
422 exit(EXIT_FAILURE); 367 exit(EXIT_FAILURE);
423 } 368 }
424 suid = true; 369 suid = true;
425 } 370 }
426#endif 371#endif
427 372
428 wlc_log_set_handler(wlc_log_handler);
429 log_kernel(); 373 log_kernel();
430 log_distro(); 374 log_distro();
431 log_env();
432 detect_proprietary(); 375 detect_proprietary();
433 detect_raspi(); 376 detect_raspi();
434 377
435 input_devices = create_list();
436
437 /* Changing code earlier than this point requires detailed review */
438 /* (That code runs as root on systems without logind, and wlc_init drops to
439 * another user.) */
440 register_wlc_handlers();
441 if (!wlc_init()) {
442 return 1;
443 }
444 register_extensions();
445
446#ifdef __linux__ 378#ifdef __linux__
447 if (suid) { 379 drop_permissions(suid);
448 // Drop every cap except CAP_SYS_PTRACE
449 cap_t caps = cap_init();
450 cap_value_t keep = CAP_SYS_PTRACE;
451 sway_log(L_INFO, "Dropping extra capabilities");
452 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
453 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
454 cap_set_proc(caps)) {
455 sway_log(L_ERROR, "Failed to drop extra capabilities");
456 exit(EXIT_FAILURE);
457 }
458 }
459#endif 380#endif
460 // handle SIGTERM signals 381 // handle SIGTERM signals
461 signal(SIGTERM, sig_handler); 382 signal(SIGTERM, sig_handler);
@@ -463,11 +384,16 @@ int main(int argc, char **argv) {
463 // prevent ipc from crashing sway 384 // prevent ipc from crashing sway
464 signal(SIGPIPE, SIG_IGN); 385 signal(SIGPIPE, SIG_IGN);
465 386
466 sway_log(L_INFO, "Starting sway version " SWAY_VERSION "\n"); 387 wlr_log(L_INFO, "Starting sway version " SWAY_VERSION);
388
389 layout_init();
467 390
468 init_layout(); 391 if (!server_init(&server)) {
392 return 1;
393 }
469 394
470 ipc_init(); 395 ipc_init(&server);
396 log_env();
471 397
472 if (validate) { 398 if (validate) {
473 bool valid = load_main_config(config_path, false); 399 bool valid = load_main_config(config_path, false);
@@ -484,11 +410,17 @@ int main(int argc, char **argv) {
484 410
485 security_sanity_check(); 411 security_sanity_check();
486 412
413 // TODO: wait for server to be ready
414 // TODO: consume config->cmd_queue
415 config->active = true;
416
487 if (!terminate_request) { 417 if (!terminate_request) {
488 wlc_run(); 418 server_run(&server);
489 } 419 }
490 420
491 list_free(input_devices); 421 wlr_log(L_INFO, "Shutting down sway");
422
423 server_fini(&server);
492 424
493 ipc_terminate(); 425 ipc_terminate();
494 426
diff --git a/sway/meson.build b/sway/meson.build
new file mode 100644
index 00000000..9e55e335
--- /dev/null
+++ b/sway/meson.build
@@ -0,0 +1,131 @@
1sway_sources = files(
2 'main.c',
3 'server.c',
4 'commands.c',
5 'config.c',
6 'criteria.c',
7 'debug-tree.c',
8 'ipc-json.c',
9 'ipc-server.c',
10 'security.c',
11
12 'desktop/desktop.c',
13 'desktop/output.c',
14 'desktop/layer_shell.c',
15 'desktop/wl_shell.c',
16 'desktop/xdg_shell_v6.c',
17 'desktop/xwayland.c',
18
19 'input/input-manager.c',
20 'input/seat.c',
21 'input/cursor.c',
22 'input/keyboard.c',
23
24 'config/bar.c',
25 'config/output.c',
26 'config/seat.c',
27 'config/input.c',
28
29 'commands/bar.c',
30 'commands/bind.c',
31 'commands/default_orientation.c',
32 'commands/exit.c',
33 'commands/exec.c',
34 'commands/exec_always.c',
35 'commands/focus.c',
36 'commands/focus_follows_mouse.c',
37 'commands/kill.c',
38 'commands/opacity.c',
39 'commands/include.c',
40 'commands/input.c',
41 'commands/layout.c',
42 'commands/mode.c',
43 'commands/mouse_warping.c',
44 'commands/move.c',
45 'commands/output.c',
46 'commands/reload.c',
47 'commands/resize.c',
48 'commands/seat.c',
49 'commands/seat/attach.c',
50 'commands/seat/cursor.c',
51 'commands/seat/fallback.c',
52 'commands/set.c',
53 'commands/split.c',
54 'commands/swaybg_command.c',
55 'commands/workspace.c',
56 'commands/ws_auto_back_and_forth.c',
57
58 'commands/bar/activate_button.c',
59 'commands/bar/binding_mode_indicator.c',
60 'commands/bar/bindsym.c',
61 'commands/bar/colors.c',
62 'commands/bar/context_button.c',
63 'commands/bar/font.c',
64 'commands/bar/height.c',
65 'commands/bar/hidden_state.c',
66 'commands/bar/icon_theme.c',
67 'commands/bar/id.c',
68 'commands/bar/mode.c',
69 'commands/bar/modifier.c',
70 'commands/bar/output.c',
71 'commands/bar/pango_markup.c',
72 'commands/bar/position.c',
73 'commands/bar/secondary_button.c',
74 'commands/bar/separator_symbol.c',
75 'commands/bar/status_command.c',
76 'commands/bar/strip_workspace_numbers.c',
77 'commands/bar/swaybar_command.c',
78 'commands/bar/tray_output.c',
79 'commands/bar/tray_padding.c',
80 'commands/bar/workspace_buttons.c',
81 'commands/bar/wrap_scroll.c',
82
83 'commands/input/accel_profile.c',
84 'commands/input/click_method.c',
85 'commands/input/drag_lock.c',
86 'commands/input/dwt.c',
87 'commands/input/events.c',
88 'commands/input/left_handed.c',
89 'commands/input/map_to_output.c',
90 'commands/input/middle_emulation.c',
91 'commands/input/natural_scroll.c',
92 'commands/input/pointer_accel.c',
93 'commands/input/scroll_method.c',
94 'commands/input/tap.c',
95 'commands/input/xkb_layout.c',
96 'commands/input/xkb_model.c',
97 'commands/input/xkb_options.c',
98 'commands/input/xkb_rules.c',
99 'commands/input/xkb_variant.c',
100
101 'tree/container.c',
102 'tree/layout.c',
103 'tree/view.c',
104 'tree/workspace.c',
105 'tree/output.c',
106)
107
108sway_deps = [
109 cairo,
110 gdk_pixbuf,
111 jsonc,
112 libcap,
113 libinput,
114 math,
115 pango,
116 pcre,
117 pixman,
118 server_protos,
119 wayland_server,
120 wlroots,
121 xkbcommon,
122]
123
124executable(
125 'sway',
126 sway_sources,
127 include_directories: [sway_inc],
128 dependencies: sway_deps,
129 link_with: [lib_sway_common],
130 install: true
131)
diff --git a/sway/output.c b/sway/output.c
deleted file mode 100644
index c0f29c5a..00000000
--- a/sway/output.c
+++ /dev/null
@@ -1,277 +0,0 @@
1#include <strings.h>
2#include <ctype.h>
3#include <stdlib.h>
4#include "sway/output.h"
5#include "log.h"
6#include "list.h"
7
8void output_get_scaled_size(wlc_handle handle, struct wlc_size *size) {
9 *size = *wlc_output_get_resolution(handle);
10 uint32_t scale = wlc_output_get_scale(handle);
11 size->w /= scale;
12 size->h /= scale;
13}
14
15swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) {
16 swayc_t *output = NULL;
17 // If there is no output directly next to the current one, use
18 // swayc_opposite_output to wrap.
19 if (strcasecmp(name, "left") == 0) {
20 output = swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true);
21 if (!output) {
22 output = swayc_opposite_output(MOVE_RIGHT, abs_pos);
23 }
24 } else if (strcasecmp(name, "right") == 0) {
25 output = swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true);
26 if (!output) {
27 output = swayc_opposite_output(MOVE_LEFT, abs_pos);
28 }
29 } else if (strcasecmp(name, "up") == 0) {
30 output = swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true);
31 if (!output) {
32 output = swayc_opposite_output(MOVE_DOWN, abs_pos);
33 }
34 } else if (strcasecmp(name, "down") == 0) {
35 output = swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true);
36 if (!output) {
37 output = swayc_opposite_output(MOVE_UP, abs_pos);
38 }
39 } else {
40 for(int i = 0; i < root_container.children->length; ++i) {
41 swayc_t *c = root_container.children->items[i];
42 if (c->type == C_OUTPUT && strcasecmp(c->name, name) == 0) {
43 return c;
44 }
45 }
46 }
47 return output;
48}
49
50swayc_t *swayc_opposite_output(enum movement_direction dir,
51 const struct wlc_point *abs_pos) {
52
53 // Search through all the outputs and pick the output whose edge covers the
54 // given position, and is at leftmost/rightmost/upmost/downmost side of the
55 // screen (decided by the direction given).
56 swayc_t *opposite = NULL;
57 char *dir_text = NULL;
58 switch(dir) {
59 case MOVE_LEFT:
60 case MOVE_RIGHT: ;
61 for (int i = 0; i < root_container.children->length; ++i) {
62 swayc_t *c = root_container.children->items[i];
63 if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
64 if (!opposite) {
65 opposite = c;
66 } else if ((dir == MOVE_LEFT && c->x < opposite->x)
67 || (dir == MOVE_RIGHT && c->x > opposite->x)) {
68 opposite = c;
69 }
70 }
71 }
72 dir_text = dir == MOVE_LEFT ? "leftmost" : "rightmost";
73 break;
74 case MOVE_UP:
75 case MOVE_DOWN: ;
76 for (int i = 0; i < root_container.children->length; ++i) {
77 swayc_t *c = root_container.children->items[i];
78 if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
79 if (!opposite) {
80 opposite = c;
81 } else if ((dir == MOVE_UP && c->y < opposite->y)
82 || (dir == MOVE_DOWN && c->y > opposite->y)) {
83 opposite = c;
84 }
85 }
86 }
87 dir_text = dir == MOVE_UP ? "upmost" : "downmost";
88 break;
89 default:
90 sway_abort("Function called with invalid argument.");
91 break;
92 }
93 if (opposite) {
94 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s from y-position %i",
95 opposite->name, opposite->width, opposite->height, opposite->x, opposite->y,
96 dir_text, abs_pos->y);
97 }
98 return opposite;
99}
100
101// Position is where on the edge (as absolute position) the adjacent output should be searched for.
102swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir,
103 const struct wlc_point *abs_pos, bool pick_closest) {
104
105 if (!output) {
106 output = swayc_active_output();
107 }
108 // In order to find adjacent outputs we need to test that the outputs are
109 // aligned on one axis (decided by the direction given) and that the given
110 // position is within the edge of the adjacent output. If no such output
111 // exists we pick the adjacent output within the edge that is closest to
112 // the given position, if any.
113 swayc_t *adjacent = NULL;
114 char *dir_text = NULL;
115 switch(dir) {
116 case MOVE_LEFT:
117 case MOVE_RIGHT: ;
118 double delta_y = 0;
119 for(int i = 0; i < root_container.children->length; ++i) {
120 swayc_t *c = root_container.children->items[i];
121 if (c == output || c->type != C_OUTPUT) {
122 continue;
123 }
124 bool x_aligned = dir == MOVE_LEFT ?
125 c->x + c->width == output->x :
126 c->x == output->x + output->width;
127 if (!x_aligned) {
128 continue;
129 }
130 if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
131 delta_y = 0;
132 adjacent = c;
133 break;
134 } else if (pick_closest) {
135 // track closest adjacent output
136 double top_y = c->y, bottom_y = c->y + c->height;
137 if (top_y >= output->y && top_y <= output->y + output->height) {
138 double delta = top_y - abs_pos->y;
139 if (delta < 0) delta = -delta;
140 if (delta < delta_y || !adjacent) {
141 delta_y = delta;
142 adjacent = c;
143 }
144 }
145 // we check both points and pick the closest
146 if (bottom_y >= output->y && bottom_y <= output->y + output->height) {
147 double delta = bottom_y - abs_pos->y;
148 if (delta < 0) delta = -delta;
149 if (delta < delta_y || !adjacent) {
150 delta_y = delta;
151 adjacent = c;
152 }
153 }
154 }
155 }
156 dir_text = dir == MOVE_LEFT ? "left of" : "right of";
157 if (adjacent && delta_y == 0) {
158 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)",
159 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
160 dir_text, output->name, abs_pos->y);
161 } else if (adjacent) {
162 // so we end up picking the closest adjacent output because
163 // there is no directly adjacent to the given position
164 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)",
165 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
166 dir_text, output->name, abs_pos->y, delta_y);
167 }
168 break;
169 case MOVE_UP:
170 case MOVE_DOWN: ;
171 double delta_x = 0;
172 for(int i = 0; i < root_container.children->length; ++i) {
173 swayc_t *c = root_container.children->items[i];
174 if (c == output || c->type != C_OUTPUT) {
175 continue;
176 }
177 bool y_aligned = dir == MOVE_UP ?
178 c->y + c->height == output->y :
179 c->y == output->y + output->height;
180 if (!y_aligned) {
181 continue;
182 }
183 if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
184 delta_x = 0;
185 adjacent = c;
186 break;
187 } else if (pick_closest) {
188 // track closest adjacent output
189 double left_x = c->x, right_x = c->x + c->width;
190 if (left_x >= output->x && left_x <= output->x + output->width) {
191 double delta = left_x - abs_pos->x;
192 if (delta < 0) delta = -delta;
193 if (delta < delta_x || !adjacent) {
194 delta_x = delta;
195 adjacent = c;
196 }
197 }
198 // we check both points and pick the closest
199 if (right_x >= output->x && right_x <= output->x + output->width) {
200 double delta = right_x - abs_pos->x;
201 if (delta < 0) delta = -delta;
202 if (delta < delta_x || !adjacent) {
203 delta_x = delta;
204 adjacent = c;
205 }
206 }
207 }
208 }
209 dir_text = dir == MOVE_UP ? "above" : "below";
210 if (adjacent && delta_x == 0) {
211 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)",
212 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
213 dir_text, output->name, abs_pos->x);
214 } else if (adjacent) {
215 // so we end up picking the closest adjacent output because
216 // there is no directly adjacent to the given position
217 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)",
218 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
219 dir_text, output->name, abs_pos->x, delta_x);
220 }
221 break;
222 default:
223 sway_abort("Function called with invalid argument.");
224 break;
225 }
226 return adjacent;
227}
228
229void get_absolute_position(swayc_t *container, struct wlc_point *point) {
230 if (!container || !point)
231 sway_abort("Need container and wlc_point (was %p, %p).", container, point);
232
233 if (container->type == C_OUTPUT) {
234 // Coordinates are already absolute.
235 point->x = container->x;
236 point->y = container->y;
237 } else {
238 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
239 if (container->type == C_WORKSPACE) {
240 // Workspace coordinates are actually wrong/arbitrary, but should
241 // be same as output.
242 point->x = output->x;
243 point->y = output->y;
244 } else {
245 point->x = output->x + container->x;
246 point->y = output->y + container->y;
247 }
248 }
249}
250
251void get_absolute_center_position(swayc_t *container, struct wlc_point *point) {
252 get_absolute_position(container, point);
253 point->x += container->width/2;
254 point->y += container->height/2;
255}
256
257static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
258 swayc_t *a = *(void **)_a;
259 swayc_t *b = *(void **)_b;
260 int retval = 0;
261
262 if (isdigit(a->name[0]) && isdigit(b->name[0])) {
263 int a_num = strtol(a->name, NULL, 10);
264 int b_num = strtol(b->name, NULL, 10);
265 retval = (a_num < b_num) ? -1 : (a_num > b_num);
266 } else if (isdigit(a->name[0])) {
267 retval = -1;
268 } else if (isdigit(b->name[0])) {
269 retval = 1;
270 }
271
272 return retval;
273}
274
275void sort_workspaces(swayc_t *output) {
276 list_stable_sort(output->children, sort_workspace_cmp_qsort);
277}
diff --git a/sway/security.c b/sway/security.c
index fcd70f9d..cc0d3f66 100644
--- a/sway/security.c
+++ b/sway/security.c
@@ -1,102 +1,7 @@
1#define _XOPEN_SOURCE 700 1#define _XOPEN_SOURCE 700
2#include <sys/types.h> 2#include <stdlib.h>
3#include <sys/stat.h>
4#include <string.h> 3#include <string.h>
5#include <unistd.h>
6#include <stdio.h>
7#include "sway/config.h"
8#include "sway/security.h" 4#include "sway/security.h"
9#include "log.h"
10
11static bool validate_ipc_target(const char *program) {
12 struct stat sb;
13
14 sway_log(L_DEBUG, "Validating IPC target '%s'", program);
15
16 if (!strcmp(program, "*")) {
17 return true;
18 }
19 if (lstat(program, &sb) == -1) {
20 return false;
21 }
22 if (!S_ISREG(sb.st_mode)) {
23 sway_log(L_ERROR,
24 "IPC target '%s' MUST be/point at an existing regular file",
25 program);
26 return false;
27 }
28 if (sb.st_uid != 0) {
29#ifdef NDEBUG
30 sway_log(L_ERROR, "IPC target '%s' MUST be owned by root", program);
31 return false;
32#else
33 sway_log(L_INFO, "IPC target '%s' MUST be owned by root (waived for debug build)", program);
34 return true;
35#endif
36 }
37 if (sb.st_mode & S_IWOTH) {
38 sway_log(L_ERROR, "IPC target '%s' MUST NOT be world writable", program);
39 return false;
40 }
41
42 return true;
43}
44
45struct feature_policy *alloc_feature_policy(const char *program) {
46 uint32_t default_policy = 0;
47
48 if (!validate_ipc_target(program)) {
49 return NULL;
50 }
51 for (int i = 0; i < config->feature_policies->length; ++i) {
52 struct feature_policy *policy = config->feature_policies->items[i];
53 if (strcmp(policy->program, "*") == 0) {
54 default_policy = policy->features;
55 break;
56 }
57 }
58
59 struct feature_policy *policy = malloc(sizeof(struct feature_policy));
60 if (!policy) {
61 return NULL;
62 }
63 policy->program = strdup(program);
64 if (!policy->program) {
65 free(policy);
66 return NULL;
67 }
68 policy->features = default_policy;
69
70 return policy;
71}
72
73struct ipc_policy *alloc_ipc_policy(const char *program) {
74 uint32_t default_policy = 0;
75
76 if (!validate_ipc_target(program)) {
77 return NULL;
78 }
79 for (int i = 0; i < config->ipc_policies->length; ++i) {
80 struct ipc_policy *policy = config->ipc_policies->items[i];
81 if (strcmp(policy->program, "*") == 0) {
82 default_policy = policy->features;
83 break;
84 }
85 }
86
87 struct ipc_policy *policy = malloc(sizeof(struct ipc_policy));
88 if (!policy) {
89 return NULL;
90 }
91 policy->program = strdup(program);
92 if (!policy->program) {
93 free(policy);
94 return NULL;
95 }
96 policy->features = default_policy;
97
98 return policy;
99}
100 5
101struct command_policy *alloc_command_policy(const char *command) { 6struct command_policy *alloc_command_policy(const char *command) {
102 struct command_policy *policy = malloc(sizeof(struct command_policy)); 7 struct command_policy *policy = malloc(sizeof(struct command_policy));
@@ -111,118 +16,3 @@ struct command_policy *alloc_command_policy(const char *command) {
111 policy->context = 0; 16 policy->context = 0;
112 return policy; 17 return policy;
113} 18}
114
115static const char *get_pid_exe(pid_t pid) {
116#ifdef __FreeBSD__
117 const char *fmt = "/proc/%d/file";
118#else
119 const char *fmt = "/proc/%d/exe";
120#endif
121 int pathlen = snprintf(NULL, 0, fmt, pid);
122 char *path = malloc(pathlen + 1);
123 if (path) {
124 snprintf(path, pathlen + 1, fmt, pid);
125 }
126
127 static char link[2048];
128
129 ssize_t len = !path ? -1 : readlink(path, link, sizeof(link));
130 if (len < 0) {
131 sway_log(L_INFO,
132 "WARNING: unable to read %s for security check. Using default policy.",
133 path);
134 strcpy(link, "*");
135 } else {
136 link[len] = '\0';
137 }
138 free(path);
139
140 return link;
141}
142
143struct feature_policy *get_feature_policy(const char *name) {
144 struct feature_policy *policy = NULL;
145
146 for (int i = 0; i < config->feature_policies->length; ++i) {
147 struct feature_policy *p = config->feature_policies->items[i];
148 if (strcmp(p->program, name) == 0) {
149 policy = p;
150 break;
151 }
152 }
153 if (!policy) {
154 policy = alloc_feature_policy(name);
155 sway_assert(policy, "Unable to allocate security policy");
156 if (policy) {
157 list_add(config->feature_policies, policy);
158 }
159 }
160 return policy;
161}
162
163uint32_t get_feature_policy_mask(pid_t pid) {
164 uint32_t default_policy = 0;
165 const char *link = get_pid_exe(pid);
166
167 for (int i = 0; i < config->feature_policies->length; ++i) {
168 struct feature_policy *policy = config->feature_policies->items[i];
169 if (strcmp(policy->program, "*") == 0) {
170 default_policy = policy->features;
171 }
172 if (strcmp(policy->program, link) == 0) {
173 return policy->features;
174 }
175 }
176
177 return default_policy;
178}
179
180uint32_t get_ipc_policy_mask(pid_t pid) {
181 uint32_t default_policy = 0;
182 const char *link = get_pid_exe(pid);
183
184 for (int i = 0; i < config->ipc_policies->length; ++i) {
185 struct ipc_policy *policy = config->ipc_policies->items[i];
186 if (strcmp(policy->program, "*") == 0) {
187 default_policy = policy->features;
188 }
189 if (strcmp(policy->program, link) == 0) {
190 return policy->features;
191 }
192 }
193
194 return default_policy;
195}
196
197uint32_t get_command_policy_mask(const char *cmd) {
198 uint32_t default_policy = 0;
199
200 for (int i = 0; i < config->command_policies->length; ++i) {
201 struct command_policy *policy = config->command_policies->items[i];
202 if (strcmp(policy->command, "*") == 0) {
203 default_policy = policy->context;
204 }
205 if (strcmp(policy->command, cmd) == 0) {
206 return policy->context;
207 }
208 }
209
210 return default_policy;
211}
212
213const char *command_policy_str(enum command_context context) {
214 switch (context) {
215 case CONTEXT_ALL:
216 return "all";
217 case CONTEXT_CONFIG:
218 return "config";
219 case CONTEXT_BINDING:
220 return "binding";
221 case CONTEXT_IPC:
222 return "IPC";
223 case CONTEXT_CRITERIA:
224 return "criteria";
225 default:
226 return "unknown";
227 }
228}
diff --git a/sway/server.c b/sway/server.c
new file mode 100644
index 00000000..c1125f14
--- /dev/null
+++ b/sway/server.c
@@ -0,0 +1,141 @@
1#define _POSIX_C_SOURCE 200112L
2#include <assert.h>
3#include <stdbool.h>
4#include <stdlib.h>
5#include <wayland-server.h>
6#include <wlr/backend.h>
7#include <wlr/backend/session.h>
8#include <wlr/render/wlr_renderer.h>
9#include <wlr/types/wlr_compositor.h>
10#include <wlr/types/wlr_gamma_control.h>
11#include <wlr/types/wlr_linux_dmabuf.h>
12#include <wlr/types/wlr_layer_shell.h>
13#include <wlr/types/wlr_primary_selection.h>
14#include <wlr/types/wlr_screenshooter.h>
15#include <wlr/types/wlr_server_decoration.h>
16#include <wlr/types/wlr_xcursor_manager.h>
17#include <wlr/types/wlr_xdg_output.h>
18#include <wlr/types/wlr_wl_shell.h>
19#include <wlr/util/log.h>
20// TODO WLR: make Xwayland optional
21#include <wlr/xwayland.h>
22#include "sway/commands.h"
23#include "sway/config.h"
24#include "sway/input/input-manager.h"
25#include "sway/server.h"
26#include "sway/tree/layout.h"
27
28static void server_ready(struct wl_listener *listener, void *data) {
29 wlr_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
30 // Execute commands until there are none left
31 config->active = true;
32 while (config->cmd_queue->length) {
33 char *line = config->cmd_queue->items[0];
34 struct cmd_results *res = execute_command(line, NULL);
35 if (res->status != CMD_SUCCESS) {
36 wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error);
37 }
38 free_cmd_results(res);
39 free(line);
40 list_del(config->cmd_queue, 0);
41 }
42}
43
44bool server_init(struct sway_server *server) {
45 wlr_log(L_DEBUG, "Initializing Wayland server");
46
47 server->wl_display = wl_display_create();
48 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
49 server->backend = wlr_backend_autocreate(server->wl_display);
50
51 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend);
52 assert(renderer);
53
54 wl_display_init_shm(server->wl_display);
55
56 server->compositor = wlr_compositor_create(server->wl_display, renderer);
57 server->data_device_manager =
58 wlr_data_device_manager_create(server->wl_display);
59
60 wlr_screenshooter_create(server->wl_display);
61 wlr_gamma_control_manager_create(server->wl_display);
62 wlr_primary_selection_device_manager_create(server->wl_display);
63
64 server->new_output.notify = handle_new_output;
65 wl_signal_add(&server->backend->events.new_output, &server->new_output);
66
67 wlr_xdg_output_manager_create(server->wl_display,
68 root_container.sway_root->output_layout);
69
70 server->layer_shell = wlr_layer_shell_create(server->wl_display);
71 wl_signal_add(&server->layer_shell->events.new_surface,
72 &server->layer_shell_surface);
73 server->layer_shell_surface.notify = handle_layer_shell_surface;
74
75 server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
76 wl_signal_add(&server->xdg_shell_v6->events.new_surface,
77 &server->xdg_shell_v6_surface);
78 server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface;
79
80 server->wl_shell = wlr_wl_shell_create(server->wl_display);
81 wl_signal_add(&server->wl_shell->events.new_surface,
82 &server->wl_shell_surface);
83 server->wl_shell_surface.notify = handle_wl_shell_surface;
84
85 // TODO make xwayland optional
86 server->xwayland =
87 wlr_xwayland_create(server->wl_display, server->compositor);
88 wl_signal_add(&server->xwayland->events.new_surface,
89 &server->xwayland_surface);
90 server->xwayland_surface.notify = handle_xwayland_surface;
91 wl_signal_add(&server->xwayland->events.ready,
92 &server->xwayland_ready);
93 // TODO: call server_ready now if xwayland is not enabled
94 server->xwayland_ready.notify = server_ready;
95
96 // TODO: configurable cursor theme and size
97 server->xcursor_manager = wlr_xcursor_manager_create(NULL, 24);
98 wlr_xcursor_manager_load(server->xcursor_manager, 1);
99 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
100 server->xcursor_manager, "left_ptr", 1);
101 if (xcursor != NULL) {
102 struct wlr_xcursor_image *image = xcursor->images[0];
103 wlr_xwayland_set_cursor(server->xwayland, image->buffer,
104 image->width * 4, image->width, image->height, image->hotspot_x,
105 image->hotspot_y);
106 }
107
108 // TODO: Integration with sway borders
109 struct wlr_server_decoration_manager *deco_manager =
110 wlr_server_decoration_manager_create(server->wl_display);
111 wlr_server_decoration_manager_set_default_mode(
112 deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
113
114 wlr_linux_dmabuf_create(server->wl_display, renderer);
115
116 server->socket = wl_display_add_socket_auto(server->wl_display);
117 if (!server->socket) {
118 wlr_log(L_ERROR, "Unable to open wayland socket");
119 wlr_backend_destroy(server->backend);
120 return false;
121 }
122
123 input_manager = input_manager_create(server);
124 return true;
125}
126
127void server_fini(struct sway_server *server) {
128 // TODO
129}
130
131void server_run(struct sway_server *server) {
132 wlr_log(L_INFO, "Running compositor on wayland display '%s'",
133 server->socket);
134 setenv("WAYLAND_DISPLAY", server->socket, true);
135 if (!wlr_backend_start(server->backend)) {
136 wlr_log(L_ERROR, "Failed to start backend");
137 wlr_backend_destroy(server->backend);
138 return;
139 }
140 wl_display_run(server->wl_display);
141}
diff --git a/sway/sway-input.5.txt b/sway/sway-input.5.txt
index f0c8f87c..05725360 100644
--- a/sway/sway-input.5.txt
+++ b/sway/sway-input.5.txt
@@ -1,5 +1,5 @@
1///// 1/////
2vim:set ts=4 sw=4 tw=82 noet: 2vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
3///// 3/////
4sway-input (5) 4sway-input (5)
5============== 5==============
@@ -11,12 +11,57 @@ sway-input - input configuration file and commands
11Description 11Description
12----------- 12-----------
13 13
14Sway allows for configuration of libinput devices within the sway configuration file. 14Sway allows for configuration of devices within the sway configuration file.
15sway-input commands must be used inside an _input { }_ block in the config. 15sway-input commands must be used inside an _input { }_ block in the config.
16To obtain a list of available device identifiers, run **swaymsg -t get_inputs**. 16To obtain a list of available device identifiers, run **swaymsg -t get_inputs**.
17 17
18Commands 18Input Commands
19-------- 19--------------
20
21Keyboard Configuration
22~~~~~~~~~~~~~~~~~~~~~~
23
24For more information on these xkb configuration options, see
25**xkeyboard-config**(7).
26
27**input** <identifier> xkb_layout <layout_name>::
28 Sets the layout of the keyboard like _us_ or _de_.
29
30**input** <identifier> xkb_model <model_name>::
31 Sets the model of the keyboard. This has an influence for some extra keys your
32 keyboard might have.
33
34**input** <identifier> xkb_options <options>::
35 Sets extra xkb configuration options for the keyboard.
36
37**input** <identifier> xkb_rules <rules>::
38 Sets files of rules to be used for keyboard mapping composition.
39
40**input** <identifier> xkb_variant <variant>::
41 Sets the variant of the keyboard like _dvorak_ or _colemak_.
42
43Mapping Configuration
44---------------------
45
46**input** <identifier> map_to_output <identifier>::
47 Maps inputs from this device to the specified output. Only meaningful if the
48 device is a pointer, touch, or drawing tablet device.
49
50**input** <identifier> map_to_region <WxH\@X,Y>::
51 Maps inputs from this device to the specified region of the global output
52 layout. Only meaningful if the device is a pointer, touch, or drawing tablet
53 device.
54
55**input** <identifier> map_region <WxH\@X,Y>::
56 Ignores inputs from this device that do not occur within the specified region.
57 Can be in millimeters (e.g. 10mmx20mm\@10mm,20mm) or in terms of 0..1 (e.g.
58 0.5x0.5\@0,0). Not all devices support millimeters. Only meaningful if the
59 device is not a keyboard an provides events in absolute terms (such as a
60 drawing tablet or touch screen - most pointers provide events relative to the
61 previous frame).
62
63Libinput Configuration
64~~~~~~~~~~~~~~~~~~~~~~
20 65
21**input** <identifier> accel_profile <adaptive|flat>:: 66**input** <identifier> accel_profile <adaptive|flat>::
22 Sets the pointer acceleration profile for the specified input device. 67 Sets the pointer acceleration profile for the specified input device.
@@ -53,6 +98,27 @@ Commands
53**input** <identifier> tap <enabled|disabled>:: 98**input** <identifier> tap <enabled|disabled>::
54 Enables or disables tap for specified input device. 99 Enables or disables tap for specified input device.
55 100
101Seat Configuration
102------------------
103
104Configure options for multiseat mode. sway-seat commands must be used inside a
105_seat { }_ block in the config.
106
107A _seat_ is a collection of input devices that act independently of each other.
108Seats are identified by name and the default seat is _seat0_ if no seats are
109configured. Each seat has an independent keyboard focus and a separate cursor that
110is controlled by the pointer devices of the seat. This is useful for multiple
111people using the desktop at the same time with their own devices (each sitting in
112their own "seat").
113
114**seat** <name> attach <input_identifier>::
115 Attach an input device to this seat by its input identifier. A special value
116 of _*_ will attach all devices to the seat.
117
118**seat** <name> fallback <true|false>::
119 Set this seat as the fallback seat. A fallback seat will attach any device not
120 explicitly attached to another seat (similar to a "default" seat).
121
56See Also 122See Also
57-------- 123--------
58 124
diff --git a/sway/sway.1.txt b/sway/sway.1.txt
index 14ab9f49..17fc13da 100644
--- a/sway/sway.1.txt
+++ b/sway/sway.1.txt
@@ -1,5 +1,5 @@
1///// 1/////
2vim:set ts=4 sw=4 tw=82 noet: 2vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
3///// 3/////
4:quotes.~: 4:quotes.~:
5 5
@@ -93,27 +93,6 @@ The following environment variables have an effect on sway:
93*SWAYSOCK*:: 93*SWAYSOCK*::
94 Specifies the path to the sway IPC socket. 94 Specifies the path to the sway IPC socket.
95 95
96*WLC_DRM_DEVICE*::
97 Specifies the device to use in DRM mode.
98
99*WLC_SHM*::
100 Set 1 to force EGL clients to use shared memory.
101
102*WLC_OUTPUTS*::
103 Number of fake outputs to use when running in X11 mode.
104
105*WLC_XWAYLAND*::
106 Set to 0 to disable Xwayland support.
107
108*WLC_LIBINPUT*::
109 Set to 1 to force libinput (even in X11 mode).
110
111*WLC_REPEAT_DELAY*::
112 Configures the keyboard repeat delay.
113
114*WLC_REPEAT_RATE*::
115 Configures the keyboard repeat rate.
116
117*XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*:: 96*XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*::
118 Configures the xkb keyboard settings. See xkeyboard-config(7). 97 Configures the xkb keyboard settings. See xkeyboard-config(7).
119 98
diff --git a/sway/sway.5.txt b/sway/sway.5.txt
index 750254c8..03975349 100644
--- a/sway/sway.5.txt
+++ b/sway/sway.5.txt
@@ -43,6 +43,9 @@ The following commands may only be used in the configuration file.
43 Sets variable $name to _value_. You can use the new variable in the arguments 43 Sets variable $name to _value_. You can use the new variable in the arguments
44 of future commands. 44 of future commands.
45 45
46**swaybg_command** <command>::
47 Executes custom bg command, default is _swaybg_.
48
46The following commands cannot be used directly in the configuration file. 49The following commands cannot be used directly in the configuration file.
47They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). 50They are expected to be used with **bindsym** or at runtime through **swaymsg**(1).
48 51
@@ -126,22 +129,21 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
126 129
127**resize** <shrink|grow> <width|height> [<amount>] [px|ppt]:: 130**resize** <shrink|grow> <width|height> [<amount>] [px|ppt]::
128 Resizes the currently focused container or view by _amount_. _amount_ is 131 Resizes the currently focused container or view by _amount_. _amount_ is
129 optional: the default value is 10 (either px or ppt depending on the view 132 optional: the default value is 10 (either px or ppt depending on the view type).
130 type). The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers 133 The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers to pixels;
131 to pixels; _ppt_ specifies that _amount_ refers to percentage points of the 134 _ppt_ specifies that _amount_ refers to percentage points of the current
132 current dimension. Floating views use px dimensions by default (but can use 135 size. Floating views use px by default (but can use ppt if specified); tiled
133 ppt if specified); tiled views use ppt dimensions by default (but can use px 136 views use ppt by default (but can use px if specified).
134 if specified).
135 137
136**resize set** <width> [px] <height> [px]:: 138**resize set** <width> [px] <height> [px]::
137 Sets the width and height of the currently focused container to _width_ pixels 139 Sets the width and height of the currently focused container to _width_ pixels
138 and _height_ pixels. The [px] parameters are optional and have no effect. This 140 and _height_ pixels. The [px] parameters are optional and have no effect. This
139 command only accepts pixel dimensions. 141 command only accepts a size in pixels.
140 142
141**resize set** <width|height> <amount> [px] [<width|height> <amount> [px]]:: 143**resize set** <width|height> <amount> [px] [<width|height> <amount> [px]]::
142 Sets the _width_ and/or _height_ of the currently focused container to 144 Sets the _width_ and/or _height_ of the currently focused container to
143 _amount_. The [px] parameters are optional and have no effect. This command 145 _amount_. The [px] parameters are optional and have no effect. This command
144 only accepts pixel dimensions. 146 only accepts a size in pixels.
145 147
146**scratchpad show**:: 148**scratchpad show**::
147 Shows a window from the scratchpad. Repeatedly using this command will cycle 149 Shows a window from the scratchpad. Repeatedly using this command will cycle
@@ -254,14 +256,14 @@ The default colors are:
254 *restart* is executed. 256 *restart* is executed.
255 257
256**floating_maximum_size** <width> x <height>:: 258**floating_maximum_size** <width> x <height>::
257 Specifies the maximum dimensions of floating windows. 259 Specifies the maximum size of floating windows.
258 Uses the container dimensions as default. 260 Uses the container size as default.
259 -1 x -1 will remove any restriction on dimensions. 261 -1 x -1 will remove any restriction on size.
260 0 x 0 has the same behavior as not setting any value. 262 0 x 0 has the same behavior as not setting any value.
261 If in conflict, this option has precedence over floating_minimum_size. 263 If in conflict, this option has precedence over floating_minimum_size.
262 264
263**floating_minimum_size** <width> x <height>:: 265**floating_minimum_size** <width> x <height>::
264 Specifies the minimum dimensions of floating windows. 266 Specifies the minimum size of floating windows.
265 Default parameters are 75 x 50. 267 Default parameters are 75 x 50.
266 -1 and 0 are invalid parameters, default will be used instead. 268 -1 and 0 are invalid parameters, default will be used instead.
267 269
@@ -313,7 +315,7 @@ The default colors are:
313**hide_edge_borders** <none|vertical|horizontal|both|smart>:: 315**hide_edge_borders** <none|vertical|horizontal|both|smart>::
314 Hide window borders adjacent to the screen edges. Default is _none_. 316 Hide window borders adjacent to the screen edges. Default is _none_.
315 317
316**input** <input device> <block of commands>:: 318**input** <input_device> <block of commands>::
317 Append _{_ to this command, the following lines will be commands to configure 319 Append _{_ to this command, the following lines will be commands to configure
318 the named input device, and _}_ on its own line will close the block. 320 the named input device, and _}_ on its own line will close the block.
319 + 321 +
@@ -321,6 +323,18 @@ The default colors are:
321 + 323 +
322 See **sway-input**(5) for details. 324 See **sway-input**(5) for details.
323 325
326**seat** <seat_name> <block of commands>::
327 Append _{_ to this command, the following lines will be commands to configure
328 the named seat, and _}_ on its own line will close the block.
329 See **sway-input**(5) for details.
330
331**seat** <seat_name> cursor <move|set> <x> <y>::
332 Move cursor relatively to current position or set to absolute screen position.
333 A value of 0 causes the axis to be ignored in both commands.
334
335**seat** <seat_name> cursor <press|release> <left|right|1|2|3...>::
336 Simulate press of mouse button specified by left, right, or numerical code.
337
324**kill**:: 338**kill**::
325 Kills (force-closes) the currently-focused container and all of its children. 339 Kills (force-closes) the currently-focused container and all of its children.
326 340
@@ -349,35 +363,44 @@ The default colors are:
349 Prevents windows matching <criteria> from being focused automatically when 363 Prevents windows matching <criteria> from being focused automatically when
350 they're created. This does not apply to the first window in a workspace. 364 they're created. This does not apply to the first window in a workspace.
351 365
352**output** <name> <resolution|res> <WIDTHxHEIGHT>:: 366**output** <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]]::
353 Configures the specified output to use the given resolution. 367 Configures the specified output to use the given mode. Modes are a combination
368 of width and height (in pixels) and a refresh rate that your display can be
369 configured to use. For a list of available modes, use swaymsg -t get_outputs.
370 +
371 Examples:
372 +
373 output HDMI-A-1 mode 1920x1080
354 + 374 +
355 _Note_: sway does not currently support setting the output mode. Your output's native 375 output HDMI-A-1 mode 1920x1080@60Hz
356 resolution will be used and the screen will be scaled from the resolution
357 specified to your native resolution.
358 376
359**output** <name> <position|pos> <X,Y>:: 377**output** <name> position|pos <X,Y>::
360 Configures the specified output to be arranged at the given position. 378 Configures the specified output to be arranged at the given position.
361 379
362**output** <name> <scale> <I>:: 380**output** <name> scale <I>::
363 Configures the specified output to be scaled up by the specified integer 381 Configures the specified output to be scaled up by the specified integer
364 scaling factor (usually 2 for HiDPI screens). 382 scaling factor (usually 2 for HiDPI screens). Fractional scaling is supported.
365 383
366**output** <name> <background|bg> <file> <mode>:: 384**output** <name> background|bg <file> <mode>::
367 Sets the wallpaper for the given output to the specified file, using the given 385 Sets the wallpaper for the given output to the specified file, using the given
368 scaling mode (one of "stretch", "fill", "fit", "center", "tile"). 386 scaling mode (one of "stretch", "fill", "fit", "center", "tile").
369 387
370**output** <name> <background|bg> <color> solid_color:: 388**output** <name> background|bg <color> solid_color::
371 Sets the background of the given output to the specified color. _color_ should 389 Sets the background of the given output to the specified color. _color_ should
372 be specified as an _#rrggbb_ (no alpha) color. 390 be specified as an _#rrggbb_ (no alpha) color.
373 391
392**output** <name> transform <transform>::
393 Sets the background transform to the given value. Can be one of "90", "180",
394 "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270"
395 to apply a rotation and flip, or "normal" to apply no transform.
396
374**output** <name> disable:: 397**output** <name> disable::
375 Disables the specified output. 398 Disables the specified output.
376 399
377**NOTES FOR THE OUTPUT COMMAND**:: 400**NOTES FOR THE OUTPUT COMMAND**::
378 You may combine output commands into one, like so: 401 You may combine output commands into one, like so:
379 + 402 +
380 output HDMI-A-1 res 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch 403 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
381 + 404 +
382 You can get a list of output names like so: 405 You can get a list of output names like so:
383 + 406 +
@@ -397,6 +420,10 @@ The default colors are:
397 However, any mark that starts with an underscore will not be drawn even if the 420 However, any mark that starts with an underscore will not be drawn even if the
398 option is on. The default option is _on_. 421 option is on. The default option is _on_.
399 422
423**opacity** <value>::
424 Set the opacity of the window between 0 (completely transparent) and 1
425 (completely opaque).
426
400**unmark** <identifier>:: 427**unmark** <identifier>::
401 **Unmark** will remove _identifier_ from the list of current marks on a window. If 428 **Unmark** will remove _identifier_ from the list of current marks on a window. If
402 no _identifier_ is specified, then **unmark** will remove all marks. 429 no _identifier_ is specified, then **unmark** will remove all marks.
diff --git a/sway/tree/container.c b/sway/tree/container.c
new file mode 100644
index 00000000..ea1c93bb
--- /dev/null
+++ b/sway/tree/container.c
@@ -0,0 +1,534 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <stdint.h>
4#include <stdlib.h>
5#include <string.h>
6#include <strings.h>
7#include <wayland-server.h>
8#include <wlr/types/wlr_output_layout.h>
9#include <wlr/types/wlr_wl_shell.h>
10#include "sway/config.h"
11#include "sway/input/input-manager.h"
12#include "sway/input/seat.h"
13#include "sway/ipc-server.h"
14#include "sway/output.h"
15#include "sway/server.h"
16#include "sway/tree/layout.h"
17#include "sway/tree/view.h"
18#include "sway/tree/workspace.h"
19#include "log.h"
20
21static list_t *bfs_queue;
22
23static list_t *get_bfs_queue() {
24 if (!bfs_queue) {
25 bfs_queue = create_list();
26 if (!bfs_queue) {
27 wlr_log(L_ERROR, "could not allocate list for bfs queue");
28 return NULL;
29 }
30 }
31 bfs_queue->length = 0;
32
33 return bfs_queue;
34}
35
36const char *container_type_to_str(enum sway_container_type type) {
37 switch (type) {
38 case C_ROOT:
39 return "C_ROOT";
40 case C_OUTPUT:
41 return "C_OUTPUT";
42 case C_WORKSPACE:
43 return "C_WORKSPACE";
44 case C_CONTAINER:
45 return "C_CONTAINER";
46 case C_VIEW:
47 return "C_VIEW";
48 default:
49 return "C_UNKNOWN";
50 }
51}
52
53void container_create_notify(struct sway_container *container) {
54 // TODO send ipc event type based on the container type
55 wl_signal_emit(&root_container.sway_root->events.new_container, container);
56
57 if (container->type == C_VIEW || container->type == C_CONTAINER) {
58 ipc_event_window(container, "new");
59 }
60}
61
62static void container_close_notify(struct sway_container *container) {
63 if (container == NULL) {
64 return;
65 }
66 // TODO send ipc event type based on the container type
67 if (container->type == C_VIEW || container->type == C_WORKSPACE) {
68 ipc_event_window(container, "close");
69 }
70}
71
72struct sway_container *container_create(enum sway_container_type type) {
73 // next id starts at 1 because 0 is assigned to root_container in layout.c
74 static size_t next_id = 1;
75 struct sway_container *c = calloc(1, sizeof(struct sway_container));
76 if (!c) {
77 return NULL;
78 }
79 c->id = next_id++;
80 c->layout = L_NONE;
81 c->workspace_layout = L_NONE;
82 c->type = type;
83 c->alpha = 1.0f;
84
85 if (type != C_VIEW) {
86 c->children = create_list();
87 }
88
89 wl_signal_init(&c->events.destroy);
90 wl_signal_init(&c->events.reparent);
91
92 return c;
93}
94
95static void _container_destroy(struct sway_container *cont) {
96 if (cont == NULL) {
97 return;
98 }
99
100 wl_signal_emit(&cont->events.destroy, cont);
101 container_close_notify(cont);
102
103 struct sway_container *parent = cont->parent;
104 if (cont->children != NULL && cont->children->length) {
105 // remove children until there are no more, container_destroy calls
106 // container_remove_child, which removes child from this container
107 while (cont->children != NULL) {
108 struct sway_container *child = cont->children->items[0];
109 container_remove_child(child);
110 _container_destroy(child);
111 }
112 }
113 if (cont->marks) {
114 list_foreach(cont->marks, free);
115 list_free(cont->marks);
116 }
117 if (parent) {
118 parent = container_remove_child(cont);
119 }
120 if (cont->name) {
121 free(cont->name);
122 }
123 list_free(cont->children);
124 cont->children = NULL;
125 free(cont);
126}
127
128static struct sway_container *container_output_destroy(
129 struct sway_container *output) {
130 if (!sway_assert(output, "cannot destroy null output")) {
131 return NULL;
132 }
133
134 if (output->children->length > 0) {
135 // TODO save workspaces when there are no outputs.
136 // TODO also check if there will ever be no outputs except for exiting
137 // program
138 if (root_container.children->length > 1) {
139 int p = root_container.children->items[0] == output;
140 // Move workspace from this output to another output
141 while (output->children->length) {
142 struct sway_container *child = output->children->items[0];
143 container_remove_child(child);
144 container_add_child(root_container.children->items[p], child);
145 }
146 container_sort_workspaces(root_container.children->items[p]);
147 arrange_windows(root_container.children->items[p],
148 -1, -1);
149 }
150 }
151
152 wl_list_remove(&output->sway_output->destroy.link);
153 wl_list_remove(&output->sway_output->mode.link);
154 wl_list_remove(&output->sway_output->transform.link);
155 wl_list_remove(&output->sway_output->scale.link);
156
157 wl_list_remove(&output->sway_output->damage_destroy.link);
158 wl_list_remove(&output->sway_output->damage_frame.link);
159
160 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
161 _container_destroy(output);
162 return &root_container;
163}
164
165static struct sway_container *container_workspace_destroy(
166 struct sway_container *workspace) {
167 if (!sway_assert(workspace, "cannot destroy null workspace")) {
168 return NULL;
169 }
170
171 // Do not destroy this if it's the last workspace on this output
172 struct sway_container *output = container_parent(workspace, C_OUTPUT);
173 if (output && output->children->length == 1) {
174 return NULL;
175 }
176
177 struct sway_container *parent = workspace->parent;
178 if (workspace->children->length == 0) {
179 // destroy the WS if there are no children (TODO check for floating)
180 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
181 ipc_event_workspace(workspace, NULL, "empty");
182 } else {
183 // Move children to a different workspace on this output
184 struct sway_container *new_workspace = NULL;
185 // TODO move floating
186 for (int i = 0; i < output->children->length; i++) {
187 if (output->children->items[i] != workspace) {
188 new_workspace = output->children->items[i];
189 break;
190 }
191 }
192
193 wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
194 workspace->name, new_workspace->name);
195 for (int i = 0; i < workspace->children->length; i++) {
196 container_move_to(workspace->children->items[i], new_workspace);
197 }
198 }
199
200 _container_destroy(workspace);
201
202 output_damage_whole(output->sway_output);
203
204 return parent;
205}
206
207static void container_root_finish(struct sway_container *con) {
208 wlr_log(L_ERROR, "TODO: destroy the root container");
209}
210
211bool container_reap_empty(struct sway_container *con) {
212 switch (con->type) {
213 case C_ROOT:
214 case C_OUTPUT:
215 // dont reap these
216 break;
217 case C_WORKSPACE:
218 if (!workspace_is_visible(con) && con->children->length == 0) {
219 wlr_log(L_DEBUG, "Destroying workspace via reaper");
220 container_workspace_destroy(con);
221 return true;
222 }
223 break;
224 case C_CONTAINER:
225 if (con->children->length == 0) {
226 _container_destroy(con);
227 return true;
228 }
229 case C_VIEW:
230 break;
231 case C_TYPES:
232 sway_assert(false, "container_reap_empty called on an invalid "
233 "container");
234 break;
235 }
236
237 return false;
238}
239
240struct sway_container *container_reap_empty_recursive(
241 struct sway_container *con) {
242 while (con) {
243 struct sway_container *next = con->parent;
244 if (!container_reap_empty(con)) {
245 break;
246 }
247 con = next;
248 }
249 return con;
250}
251
252struct sway_container *container_flatten(struct sway_container *container) {
253 while (container->type == C_CONTAINER && container->children->length == 1) {
254 struct sway_container *child = container->children->items[0];
255 struct sway_container *parent = container->parent;
256 container_replace_child(container, child);
257 container_destroy(container);
258 container = parent;
259 }
260 return container;
261}
262
263struct sway_container *container_destroy(struct sway_container *con) {
264 if (con == NULL) {
265 return NULL;
266 }
267
268 struct sway_container *parent = con->parent;
269
270 switch (con->type) {
271 case C_ROOT:
272 container_root_finish(con);
273 break;
274 case C_OUTPUT:
275 // dont try to reap the root after this
276 container_output_destroy(con);
277 break;
278 case C_WORKSPACE:
279 // dont try to reap the output after this
280 container_workspace_destroy(con);
281 break;
282 case C_CONTAINER:
283 if (con->children->length) {
284 for (int i = 0; i < con->children->length; ++i) {
285 struct sway_container *child = con->children->items[0];
286 container_remove_child(child);
287 container_add_child(parent, child);
288 }
289 }
290 _container_destroy(con);
291 break;
292 case C_VIEW:
293 _container_destroy(con);
294 break;
295 case C_TYPES:
296 wlr_log(L_ERROR, "container_destroy called on an invalid "
297 "container");
298 break;
299 }
300
301 return container_reap_empty_recursive(parent);
302}
303
304static void container_close_func(struct sway_container *container, void *data) {
305 if (container->type == C_VIEW) {
306 view_close(container->sway_view);
307 }
308}
309
310struct sway_container *container_close(struct sway_container *con) {
311 if (!sway_assert(con != NULL,
312 "container_close called with a NULL container")) {
313 return NULL;
314 }
315
316 struct sway_container *parent = con->parent;
317
318 if (con->type == C_VIEW) {
319 view_close(con->sway_view);
320 } else {
321 container_for_each_descendant_dfs(con, container_close_func, NULL);
322 }
323
324 return parent;
325}
326
327struct sway_container *container_view_create(struct sway_container *sibling,
328 struct sway_view *sway_view) {
329 if (!sway_assert(sibling,
330 "container_view_create called with NULL sibling/parent")) {
331 return NULL;
332 }
333 const char *title = view_get_title(sway_view);
334 struct sway_container *swayc = container_create(C_VIEW);
335 wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s",
336 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
337 // Setup values
338 swayc->sway_view = sway_view;
339 swayc->name = title ? strdup(title) : NULL;
340 swayc->width = 0;
341 swayc->height = 0;
342
343 if (sibling->type == C_WORKSPACE) {
344 // Case of focused workspace, just create as child of it
345 container_add_child(sibling, swayc);
346 } else {
347 // Regular case, create as sibling of current container
348 container_add_sibling(sibling, swayc);
349 }
350 container_create_notify(swayc);
351 return swayc;
352}
353
354void container_descendants(struct sway_container *root,
355 enum sway_container_type type,
356 void (*func)(struct sway_container *item, void *data), void *data) {
357 for (int i = 0; i < root->children->length; ++i) {
358 struct sway_container *item = root->children->items[i];
359 if (item->type == type) {
360 func(item, data);
361 }
362 if (item->children && item->children->length) {
363 container_descendants(item, type, func, data);
364 }
365 }
366}
367
368struct sway_container *container_find(struct sway_container *container,
369 bool (*test)(struct sway_container *view, void *data), void *data) {
370 if (!container->children) {
371 return NULL;
372 }
373 // TODO: floating windows
374 for (int i = 0; i < container->children->length; ++i) {
375 struct sway_container *child = container->children->items[i];
376 if (test(child, data)) {
377 return child;
378 } else {
379 struct sway_container *res = container_find(child, test, data);
380 if (res) {
381 return res;
382 }
383 }
384 }
385 return NULL;
386}
387
388struct sway_container *container_parent(struct sway_container *container,
389 enum sway_container_type type) {
390 if (!sway_assert(container, "container is NULL")) {
391 return NULL;
392 }
393 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
394 return NULL;
395 }
396 do {
397 container = container->parent;
398 } while (container && container->type != type);
399 return container;
400}
401
402struct sway_container *container_at(struct sway_container *parent,
403 double lx, double ly,
404 struct wlr_surface **surface, double *sx, double *sy) {
405 list_t *queue = get_bfs_queue();
406 if (!queue) {
407 return NULL;
408 }
409
410 list_add(queue, parent);
411
412 struct sway_container *swayc = NULL;
413 while (queue->length) {
414 swayc = queue->items[0];
415 list_del(queue, 0);
416 if (swayc->type == C_VIEW) {
417 struct sway_view *sview = swayc->sway_view;
418 struct sway_container *soutput = container_parent(swayc, C_OUTPUT);
419 struct wlr_box *output_box =
420 wlr_output_layout_get_box(
421 root_container.sway_root->output_layout,
422 soutput->sway_output->wlr_output);
423 double ox = lx - output_box->x;
424 double oy = ly - output_box->y;
425 double view_sx = ox - swayc->x;
426 double view_sy = oy - swayc->y;
427
428 double _sx, _sy;
429 struct wlr_surface *_surface;
430 switch (sview->type) {
431 case SWAY_VIEW_XWAYLAND:
432 _surface = wlr_surface_surface_at(sview->surface,
433 view_sx, view_sy, &_sx, &_sy);
434 break;
435 case SWAY_VIEW_WL_SHELL:
436 _surface = wlr_wl_shell_surface_surface_at(
437 sview->wlr_wl_shell_surface,
438 view_sx, view_sy, &_sx, &_sy);
439 break;
440 case SWAY_VIEW_XDG_SHELL_V6:
441 // the top left corner of the sway container is the
442 // coordinate of the top left corner of the window geometry
443 view_sx += sview->wlr_xdg_surface_v6->geometry.x;
444 view_sy += sview->wlr_xdg_surface_v6->geometry.y;
445
446 _surface = wlr_xdg_surface_v6_surface_at(
447 sview->wlr_xdg_surface_v6,
448 view_sx, view_sy, &_sx, &_sy);
449 break;
450 }
451 if (_surface) {
452 *sx = _sx;
453 *sy = _sy;
454 *surface = _surface;
455 return swayc;
456 }
457 } else {
458 list_cat(queue, swayc->children);
459 }
460 }
461
462 return NULL;
463}
464
465void container_for_each_descendant_dfs(struct sway_container *container,
466 void (*f)(struct sway_container *container, void *data),
467 void *data) {
468 if (container) {
469 if (container->children) {
470 for (int i = 0; i < container->children->length; ++i) {
471 struct sway_container *child =
472 container->children->items[i];
473 container_for_each_descendant_dfs(child, f, data);
474 }
475 }
476 f(container, data);
477 }
478}
479
480void container_for_each_descendant_bfs(struct sway_container *con,
481 void (*f)(struct sway_container *con, void *data), void *data) {
482 list_t *queue = get_bfs_queue();
483 if (!queue) {
484 return;
485 }
486
487 if (queue == NULL) {
488 wlr_log(L_ERROR, "could not allocate list");
489 return;
490 }
491
492 list_add(queue, con);
493
494 struct sway_container *current = NULL;
495 while (queue->length) {
496 current = queue->items[0];
497 list_del(queue, 0);
498 f(current, data);
499 // TODO floating containers
500 list_cat(queue, current->children);
501 }
502}
503
504bool container_has_anscestor(struct sway_container *descendant,
505 struct sway_container *anscestor) {
506 while (descendant->type != C_ROOT) {
507 descendant = descendant->parent;
508 if (descendant == anscestor) {
509 return true;
510 }
511 }
512 return false;
513}
514
515static bool find_child_func(struct sway_container *con, void *data) {
516 struct sway_container *child = data;
517 return con == child;
518}
519
520bool container_has_child(struct sway_container *con,
521 struct sway_container *child) {
522 if (con == NULL || con->type == C_VIEW || con->children->length == 0) {
523 return false;
524 }
525 return container_find(con, find_child_func, child);
526}
527
528void container_damage_whole(struct sway_container *con) {
529 struct sway_container *output = con;
530 if (output->type != C_OUTPUT) {
531 output = container_parent(output, C_OUTPUT);
532 }
533 output_damage_whole_container(output->sway_output, con);
534}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
new file mode 100644
index 00000000..ae76ca26
--- /dev/null
+++ b/sway/tree/layout.c
@@ -0,0 +1,1030 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h>
3#include <math.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <string.h>
7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_output_layout.h>
9#include "sway/debug.h"
10#include "sway/tree/container.h"
11#include "sway/tree/layout.h"
12#include "sway/output.h"
13#include "sway/tree/workspace.h"
14#include "sway/tree/view.h"
15#include "sway/input/seat.h"
16#include "sway/ipc-server.h"
17#include "list.h"
18#include "log.h"
19
20struct sway_container root_container;
21
22static void output_layout_handle_change(struct wl_listener *listener,
23 void *data) {
24 struct wlr_output_layout *output_layout =
25 root_container.sway_root->output_layout;
26 const struct wlr_box *layout_box =
27 wlr_output_layout_get_box(output_layout, NULL);
28 root_container.x = layout_box->x;
29 root_container.y = layout_box->y;
30 root_container.width = layout_box->width;
31 root_container.height = layout_box->height;
32
33 arrange_windows(&root_container, layout_box->width, layout_box->height);
34}
35
36struct sway_container *container_set_layout(struct sway_container *container,
37 enum sway_container_layout layout) {
38 if (container->type == C_WORKSPACE) {
39 container->workspace_layout = layout;
40 if (layout == L_HORIZ || layout == L_VERT) {
41 container->layout = layout;
42 }
43 } else {
44 container->layout = layout;
45 }
46 return container;
47}
48
49void layout_init(void) {
50 root_container.id = 0; // normally assigned in new_swayc()
51 root_container.type = C_ROOT;
52 root_container.layout = L_NONE;
53 root_container.name = strdup("root");
54 root_container.children = create_list();
55 wl_signal_init(&root_container.events.destroy);
56
57 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
58 root_container.sway_root->output_layout = wlr_output_layout_create();
59 wl_list_init(&root_container.sway_root->xwayland_unmanaged);
60 wl_signal_init(&root_container.sway_root->events.new_container);
61
62 root_container.sway_root->output_layout_change.notify =
63 output_layout_handle_change;
64 wl_signal_add(&root_container.sway_root->output_layout->events.change,
65 &root_container.sway_root->output_layout_change);
66}
67
68static int index_child(const struct sway_container *child) {
69 // TODO handle floating
70 struct sway_container *parent = child->parent;
71 int i, len;
72 len = parent->children->length;
73 for (i = 0; i < len; ++i) {
74 if (parent->children->items[i] == child) {
75 break;
76 }
77 }
78
79 if (!sway_assert(i < len, "Stray container")) {
80 return -1;
81 }
82 return i;
83}
84
85void container_insert_child(struct sway_container *parent,
86 struct sway_container *child, int i) {
87 struct sway_container *old_parent = child->parent;
88 if (old_parent) {
89 container_remove_child(child);
90 }
91 wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i);
92 list_insert(parent->children, i, child);
93 child->parent = parent;
94 wl_signal_emit(&child->events.reparent, old_parent);
95}
96
97struct sway_container *container_add_sibling(struct sway_container *fixed,
98 struct sway_container *active) {
99 // TODO handle floating
100 struct sway_container *old_parent = NULL;
101 if (active->parent) {
102 old_parent = active->parent;
103 container_remove_child(active);
104 }
105 struct sway_container *parent = fixed->parent;
106 int i = index_child(fixed);
107 list_insert(parent->children, i + 1, active);
108 active->parent = parent;
109 wl_signal_emit(&active->events.reparent, old_parent);
110 return active->parent;
111}
112
113void container_add_child(struct sway_container *parent,
114 struct sway_container *child) {
115 wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
116 child, child->type, child->width, child->height,
117 parent, parent->type, parent->width, parent->height);
118 list_add(parent->children, child);
119 child->parent = parent;
120}
121
122struct sway_container *container_remove_child(struct sway_container *child) {
123 struct sway_container *parent = child->parent;
124 for (int i = 0; i < parent->children->length; ++i) {
125 if (parent->children->items[i] == child) {
126 list_del(parent->children, i);
127 break;
128 }
129 }
130 child->parent = NULL;
131 return parent;
132}
133
134void container_move_to(struct sway_container *container,
135 struct sway_container *destination) {
136 if (container == destination
137 || container_has_anscestor(container, destination)) {
138 return;
139 }
140 struct sway_container *old_parent = container_remove_child(container);
141 container->width = container->height = 0;
142 struct sway_container *new_parent;
143 if (destination->type == C_VIEW) {
144 new_parent = container_add_sibling(destination, container);
145 } else {
146 new_parent = destination;
147 container_add_child(destination, container);
148 }
149 wl_signal_emit(&container->events.reparent, old_parent);
150 if (container->type == C_WORKSPACE) {
151 struct sway_seat *seat = input_manager_get_default_seat(
152 input_manager);
153 if (old_parent->children->length == 0) {
154 char *ws_name = workspace_next_name(old_parent->name);
155 struct sway_container *ws =
156 workspace_create(old_parent, ws_name);
157 free(ws_name);
158 seat_set_focus(seat, ws);
159 }
160 container_sort_workspaces(new_parent);
161 seat_set_focus(seat, new_parent);
162 }
163 if (old_parent) {
164 arrange_windows(old_parent, -1, -1);
165 }
166 arrange_windows(new_parent, -1, -1);
167}
168
169static bool sway_dir_to_wlr(enum movement_direction dir,
170 enum wlr_direction *out) {
171 switch (dir) {
172 case MOVE_UP:
173 *out = WLR_DIRECTION_UP;
174 break;
175 case MOVE_DOWN:
176 *out = WLR_DIRECTION_DOWN;
177 break;
178 case MOVE_LEFT:
179 *out = WLR_DIRECTION_LEFT;
180 break;
181 case MOVE_RIGHT:
182 *out = WLR_DIRECTION_RIGHT;
183 break;
184 default:
185 return false;
186 }
187
188 return true;
189}
190
191static bool is_parallel(enum sway_container_layout layout,
192 enum movement_direction dir) {
193 switch (layout) {
194 case L_TABBED:
195 case L_STACKED:
196 case L_HORIZ:
197 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
198 case L_VERT:
199 return dir == MOVE_UP || dir == MOVE_DOWN;
200 default:
201 return false;
202 }
203}
204
205static enum movement_direction invert_movement(enum movement_direction dir) {
206 switch (dir) {
207 case MOVE_LEFT:
208 return MOVE_RIGHT;
209 case MOVE_RIGHT:
210 return MOVE_LEFT;
211 case MOVE_UP:
212 return MOVE_DOWN;
213 case MOVE_DOWN:
214 return MOVE_UP;
215 default:
216 sway_assert(0, "This function expects left|right|up|down");
217 return MOVE_LEFT;
218 }
219}
220
221static int move_offs(enum movement_direction move_dir) {
222 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
223}
224
225/* Gets the index of the most extreme member based on the movement offset */
226static int container_limit(struct sway_container *container,
227 enum movement_direction move_dir) {
228 return move_offs(move_dir) < 0 ? 0 : container->children->length;
229}
230
231/* Takes one child, sets it aside, wraps the rest of the children in a new
232 * container, switches the layout of the workspace, and drops the child back in.
233 * In other words, rejigger it. */
234static void workspace_rejigger(struct sway_container *ws,
235 struct sway_container *child, enum movement_direction move_dir) {
236 struct sway_container *original_parent = child->parent;
237 struct sway_container *new_parent =
238 container_split(ws, ws->layout);
239
240 container_remove_child(child);
241 for (int i = 0; i < ws->children->length; ++i) {
242 struct sway_container *_child = ws->children->items[i];
243 container_move_to(new_parent, _child);
244 }
245
246 int index = move_offs(move_dir);
247 container_insert_child(ws, child, index < 0 ? 0 : 1);
248 container_set_layout(ws,
249 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT);
250
251 container_flatten(ws);
252 container_reap_empty_recursive(original_parent);
253 wl_signal_emit(&child->events.reparent, original_parent);
254 container_create_notify(new_parent);
255 arrange_windows(ws, -1, -1);
256}
257
258void container_move(struct sway_container *container,
259 enum movement_direction move_dir, int move_amt) {
260 if (!sway_assert(
261 container->type != C_CONTAINER || container->type != C_VIEW,
262 "Can only move containers and views")) {
263 return;
264 }
265 int offs = move_offs(move_dir);
266
267 struct sway_container *sibling = NULL;
268 struct sway_container *current = container;
269 struct sway_container *parent = current->parent;
270
271 if (parent != container_flatten(parent)) {
272 // Special case: we were the last one in this container, so flatten it
273 // and leave
274 update_debug_tree();
275 return;
276 }
277
278 while (!sibling) {
279 if (current->type == C_ROOT) {
280 return;
281 }
282
283 parent = current->parent;
284 wlr_log(L_DEBUG, "Visiting %p %s '%s'", current,
285 container_type_to_str(current->type), current->name);
286
287 int index = index_child(current);
288
289 switch (current->type) {
290 case C_OUTPUT: {
291 enum wlr_direction wlr_dir;
292 sway_dir_to_wlr(move_dir, &wlr_dir);
293 double ref_lx = current->x + current->width / 2;
294 double ref_ly = current->y + current->height / 2;
295 struct wlr_output *next = wlr_output_layout_adjacent_output(
296 root_container.sway_root->output_layout, wlr_dir,
297 current->sway_output->wlr_output, ref_lx, ref_ly);
298 if (!next) {
299 wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go");
300 return;
301 }
302 struct sway_output *next_output = next->data;
303 current = next_output->swayc;
304 wlr_log(L_DEBUG, "Selected next output (%s)", current->name);
305 // Select workspace and get outta here
306 current = seat_get_focus_inactive(
307 config->handler_context.seat, current);
308 if (current->type != C_WORKSPACE) {
309 current = container_parent(current, C_WORKSPACE);
310 }
311 sibling = current;
312 break;
313 }
314 case C_WORKSPACE:
315 if (!is_parallel(current->layout, move_dir)) {
316 if (current->children->length > 2) {
317 wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)",
318 current->children->length);
319 workspace_rejigger(current, container, move_dir);
320 } else if (current->children->length == 2) {
321 wlr_log(L_DEBUG, "Changing workspace layout");
322 container_set_layout(current,
323 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ?
324 L_HORIZ : L_VERT);
325 container_insert_child(current, container, offs < 0 ? 0 : 1);
326 arrange_windows(current, -1, -1);
327 }
328 return;
329 } else {
330 wlr_log(L_DEBUG, "Selecting output");
331 current = current->parent;
332 }
333 break;
334 case C_CONTAINER:
335 case C_VIEW:
336 if (is_parallel(parent->layout, move_dir)) {
337 if ((index == parent->children->length - 1 && offs > 0)
338 || (index == 0 && offs < 0)) {
339 if (current->parent == container->parent) {
340 wlr_log(L_DEBUG, "Hit limit, selecting parent");
341 current = current->parent;
342 } else {
343 wlr_log(L_DEBUG, "Hit limit, "
344 "promoting descendant to sibling");
345 // Special case
346 struct sway_container *old_parent = container->parent;
347 container_insert_child(current->parent, container,
348 index + (offs < 0 ? 0 : 1));
349 container->width = container->height = 0;
350 arrange_windows(current->parent, -1, -1);
351 arrange_windows(old_parent, -1, -1);
352 return;
353 }
354 } else {
355 sibling = parent->children->items[index + offs];
356 wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id);
357 }
358 } else {
359 wlr_log(L_DEBUG, "Moving up to find a parallel container");
360 current = current->parent;
361 }
362 break;
363 default:
364 sway_assert(0, "Not expecting to see container of type %s here",
365 container_type_to_str(current->type));
366 return;
367 }
368 }
369
370 // Part two: move stuff around
371 int index = index_child(container);
372 struct sway_container *old_parent = container->parent;
373
374 while (sibling) {
375 switch (sibling->type) {
376 case C_VIEW:
377 if (sibling->parent == container->parent) {
378 wlr_log(L_DEBUG, "Swapping siblings");
379 sibling->parent->children->items[index + offs] = container;
380 sibling->parent->children->items[index] = sibling;
381 arrange_windows(sibling->parent, -1, -1);
382 } else {
383 wlr_log(L_DEBUG, "Promoting to sibling of cousin");
384 container_insert_child(sibling->parent, container,
385 index_child(sibling) + (offs > 0 ? 0 : 1));
386 container->width = container->height = 0;
387 arrange_windows(sibling->parent, -1, -1);
388 arrange_windows(old_parent, -1, -1);
389 }
390 sibling = NULL;
391 break;
392 case C_WORKSPACE: // Note: only in the case of moving between outputs
393 case C_CONTAINER:
394 if (is_parallel(sibling->layout, move_dir)) {
395 int limit = container_limit(sibling, invert_movement(move_dir));
396 wlr_log(L_DEBUG, "limit: %d", limit);
397 wlr_log(L_DEBUG,
398 "Reparenting container (parallel) to index %d "
399 "(move dir: %d)", limit, move_dir);
400 container_insert_child(sibling, container, limit);
401 container->width = container->height = 0;
402 arrange_windows(sibling, -1, -1);
403 arrange_windows(old_parent, -1, -1);
404 sibling = NULL;
405 } else {
406 wlr_log(L_DEBUG, "Reparenting container (perpendicular)");
407 container_remove_child(container);
408 struct sway_container *focus_inactive = seat_get_focus_inactive(
409 config->handler_context.seat, sibling);
410 if (focus_inactive) {
411 while (focus_inactive->parent != sibling) {
412 focus_inactive = focus_inactive->parent;
413 }
414 wlr_log(L_DEBUG, "Focus inactive: id:%zd",
415 focus_inactive->id);
416 sibling = focus_inactive;
417 continue;
418 } else if (sibling->children->length) {
419 wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily");
420 container_add_sibling(sibling->children->items[0], container);
421 } else {
422 wlr_log(L_DEBUG, "No kiddos, adding container alone");
423 container_add_child(sibling, container);
424 }
425 container->width = container->height = 0;
426 arrange_windows(sibling, -1, -1);
427 arrange_windows(old_parent, -1, -1);
428 sibling = NULL;
429 }
430 break;
431 default:
432 sway_assert(0, "Not expecting to see container of type %s here",
433 container_type_to_str(sibling->type));
434 return;
435 }
436 }
437
438 if (old_parent) {
439 seat_set_focus(config->handler_context.seat, old_parent);
440 seat_set_focus(config->handler_context.seat, container);
441 }
442
443 struct sway_container *last_ws = old_parent;
444 struct sway_container *next_ws = container->parent;
445 if (last_ws && last_ws->type != C_WORKSPACE) {
446 last_ws = container_parent(last_ws, C_WORKSPACE);
447 }
448 if (next_ws && next_ws->type != C_WORKSPACE) {
449 next_ws = container_parent(next_ws, C_WORKSPACE);
450 }
451 if (last_ws && next_ws && last_ws != next_ws) {
452 ipc_event_workspace(last_ws, container, "focus");
453 }
454}
455
456enum sway_container_layout container_get_default_layout(
457 struct sway_container *con) {
458 if (con->type != C_OUTPUT) {
459 con = container_parent(con, C_OUTPUT);
460 }
461
462 if (!sway_assert(con != NULL,
463 "container_get_default_layout must be called on an attached"
464 " container below the root container")) {
465 return 0;
466 }
467
468 if (config->default_layout != L_NONE) {
469 return config->default_layout;
470 } else if (config->default_orientation != L_NONE) {
471 return config->default_orientation;
472 } else if (con->width >= con->height) {
473 return L_HORIZ;
474 } else {
475 return L_VERT;
476 }
477}
478
479static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
480 struct sway_container *a = *(void **)_a;
481 struct sway_container *b = *(void **)_b;
482 int retval = 0;
483
484 if (isdigit(a->name[0]) && isdigit(b->name[0])) {
485 int a_num = strtol(a->name, NULL, 10);
486 int b_num = strtol(b->name, NULL, 10);
487 retval = (a_num < b_num) ? -1 : (a_num > b_num);
488 } else if (isdigit(a->name[0])) {
489 retval = -1;
490 } else if (isdigit(b->name[0])) {
491 retval = 1;
492 }
493
494 return retval;
495}
496
497void container_sort_workspaces(struct sway_container *output) {
498 list_stable_sort(output->children, sort_workspace_cmp_qsort);
499}
500
501static void apply_horiz_layout(struct sway_container *container, const double x,
502 const double y, const double width,
503 const double height, const int start,
504 const int end);
505
506static void apply_vert_layout(struct sway_container *container, const double x,
507 const double y, const double width,
508 const double height, const int start,
509 const int end);
510
511void arrange_windows(struct sway_container *container,
512 double width, double height) {
513 if (config->reloading) {
514 return;
515 }
516 int i;
517 if (width == -1 || height == -1) {
518 width = container->width;
519 height = container->height;
520 }
521 // pixels are indivisible. if we don't round the pixels, then the view
522 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
523 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
524 width = floor(width);
525 height = floor(height);
526
527 wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
528 container->name, container->width, container->height, container->x,
529 container->y);
530
531 double x = 0, y = 0;
532 switch (container->type) {
533 case C_ROOT:
534 for (i = 0; i < container->children->length; ++i) {
535 struct sway_container *output = container->children->items[i];
536 const struct wlr_box *output_box = wlr_output_layout_get_box(
537 container->sway_root->output_layout,
538 output->sway_output->wlr_output);
539 output->x = output_box->x;
540 output->y = output_box->y;
541 output->width = output_box->width;
542 output->height = output_box->height;
543 wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f",
544 output->name, output->x, output->y);
545 arrange_windows(output, output_box->width, output_box->height);
546 }
547 return;
548 case C_OUTPUT:
549 // arrange all workspaces:
550 for (i = 0; i < container->children->length; ++i) {
551 struct sway_container *child = container->children->items[i];
552 arrange_windows(child, -1, -1);
553 }
554 return;
555 case C_WORKSPACE:
556 {
557 struct sway_container *output =
558 container_parent(container, C_OUTPUT);
559 struct wlr_box *area = &output->sway_output->usable_area;
560 wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d",
561 area->width, area->height, area->x, area->y);
562 container->width = width = area->width;
563 container->height = height = area->height;
564 container->x = x = area->x;
565 container->y = y = area->y;
566 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f",
567 container->name, container->x, container->y);
568 }
569 // children are properly handled below
570 break;
571 case C_VIEW:
572 {
573 container->width = width;
574 container->height = height;
575 view_configure(container->sway_view, container->x, container->y,
576 container->width, container->height);
577 wlr_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f",
578 container->width, container->height,
579 container->x, container->y);
580 }
581 return;
582 default:
583 container->width = width;
584 container->height = height;
585 x = container->x;
586 y = container->y;
587 break;
588 }
589
590 switch (container->layout) {
591 case L_HORIZ:
592 apply_horiz_layout(container, x, y, width, height, 0,
593 container->children->length);
594 break;
595 case L_VERT:
596 apply_vert_layout(container, x, y, width, height, 0,
597 container->children->length);
598 break;
599 default:
600 wlr_log(L_DEBUG, "TODO: arrange layout type %d", container->layout);
601 apply_horiz_layout(container, x, y, width, height, 0,
602 container->children->length);
603 break;
604 }
605 container_damage_whole(container);
606 // TODO: Make this less shitty
607 update_debug_tree();
608}
609
610static void apply_horiz_layout(struct sway_container *container,
611 const double x, const double y,
612 const double width, const double height,
613 const int start, const int end) {
614 double scale = 0;
615 // Calculate total width
616 for (int i = start; i < end; ++i) {
617 double *old_width =
618 &((struct sway_container *)container->children->items[i])->width;
619 if (*old_width <= 0) {
620 if (end - start > 1) {
621 *old_width = width / (end - start - 1);
622 } else {
623 *old_width = width;
624 }
625 }
626 scale += *old_width;
627 }
628 scale = width / scale;
629
630 // Resize windows
631 double child_x = x;
632 if (scale > 0.1) {
633 wlr_log(L_DEBUG, "Arranging %p horizontally", container);
634 for (int i = start; i < end; ++i) {
635 struct sway_container *child = container->children->items[i];
636 wlr_log(L_DEBUG,
637 "Calculating arrangement for %p:%d (will scale %f by %f)",
638 child, child->type, width, scale);
639
640 if (child->type == C_VIEW) {
641 view_configure(child->sway_view, child_x, y, child->width,
642 child->height);
643 } else {
644 child->x = child_x;
645 child->y = y;
646 }
647
648 if (i == end - 1) {
649 double remaining_width = x + width - child_x;
650 arrange_windows(child, remaining_width, height);
651 } else {
652 arrange_windows(child, child->width * scale, height);
653 }
654 child_x += child->width;
655 }
656
657 // update focused view border last because it may
658 // depend on the title bar geometry of its siblings.
659 /* TODO WLR
660 if (focused && container->children->length > 1) {
661 update_container_border(focused);
662 }
663 */
664 }
665}
666
667void apply_vert_layout(struct sway_container *container,
668 const double x, const double y,
669 const double width, const double height, const int start,
670 const int end) {
671 int i;
672 double scale = 0;
673 // Calculate total height
674 for (i = start; i < end; ++i) {
675 double *old_height =
676 &((struct sway_container *)container->children->items[i])->height;
677 if (*old_height <= 0) {
678 if (end - start > 1) {
679 *old_height = height / (end - start - 1);
680 } else {
681 *old_height = height;
682 }
683 }
684 scale += *old_height;
685 }
686 scale = height / scale;
687
688 // Resize
689 double child_y = y;
690 if (scale > 0.1) {
691 wlr_log(L_DEBUG, "Arranging %p vertically", container);
692 for (i = start; i < end; ++i) {
693 struct sway_container *child = container->children->items[i];
694 wlr_log(L_DEBUG,
695 "Calculating arrangement for %p:%d (will scale %f by %f)",
696 child, child->type, height, scale);
697 if (child->type == C_VIEW) {
698 view_configure(child->sway_view, x, child_y, child->width,
699 child->height);
700 } else {
701 child->x = x;
702 child->y = child_y;
703 }
704
705 if (i == end - 1) {
706 double remaining_height = y + height - child_y;
707 arrange_windows(child, width, remaining_height);
708 } else {
709 arrange_windows(child, width, child->height * scale);
710 }
711 child_y += child->height;
712 }
713
714 // update focused view border last because it may
715 // depend on the title bar geometry of its siblings.
716 /* TODO WLR
717 if (focused && container->children->length > 1) {
718 update_container_border(focused);
719 }
720 */
721 }
722}
723
724/**
725 * Get swayc in the direction of newly entered output.
726 */
727static struct sway_container *get_swayc_in_output_direction(
728 struct sway_container *output, enum movement_direction dir,
729 struct sway_seat *seat) {
730 if (!output) {
731 return NULL;
732 }
733
734 struct sway_container *ws = seat_get_focus_inactive(seat, output);
735 if (ws->type != C_WORKSPACE) {
736 ws = container_parent(ws, C_WORKSPACE);
737 }
738
739 if (ws == NULL) {
740 wlr_log(L_ERROR, "got an output without a workspace");
741 return NULL;
742 }
743
744 if (ws->children->length > 0) {
745 switch (dir) {
746 case MOVE_LEFT:
747 // get most right child of new output
748 return ws->children->items[ws->children->length-1];
749 case MOVE_RIGHT:
750 // get most left child of new output
751 return ws->children->items[0];
752 case MOVE_UP:
753 case MOVE_DOWN: {
754 struct sway_container *focused =
755 seat_get_focus_inactive(seat, ws);
756 if (focused && focused->parent) {
757 struct sway_container *parent = focused->parent;
758 if (parent->layout == L_VERT) {
759 if (dir == MOVE_UP) {
760 // get child furthest down on new output
761 int idx = parent->children->length - 1;
762 return parent->children->items[idx];
763 } else if (dir == MOVE_DOWN) {
764 // get child furthest up on new output
765 return parent->children->items[0];
766 }
767 }
768 return focused;
769 }
770 break;
771 }
772 default:
773 break;
774 }
775 }
776
777 return ws;
778}
779
780static void get_layout_center_position(struct sway_container *container,
781 int *x, int *y) {
782 // FIXME view coords are inconsistently referred to in layout/output systems
783 if (container->type == C_OUTPUT) {
784 *x = container->x + container->width/2;
785 *y = container->y + container->height/2;
786 } else {
787 struct sway_container *output = container_parent(container, C_OUTPUT);
788 if (container->type == C_WORKSPACE) {
789 // Workspace coordinates are actually wrong/arbitrary, but should
790 // be same as output.
791 *x = output->x;
792 *y = output->y;
793 } else {
794 *x = output->x + container->x;
795 *y = output->y + container->y;
796 }
797 }
798}
799
800static struct sway_container *sway_output_from_wlr(struct wlr_output *output) {
801 if (output == NULL) {
802 return NULL;
803 }
804 for (int i = 0; i < root_container.children->length; ++i) {
805 struct sway_container *o = root_container.children->items[i];
806 if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
807 return o;
808 }
809 }
810 return NULL;
811}
812
813struct sway_container *container_get_in_direction(
814 struct sway_container *container, struct sway_seat *seat,
815 enum movement_direction dir) {
816 if (dir == MOVE_CHILD) {
817 return seat_get_focus_inactive(seat, container);
818 }
819
820 struct sway_container *parent = container->parent;
821 if (dir == MOVE_PARENT) {
822 if (parent->type == C_OUTPUT) {
823 return NULL;
824 } else {
825 return parent;
826 }
827 }
828
829 // TODO WLR fullscreen
830 /*
831 if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
832 wlr_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
833 container = container_parent(container, C_OUTPUT);
834 get_layout_center_position(container, &abs_pos);
835 struct sway_container *output =
836 swayc_adjacent_output(container, dir, &abs_pos, true);
837 return get_swayc_in_output_direction(output, dir);
838 }
839 if (container->type == C_WORKSPACE && container->fullscreen) {
840 sway_log(L_DEBUG, "Moving to fullscreen view");
841 return container->fullscreen;
842 }
843 */
844
845 struct sway_container *wrap_candidate = NULL;
846 while (true) {
847 bool can_move = false;
848 int desired;
849 int idx = index_child(container);
850 if (parent->type == C_ROOT) {
851 enum wlr_direction wlr_dir = 0;
852 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
853 "got invalid direction: %d", dir)) {
854 return NULL;
855 }
856 int lx, ly;
857 get_layout_center_position(container, &lx, &ly);
858 struct wlr_output_layout *layout =
859 root_container.sway_root->output_layout;
860 struct wlr_output *wlr_adjacent =
861 wlr_output_layout_adjacent_output(layout, wlr_dir,
862 container->sway_output->wlr_output, lx, ly);
863 struct sway_container *adjacent =
864 sway_output_from_wlr(wlr_adjacent);
865
866 if (!adjacent || adjacent == container) {
867 return wrap_candidate;
868 }
869 struct sway_container *next =
870 get_swayc_in_output_direction(adjacent, dir, seat);
871 if (next == NULL) {
872 return NULL;
873 }
874 if (next->children && next->children->length) {
875 // TODO consider floating children as well
876 return seat_get_focus_inactive_view(seat, next);
877 } else {
878 return next;
879 }
880 } else {
881 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
882 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
883 can_move = true;
884 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
885 }
886 } else {
887 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
888 can_move = true;
889 desired = idx + (dir == MOVE_UP ? -1 : 1);
890 }
891 }
892 }
893
894 if (can_move) {
895 // TODO handle floating
896 if (desired < 0 || desired >= parent->children->length) {
897 can_move = false;
898 int len = parent->children->length;
899 if (!wrap_candidate && len > 1) {
900 if (desired < 0) {
901 wrap_candidate = parent->children->items[len-1];
902 } else {
903 wrap_candidate = parent->children->items[0];
904 }
905 if (config->force_focus_wrapping) {
906 return wrap_candidate;
907 }
908 }
909 } else {
910 struct sway_container *desired_con = parent->children->items[desired];
911 wlr_log(L_DEBUG,
912 "cont %d-%p dir %i sibling %d: %p", idx,
913 container, dir, desired, desired_con);
914 struct sway_container *next = seat_get_focus_inactive_view(seat, desired_con);
915 return next;
916 }
917 }
918
919 if (!can_move) {
920 container = parent;
921 parent = parent->parent;
922 if (!parent) {
923 // wrapping is the last chance
924 return wrap_candidate;
925 }
926 }
927 }
928}
929
930struct sway_container *container_replace_child(struct sway_container *child,
931 struct sway_container *new_child) {
932 struct sway_container *parent = child->parent;
933 if (parent == NULL) {
934 return NULL;
935 }
936 int i = index_child(child);
937
938 // TODO floating
939 if (new_child->parent) {
940 container_remove_child(new_child);
941 }
942 parent->children->items[i] = new_child;
943 new_child->parent = parent;
944 child->parent = NULL;
945
946 // Set geometry for new child
947 new_child->x = child->x;
948 new_child->y = child->y;
949 new_child->width = child->width;
950 new_child->height = child->height;
951
952 // reset geometry for child
953 child->width = 0;
954 child->height = 0;
955
956 return parent;
957}
958
959struct sway_container *container_split(struct sway_container *child,
960 enum sway_container_layout layout) {
961 // TODO floating: cannot split a floating container
962 if (!sway_assert(child, "child cannot be null")) {
963 return NULL;
964 }
965 if (child->type == C_WORKSPACE && child->children->length == 0) {
966 // Special case: this just behaves like splitt
967 child->prev_layout = child->layout;
968 child->layout = layout;
969 arrange_windows(child, -1, -1);
970 return child;
971 }
972
973 struct sway_container *cont = container_create(C_CONTAINER);
974
975 wlr_log(L_DEBUG, "creating container %p around %p", cont, child);
976
977 cont->prev_layout = L_NONE;
978 cont->width = child->width;
979 cont->height = child->height;
980 cont->x = child->x;
981 cont->y = child->y;
982
983 if (child->type == C_WORKSPACE) {
984 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
985 struct sway_container *workspace = child;
986 bool set_focus = (seat_get_focus(seat) == workspace);
987
988 while (workspace->children->length) {
989 struct sway_container *ws_child = workspace->children->items[0];
990 container_remove_child(ws_child);
991 container_add_child(cont, ws_child);
992 }
993
994 container_add_child(workspace, cont);
995 enum sway_container_layout old_layout = workspace->layout;
996 container_set_layout(workspace, layout);
997 cont->layout = old_layout;
998
999 if (set_focus) {
1000 seat_set_focus(seat, cont);
1001 }
1002 } else {
1003 cont->layout = layout;
1004 container_replace_child(child, cont);
1005 container_add_child(cont, child);
1006 }
1007
1008 return cont;
1009}
1010
1011void container_recursive_resize(struct sway_container *container,
1012 double amount, enum resize_edge edge) {
1013 bool layout_match = true;
1014 wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
1015 if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) {
1016 container->width += amount;
1017 layout_match = container->layout == L_HORIZ;
1018 } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) {
1019 container->height += amount;
1020 layout_match = container->layout == L_VERT;
1021 }
1022 if (container->children) {
1023 for (int i = 0; i < container->children->length; i++) {
1024 struct sway_container *child = container->children->items[i];
1025 double amt = layout_match ?
1026 amount / container->children->length : amount;
1027 container_recursive_resize(child, amt, edge);
1028 }
1029 }
1030}
diff --git a/sway/tree/output.c b/sway/tree/output.c
new file mode 100644
index 00000000..6c7044a2
--- /dev/null
+++ b/sway/tree/output.c
@@ -0,0 +1,73 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include <strings.h>
4#include "sway/output.h"
5#include "sway/tree/output.h"
6#include "sway/tree/workspace.h"
7#include "log.h"
8
9struct sway_container *output_create(
10 struct sway_output *sway_output) {
11 struct wlr_box size;
12 wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
13 &size.height);
14
15 const char *name = sway_output->wlr_output->name;
16 char identifier[128];
17 output_get_identifier(identifier, sizeof(identifier), sway_output);
18
19 struct output_config *oc = NULL, *all = NULL;
20 for (int i = 0; i < config->output_configs->length; ++i) {
21 struct output_config *cur = config->output_configs->items[i];
22
23 if (strcasecmp(name, cur->name) == 0 ||
24 strcasecmp(identifier, cur->name) == 0) {
25 wlr_log(L_DEBUG, "Matched output config for %s", name);
26 oc = cur;
27 }
28 if (strcasecmp("*", cur->name) == 0) {
29 wlr_log(L_DEBUG, "Matched wildcard output config for %s", name);
30 all = cur;
31 }
32
33 if (oc && all) {
34 break;
35 }
36 }
37 if (!oc) {
38 oc = all;
39 }
40
41 if (oc && !oc->enabled) {
42 return NULL;
43 }
44
45 struct sway_container *output = container_create(C_OUTPUT);
46 output->sway_output = sway_output;
47 output->name = strdup(name);
48 if (output->name == NULL) {
49 container_destroy(output);
50 return NULL;
51 }
52
53 apply_output_config(oc, output);
54 container_add_child(&root_container, output);
55 load_swaybars();
56
57 // Create workspace
58 char *ws_name = workspace_next_name(output->name);
59 wlr_log(L_DEBUG, "Creating default workspace %s", ws_name);
60 struct sway_container *ws = workspace_create(output, ws_name);
61 // Set each seat's focus if not already set
62 struct sway_seat *seat = NULL;
63 wl_list_for_each(seat, &input_manager->seats, link) {
64 if (!seat->has_focus) {
65 seat_set_focus(seat, ws);
66 }
67 }
68
69 free(ws_name);
70 container_create_notify(output);
71 return output;
72}
73
diff --git a/sway/tree/view.c b/sway/tree/view.c
new file mode 100644
index 00000000..99b44720
--- /dev/null
+++ b/sway/tree/view.c
@@ -0,0 +1,329 @@
1#include <stdlib.h>
2#include <wayland-server.h>
3#include <wlr/types/wlr_output_layout.h>
4#include "log.h"
5#include "sway/output.h"
6#include "sway/tree/container.h"
7#include "sway/tree/layout.h"
8#include "sway/tree/view.h"
9
10void view_init(struct sway_view *view, enum sway_view_type type,
11 const struct sway_view_impl *impl) {
12 view->type = type;
13 view->impl = impl;
14 wl_signal_init(&view->events.unmap);
15}
16
17void view_destroy(struct sway_view *view) {
18 if (view == NULL) {
19 return;
20 }
21
22 if (view->surface != NULL) {
23 view_unmap(view);
24 }
25
26 container_destroy(view->swayc);
27
28 if (view->impl->destroy) {
29 view->impl->destroy(view);
30 } else {
31 free(view);
32 }
33}
34
35const char *view_get_title(struct sway_view *view) {
36 if (view->impl->get_prop) {
37 return view->impl->get_prop(view, VIEW_PROP_TITLE);
38 }
39 return NULL;
40}
41
42const char *view_get_app_id(struct sway_view *view) {
43 if (view->impl->get_prop) {
44 return view->impl->get_prop(view, VIEW_PROP_APP_ID);
45 }
46 return NULL;
47}
48
49const char *view_get_class(struct sway_view *view) {
50 if (view->impl->get_prop) {
51 return view->impl->get_prop(view, VIEW_PROP_CLASS);
52 }
53 return NULL;
54}
55
56const char *view_get_instance(struct sway_view *view) {
57 if (view->impl->get_prop) {
58 return view->impl->get_prop(view, VIEW_PROP_INSTANCE);
59 }
60 return NULL;
61}
62
63void view_configure(struct sway_view *view, double ox, double oy, int width,
64 int height) {
65 if (view->impl->configure) {
66 view->impl->configure(view, ox, oy, width, height);
67 }
68}
69
70void view_set_activated(struct sway_view *view, bool activated) {
71 if (view->impl->set_activated) {
72 view->impl->set_activated(view, activated);
73 }
74}
75
76void view_close(struct sway_view *view) {
77 if (view->impl->close) {
78 view->impl->close(view);
79 }
80}
81
82void view_damage(struct sway_view *view, bool whole) {
83 for (int i = 0; i < root_container.children->length; ++i) {
84 struct sway_container *cont = root_container.children->items[i];
85 if (cont->type == C_OUTPUT) {
86 output_damage_view(cont->sway_output, view, whole);
87 }
88 }
89}
90
91static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
92 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
93
94 box->x = output->x + view->swayc->x;
95 box->y = output->y + view->swayc->y;
96 box->width = view->width;
97 box->height = view->height;
98}
99
100void view_for_each_surface(struct sway_view *view,
101 wlr_surface_iterator_func_t iterator, void *user_data) {
102 if (view->impl->for_each_surface) {
103 view->impl->for_each_surface(view, iterator, user_data);
104 } else {
105 wlr_surface_for_each_surface(view->surface, iterator, user_data);
106 }
107}
108
109static void view_subsurface_create(struct sway_view *view,
110 struct wlr_subsurface *subsurface);
111
112static void view_init_subsurfaces(struct sway_view *view,
113 struct wlr_surface *surface);
114
115static void view_handle_surface_new_subsurface(struct wl_listener *listener,
116 void *data) {
117 struct sway_view *view =
118 wl_container_of(listener, view, surface_new_subsurface);
119 struct wlr_subsurface *subsurface = data;
120 view_subsurface_create(view, subsurface);
121}
122
123static void surface_send_enter_iterator(struct wlr_surface *surface,
124 int x, int y, void *data) {
125 struct wlr_output *wlr_output = data;
126 wlr_surface_send_enter(surface, wlr_output);
127}
128
129static void surface_send_leave_iterator(struct wlr_surface *surface,
130 int x, int y, void *data) {
131 struct wlr_output *wlr_output = data;
132 wlr_surface_send_leave(surface, wlr_output);
133}
134
135static void view_handle_container_reparent(struct wl_listener *listener,
136 void *data) {
137 struct sway_view *view =
138 wl_container_of(listener, view, container_reparent);
139 struct sway_container *old_parent = data;
140
141 struct sway_container *old_output = old_parent;
142 if (old_output != NULL && old_output->type != C_OUTPUT) {
143 old_output = container_parent(old_output, C_OUTPUT);
144 }
145
146 struct sway_container *new_output = view->swayc->parent;
147 if (new_output != NULL && new_output->type != C_OUTPUT) {
148 new_output = container_parent(new_output, C_OUTPUT);
149 }
150
151 if (old_output == new_output) {
152 return;
153 }
154
155 if (old_output != NULL) {
156 view_for_each_surface(view, surface_send_leave_iterator,
157 old_output->sway_output->wlr_output);
158 }
159 if (new_output != NULL) {
160 view_for_each_surface(view, surface_send_enter_iterator,
161 new_output->sway_output->wlr_output);
162 }
163}
164
165void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
166 if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
167 return;
168 }
169
170 struct sway_seat *seat = input_manager_current_seat(input_manager);
171 struct sway_container *focus = seat_get_focus_inactive(seat,
172 &root_container);
173 struct sway_container *cont = container_view_create(focus, view);
174
175 view->surface = wlr_surface;
176 view->swayc = cont;
177
178 view_init_subsurfaces(view, wlr_surface);
179 wl_signal_add(&wlr_surface->events.new_subsurface,
180 &view->surface_new_subsurface);
181 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
182
183 wl_signal_add(&view->swayc->events.reparent, &view->container_reparent);
184 view->container_reparent.notify = view_handle_container_reparent;
185
186 arrange_windows(cont->parent, -1, -1);
187 input_manager_set_focus(input_manager, cont);
188
189 view_damage(view, true);
190 view_handle_container_reparent(&view->container_reparent, NULL);
191}
192
193void view_unmap(struct sway_view *view) {
194 if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
195 return;
196 }
197
198 wl_signal_emit(&view->events.unmap, view);
199
200 view_damage(view, true);
201
202 wl_list_remove(&view->surface_new_subsurface.link);
203 wl_list_remove(&view->container_reparent.link);
204
205 struct sway_container *parent = container_destroy(view->swayc);
206
207 view->swayc = NULL;
208 view->surface = NULL;
209
210 arrange_windows(parent, -1, -1);
211}
212
213void view_update_position(struct sway_view *view, double ox, double oy) {
214 if (view->swayc->x == ox && view->swayc->y == oy) {
215 return;
216 }
217
218 view_damage(view, true);
219 view->swayc->x = ox;
220 view->swayc->y = oy;
221 view_damage(view, true);
222}
223
224void view_update_size(struct sway_view *view, int width, int height) {
225 if (view->width == width && view->height == height) {
226 return;
227 }
228
229 view_damage(view, true);
230 view->width = width;
231 view->height = height;
232 view_damage(view, true);
233}
234
235
236static void view_subsurface_create(struct sway_view *view,
237 struct wlr_subsurface *subsurface) {
238 struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child));
239 if (child == NULL) {
240 wlr_log(L_ERROR, "Allocation failed");
241 return;
242 }
243 view_child_init(child, NULL, view, subsurface->surface);
244}
245
246static void view_child_handle_surface_commit(struct wl_listener *listener,
247 void *data) {
248 struct sway_view_child *child =
249 wl_container_of(listener, child, surface_commit);
250 // TODO: only accumulate damage from the child
251 view_damage(child->view, false);
252}
253
254static void view_child_handle_surface_new_subsurface(
255 struct wl_listener *listener, void *data) {
256 struct sway_view_child *child =
257 wl_container_of(listener, child, surface_new_subsurface);
258 struct wlr_subsurface *subsurface = data;
259 view_subsurface_create(child->view, subsurface);
260}
261
262static void view_child_handle_surface_destroy(struct wl_listener *listener,
263 void *data) {
264 struct sway_view_child *child =
265 wl_container_of(listener, child, surface_destroy);
266 view_child_destroy(child);
267}
268
269static void view_child_handle_view_unmap(struct wl_listener *listener,
270 void *data) {
271 struct sway_view_child *child =
272 wl_container_of(listener, child, view_unmap);
273 view_child_destroy(child);
274}
275
276static void view_init_subsurfaces(struct sway_view *view,
277 struct wlr_surface *surface) {
278 struct wlr_subsurface *subsurface;
279 wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
280 view_subsurface_create(view, subsurface);
281 }
282}
283
284void view_child_init(struct sway_view_child *child,
285 const struct sway_view_child_impl *impl, struct sway_view *view,
286 struct wlr_surface *surface) {
287 child->impl = impl;
288 child->view = view;
289 child->surface = surface;
290
291 wl_signal_add(&surface->events.commit, &child->surface_commit);
292 child->surface_commit.notify = view_child_handle_surface_commit;
293 wl_signal_add(&surface->events.new_subsurface,
294 &child->surface_new_subsurface);
295 child->surface_new_subsurface.notify =
296 view_child_handle_surface_new_subsurface;
297 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
298 child->surface_destroy.notify = view_child_handle_surface_destroy;
299 wl_signal_add(&view->events.unmap, &child->view_unmap);
300 child->view_unmap.notify = view_child_handle_view_unmap;
301
302 struct sway_container *output = child->view->swayc->parent;
303 if (output != NULL) {
304 if (output->type != C_OUTPUT) {
305 output = container_parent(output, C_OUTPUT);
306 }
307 wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
308 }
309
310 view_init_subsurfaces(child->view, surface);
311
312 // TODO: only damage the whole child
313 view_damage(child->view, true);
314}
315
316void view_child_destroy(struct sway_view_child *child) {
317 // TODO: only damage the whole child
318 view_damage(child->view, true);
319
320 wl_list_remove(&child->surface_commit.link);
321 wl_list_remove(&child->surface_destroy.link);
322 wl_list_remove(&child->view_unmap.link);
323
324 if (child->impl && child->impl->destroy) {
325 child->impl->destroy(child);
326 } else {
327 free(child);
328 }
329}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
new file mode 100644
index 00000000..316f01e4
--- /dev/null
+++ b/sway/tree/workspace.c
@@ -0,0 +1,401 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h>
3#include <limits.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <strings.h>
8#include "stringop.h"
9#include "sway/input/input-manager.h"
10#include "sway/input/seat.h"
11#include "sway/ipc-server.h"
12#include "sway/tree/container.h"
13#include "sway/tree/workspace.h"
14#include "log.h"
15#include "util.h"
16
17static struct sway_container *get_workspace_initial_output(const char *name) {
18 struct sway_container *parent;
19 // Search for workspace<->output pair
20 int e = config->workspace_outputs->length;
21 for (int i = 0; i < config->workspace_outputs->length; ++i) {
22 struct workspace_output *wso = config->workspace_outputs->items[i];
23 if (strcasecmp(wso->workspace, name) == 0) {
24 // Find output to use if it exists
25 e = root_container.children->length;
26 for (i = 0; i < e; ++i) {
27 parent = root_container.children->items[i];
28 if (strcmp(parent->name, wso->output) == 0) {
29 return parent;
30 }
31 }
32 break;
33 }
34 }
35 // Otherwise put it on the focused output
36 struct sway_seat *seat = input_manager_current_seat(input_manager);
37 struct sway_container *focus =
38 seat_get_focus_inactive(seat, &root_container);
39 parent = focus;
40 parent = container_parent(parent, C_OUTPUT);
41 return parent;
42}
43
44struct sway_container *workspace_create(struct sway_container *output,
45 const char *name) {
46 if (output == NULL) {
47 output = get_workspace_initial_output(name);
48 }
49
50 wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name);
51 struct sway_container *workspace = container_create(C_WORKSPACE);
52
53 workspace->x = output->x;
54 workspace->y = output->y;
55 workspace->width = output->width;
56 workspace->height = output->height;
57 workspace->name = !name ? NULL : strdup(name);
58 workspace->prev_layout = L_NONE;
59 workspace->layout = container_get_default_layout(output);
60 workspace->workspace_layout = workspace->layout;
61
62 container_add_child(output, workspace);
63 container_sort_workspaces(output);
64 container_create_notify(workspace);
65
66 return workspace;
67}
68
69char *prev_workspace_name = NULL;
70struct workspace_by_number_data {
71 int len;
72 const char *cset;
73 const char *name;
74};
75
76void next_name_map(struct sway_container *ws, void *data) {
77 int *count = data;
78 ++count;
79}
80
81static bool workspace_valid_on_output(const char *output_name,
82 const char *ws_name) {
83 int i;
84 for (i = 0; i < config->workspace_outputs->length; ++i) {
85 struct workspace_output *wso = config->workspace_outputs->items[i];
86 if (strcasecmp(wso->workspace, ws_name) == 0) {
87 if (strcasecmp(wso->output, output_name) != 0) {
88 return false;
89 }
90 }
91 }
92
93 return true;
94}
95
96char *workspace_next_name(const char *output_name) {
97 wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s",
98 output_name);
99 int l = 1;
100 // Scan all workspace bindings to find the next available workspace name,
101 // if none are found/available then default to a number
102 struct sway_mode *mode = config->current_mode;
103
104 // TODO: iterate over keycode bindings too
105 int order = INT_MAX;
106 char *target = NULL;
107 for (int i = 0; i < mode->keysym_bindings->length; ++i) {
108 struct sway_binding *binding = mode->keysym_bindings->items[i];
109 char *cmdlist = strdup(binding->command);
110 char *dup = cmdlist;
111 char *name = NULL;
112
113 // workspace n
114 char *cmd = argsep(&cmdlist, " ");
115 if (cmdlist) {
116 name = argsep(&cmdlist, ",;");
117 }
118
119 if (strcmp("workspace", cmd) == 0 && name) {
120 wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
121 char *_target = strdup(name);
122 strip_quotes(_target);
123 while (isspace(*_target)) {
124 memmove(_target, _target+1, strlen(_target+1));
125 }
126
127 // Make sure that the command references an actual workspace
128 // not a command about workspaces
129 if (strcmp(_target, "next") == 0 ||
130 strcmp(_target, "prev") == 0 ||
131 strcmp(_target, "next_on_output") == 0 ||
132 strcmp(_target, "prev_on_output") == 0 ||
133 strcmp(_target, "number") == 0 ||
134 strcmp(_target, "back_and_forth") == 0 ||
135 strcmp(_target, "current") == 0)
136 {
137 free(_target);
138 free(dup);
139 continue;
140 }
141
142 // If the command is workspace number <name>, isolate the name
143 if (strncmp(_target, "number ", strlen("number ")) == 0) {
144 size_t length = strlen(_target) - strlen("number ") + 1;
145 char *temp = malloc(length);
146 strncpy(temp, _target + strlen("number "), length - 1);
147 temp[length - 1] = '\0';
148 free(_target);
149 _target = temp;
150 wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target);
151
152 // Make sure the workspace number doesn't already exist
153 if (workspace_by_number(_target)) {
154 free(_target);
155 free(dup);
156 continue;
157 }
158 }
159
160 // Make sure that the workspace doesn't already exist
161 if (workspace_by_name(_target)) {
162 free(_target);
163 free(dup);
164 continue;
165 }
166
167 // make sure that the workspace can appear on the given
168 // output
169 if (!workspace_valid_on_output(output_name, _target)) {
170 free(_target);
171 free(dup);
172 continue;
173 }
174
175 if (binding->order < order) {
176 order = binding->order;
177 free(target);
178 target = _target;
179 wlr_log(L_DEBUG, "Workspace: Found free name %s", _target);
180 }
181 }
182 free(dup);
183 }
184 if (target != NULL) {
185 return target;
186 }
187 // As a fall back, get the current number of active workspaces
188 // and return that + 1 for the next workspace's name
189 int ws_num = root_container.children->length;
190 if (ws_num >= 10) {
191 l = 2;
192 } else if (ws_num >= 100) {
193 l = 3;
194 }
195 char *name = malloc(l + 1);
196 if (!name) {
197 wlr_log(L_ERROR, "Could not allocate workspace name");
198 return NULL;
199 }
200 sprintf(name, "%d", ws_num++);
201 return name;
202}
203
204static bool _workspace_by_number(struct sway_container *view, void *data) {
205 if (view->type != C_WORKSPACE) {
206 return false;
207 }
208 struct workspace_by_number_data *wbnd = data;
209 int a = strspn(view->name, wbnd->cset);
210 return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
211}
212
213struct sway_container *workspace_by_number(const char* name) {
214 struct workspace_by_number_data wbnd = {0, "1234567890", name};
215 wbnd.len = strspn(name, wbnd.cset);
216 if (wbnd.len <= 0) {
217 return NULL;
218 }
219 return container_find(&root_container,
220 _workspace_by_number, (void *) &wbnd);
221}
222
223static bool _workspace_by_name(struct sway_container *view, void *data) {
224 return (view->type == C_WORKSPACE) &&
225 (strcasecmp(view->name, (char *) data) == 0);
226}
227
228struct sway_container *workspace_by_name(const char *name) {
229 struct sway_seat *seat = input_manager_current_seat(input_manager);
230 struct sway_container *current_workspace = NULL, *current_output = NULL;
231 struct sway_container *focus = seat_get_focus(seat);
232 if (focus) {
233 current_workspace = container_parent(focus, C_WORKSPACE);
234 current_output = container_parent(focus, C_OUTPUT);
235 }
236 if (strcmp(name, "prev") == 0) {
237 return workspace_prev(current_workspace);
238 } else if (strcmp(name, "prev_on_output") == 0) {
239 return workspace_output_prev(current_output);
240 } else if (strcmp(name, "next") == 0) {
241 return workspace_next(current_workspace);
242 } else if (strcmp(name, "next_on_output") == 0) {
243 return workspace_output_next(current_output);
244 } else if (strcmp(name, "current") == 0) {
245 return current_workspace;
246 } else {
247 return container_find(&root_container, _workspace_by_name,
248 (void *)name);
249 }
250}
251
252/**
253 * Get the previous or next workspace on the specified output. Wraps around at
254 * the end and beginning. If next is false, the previous workspace is returned,
255 * otherwise the next one is returned.
256 */
257struct sway_container *workspace_output_prev_next_impl(
258 struct sway_container *output, bool next) {
259 if (!sway_assert(output->type == C_OUTPUT,
260 "Argument must be an output, is %d", output->type)) {
261 return NULL;
262 }
263
264 struct sway_seat *seat = input_manager_current_seat(input_manager);
265 struct sway_container *focus = seat_get_focus_inactive(seat, output);
266 struct sway_container *workspace = (focus->type == C_WORKSPACE ?
267 focus :
268 container_parent(focus, C_WORKSPACE));
269
270 int i;
271 for (i = 0; i < output->children->length; i++) {
272 if (output->children->items[i] == workspace) {
273 return output->children->items[
274 wrap(i + (next ? 1 : -1), output->children->length)];
275 }
276 }
277
278 // Doesn't happen, at worst the for loop returns the previously active
279 // workspace
280 return NULL;
281}
282
283/**
284 * Get the previous or next workspace. If the first/last workspace on an output
285 * is active, proceed to the previous/next output's previous/next workspace. If
286 * next is false, the previous workspace is returned, otherwise the next one is
287 * returned.
288 */
289struct sway_container *workspace_prev_next_impl(
290 struct sway_container *workspace, bool next) {
291 if (!sway_assert(workspace->type == C_WORKSPACE,
292 "Argument must be a workspace, is %d", workspace->type)) {
293 return NULL;
294 }
295
296 struct sway_container *current_output = workspace->parent;
297 int offset = next ? 1 : -1;
298 int start = next ? 0 : 1;
299 int end;
300 if (next) {
301 end = current_output->children->length - 1;
302 } else {
303 end = current_output->children->length;
304 }
305 int i;
306 for (i = start; i < end; i++) {
307 if (current_output->children->items[i] == workspace) {
308 return current_output->children->items[i + offset];
309 }
310 }
311
312 // Given workspace is the first/last on the output, jump to the
313 // previous/next output
314 int num_outputs = root_container.children->length;
315 for (i = 0; i < num_outputs; i++) {
316 if (root_container.children->items[i] == current_output) {
317 struct sway_container *next_output = root_container.children->items[
318 wrap(i + offset, num_outputs)];
319 return workspace_output_prev_next_impl(next_output, next);
320 }
321 }
322
323 // Doesn't happen, at worst the for loop returns the previously active
324 // workspace on the active output
325 return NULL;
326}
327
328struct sway_container *workspace_output_next(struct sway_container *current) {
329 return workspace_output_prev_next_impl(current, true);
330}
331
332struct sway_container *workspace_next(struct sway_container *current) {
333 return workspace_prev_next_impl(current, true);
334}
335
336struct sway_container *workspace_output_prev(struct sway_container *current) {
337 return workspace_output_prev_next_impl(current, false);
338}
339
340struct sway_container *workspace_prev(struct sway_container *current) {
341 return workspace_prev_next_impl(current, false);
342}
343
344bool workspace_switch(struct sway_container *workspace) {
345 if (!workspace) {
346 return false;
347 }
348 struct sway_seat *seat = input_manager_current_seat(input_manager);
349 struct sway_container *focus =
350 seat_get_focus_inactive(seat, &root_container);
351 if (!seat || !focus) {
352 return false;
353 }
354 struct sway_container *active_ws = focus;
355 if (active_ws->type != C_WORKSPACE) {
356 active_ws = container_parent(focus, C_WORKSPACE);
357 }
358
359 if (config->auto_back_and_forth
360 && active_ws == workspace
361 && prev_workspace_name) {
362 struct sway_container *new_ws = workspace_by_name(prev_workspace_name);
363 workspace = new_ws ?
364 new_ws :
365 workspace_create(NULL, prev_workspace_name);
366 }
367
368 if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name)
369 && active_ws != workspace)) {
370 free(prev_workspace_name);
371 prev_workspace_name = malloc(strlen(active_ws->name) + 1);
372 if (!prev_workspace_name) {
373 wlr_log(L_ERROR, "Unable to allocate previous workspace name");
374 return false;
375 }
376 strcpy(prev_workspace_name, active_ws->name);
377 }
378
379 // TODO: Deal with sticky containers
380
381 wlr_log(L_DEBUG, "Switching to workspace %p:%s",
382 workspace, workspace->name);
383 struct sway_container *next = seat_get_focus_inactive(seat, workspace);
384 if (next == NULL) {
385 next = workspace;
386 }
387 seat_set_focus(seat, next);
388 struct sway_container *output = container_parent(workspace, C_OUTPUT);
389 arrange_windows(output, -1, -1);
390 return true;
391}
392
393bool workspace_is_visible(struct sway_container *ws) {
394 struct sway_container *output = container_parent(ws, C_OUTPUT);
395 struct sway_seat *seat = input_manager_current_seat(input_manager);
396 struct sway_container *focus = seat_get_focus_inactive(seat, output);
397 if (focus->type != C_WORKSPACE) {
398 focus = container_parent(focus, C_WORKSPACE);
399 }
400 return focus == ws;
401}
diff --git a/sway/workspace.c b/sway/workspace.c
deleted file mode 100644
index e0367190..00000000
--- a/sway/workspace.c
+++ /dev/null
@@ -1,374 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdbool.h>
4#include <limits.h>
5#include <ctype.h>
6#include <wlc/wlc.h>
7#include <string.h>
8#include <strings.h>
9#include <sys/types.h>
10#include "sway/ipc-server.h"
11#include "sway/extensions.h"
12#include "sway/workspace.h"
13#include "sway/layout.h"
14#include "sway/container.h"
15#include "sway/handlers.h"
16#include "sway/config.h"
17#include "sway/focus.h"
18#include "stringop.h"
19#include "util.h"
20#include "list.h"
21#include "log.h"
22#include "ipc.h"
23
24char *prev_workspace_name = NULL;
25struct workspace_by_number_data {
26 int len;
27 const char *cset;
28 const char *name;
29};
30
31static bool workspace_valid_on_output(const char *output_name, const char *ws_name) {
32 int i;
33 for (i = 0; i < config->workspace_outputs->length; ++i) {
34 struct workspace_output *wso = config->workspace_outputs->items[i];
35 if (strcasecmp(wso->workspace, ws_name) == 0) {
36 if (strcasecmp(wso->output, output_name) != 0) {
37 return false;
38 }
39 }
40 }
41
42 return true;
43}
44
45char *workspace_next_name(const char *output_name) {
46 sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name);
47 int i;
48 int l = 1;
49 // Scan all workspace bindings to find the next available workspace name,
50 // if none are found/available then default to a number
51 struct sway_mode *mode = config->current_mode;
52
53 int order = INT_MAX;
54 char *target = NULL;
55 for (i = 0; i < mode->bindings->length; ++i) {
56 struct sway_binding *binding = mode->bindings->items[i];
57 char *cmdlist = strdup(binding->command);
58 char *dup = cmdlist;
59 char *name = NULL;
60
61 // workspace n
62 char *cmd = argsep(&cmdlist, " ");
63 if (cmdlist) {
64 name = argsep(&cmdlist, ",;");
65 }
66
67 if (strcmp("workspace", cmd) == 0 && name) {
68 sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
69 char *_target = strdup(name);
70 strip_quotes(_target);
71 while (isspace(*_target))
72 _target++;
73
74 // Make sure that the command references an actual workspace
75 // not a command about workspaces
76 if (strcmp(_target, "next") == 0 ||
77 strcmp(_target, "prev") == 0 ||
78 strcmp(_target, "next_on_output") == 0 ||
79 strcmp(_target, "prev_on_output") == 0 ||
80 strcmp(_target, "number") == 0 ||
81 strcmp(_target, "back_and_forth") == 0 ||
82 strcmp(_target, "current") == 0)
83 {
84 free(_target);
85 free(dup);
86 continue;
87 }
88
89 // Make sure that the workspace doesn't already exist
90 if (workspace_by_name(_target)) {
91 free(_target);
92 free(dup);
93 continue;
94 }
95
96 // make sure that the workspace can appear on the given
97 // output
98 if (!workspace_valid_on_output(output_name, _target)) {
99 free(_target);
100 free(dup);
101 continue;
102 }
103
104 if (binding->order < order) {
105 order = binding->order;
106 free(target);
107 target = _target;
108 sway_log(L_DEBUG, "Workspace: Found free name %s", _target);
109 }
110 }
111 free(dup);
112 }
113 if (target != NULL) {
114 return target;
115 }
116 // As a fall back, get the current number of active workspaces
117 // and return that + 1 for the next workspace's name
118 int ws_num = root_container.children->length;
119 if (ws_num >= 10) {
120 l = 2;
121 } else if (ws_num >= 100) {
122 l = 3;
123 }
124 char *name = malloc(l + 1);
125 if (!name) {
126 sway_log(L_ERROR, "Could not allocate workspace name");
127 return NULL;
128 }
129 sprintf(name, "%d", ws_num++);
130 return name;
131}
132
133swayc_t *workspace_create(const char* name) {
134 swayc_t *parent;
135 // Search for workspace<->output pair
136 int i, e = config->workspace_outputs->length;
137 for (i = 0; i < e; ++i) {
138 struct workspace_output *wso = config->workspace_outputs->items[i];
139 if (strcasecmp(wso->workspace, name) == 0)
140 {
141 // Find output to use if it exists
142 e = root_container.children->length;
143 for (i = 0; i < e; ++i) {
144 parent = root_container.children->items[i];
145 if (strcmp(parent->name, wso->output) == 0) {
146 return new_workspace(parent, name);
147 }
148 }
149 break;
150 }
151 }
152 // Otherwise create a new one
153 parent = get_focused_container(&root_container);
154 parent = swayc_parent_by_type(parent, C_OUTPUT);
155 return new_workspace(parent, name);
156}
157
158static bool _workspace_by_name(swayc_t *view, void *data) {
159 return (view->type == C_WORKSPACE) &&
160 (strcasecmp(view->name, (char *) data) == 0);
161}
162
163swayc_t *workspace_by_name(const char* name) {
164 if (strcmp(name, "prev") == 0) {
165 return workspace_prev();
166 }
167 else if (strcmp(name, "prev_on_output") == 0) {
168 return workspace_output_prev();
169 }
170 else if (strcmp(name, "next") == 0) {
171 return workspace_next();
172 }
173 else if (strcmp(name, "next_on_output") == 0) {
174 return workspace_output_next();
175 }
176 else if (strcmp(name, "current") == 0) {
177 return swayc_active_workspace();
178 }
179 else {
180 return swayc_by_test(&root_container, _workspace_by_name, (void *) name);
181 }
182}
183
184static bool _workspace_by_number(swayc_t *view, void *data) {
185 if (view->type != C_WORKSPACE) {
186 return false;
187 }
188 struct workspace_by_number_data *wbnd = data;
189 int a = strspn(view->name, wbnd->cset);
190 return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
191}
192swayc_t *workspace_by_number(const char* name) {
193 struct workspace_by_number_data wbnd = {0, "1234567890", name};
194 wbnd.len = strspn(name, wbnd.cset);
195 if (wbnd.len <= 0) {
196 return NULL;
197 }
198 return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd);
199}
200
201/**
202 * Get the previous or next workspace on the specified output.
203 * Wraps around at the end and beginning.
204 * If next is false, the previous workspace is returned, otherwise the next one is returned.
205 */
206swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
207 if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) {
208 return NULL;
209 }
210
211 int i;
212 for (i = 0; i < output->children->length; i++) {
213 if (output->children->items[i] == output->focused) {
214 return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)];
215 }
216 }
217
218 // Doesn't happen, at worst the for loop returns the previously active workspace
219 return NULL;
220}
221
222/**
223 * Get the previous or next workspace. If the first/last workspace on an output is active,
224 * proceed to the previous/next output's previous/next workspace.
225 * If next is false, the previous workspace is returned, otherwise the next one is returned.
226 */
227swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) {
228 if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) {
229 return NULL;
230 }
231
232 swayc_t *current_output = workspace->parent;
233 int offset = next ? 1 : -1;
234 int start = next ? 0 : 1;
235 int end = next ? (current_output->children->length) - 1 : current_output->children->length;
236 int i;
237 for (i = start; i < end; i++) {
238 if (current_output->children->items[i] == workspace) {
239 return current_output->children->items[i + offset];
240 }
241 }
242
243 // Given workspace is the first/last on the output, jump to the previous/next output
244 int num_outputs = root_container.children->length;
245 for (i = 0; i < num_outputs; i++) {
246 if (root_container.children->items[i] == current_output) {
247 swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)];
248 return workspace_output_prev_next_impl(next_output, next);
249 }
250 }
251
252 // Doesn't happen, at worst the for loop returns the previously active workspace on the active output
253 return NULL;
254}
255
256swayc_t *workspace_output_next() {
257 return workspace_output_prev_next_impl(swayc_active_output(), true);
258}
259
260swayc_t *workspace_next() {
261 return workspace_prev_next_impl(swayc_active_workspace(), true);
262}
263
264swayc_t *workspace_output_prev() {
265 return workspace_output_prev_next_impl(swayc_active_output(), false);
266}
267
268swayc_t *workspace_prev() {
269 return workspace_prev_next_impl(swayc_active_workspace(), false);
270}
271
272bool workspace_switch(swayc_t *workspace) {
273 if (!workspace) {
274 return false;
275 }
276 swayc_t *active_ws = swayc_active_workspace();
277 if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) {
278 swayc_t *new_ws = workspace_by_name(prev_workspace_name);
279 workspace = new_ws ? new_ws : workspace_create(prev_workspace_name);
280 }
281
282 if (!prev_workspace_name
283 || (strcmp(prev_workspace_name, active_ws->name)
284 && active_ws != workspace)) {
285 free(prev_workspace_name);
286 prev_workspace_name = malloc(strlen(active_ws->name) + 1);
287 if (!prev_workspace_name) {
288 sway_log(L_ERROR, "Unable to allocate previous workspace name");
289 return false;
290 }
291 strcpy(prev_workspace_name, active_ws->name);
292 }
293
294 // move sticky containers
295 if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) {
296 // don't change list while traversing it, use intermediate list instead
297 list_t *stickies = create_list();
298 for (int i = 0; i < active_ws->floating->length; i++) {
299 swayc_t *cont = active_ws->floating->items[i];
300 if (cont->sticky) {
301 list_add(stickies, cont);
302 }
303 }
304 for (int i = 0; i < stickies->length; i++) {
305 swayc_t *cont = stickies->items[i];
306 sway_log(L_DEBUG, "Moving sticky container %p to %p:%s",
307 cont, workspace, workspace->name);
308 swayc_t *parent = remove_child(cont);
309 add_floating(workspace, cont);
310 // Destroy old container if we need to
311 destroy_container(parent);
312 }
313 list_free(stickies);
314 }
315 sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
316 if (!set_focused_container(get_focused_view(workspace))) {
317 return false;
318 }
319 swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
320 arrange_backgrounds();
321 arrange_windows(output, -1, -1);
322 return true;
323}
324
325swayc_t *workspace_for_pid(pid_t pid) {
326 int i;
327 swayc_t *ws = NULL;
328 struct pid_workspace *pw = NULL;
329
330 sway_log(L_DEBUG, "looking for workspace for pid %d", pid);
331
332 // leaving this here as it's useful for debugging
333 // sway_log(L_DEBUG, "all pid_workspaces");
334 // for (int k = 0; k < config->pid_workspaces->length; k++) {
335 // pw = config->pid_workspaces->items[k];
336 // sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added);
337 // }
338
339 do {
340 for (i = 0; i < config->pid_workspaces->length; i++) {
341 pw = config->pid_workspaces->items[i];
342 pid_t *pw_pid = pw->pid;
343
344 if (pid == *pw_pid) {
345 sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace);
346 break; // out of for loop
347 }
348
349 pw = NULL;
350 }
351
352 if (pw) {
353 break; // out of do-while loop
354 }
355
356 pid = get_parent_pid(pid);
357 // no sense in looking for matches for pid 0.
358 // also, if pid == getpid(), that is the compositor's
359 // pid, which definitely isn't helpful
360 } while (pid > 0 && pid != getpid());
361
362 if (pw) {
363 ws = workspace_by_name(pw->workspace);
364
365 if (!ws) {
366 sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid);
367 ws = workspace_create(pw->workspace);
368 }
369
370 list_del(config->pid_workspaces, i);
371 }
372
373 return ws;
374}
diff --git a/swaybar/CMakeLists.txt b/swaybar/CMakeLists.txt
deleted file mode 100644
index 48ededdd..00000000
--- a/swaybar/CMakeLists.txt
+++ /dev/null
@@ -1,51 +0,0 @@
1include_directories(
2 ${PROTOCOLS_INCLUDE_DIRS}
3 ${WAYLAND_INCLUDE_DIR}
4 ${CAIRO_INCLUDE_DIRS}
5 ${PANGO_INCLUDE_DIRS}
6 ${JSONC_INCLUDE_DIRS}
7 ${XKBCOMMON_INCLUDE_DIRS}
8 ${DBUS_INCLUDE_DIRS}
9)
10if (ENABLE_TRAY)
11 file(GLOB tray
12 tray/*.c
13 )
14endif()
15
16add_executable(swaybar
17 main.c
18 config.c
19 render.c
20 bar.c
21 status_line.c
22 ipc.c
23 event_loop.c
24 ${tray}
25)
26
27target_link_libraries(swaybar
28 sway-common
29 sway-wayland
30 ${WAYLAND_CLIENT_LIBRARIES}
31 ${WAYLAND_CURSOR_LIBRARIES}
32 ${CAIRO_LIBRARIES}
33 ${PANGO_LIBRARIES}
34 ${JSONC_LIBRARIES}
35 ${DBUS_LIBRARIES}
36)
37
38if (WITH_GDK_PIXBUF)
39 include_directories(
40 ${GDK_PIXBUF_INCLUDE_DIRS}
41 )
42endif()
43
44target_link_libraries(swaybar rt)
45
46install(
47 TARGETS swaybar
48 RUNTIME
49 DESTINATION bin
50 COMPONENT runtime
51)
diff --git a/swaybar/bar.c b/swaybar/bar.c
index f12923a8..d51c4ec7 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,390 +1,456 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <assert.h>
3#include <errno.h>
4#include <fcntl.h>
5#include <poll.h>
6#include <signal.h>
2#include <stdlib.h> 7#include <stdlib.h>
3#include <unistd.h>
4#include <string.h> 8#include <string.h>
5#include <fcntl.h>
6#include <errno.h>
7#include <sys/wait.h> 9#include <sys/wait.h>
8#include <signal.h> 10#include <unistd.h>
9#include <poll.h> 11#include <wayland-client.h>
12#include <wayland-cursor.h>
13#include <wlr/util/log.h>
10#ifdef __FreeBSD__ 14#ifdef __FreeBSD__
11#include <dev/evdev/input-event-codes.h> 15#include <dev/evdev/input-event-codes.h>
12#else 16#else
13#include <linux/input-event-codes.h> 17#include <linux/input-event-codes.h>
14#endif 18#endif
15#ifdef ENABLE_TRAY
16#include <dbus/dbus.h>
17#include "swaybar/tray/sni_watcher.h"
18#include "swaybar/tray/tray.h"
19#include "swaybar/tray/sni.h"
20#endif
21#include "swaybar/ipc.h"
22#include "swaybar/render.h" 19#include "swaybar/render.h"
23#include "swaybar/config.h" 20#include "swaybar/config.h"
24#include "swaybar/status_line.h"
25#include "swaybar/event_loop.h" 21#include "swaybar/event_loop.h"
22#include "swaybar/status_line.h"
26#include "swaybar/bar.h" 23#include "swaybar/bar.h"
24#include "swaybar/ipc.h"
27#include "ipc-client.h" 25#include "ipc-client.h"
28#include "list.h" 26#include "list.h"
29#include "log.h" 27#include "log.h"
28#include "pango.h"
29#include "pool-buffer.h"
30#include "wlr-layer-shell-unstable-v1-client-protocol.h"
30 31
31static void bar_init(struct bar *bar) { 32static void bar_init(struct swaybar *bar) {
32 bar->config = init_config(); 33 bar->config = init_config();
33 bar->status = init_status_line(); 34 wl_list_init(&bar->outputs);
34 bar->outputs = create_list();
35} 35}
36 36
37static void spawn_status_cmd_proc(struct bar *bar) { 37static void swaybar_output_free(struct swaybar_output *output) {
38 if (bar->config->status_command) { 38 if (!output) {
39 int pipe_read_fd[2]; 39 return;
40 int pipe_write_fd[2]; 40 }
41 41 wlr_log(L_DEBUG, "Removing output %s", output->name);
42 if (pipe(pipe_read_fd) != 0) { 42 zwlr_layer_surface_v1_destroy(output->layer_surface);
43 sway_log(L_ERROR, "Unable to create pipes for status_command fork"); 43 wl_surface_destroy(output->surface);
44 return; 44 wl_output_destroy(output->output);
45 } 45 destroy_buffer(&output->buffers[0]);
46 if (pipe(pipe_write_fd) != 0) { 46 destroy_buffer(&output->buffers[1]);
47 sway_log(L_ERROR, "Unable to create pipe for status_command fork (write)"); 47 struct swaybar_workspace *ws, *ws_tmp;
48 close(pipe_read_fd[0]); 48 wl_list_for_each_safe(ws, ws_tmp, &output->workspaces, link) {
49 close(pipe_read_fd[1]); 49 wl_list_remove(&ws->link);
50 return; 50 free(ws->name);
51 } 51 free(ws);
52 52 }
53 bar->status_command_pid = fork(); 53 struct swaybar_hotspot *hotspot, *hotspot_tmp;
54 if (bar->status_command_pid == 0) { 54 wl_list_for_each_safe(hotspot, hotspot_tmp, &output->hotspots, link) {
55 close(pipe_read_fd[0]); 55 if (hotspot->destroy) {
56 dup2(pipe_read_fd[1], STDOUT_FILENO); 56 hotspot->destroy(hotspot->data);
57 close(pipe_read_fd[1]);
58
59 dup2(pipe_write_fd[0], STDIN_FILENO);
60 close(pipe_write_fd[0]);
61 close(pipe_write_fd[1]);
62
63 char *const cmd[] = {
64 "sh",
65 "-c",
66 bar->config->status_command,
67 NULL,
68 };
69 execvp(cmd[0], cmd);
70 return;
71 } 57 }
72 58 free(hotspot);
73 close(pipe_read_fd[1]);
74 bar->status_read_fd = pipe_read_fd[0];
75 fcntl(bar->status_read_fd, F_SETFL, O_NONBLOCK);
76
77 close(pipe_write_fd[0]);
78 bar->status_write_fd = pipe_write_fd[1];
79 fcntl(bar->status_write_fd, F_SETFL, O_NONBLOCK);
80 } 59 }
60 wl_list_remove(&output->link);
61 free(output->name);
62 free(output);
81} 63}
82 64
83struct output *new_output(const char *name) { 65static void layer_surface_configure(void *data,
84 struct output *output = malloc(sizeof(struct output)); 66 struct zwlr_layer_surface_v1 *surface,
85 output->name = strdup(name); 67 uint32_t serial, uint32_t width, uint32_t height) {
86 output->window = NULL; 68 struct swaybar_output *output = data;
87 output->registry = NULL; 69 output->width = width;
88 output->workspaces = create_list(); 70 output->height = height;
89#ifdef ENABLE_TRAY 71 zwlr_layer_surface_v1_ack_configure(surface, serial);
90 output->items = create_list(); 72 render_frame(output->bar, output);
91#endif
92 return output;
93} 73}
94 74
95static void mouse_button_notify(struct window *window, int x, int y, 75static void layer_surface_closed(void *_output,
96 uint32_t button, uint32_t state_w) { 76 struct zwlr_layer_surface_v1 *surface) {
97 sway_log(L_DEBUG, "Mouse button %d clicked at %d %d %d", button, x, y, state_w); 77 struct swaybar_output *output = _output;
98 if (!state_w) { 78 swaybar_output_free(output);
99 return; 79}
100 }
101 80
102 struct output *clicked_output = NULL; 81struct zwlr_layer_surface_v1_listener layer_surface_listener = {
103 for (int i = 0; i < swaybar.outputs->length; i++) { 82 .configure = layer_surface_configure,
104 struct output *output = swaybar.outputs->items[i]; 83 .closed = layer_surface_closed,
105 if (window == output->window) { 84};
106 clicked_output = output; 85
86static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
87 uint32_t serial, struct wl_surface *surface,
88 wl_fixed_t surface_x, wl_fixed_t surface_y) {
89 struct swaybar *bar = data;
90 struct swaybar_pointer *pointer = &bar->pointer;
91 struct swaybar_output *output;
92 wl_list_for_each(output, &bar->outputs, link) {
93 if (output->surface == surface) {
94 pointer->current = output;
107 break; 95 break;
108 } 96 }
109 } 97 }
110 98 int max_scale = 1;
111 if (!sway_assert(clicked_output != NULL, "Got pointer event for non-existing output")) { 99 struct swaybar_output *_output;
112 return; 100 wl_list_for_each(_output, &bar->outputs, link) {
113 } 101 if (_output->scale > max_scale) {
114 102 max_scale = _output->scale;
115 double button_x = 0.5;
116 for (int i = 0; i < clicked_output->workspaces->length; i++) {
117 struct workspace *workspace = clicked_output->workspaces->items[i];
118 int button_width, button_height;
119
120 workspace_button_size(window, workspace->name, &button_width, &button_height);
121
122 button_x += button_width;
123 if (x <= button_x) {
124 ipc_send_workspace_command(workspace->name);
125 break;
126 } 103 }
127 } 104 }
105 wl_surface_set_buffer_scale(pointer->cursor_surface, max_scale);
106 wl_surface_attach(pointer->cursor_surface,
107 wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);
108 wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface,
109 pointer->cursor_image->hotspot_x / max_scale,
110 pointer->cursor_image->hotspot_y / max_scale);
111 wl_surface_commit(pointer->cursor_surface);
112}
128 113
129 switch (button) { 114static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
130 case BTN_LEFT: 115 uint32_t serial, struct wl_surface *surface) {
131 status_line_mouse_event(&swaybar, x, y, 1); 116 struct swaybar *bar = data;
132 break; 117 bar->pointer.current = NULL;
133 case BTN_MIDDLE: 118}
134 status_line_mouse_event(&swaybar, x, y, 2);
135 break;
136 case BTN_RIGHT:
137 status_line_mouse_event(&swaybar, x, y, 3);
138 break;
139 }
140
141#ifdef ENABLE_TRAY
142 tray_mouse_event(clicked_output, x, y, button, state_w);
143#endif
144 119
120static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
121 uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
122 struct swaybar *bar = data;
123 bar->pointer.x = wl_fixed_to_int(surface_x);
124 bar->pointer.y = wl_fixed_to_int(surface_y);
145} 125}
146 126
147static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) { 127static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
148 sway_log(L_DEBUG, "Mouse wheel scrolled %s", direction == SCROLL_UP ? "up" : "down"); 128 uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
149 129 struct swaybar *bar = data;
150 // If there are status blocks and click_events are enabled 130 struct swaybar_pointer *pointer = &bar->pointer;
151 // check if the position is within the status area and if so 131 struct swaybar_output *output = pointer->current;
152 // tell the status line to output the event and skip workspace 132 if (!sway_assert(output, "button with no active output")) {
153 // switching below. 133 return;
154 int num_blocks = swaybar.status->block_line->length; 134 }
155 if (swaybar.status->click_events && num_blocks > 0) { 135 if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
156 struct status_block *first_block = swaybar.status->block_line->items[0]; 136 return;
157 int x = window->pointer_input.last_x; 137 }
158 int y = window->pointer_input.last_y; 138 struct swaybar_hotspot *hotspot;
159 if (x > first_block->x) { 139 wl_list_for_each(hotspot, &output->hotspots, link) {
160 if (direction == SCROLL_UP) { 140 double x = pointer->x * output->scale;
161 status_line_mouse_event(&swaybar, x, y, 4); 141 double y = pointer->y * output->scale;
162 } else { 142 if (x >= hotspot->x
163 status_line_mouse_event(&swaybar, x, y, 5); 143 && y >= hotspot->y
164 } 144 && x < hotspot->x + hotspot->width
165 return; 145 && y < hotspot->y + hotspot->height) {
146 hotspot->callback(output, pointer->x, pointer->y,
147 button, hotspot->data);
166 } 148 }
167 } 149 }
150}
168 151
169 if (!swaybar.config->wrap_scroll) { 152static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
170 // Find output this window lives on 153 uint32_t time, uint32_t axis, wl_fixed_t value) {
171 int i; 154 struct swaybar *bar = data;
172 struct output *output = NULL; 155 struct swaybar_output *output = bar->pointer.current;
173 for (i = 0; i < swaybar.outputs->length; ++i) { 156 if (!sway_assert(output, "axis with no active output")) {
174 output = swaybar.outputs->items[i]; 157 return;
175 if (output->window == window) { 158 }
176 break; 159 double amt = wl_fixed_to_double(value);
177 } 160 if (!bar->config->wrap_scroll) {
178 } 161 int i = 0;
179 if (!sway_assert(i != swaybar.outputs->length, "Unknown window in scroll event")) { 162 struct swaybar_workspace *ws = NULL;
180 return; 163 wl_list_for_each(ws, &output->workspaces, link) {
181 }
182 int focused = -1;
183 for (i = 0; i < output->workspaces->length; ++i) {
184 struct workspace *ws = output->workspaces->items[i];
185 if (ws->focused) { 164 if (ws->focused) {
186 focused = i;
187 break; 165 break;
188 } 166 }
167 ++i;
189 } 168 }
190 if (!sway_assert(focused != -1, "Scroll wheel event received on inactive output")) { 169 int len = wl_list_length(&output->workspaces);
170 if (!sway_assert(i != len, "axis with null workspace")) {
191 return; 171 return;
192 } 172 }
193 if ((focused == 0 && direction == SCROLL_UP) || 173 if (i == 0 && amt > 0) {
194 (focused == output->workspaces->length - 1 && direction == SCROLL_DOWN)) { 174 return; // Do not wrap
195 // Do not wrap 175 }
196 return; 176 if (i == len - 1 && amt < 0) {
177 return; // Do not wrap
197 } 178 }
198 } 179 }
199 180
200 const char *workspace_name = direction == SCROLL_UP ? "prev_on_output" : "next_on_output"; 181 const char *workspace_name =
201 ipc_send_workspace_command(workspace_name); 182 amt < 0 ? "prev_on_output" : "next_on_output";
183 ipc_send_workspace_command(bar, workspace_name);
202} 184}
203 185
204void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) { 186static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
205 /* initialize bar with default values */ 187 // Who cares
206 bar_init(bar); 188}
207
208 /* Initialize event loop lists */
209 init_event_loop();
210
211 /* connect to sway ipc */
212 bar->ipc_socketfd = ipc_open_socket(socket_path);
213 bar->ipc_event_socketfd = ipc_open_socket(socket_path);
214
215 ipc_bar_init(bar, bar_id);
216
217 int i;
218 for (i = 0; i < bar->outputs->length; ++i) {
219 struct output *bar_output = bar->outputs->items[i];
220
221 bar_output->registry = registry_poll();
222
223 if (!bar_output->registry->desktop_shell) {
224 sway_abort("swaybar requires the compositor to support the desktop-shell extension.");
225 }
226
227 struct output_state *output = bar_output->registry->outputs->items[bar_output->idx];
228
229 bar_output->window = window_setup(bar_output->registry,
230 output->width / output->scale, 30, output->scale, false);
231 if (!bar_output->window) {
232 sway_abort("Failed to create window.");
233 }
234 desktop_shell_set_panel(bar_output->registry->desktop_shell,
235 output->output, bar_output->window->surface);
236 desktop_shell_set_panel_position(bar_output->registry->desktop_shell,
237 bar->config->position);
238 189
239 window_make_shell(bar_output->window); 190static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
191 uint32_t axis_source) {
192 // Who cares
193}
240 194
241 /* set font */ 195static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
242 bar_output->window->font = bar->config->font; 196 uint32_t time, uint32_t axis) {
197 // Who cares
198}
243 199
244 /* set mouse event callbacks */ 200static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
245 bar_output->window->pointer_input.notify_button = mouse_button_notify; 201 uint32_t axis, int32_t discrete) {
246 bar_output->window->pointer_input.notify_scroll = mouse_scroll_notify; 202 // Who cares
203}
247 204
248 /* set window height */ 205struct wl_pointer_listener pointer_listener = {
249 set_window_height(bar_output->window, bar->config->height); 206 .enter = wl_pointer_enter,
207 .leave = wl_pointer_leave,
208 .motion = wl_pointer_motion,
209 .button = wl_pointer_button,
210 .axis = wl_pointer_axis,
211 .frame = wl_pointer_frame,
212 .axis_source = wl_pointer_axis_source,
213 .axis_stop = wl_pointer_axis_stop,
214 .axis_discrete = wl_pointer_axis_discrete,
215};
216
217static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
218 enum wl_seat_capability caps) {
219 struct swaybar *bar = data;
220 if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
221 bar->pointer.pointer = wl_seat_get_pointer(wl_seat);
222 wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar);
250 } 223 }
251 /* spawn status command */ 224}
252 spawn_status_cmd_proc(bar);
253 225
254#ifdef ENABLE_TRAY 226static void seat_handle_name(void *data, struct wl_seat *wl_seat,
255 init_tray(bar); 227 const char *name) {
256#endif 228 // Who cares
257} 229}
258 230
259bool dirty = true; 231const struct wl_seat_listener seat_listener = {
232 .capabilities = seat_handle_capabilities,
233 .name = seat_handle_name,
234};
260 235
261static void respond_ipc(int fd, short mask, void *_bar) { 236static void output_geometry(void *data, struct wl_output *output, int32_t x,
262 struct bar *bar = (struct bar *)_bar; 237 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
263 sway_log(L_DEBUG, "Got IPC event."); 238 const char *make, const char *model, int32_t transform) {
264 dirty = handle_ipc_event(bar); 239 // Who cares
265} 240}
266 241
267static void respond_command(int fd, short mask, void *_bar) { 242static void output_mode(void *data, struct wl_output *output, uint32_t flags,
268 struct bar *bar = (struct bar *)_bar; 243 int32_t width, int32_t height, int32_t refresh) {
269 dirty = handle_status_line(bar); 244 // Who cares
270} 245}
271 246
272static void respond_output(int fd, short mask, void *_output) { 247static void output_done(void *data, struct wl_output *output) {
273 struct output *output = (struct output *)_output; 248 // Who cares
274 if (wl_display_dispatch(output->registry->display) == -1) { 249}
275 sway_log(L_ERROR, "failed to dispatch wl: %d", errno); 250
251static void output_scale(void *data, struct wl_output *wl_output,
252 int32_t factor) {
253 struct swaybar_output *output = data;
254 output->scale = factor;
255 if (output->surface) {
256 render_frame(output->bar, output);
276 } 257 }
277} 258}
278 259
279void bar_run(struct bar *bar) { 260struct wl_output_listener output_listener = {
280 add_event(bar->ipc_event_socketfd, POLLIN, respond_ipc, bar); 261 .geometry = output_geometry,
281 add_event(bar->status_read_fd, POLLIN, respond_command, bar); 262 .mode = output_mode,
263 .done = output_done,
264 .scale = output_scale,
265};
282 266
283 int i; 267static bool bar_uses_output(struct swaybar *bar, size_t output_index) {
284 for (i = 0; i < bar->outputs->length; ++i) { 268 if (bar->config->all_outputs) {
285 struct output *output = bar->outputs->items[i]; 269 return true;
286 add_event(wl_display_get_fd(output->registry->display),
287 POLLIN, respond_output, output);
288 } 270 }
289 271 struct config_output *coutput;
290 while (1) { 272 wl_list_for_each(coutput, &bar->config->outputs, link) {
291 if (dirty) { 273 if (coutput->index == output_index) {
292 int i; 274 return true;
293 for (i = 0; i < bar->outputs->length; ++i) {
294 struct output *output = bar->outputs->items[i];
295 if (window_prerender(output->window) && output->window->cairo) {
296 render(output, bar->config, bar->status);
297 window_render(output->window);
298 wl_display_flush(output->registry->display);
299 }
300 }
301 } 275 }
276 }
277 return false;
278}
302 279
303 dirty = false; 280static void handle_global(void *data, struct wl_registry *registry,
304 281 uint32_t name, const char *interface, uint32_t version) {
305 event_loop_poll(); 282 struct swaybar *bar = data;
306#ifdef ENABLE_TRAY 283 if (strcmp(interface, wl_compositor_interface.name) == 0) {
307 dispatch_dbus(); 284 bar->compositor = wl_registry_bind(registry, name,
308#endif 285 &wl_compositor_interface, 3);
286 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
287 bar->seat = wl_registry_bind(registry, name,
288 &wl_seat_interface, 1);
289 wl_seat_add_listener(bar->seat, &seat_listener, bar);
290 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
291 bar->shm = wl_registry_bind(registry, name,
292 &wl_shm_interface, 1);
293 } else if (strcmp(interface, wl_output_interface.name) == 0) {
294 static size_t output_index = 0;
295 if (bar_uses_output(bar, output_index)) {
296 struct swaybar_output *output =
297 calloc(1, sizeof(struct swaybar_output));
298 output->bar = bar;
299 output->output = wl_registry_bind(registry, name,
300 &wl_output_interface, 3);
301 wl_output_add_listener(output->output, &output_listener, output);
302 output->scale = 1;
303 output->index = output_index;
304 output->wl_name = name;
305 wl_list_init(&output->workspaces);
306 wl_list_init(&output->hotspots);
307 wl_list_insert(&bar->outputs, &output->link);
308 }
309 ++output_index;
310 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
311 bar->layer_shell = wl_registry_bind(
312 registry, name, &zwlr_layer_shell_v1_interface, 1);
309 } 313 }
310} 314}
311 315
312void free_workspaces(list_t *workspaces) { 316static void handle_global_remove(void *data, struct wl_registry *registry,
313 int i; 317 uint32_t name) {
314 for (i = 0; i < workspaces->length; ++i) { 318 struct swaybar *bar = data;
315 struct workspace *ws = workspaces->items[i]; 319 struct swaybar_output *output, *tmp;
316 free(ws->name); 320 wl_list_for_each_safe(output, tmp, &bar->outputs, link) {
317 free(ws); 321 if (output->wl_name == name) {
322 swaybar_output_free(output);
323 break;
324 }
318 } 325 }
319 list_free(workspaces);
320} 326}
321 327
322static void free_output(struct output *output) { 328static const struct wl_registry_listener registry_listener = {
323 window_teardown(output->window); 329 .global = handle_global,
324 if (output->registry) { 330 .global_remove = handle_global_remove,
325 registry_teardown(output->registry); 331};
332
333static void render_all_frames(struct swaybar *bar) {
334 struct swaybar_output *output;
335 wl_list_for_each(output, &bar->outputs, link) {
336 render_frame(bar, output);
326 } 337 }
338}
327 339
328 free(output->name); 340void bar_setup(struct swaybar *bar,
341 const char *socket_path, const char *bar_id) {
342 bar_init(bar);
343 init_event_loop();
329 344
330 if (output->workspaces) { 345 bar->ipc_socketfd = ipc_open_socket(socket_path);
331 free_workspaces(output->workspaces); 346 bar->ipc_event_socketfd = ipc_open_socket(socket_path);
347 ipc_initialize(bar, bar_id);
348 if (bar->config->status_command) {
349 bar->status = status_line_init(bar->config->status_command);
332 } 350 }
333 351
334 free(output); 352 bar->display = wl_display_connect(NULL);
335} 353 assert(bar->display);
336 354
337static void free_outputs(list_t *outputs) { 355 struct wl_registry *registry = wl_display_get_registry(bar->display);
338 int i; 356 wl_registry_add_listener(registry, &registry_listener, bar);
339 for (i = 0; i < outputs->length; ++i) { 357 wl_display_roundtrip(bar->display);
340 free_output(outputs->items[i]); 358 assert(bar->compositor && bar->layer_shell && bar->shm);
359 wl_display_roundtrip(bar->display);
360
361 struct swaybar_pointer *pointer = &bar->pointer;
362
363 int max_scale = 1;
364 struct swaybar_output *output;
365 wl_list_for_each(output, &bar->outputs, link) {
366 if (output->scale > max_scale) {
367 max_scale = output->scale;
368 }
341 } 369 }
342 list_free(outputs);
343}
344 370
345static void terminate_status_command(pid_t pid) { 371 pointer->cursor_theme = wl_cursor_theme_load(
346 if (pid) { 372 NULL, 24 * max_scale, bar->shm);
347 // terminate status_command process 373 assert(pointer->cursor_theme);
348 int ret = kill(pid, SIGTERM); 374 struct wl_cursor *cursor;
349 if (ret != 0) { 375 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr");
350 sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid); 376 assert(cursor);
351 } else { 377 pointer->cursor_image = cursor->images[0];
352 int status; 378 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor);
353 waitpid(pid, &status, 0); 379 assert(pointer->cursor_surface);
380
381 wl_list_for_each(output, &bar->outputs, link) {
382 struct config_output *coutput;
383 wl_list_for_each(coutput, &bar->config->outputs, link) {
384 if (coutput->index == output->index) {
385 output->name = strdup(coutput->name);
386 break;
387 }
354 } 388 }
389 output->surface = wl_compositor_create_surface(bar->compositor);
390 assert(output->surface);
391 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
392 bar->layer_shell, output->surface, output->output,
393 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
394 assert(output->layer_surface);
395 zwlr_layer_surface_v1_add_listener(output->layer_surface,
396 &layer_surface_listener, output);
397 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
398 bar->config->position);
355 } 399 }
400 ipc_get_workspaces(bar);
401 render_all_frames(bar);
356} 402}
357 403
358void bar_teardown(struct bar *bar) { 404static void display_in(int fd, short mask, void *_bar) {
359 if (bar->config) { 405 struct swaybar *bar = (struct swaybar *)_bar;
360 free_config(bar->config); 406 if (wl_display_dispatch(bar->display) == -1) {
407 bar_teardown(bar);
408 exit(0);
361 } 409 }
410}
362 411
363 if (bar->outputs) { 412static void ipc_in(int fd, short mask, void *_bar) {
364 free_outputs(bar->outputs); 413 struct swaybar *bar = (struct swaybar *)_bar;
414 if (handle_ipc_readable(bar)) {
415 render_all_frames(bar);
365 } 416 }
417}
366 418
367 if (bar->status) { 419static void status_in(int fd, short mask, void *_bar) {
368 free_status_line(bar->status); 420 struct swaybar *bar = (struct swaybar *)_bar;
421 if (status_handle_readable(bar->status)) {
422 render_all_frames(bar);
369 } 423 }
424}
370 425
371 /* close sockets/pipes */ 426void bar_run(struct swaybar *bar) {
372 if (bar->status_read_fd) { 427 add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar);
373 close(bar->status_read_fd); 428 add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar);
429 if (bar->status) {
430 add_event(bar->status->read_fd, POLLIN, status_in, bar);
374 } 431 }
375 432 while (1) {
376 if (bar->status_write_fd) { 433 event_loop_poll();
377 close(bar->status_write_fd);
378 } 434 }
435}
379 436
380 if (bar->ipc_socketfd) { 437static void free_outputs(struct wl_list *list) {
381 close(bar->ipc_socketfd); 438 struct swaybar_output *output, *tmp;
439 wl_list_for_each_safe(output, tmp, list, link) {
440 wl_list_remove(&output->link);
441 free(output->name);
442 free(output);
382 } 443 }
444}
383 445
384 if (bar->ipc_event_socketfd) { 446void bar_teardown(struct swaybar *bar) {
385 close(bar->ipc_event_socketfd); 447 free_outputs(&bar->outputs);
448 if (bar->config) {
449 free_config(bar->config);
450 }
451 close(bar->ipc_event_socketfd);
452 close(bar->ipc_socketfd);
453 if (bar->status) {
454 status_line_free(bar->status);
386 } 455 }
387
388 /* terminate status command process */
389 terminate_status_command(bar->status_command_pid);
390} 456}
diff --git a/swaybar/config.c b/swaybar/config.c
index 8fe552f2..9169ad27 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -1,40 +1,32 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include "wayland-desktop-shell-client-protocol.h"
5#include "log.h"
6#include "swaybar/config.h" 4#include "swaybar/config.h"
5#include "wlr-layer-shell-unstable-v1-client-protocol.h"
7 6
8uint32_t parse_position(const char *position) { 7uint32_t parse_position(const char *position) {
8 uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
9 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
10 uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
11 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
9 if (strcmp("top", position) == 0) { 12 if (strcmp("top", position) == 0) {
10 return DESKTOP_SHELL_PANEL_POSITION_TOP; 13 return ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | horiz;
11 } else if (strcmp("bottom", position) == 0) { 14 } else if (strcmp("bottom", position) == 0) {
12 return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; 15 return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
13 } else if (strcmp("left", position) == 0) { 16 } else if (strcmp("left", position) == 0) {
14 return DESKTOP_SHELL_PANEL_POSITION_LEFT; 17 return ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | vert;
15 } else if (strcmp("right", position) == 0) { 18 } else if (strcmp("right", position) == 0) {
16 return DESKTOP_SHELL_PANEL_POSITION_RIGHT; 19 return ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | vert;
17 } else { 20 } else {
18 return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; 21 return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
19 } 22 }
20} 23}
21 24
22char *parse_font(const char *font) { 25struct swaybar_config *init_config() {
23 char *new_font = NULL; 26 struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config));
24 if (strncmp("pango:", font, 6) == 0) {
25 font += 6;
26 }
27
28 new_font = strdup(font);
29
30 return new_font;
31}
32
33struct config *init_config() {
34 struct config *config = calloc(1, sizeof(struct config));
35 config->status_command = NULL; 27 config->status_command = NULL;
36 config->pango_markup = false; 28 config->pango_markup = false;
37 config->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; 29 config->position = parse_position("bottom");
38 config->font = strdup("monospace 10"); 30 config->font = strdup("monospace 10");
39 config->mode = NULL; 31 config->mode = NULL;
40 config->sep_symbol = NULL; 32 config->sep_symbol = NULL;
@@ -42,27 +34,16 @@ struct config *init_config() {
42 config->binding_mode_indicator = true; 34 config->binding_mode_indicator = true;
43 config->wrap_scroll = false; 35 config->wrap_scroll = false;
44 config->workspace_buttons = true; 36 config->workspace_buttons = true;
45 config->all_outputs = false; 37 wl_list_init(&config->outputs);
46 config->outputs = create_list();
47 38
48 /* height */ 39 /* height */
49 config->height = 0; 40 config->height = 0;
50 41
51#ifdef ENABLE_TRAY
52 config->tray_output = NULL;
53 config->icon_theme = NULL;
54 config->tray_padding = 2;
55 /**
56 * These constants are used by wayland and are defined in
57 * linux/input-event-codes.h
58 */
59 config->activate_button = 0x110; /* BTN_LEFT */
60 config->context_button = 0x111; /* BTN_RIGHT */
61#endif
62
63 /* colors */ 42 /* colors */
64 config->colors.background = 0x000000FF; 43 config->colors.background = 0x000000FF;
44 config->colors.focused_background = 0x000000FF;
65 config->colors.statusline = 0xFFFFFFFF; 45 config->colors.statusline = 0xFFFFFFFF;
46 config->colors.focused_statusline = 0xFFFFFFFF;
66 config->colors.separator = 0x666666FF; 47 config->colors.separator = 0x666666FF;
67 48
68 config->colors.focused_workspace.border = 0x4C7899FF; 49 config->colors.focused_workspace.border = 0x4C7899FF;
@@ -88,7 +69,7 @@ struct config *init_config() {
88 return config; 69 return config;
89} 70}
90 71
91void free_config(struct config *config) { 72void free_config(struct swaybar_config *config) {
92 free(config->status_command); 73 free(config->status_command);
93 free(config->font); 74 free(config->font);
94 free(config->mode); 75 free(config->mode);
diff --git a/swaybar/event_loop.c b/swaybar/event_loop.c
index 0d1be1da..748372ed 100644
--- a/swaybar/event_loop.c
+++ b/swaybar/event_loop.c
@@ -4,19 +4,18 @@
4#include <string.h> 4#include <string.h>
5#include <strings.h> 5#include <strings.h>
6#include <poll.h> 6#include <poll.h>
7#include "swaybar/bar.h" 7#include <time.h>
8#include "swaybar/event_loop.h" 8#include "swaybar/event_loop.h"
9#include "list.h" 9#include "list.h"
10#include "log.h"
11 10
12struct event_item { 11struct event_item {
13 void(*cb)(int fd, short mask, void *data); 12 void (*cb)(int fd, short mask, void *data);
14 void *data; 13 void *data;
15}; 14};
16 15
17struct timer_item { 16struct timer_item {
18 timer_t timer; 17 timer_t timer;
19 void(*cb)(timer_t timer, void *data); 18 void (*cb)(timer_t timer, void *data);
20 void *data; 19 void *data;
21}; 20};
22 21
@@ -138,7 +137,8 @@ void event_loop_poll() {
138void init_event_loop() { 137void init_event_loop() {
139 event_loop.fds.length = 0; 138 event_loop.fds.length = 0;
140 event_loop.fds.capacity = 10; 139 event_loop.fds.capacity = 10;
141 event_loop.fds.items = malloc(event_loop.fds.capacity * sizeof(struct pollfd)); 140 event_loop.fds.items = malloc(
141 event_loop.fds.capacity * sizeof(struct pollfd));
142 event_loop.items = create_list(); 142 event_loop.items = create_list();
143 event_loop.timers = create_list(); 143 event_loop.timers = create_list();
144} 144}
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
new file mode 100644
index 00000000..923ad755
--- /dev/null
+++ b/swaybar/i3bar.c
@@ -0,0 +1,215 @@
1#define _POSIX_C_SOURCE 200809L
2#include <json-c/json.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6#include <wlr/util/log.h>
7#include "swaybar/config.h"
8#include "swaybar/status_line.h"
9
10static void i3bar_block_free(struct i3bar_block *block) {
11 if (!block) {
12 return;
13 }
14 wl_list_remove(&block->link);
15 free(block->full_text);
16 free(block->short_text);
17 free(block->align);
18 free(block->name);
19 free(block->instance);
20 free(block->color);
21}
22
23static bool i3bar_parse_json(struct status_line *status, const char *text) {
24 struct i3bar_block *block, *tmp;
25 wl_list_for_each_safe(block, tmp, &status->blocks, link) {
26 i3bar_block_free(block);
27 }
28 json_object *results = json_tokener_parse(text);
29 if (!results) {
30 status_error(status, "[failed to parse i3bar json]");
31 return false;
32 }
33 wlr_log(L_DEBUG, "Got i3bar json: '%s'", text);
34 for (size_t i = 0; i < json_object_array_length(results); ++i) {
35 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
36 json_object *name, *instance, *separator, *separator_block_width;
37 json_object *background, *border, *border_top, *border_bottom;
38 json_object *border_left, *border_right, *markup;
39 json_object *json = json_object_array_get_idx(results, i);
40 if (!json) {
41 continue;
42 }
43 json_object_object_get_ex(json, "full_text", &full_text);
44 json_object_object_get_ex(json, "short_text", &short_text);
45 json_object_object_get_ex(json, "color", &color);
46 json_object_object_get_ex(json, "min_width", &min_width);
47 json_object_object_get_ex(json, "align", &align);
48 json_object_object_get_ex(json, "urgent", &urgent);
49 json_object_object_get_ex(json, "name", &name);
50 json_object_object_get_ex(json, "instance", &instance);
51 json_object_object_get_ex(json, "markup", &markup);
52 json_object_object_get_ex(json, "separator", &separator);
53 json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
54 json_object_object_get_ex(json, "background", &background);
55 json_object_object_get_ex(json, "border", &border);
56 json_object_object_get_ex(json, "border_top", &border_top);
57 json_object_object_get_ex(json, "border_bottom", &border_bottom);
58 json_object_object_get_ex(json, "border_left", &border_left);
59 json_object_object_get_ex(json, "border_right", &border_right);
60
61 struct i3bar_block *block = calloc(1, sizeof(struct i3bar_block));
62 block->full_text = full_text ?
63 strdup(json_object_get_string(full_text)) : NULL;
64 block->short_text = short_text ?
65 strdup(json_object_get_string(short_text)) : NULL;
66 if (color) {
67 block->color = malloc(sizeof(uint32_t));
68 *block->color = parse_color(json_object_get_string(color));
69 }
70 if (min_width) {
71 json_type type = json_object_get_type(min_width);
72 if (type == json_type_int) {
73 block->min_width = json_object_get_int(min_width);
74 } else if (type == json_type_string) {
75 /* the width will be calculated when rendering */
76 block->min_width = 0;
77 }
78 }
79 block->align = strdup(align ? json_object_get_string(align) : "left");
80 block->urgent = urgent ? json_object_get_int(urgent) : false;
81 block->name = name ? strdup(json_object_get_string(name)) : NULL;
82 block->instance = instance ?
83 strdup(json_object_get_string(instance)) : NULL;
84 if (markup) {
85 block->markup = false;
86 const char *markup_str = json_object_get_string(markup);
87 if (strcmp(markup_str, "pango") == 0) {
88 block->markup = true;
89 }
90 }
91 block->separator = separator ? json_object_get_int(separator) : true;
92 block->separator_block_width = separator_block_width ?
93 json_object_get_int(separator_block_width) : 9;
94 // Airblader features
95 block->background = background ?
96 parse_color(json_object_get_string(background)) : 0;
97 block->border = border ?
98 parse_color(json_object_get_string(border)) : 0;
99 block->border_top = border_top ? json_object_get_int(border_top) : 1;
100 block->border_bottom = border_bottom ?
101 json_object_get_int(border_bottom) : 1;
102 block->border_left = border_left ? json_object_get_int(border_left) : 1;
103 block->border_right = border_right ?
104 json_object_get_int(border_right) : 1;
105 wl_list_insert(&status->blocks, &block->link);
106 }
107 return true;
108}
109
110bool i3bar_handle_readable(struct status_line *status) {
111 struct i3bar_protocol_state *state = &status->i3bar_state;
112
113 char *cur = &state->buffer[state->buffer_index];
114 ssize_t n = read(status->read_fd, cur,
115 state->buffer_size - state->buffer_index);
116 if (n == -1) {
117 status_error(status, "[failed to read from status command]");
118 return false;
119 }
120
121 if (n == (ssize_t)(state->buffer_size - state->buffer_index)) {
122 state->buffer_size = state->buffer_size * 2;
123 char *new_buffer = realloc(state->buffer, state->buffer_size);
124 if (!new_buffer) {
125 free(state->buffer);
126 status_error(status, "[failed to allocate buffer]");
127 return true;
128 }
129 state->current_node += new_buffer - state->buffer;
130 cur += new_buffer - state->buffer;
131 state->buffer = new_buffer;
132 }
133
134 cur[n] = '\0';
135 bool redraw = false;
136 while (*cur) {
137 if (state->nodes[state->depth] == JSON_NODE_STRING) {
138 if (!state->escape && *cur == '"') {
139 --state->depth;
140 }
141 state->escape = !state->escape && *cur == '\\';
142 } else {
143 switch (*cur) {
144 case '[':
145 ++state->depth;
146 if (state->depth >
147 sizeof(state->nodes) / sizeof(state->nodes[0])) {
148 status_error(status, "[i3bar json too deep]");
149 return false;
150 }
151 state->nodes[state->depth] = JSON_NODE_ARRAY;
152 if (state->depth == 1) {
153 state->current_node = cur;
154 }
155 break;
156 case ']':
157 if (state->nodes[state->depth] != JSON_NODE_ARRAY) {
158 status_error(status, "[failed to parse i3bar json]");
159 return false;
160 }
161 --state->depth;
162 if (state->depth == 0) {
163 // cur[1] is valid since cur[0] != '\0'
164 char p = cur[1];
165 cur[1] = '\0';
166 redraw = i3bar_parse_json(
167 status, state->current_node) || redraw;
168 cur[1] = p;
169 memmove(state->buffer, cur,
170 state->buffer_size - (cur - state->buffer));
171 cur = state->buffer;
172 state->current_node = cur + 1;
173 }
174 break;
175 case '"':
176 ++state->depth;
177 if (state->depth >
178 sizeof(state->nodes) / sizeof(state->nodes[0])) {
179 status_error(status, "[i3bar json too deep]");
180 return false;
181 }
182 state->nodes[state->depth] = JSON_NODE_STRING;
183 break;
184 }
185 }
186 ++cur;
187 }
188 state->buffer_index = cur - state->buffer;
189 return redraw;
190}
191
192void i3bar_block_send_click(struct status_line *status,
193 struct i3bar_block *block, int x, int y, uint32_t button) {
194 wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");
195 if (!block->name || !status->i3bar_state.click_events) {
196 return;
197 }
198
199 struct json_object *event_json = json_object_new_object();
200 json_object_object_add(event_json, "name",
201 json_object_new_string(block->name));
202 if (block->instance) {
203 json_object_object_add(event_json, "instance",
204 json_object_new_string(block->instance));
205 }
206
207 json_object_object_add(event_json, "button", json_object_new_int(button));
208 json_object_object_add(event_json, "x", json_object_new_int(x));
209 json_object_object_add(event_json, "y", json_object_new_int(y));
210 if (dprintf(status->write_fd, "%s\n",
211 json_object_to_json_string(event_json)) < 0) {
212 status_error(status, "[failed to write click event]");
213 }
214 json_object_put(event_json);
215}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 93d1219c..ed5d9a31 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -1,37 +1,140 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <limits.h>
2#include <string.h> 3#include <string.h>
3#include <strings.h> 4#include <strings.h>
4#include <json-c/json.h> 5#include <json-c/json.h>
6#include <wlr/util/log.h>
5#include "swaybar/config.h" 7#include "swaybar/config.h"
6#include "swaybar/ipc.h" 8#include "swaybar/ipc.h"
7#include "ipc-client.h" 9#include "ipc-client.h"
8#include "list.h"
9#include "log.h"
10
11void ipc_send_workspace_command(const char *workspace_name) {
12 uint32_t size = strlen("workspace \"\"") + strlen(workspace_name) + 1;
13 10
11void ipc_send_workspace_command(struct swaybar *bar, const char *ws) {
12 const char *fmt = "workspace \"%s\"";
13 uint32_t size = snprintf(NULL, 0, fmt, ws);
14 char command[size]; 14 char command[size];
15 sprintf(command, "workspace \"%s\"", workspace_name); 15 snprintf(command, size, fmt, ws);
16 ipc_single_command(bar->ipc_socketfd, IPC_COMMAND, command, &size);
17}
16 18
17 ipc_single_command(swaybar.ipc_socketfd, IPC_COMMAND, command, &size); 19char *parse_font(const char *font) {
20 char *new_font = NULL;
21 if (strncmp("pango:", font, 6) == 0) {
22 font += 6;
23 }
24 new_font = strdup(font);
25 return new_font;
18} 26}
19 27
20static void ipc_parse_config(struct config *config, const char *payload) { 28static void ipc_parse_colors(
29 struct swaybar_config *config, json_object *colors) {
30 json_object *background, *statusline, *separator;
31 json_object *focused_background, *focused_statusline, *focused_separator;
32 json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
33 json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
34 json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
35 json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
36 json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
37 json_object_object_get_ex(colors, "background", &background);
38 json_object_object_get_ex(colors, "statusline", &statusline);
39 json_object_object_get_ex(colors, "separator", &separator);
40 json_object_object_get_ex(colors, "focused_background", &focused_background);
41 json_object_object_get_ex(colors, "focused_statusline", &focused_statusline);
42 json_object_object_get_ex(colors, "focused_separator", &focused_separator);
43 json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border);
44 json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg);
45 json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text);
46 json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border);
47 json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg);
48 json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text);
49 json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border);
50 json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg);
51 json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text);
52 json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border);
53 json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg);
54 json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text);
55 json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border);
56 json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg);
57 json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text);
58 if (background) {
59 config->colors.background = parse_color(
60 json_object_get_string(background));
61 }
62 if (statusline) {
63 config->colors.statusline = parse_color(
64 json_object_get_string(statusline));
65 }
66 if (separator) {
67 config->colors.separator = parse_color(
68 json_object_get_string(separator));
69 }
70 if (focused_background) {
71 config->colors.focused_background = parse_color(
72 json_object_get_string(focused_background));
73 }
74 if (focused_statusline) {
75 config->colors.focused_statusline = parse_color(
76 json_object_get_string(focused_statusline));
77 }
78 if (focused_separator) {
79 config->colors.focused_separator = parse_color(
80 json_object_get_string(focused_separator));
81 }
82 if (focused_workspace_border) {
83 config->colors.focused_workspace.border = parse_color(
84 json_object_get_string(focused_workspace_border));
85 }
86 if (focused_workspace_bg) {
87 config->colors.focused_workspace.background = parse_color(
88 json_object_get_string(focused_workspace_bg));
89 }
90 if (focused_workspace_text) {
91 config->colors.focused_workspace.text = parse_color(
92 json_object_get_string(focused_workspace_text));
93 }
94 if (active_workspace_border) {
95 config->colors.active_workspace.border = parse_color(
96 json_object_get_string(active_workspace_border));
97 }
98 if (active_workspace_bg) {
99 config->colors.active_workspace.background = parse_color(
100 json_object_get_string(active_workspace_bg));
101 }
102 if (active_workspace_text) {
103 config->colors.active_workspace.text = parse_color(
104 json_object_get_string(active_workspace_text));
105 }
106 if (inactive_workspace_border) {
107 config->colors.inactive_workspace.border = parse_color(
108 json_object_get_string(inactive_workspace_border));
109 }
110 if (inactive_workspace_bg) {
111 config->colors.inactive_workspace.background = parse_color(
112 json_object_get_string(inactive_workspace_bg));
113 }
114 if (inactive_workspace_text) {
115 config->colors.inactive_workspace.text = parse_color(
116 json_object_get_string(inactive_workspace_text));
117 }
118 if (binding_mode_border) {
119 config->colors.binding_mode.border = parse_color(
120 json_object_get_string(binding_mode_border));
121 }
122 if (binding_mode_bg) {
123 config->colors.binding_mode.background = parse_color(
124 json_object_get_string(binding_mode_bg));
125 }
126 if (binding_mode_text) {
127 config->colors.binding_mode.text = parse_color(
128 json_object_get_string(binding_mode_text));
129 }
130}
131
132static void ipc_parse_config(
133 struct swaybar_config *config, const char *payload) {
21 json_object *bar_config = json_tokener_parse(payload); 134 json_object *bar_config = json_tokener_parse(payload);
22 json_object *markup, *mode, *hidden_bar, *position, *status_command; 135 json_object *markup, *mode, *hidden_bar, *position, *status_command;
23 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; 136 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers;
24 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; 137 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs;
25#ifdef ENABLE_TRAY
26 json_object *tray_output, *icon_theme, *tray_padding, *activate_button, *context_button;
27 json_object *secondary_button;
28 json_object_object_get_ex(bar_config, "tray_output", &tray_output);
29 json_object_object_get_ex(bar_config, "icon_theme", &icon_theme);
30 json_object_object_get_ex(bar_config, "tray_padding", &tray_padding);
31 json_object_object_get_ex(bar_config, "activate_button", &activate_button);
32 json_object_object_get_ex(bar_config, "context_button", &context_button);
33 json_object_object_get_ex(bar_config, "secondary_button", &secondary_button);
34#endif
35 json_object_object_get_ex(bar_config, "mode", &mode); 138 json_object_object_get_ex(bar_config, "mode", &mode);
36 json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); 139 json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar);
37 json_object_object_get_ex(bar_config, "position", &position); 140 json_object_object_get_ex(bar_config, "position", &position);
@@ -47,230 +150,100 @@ static void ipc_parse_config(struct config *config, const char *payload) {
47 json_object_object_get_ex(bar_config, "colors", &colors); 150 json_object_object_get_ex(bar_config, "colors", &colors);
48 json_object_object_get_ex(bar_config, "outputs", &outputs); 151 json_object_object_get_ex(bar_config, "outputs", &outputs);
49 json_object_object_get_ex(bar_config, "pango_markup", &markup); 152 json_object_object_get_ex(bar_config, "pango_markup", &markup);
50
51 if (status_command) { 153 if (status_command) {
52 free(config->status_command); 154 free(config->status_command);
53 config->status_command = strdup(json_object_get_string(status_command)); 155 config->status_command = strdup(json_object_get_string(status_command));
54 } 156 }
55
56 if (position) { 157 if (position) {
57 config->position = parse_position(json_object_get_string(position)); 158 config->position = parse_position(json_object_get_string(position));
58 } 159 }
59
60 if (font) { 160 if (font) {
61 free(config->font); 161 free(config->font);
62 config->font = parse_font(json_object_get_string(font)); 162 config->font = parse_font(json_object_get_string(font));
63 } 163 }
64
65 if (sep_symbol) { 164 if (sep_symbol) {
66 free(config->sep_symbol); 165 free(config->sep_symbol);
67 config->sep_symbol = strdup(json_object_get_string(sep_symbol)); 166 config->sep_symbol = strdup(json_object_get_string(sep_symbol));
68 } 167 }
69
70 if (strip_workspace_numbers) { 168 if (strip_workspace_numbers) {
71 config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); 169 config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers);
72 } 170 }
73
74 if (binding_mode_indicator) { 171 if (binding_mode_indicator) {
75 config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); 172 config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator);
76 } 173 }
77
78 if (wrap_scroll) { 174 if (wrap_scroll) {
79 config->wrap_scroll = json_object_get_boolean(wrap_scroll); 175 config->wrap_scroll = json_object_get_boolean(wrap_scroll);
80 } 176 }
81
82 if (workspace_buttons) { 177 if (workspace_buttons) {
83 config->workspace_buttons = json_object_get_boolean(workspace_buttons); 178 config->workspace_buttons = json_object_get_boolean(workspace_buttons);
84 } 179 }
85
86 if (bar_height) { 180 if (bar_height) {
87 config->height = json_object_get_int(bar_height); 181 config->height = json_object_get_int(bar_height);
88 } 182 }
89
90 if (markup) { 183 if (markup) {
91 config->pango_markup = json_object_get_boolean(markup); 184 config->pango_markup = json_object_get_boolean(markup);
92 } 185 }
93 186
94#ifdef ENABLE_TRAY 187 struct config_output *output, *tmp;
95 if (tray_output) { 188 wl_list_for_each_safe(output, tmp, &config->outputs, link) {
96 free(config->tray_output); 189 wl_list_remove(&output->link);
97 config->tray_output = strdup(json_object_get_string(tray_output)); 190 free(output->name);
98 } 191 free(output);
99
100 if (icon_theme) {
101 free(config->icon_theme);
102 config->icon_theme = strdup(json_object_get_string(icon_theme));
103 }
104
105 if (tray_padding) {
106 config->tray_padding = json_object_get_int(tray_padding);
107 }
108
109 if (activate_button) {
110 config->activate_button = json_object_get_int(activate_button);
111 }
112
113 if (context_button) {
114 config->context_button = json_object_get_int(context_button);
115 } 192 }
116
117 if (secondary_button) {
118 config->secondary_button = json_object_get_int(secondary_button);
119 }
120#endif
121
122 // free previous outputs list
123 int i;
124 for (i = 0; i < config->outputs->length; ++i) {
125 free(config->outputs->items[i]);
126 }
127 list_free(config->outputs);
128 config->outputs = create_list();
129
130 if (outputs) { 193 if (outputs) {
131 int length = json_object_array_length(outputs); 194 int length = json_object_array_length(outputs);
132 json_object *output; 195 for (int i = 0; i < length; ++i) {
133 const char *output_str; 196 json_object *output = json_object_array_get_idx(outputs, i);
134 for (i = 0; i < length; ++i) { 197 const char *name = json_object_get_string(output);
135 output = json_object_array_get_idx(outputs, i); 198 if (strcmp("*", name) == 0) {
136 output_str = json_object_get_string(output);
137 if (strcmp("*", output_str) == 0) {
138 config->all_outputs = true; 199 config->all_outputs = true;
139 break; 200 break;
140 } 201 }
141 list_add(config->outputs, strdup(output_str)); 202 struct config_output *coutput = calloc(
203 1, sizeof(struct config_output));
204 coutput->name = strdup(name);
205 coutput->index = SIZE_MAX;
206 wl_list_insert(&config->outputs, &coutput->link);
142 } 207 }
143 } else { 208 } else {
144 config->all_outputs = true; 209 config->all_outputs = true;
145 } 210 }
146 211
147 if (colors) { 212 if (colors) {
148 json_object *background, *statusline, *separator; 213 ipc_parse_colors(config, colors);
149 json_object *focused_background, *focused_statusline, *focused_separator;
150 json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
151 json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
152 json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
153 json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
154 json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
155 json_object_object_get_ex(colors, "background", &background);
156 json_object_object_get_ex(colors, "statusline", &statusline);
157 json_object_object_get_ex(colors, "separator", &separator);
158 json_object_object_get_ex(colors, "focused_background", &focused_background);
159 json_object_object_get_ex(colors, "focused_statusline", &focused_statusline);
160 json_object_object_get_ex(colors, "focused_separator", &focused_separator);
161 json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border);
162 json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg);
163 json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text);
164 json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border);
165 json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg);
166 json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text);
167 json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border);
168 json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg);
169 json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text);
170 json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border);
171 json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg);
172 json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text);
173 json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border);
174 json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg);
175 json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text);
176 if (background) {
177 config->colors.background = parse_color(json_object_get_string(background));
178 }
179
180 if (statusline) {
181 config->colors.statusline = parse_color(json_object_get_string(statusline));
182 }
183
184 if (separator) {
185 config->colors.separator = parse_color(json_object_get_string(separator));
186 }
187
188 if (focused_background) {
189 config->colors.focused_background = parse_color(json_object_get_string(focused_background));
190 }
191
192 if (focused_statusline) {
193 config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline));
194 }
195
196 if (focused_separator) {
197 config->colors.focused_separator = parse_color(json_object_get_string(focused_separator));
198 }
199
200 if (focused_workspace_border) {
201 config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border));
202 }
203
204 if (focused_workspace_bg) {
205 config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg));
206 }
207
208 if (focused_workspace_text) {
209 config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text));
210 }
211
212 if (active_workspace_border) {
213 config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border));
214 }
215
216 if (active_workspace_bg) {
217 config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg));
218 }
219
220 if (active_workspace_text) {
221 config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text));
222 }
223
224 if (inactive_workspace_border) {
225 config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border));
226 }
227
228 if (inactive_workspace_bg) {
229 config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg));
230 }
231
232 if (inactive_workspace_text) {
233 config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text));
234 }
235
236 if (binding_mode_border) {
237 config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border));
238 }
239
240 if (binding_mode_bg) {
241 config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg));
242 }
243
244 if (binding_mode_text) {
245 config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text));
246 }
247 } 214 }
248 215
249 json_object_put(bar_config); 216 json_object_put(bar_config);
250} 217}
251 218
252static void ipc_update_workspaces(struct bar *bar) { 219static void free_workspaces(struct wl_list *list) {
253 int i; 220 struct swaybar_workspace *ws, *tmp;
254 for (i = 0; i < bar->outputs->length; ++i) { 221 wl_list_for_each_safe(ws, tmp, list, link) {
255 struct output *output = bar->outputs->items[i]; 222 wl_list_remove(&ws->link);
256 if (output->workspaces) { 223 free(ws->name);
257 free_workspaces(output->workspaces); 224 free(ws);
258 }
259 output->workspaces = create_list();
260 } 225 }
226}
261 227
228void ipc_get_workspaces(struct swaybar *bar) {
229 bar->focused_output = NULL;
230 struct swaybar_output *output;
231 wl_list_for_each(output, &bar->outputs, link) {
232 free_workspaces(&output->workspaces);
233 output->focused = false;
234 }
262 uint32_t len = 0; 235 uint32_t len = 0;
263 char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len); 236 char *res = ipc_single_command(bar->ipc_socketfd,
237 IPC_GET_WORKSPACES, NULL, &len);
264 json_object *results = json_tokener_parse(res); 238 json_object *results = json_tokener_parse(res);
265 if (!results) { 239 if (!results) {
266 free(res); 240 free(res);
267 return; 241 return;
268 } 242 }
269 243 size_t length = json_object_array_length(results);
270 int length = json_object_array_length(results);
271 json_object *ws_json; 244 json_object *ws_json;
272 json_object *num, *name, *visible, *focused, *out, *urgent; 245 json_object *num, *name, *visible, *focused, *out, *urgent;
273 for (i = 0; i < length; ++i) { 246 for (size_t i = 0; i < length; ++i) {
274 ws_json = json_object_array_get_idx(results, i); 247 ws_json = json_object_array_get_idx(results, i);
275 248
276 json_object_object_get_ex(ws_json, "num", &num); 249 json_object_object_get_ex(ws_json, "num", &num);
@@ -280,113 +253,95 @@ static void ipc_update_workspaces(struct bar *bar) {
280 json_object_object_get_ex(ws_json, "output", &out); 253 json_object_object_get_ex(ws_json, "output", &out);
281 json_object_object_get_ex(ws_json, "urgent", &urgent); 254 json_object_object_get_ex(ws_json, "urgent", &urgent);
282 255
283 int j; 256 wl_list_for_each(output, &bar->outputs, link) {
284 for (j = 0; j < bar->outputs->length; ++j) { 257 const char *ws_output = json_object_get_string(out);
285 struct output *output = bar->outputs->items[j]; 258 if (strcmp(ws_output, output->name) == 0) {
286 if (strcmp(json_object_get_string(out), output->name) == 0) { 259 struct swaybar_workspace *ws =
287 struct workspace *ws = malloc(sizeof(struct workspace)); 260 calloc(1, sizeof(struct swaybar_workspace));
288 ws->num = json_object_get_int(num); 261 ws->num = json_object_get_int(num);
289 ws->name = strdup(json_object_get_string(name)); 262 ws->name = strdup(json_object_get_string(name));
290 ws->visible = json_object_get_boolean(visible); 263 ws->visible = json_object_get_boolean(visible);
291 ws->focused = json_object_get_boolean(focused); 264 ws->focused = json_object_get_boolean(focused);
292 if (ws->focused) { 265 if (ws->focused) {
293 if (bar->focused_output) {
294 bar->focused_output->focused = false;
295 }
296 bar->focused_output = output;
297 output->focused = true; 266 output->focused = true;
298 } 267 }
299 ws->urgent = json_object_get_boolean(urgent); 268 ws->urgent = json_object_get_boolean(urgent);
300 list_add(output->workspaces, ws); 269 wl_list_insert(&output->workspaces, &ws->link);
301 } 270 }
302 } 271 }
303 } 272 }
304
305 json_object_put(results); 273 json_object_put(results);
306 free(res); 274 free(res);
307} 275}
308 276
309void ipc_bar_init(struct bar *bar, const char *bar_id) { 277static void ipc_get_outputs(struct swaybar *bar) {
310 // Get bar config 278 uint32_t len = 0;
311 uint32_t len = strlen(bar_id); 279 char *res = ipc_single_command(bar->ipc_socketfd,
312 char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); 280 IPC_GET_OUTPUTS, NULL, &len);
313
314 ipc_parse_config(bar->config, res);
315 free(res);
316
317 // Get outputs
318 len = 0;
319 res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len);
320 json_object *outputs = json_tokener_parse(res); 281 json_object *outputs = json_tokener_parse(res);
321 int i; 282 for (size_t i = 0; i < json_object_array_length(outputs); ++i) {
322 int length = json_object_array_length(outputs); 283 json_object *output = json_object_array_get_idx(outputs, i);
323 json_object *output, *output_name, *output_active; 284 json_object *output_name, *output_active;
324 const char *name;
325 bool active;
326 for (i = 0; i < length; ++i) {
327 output = json_object_array_get_idx(outputs, i);
328 json_object_object_get_ex(output, "name", &output_name); 285 json_object_object_get_ex(output, "name", &output_name);
329 json_object_object_get_ex(output, "active", &output_active); 286 json_object_object_get_ex(output, "active", &output_active);
330 name = json_object_get_string(output_name); 287 const char *name = json_object_get_string(output_name);
331 active = json_object_get_boolean(output_active); 288 bool active = json_object_get_boolean(output_active);
332 if (!active) { 289 if (!active) {
333 continue; 290 continue;
334 } 291 }
335
336 bool use_output = false;
337 if (bar->config->all_outputs) { 292 if (bar->config->all_outputs) {
338 use_output = true; 293 struct config_output *coutput = calloc(
294 1, sizeof(struct config_output));
295 coutput->name = strdup(name);
296 coutput->index = i;
297 wl_list_insert(&bar->config->outputs, &coutput->link);
339 } else { 298 } else {
340 int j = 0; 299 struct config_output *coutput;
341 for (j = 0; j < bar->config->outputs->length; ++j) { 300 wl_list_for_each(coutput, &bar->config->outputs, link) {
342 const char *conf_name = bar->config->outputs->items[j]; 301 if (strcmp(name, coutput->name) == 0) {
343 if (strcasecmp(name, conf_name) == 0) { 302 coutput->index = i;
344 use_output = true;
345 break; 303 break;
346 } 304 }
347 } 305 }
348 } 306 }
349
350 if (!use_output) {
351 continue;
352 }
353
354 // add bar to the output
355 struct output *bar_output = new_output(name);
356 bar_output->idx = i;
357 list_add(bar->outputs, bar_output);
358 } 307 }
359 free(res);
360 json_object_put(outputs); 308 json_object_put(outputs);
309 free(res);
310}
361 311
362 const char *subscribe_json = "[ \"workspace\", \"mode\" ]"; 312void ipc_initialize(struct swaybar *bar, const char *bar_id) {
363 len = strlen(subscribe_json); 313 uint32_t len = strlen(bar_id);
364 res = ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len); 314 char *res = ipc_single_command(bar->ipc_socketfd,
315 IPC_GET_BAR_CONFIG, bar_id, &len);
316 ipc_parse_config(bar->config, res);
365 free(res); 317 free(res);
318 ipc_get_outputs(bar);
366 319
367 ipc_update_workspaces(bar); 320 const char *subscribe = "[ \"workspace\", \"mode\" ]";
321 len = strlen(subscribe);
322 free(ipc_single_command(bar->ipc_event_socketfd,
323 IPC_SUBSCRIBE, subscribe, &len));
368} 324}
369 325
370bool handle_ipc_event(struct bar *bar) { 326bool handle_ipc_readable(struct swaybar *bar) {
371 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); 327 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);
372 if (!resp) { 328 if (!resp) {
373 return false; 329 return false;
374 } 330 }
375 switch (resp->type) { 331 switch (resp->type) {
376 case IPC_EVENT_WORKSPACE: 332 case IPC_EVENT_WORKSPACE:
377 ipc_update_workspaces(bar); 333 ipc_get_workspaces(bar);
378 break; 334 break;
379 case IPC_EVENT_MODE: { 335 case IPC_EVENT_MODE: {
380 json_object *result = json_tokener_parse(resp->payload); 336 json_object *result = json_tokener_parse(resp->payload);
381 if (!result) { 337 if (!result) {
382 free_ipc_response(resp); 338 free_ipc_response(resp);
383 sway_log(L_ERROR, "failed to parse payload as json"); 339 wlr_log(L_ERROR, "failed to parse payload as json");
384 return false; 340 return false;
385 } 341 }
386 json_object *json_change; 342 json_object *json_change, *json_pango_markup;
387 if (json_object_object_get_ex(result, "change", &json_change)) { 343 if (json_object_object_get_ex(result, "change", &json_change)) {
388 const char *change = json_object_get_string(json_change); 344 const char *change = json_object_get_string(json_change);
389
390 free(bar->config->mode); 345 free(bar->config->mode);
391 if (strcmp(change, "default") == 0) { 346 if (strcmp(change, "default") == 0) {
392 bar->config->mode = NULL; 347 bar->config->mode = NULL;
@@ -394,9 +349,16 @@ bool handle_ipc_event(struct bar *bar) {
394 bar->config->mode = strdup(change); 349 bar->config->mode = strdup(change);
395 } 350 }
396 } else { 351 } else {
397 sway_log(L_ERROR, "failed to parse response"); 352 wlr_log(L_ERROR, "failed to parse response");
353 json_object_put(result);
354 free_ipc_response(resp);
355 return false;
356 }
357 if (json_object_object_get_ex(result,
358 "pango_markup", &json_pango_markup)) {
359 bar->config->mode_pango_markup = json_object_get_boolean(
360 json_pango_markup);
398 } 361 }
399
400 json_object_put(result); 362 json_object_put(result);
401 break; 363 break;
402 } 364 }
@@ -404,7 +366,6 @@ bool handle_ipc_event(struct bar *bar) {
404 free_ipc_response(resp); 366 free_ipc_response(resp);
405 return false; 367 return false;
406 } 368 }
407
408 free_ipc_response(resp); 369 free_ipc_response(resp);
409 return true; 370 return true;
410} 371}
diff --git a/swaybar/main.c b/swaybar/main.c
index 0abd0755..c897e1c9 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -4,21 +4,20 @@
4#include <string.h> 4#include <string.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <getopt.h> 6#include <getopt.h>
7#include <wlr/util/log.h>
7#include "swaybar/bar.h" 8#include "swaybar/bar.h"
8#include "ipc-client.h" 9#include "ipc-client.h"
9#include "log.h"
10 10
11/* global bar state */ 11static struct swaybar swaybar;
12struct bar swaybar;
13 12
14void sway_terminate(int exit_code) { 13void sig_handler(int signal) {
15 bar_teardown(&swaybar); 14 bar_teardown(&swaybar);
16 exit(exit_code); 15 exit(0);
17} 16}
18 17
19void sig_handler(int signal) { 18void sway_terminate(int code) {
20 bar_teardown(&swaybar); 19 bar_teardown(&swaybar);
21 exit(0); 20 exit(code);
22} 21}
23 22
24int main(int argc, char **argv) { 23int main(int argc, char **argv) {
@@ -75,20 +74,23 @@ int main(int argc, char **argv) {
75 } 74 }
76 } 75 }
77 76
78 if (!bar_id) {
79 sway_abort("No bar_id passed. Provide --bar_id or let sway start swaybar");
80 }
81
82 if (debug) { 77 if (debug) {
83 init_log(L_DEBUG); 78 wlr_log_init(L_DEBUG, NULL);
84 } else { 79 } else {
85 init_log(L_ERROR); 80 wlr_log_init(L_ERROR, NULL);
81 }
82
83 if (!bar_id) {
84 wlr_log(L_ERROR, "No bar_id passed. "
85 "Provide --bar_id or let sway start swaybar");
86 return 1;
86 } 87 }
87 88
88 if (!socket_path) { 89 if (!socket_path) {
89 socket_path = get_socketpath(); 90 socket_path = get_socketpath();
90 if (!socket_path) { 91 if (!socket_path) {
91 sway_abort("Unable to retrieve socket path"); 92 wlr_log(L_ERROR, "Unable to retrieve socket path");
93 return 1;
92 } 94 }
93 } 95 }
94 96
@@ -100,9 +102,6 @@ int main(int argc, char **argv) {
100 free(bar_id); 102 free(bar_id);
101 103
102 bar_run(&swaybar); 104 bar_run(&swaybar);
103
104 // gracefully shutdown swaybar and status_command
105 bar_teardown(&swaybar); 105 bar_teardown(&swaybar);
106
107 return 0; 106 return 0;
108} 107}
diff --git a/swaybar/meson.build b/swaybar/meson.build
new file mode 100644
index 00000000..d65edb11
--- /dev/null
+++ b/swaybar/meson.build
@@ -0,0 +1,28 @@
1executable(
2 'swaybar', [
3 'bar.c',
4 'config.c',
5 'event_loop.c',
6 'i3bar.c',
7 'ipc.c',
8 'main.c',
9 'render.c',
10 'status_line.c',
11 ],
12 include_directories: [sway_inc],
13 dependencies: [
14 cairo,
15 client_protos,
16 gdk_pixbuf,
17 jsonc,
18 math,
19 pango,
20 pangocairo,
21 rt,
22 wayland_client,
23 wayland_cursor,
24 wlroots,
25 ],
26 link_with: [lib_sway_common, lib_sway_client],
27 install: true
28)
diff --git a/swaybar/render.c b/swaybar/render.c
index 6fc09078..26248d35 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,35 +1,92 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
1#include <stdlib.h> 3#include <stdlib.h>
2#include <stdint.h> 4#include <stdint.h>
3#include <string.h> 5#include <string.h>
4 6#include <wlr/util/log.h>
5#include "client/cairo.h" 7#include "cairo.h"
6#include "client/pango.h" 8#include "pango.h"
7#include "client/window.h" 9#include "pool-buffer.h"
10#include "swaybar/bar.h"
8#include "swaybar/config.h" 11#include "swaybar/config.h"
9#include "swaybar/status_line.h" 12#include "swaybar/ipc.h"
10#include "swaybar/render.h" 13#include "swaybar/render.h"
11#ifdef ENABLE_TRAY 14#include "swaybar/status_line.h"
12#include "swaybar/tray/tray.h" 15#include "wlr-layer-shell-unstable-v1-client-protocol.h"
13#include "swaybar/tray/sni.h" 16
14#endif 17static const int WS_HORIZONTAL_PADDING = 5;
15#include "log.h" 18static const double WS_VERTICAL_PADDING = 1.5;
16 19static const double BORDER_WIDTH = 1;
17 20
18/* internal spacing */ 21static uint32_t render_status_line_error(cairo_t *cairo,
19static int margin = 3; 22 struct swaybar_output *output, struct swaybar_config *config,
20static int ws_horizontal_padding = 5; 23 const char *error, double *x, uint32_t surface_height) {
21static double ws_vertical_padding = 1.5; 24 if (!error) {
22static int ws_spacing = 1; 25 return 0;
23 26 }
24/** 27
25 * Renders a sharp line of any width and height. 28 uint32_t height = surface_height * output->scale;
26 * 29
27 * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 30 cairo_set_source_u32(cairo, 0xFF0000FF);
28 * if the line has a width/height of one pixel, respectively. 31
29 */ 32 int margin = 3 * output->scale;
30static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { 33 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
31 cairo_set_source_u32(cairo, color); 34
35 int text_width, text_height;
36 get_text_size(cairo, config->font,
37 &text_width, &text_height, output->scale, false, "%s", error);
38
39 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
40 uint32_t ideal_surface_height = ideal_height / output->scale;
41 if (surface_height < ideal_surface_height) {
42 return ideal_surface_height;
43 }
44 *x -= text_width + margin;
45
46 double text_y = height / 2.0 - text_height / 2.0;
47 cairo_move_to(cairo, *x, (int)floor(text_y));
48 pango_printf(cairo, config->font, output->scale, false, "%s", error);
49 *x -= margin;
50 return surface_height;
51}
52
53static uint32_t render_status_line_text(cairo_t *cairo,
54 struct swaybar_output *output, struct swaybar_config *config,
55 const char *text, bool focused, double *x, uint32_t surface_height) {
56 if (!text) {
57 return 0;
58 }
59
60 uint32_t height = surface_height * output->scale;
61
62 cairo_set_source_u32(cairo, focused ?
63 config->colors.focused_statusline : config->colors.statusline);
32 64
65 int text_width, text_height;
66 get_text_size(cairo, config->font, &text_width, &text_height,
67 output->scale, config->pango_markup, "%s", text);
68
69 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
70 int margin = 3 * output->scale;
71
72 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
73 uint32_t ideal_surface_height = ideal_height / output->scale;
74 if (surface_height < ideal_surface_height) {
75 return ideal_surface_height;
76 }
77
78 *x -= text_width + margin;
79 double text_y = height / 2.0 - text_height / 2.0;
80 cairo_move_to(cairo, *x, (int)floor(text_y));
81 pango_printf(cairo, config->font, output->scale,
82 config->pango_markup, "%s", text);
83 *x -= margin;
84 return surface_height;
85}
86
87static void render_sharp_line(cairo_t *cairo, uint32_t color,
88 double x, double y, double width, double height) {
89 cairo_set_source_u32(cairo, color);
33 if (width > 1 && height > 1) { 90 if (width > 1 && height > 1) {
34 cairo_rectangle(cairo, x, y, width, height); 91 cairo_rectangle(cairo, x, y, width, height);
35 cairo_fill(cairo); 92 cairo_fill(cairo);
@@ -39,13 +96,11 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y
39 height += y; 96 height += y;
40 width = x; 97 width = x;
41 } 98 }
42
43 if (height == 1) { 99 if (height == 1) {
44 y += 0.5; 100 y += 0.5;
45 width += x; 101 width += x;
46 height = y; 102 height = y;
47 } 103 }
48
49 cairo_move_to(cairo, x, y); 104 cairo_move_to(cairo, x, y);
50 cairo_set_line_width(cairo, 1.0); 105 cairo_set_line_width(cairo, 1.0);
51 cairo_line_to(cairo, width, height); 106 cairo_line_to(cairo, width, height);
@@ -53,176 +108,256 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y
53 } 108 }
54} 109}
55 110
56static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { 111static void block_hotspot_callback(struct swaybar_output *output,
57 int width, height, sep_width; 112 int x, int y, uint32_t button, void *data) {
58 get_text_size(window->cairo, window->font, &width, &height, 113 struct i3bar_block *block = data;
59 window->scale, block->markup, "%s", block->full_text); 114 struct status_line *status = output->bar->status;
115 i3bar_block_send_click(status, block, x, y, button);
116}
60 117
61 int textwidth = width; 118static uint32_t render_status_block(cairo_t *cairo,
62 double block_width = width; 119 struct swaybar_config *config, struct swaybar_output *output,
120 struct i3bar_block *block, double *x,
121 uint32_t surface_height, bool focused, bool edge) {
122 if (!block->full_text || !*block->full_text) {
123 return 0;
124 }
63 125
126 uint32_t height = surface_height * output->scale;
127
128 int text_width, text_height;
129 get_text_size(cairo, config->font, &text_width, &text_height,
130 output->scale, block->markup, "%s", block->full_text);
131
132 int margin = 3 * output->scale;
133 int ws_vertical_padding = WS_VERTICAL_PADDING * 2;
134
135 int width = text_width;
64 if (width < block->min_width) { 136 if (width < block->min_width) {
65 width = block->min_width; 137 width = block->min_width;
66 } 138 }
67 139
68 *x -= width; 140 double block_width = width;
141 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
142 uint32_t ideal_surface_height = ideal_height / output->scale;
143 if (surface_height < ideal_surface_height) {
144 return ideal_surface_height;
145 }
69 146
70 if (block->border != 0 && block->border_left > 0) { 147 *x -= width;
148 if (block->border && block->border_left > 0) {
71 *x -= (block->border_left + margin); 149 *x -= (block->border_left + margin);
72 block_width += block->border_left + margin; 150 block_width += block->border_left + margin;
73 } 151 }
74 152 if (block->border && block->border_right > 0) {
75 if (block->border != 0 && block->border_right > 0) {
76 *x -= (block->border_right + margin); 153 *x -= (block->border_right + margin);
77 block_width += block->border_right + margin; 154 block_width += block->border_right + margin;
78 } 155 }
79 156
80 // Add separator 157 int sep_width, sep_height;
81 if (!edge) { 158 if (!edge) {
82 if (config->sep_symbol) { 159 if (config->sep_symbol) {
83 get_text_size(window->cairo, window->font, &sep_width, &height, 160 get_text_size(cairo, config->font, &sep_width, &sep_height,
84 window->scale, false, "%s", config->sep_symbol); 161 output->scale, false, "%s", config->sep_symbol);
162 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
163 uint32_t _ideal_surface_height = _ideal_height / output->scale;
164 if (surface_height < _ideal_surface_height) {
165 return _ideal_surface_height;
166 }
85 if (sep_width > block->separator_block_width) { 167 if (sep_width > block->separator_block_width) {
86 block->separator_block_width = sep_width + margin * 2; 168 block->separator_block_width = sep_width + margin * 2;
87 } 169 }
88 } 170 }
89
90 *x -= block->separator_block_width; 171 *x -= block->separator_block_width;
91 } else { 172 } else {
92 *x -= margin; 173 *x -= margin;
93 } 174 }
94 175
95 double pos = *x; 176 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
96 177 hotspot->x = *x;
97 block->x = (int)pos; 178 hotspot->y = 0;
98 block->width = (int)block_width; 179 hotspot->width = width;
180 hotspot->height = height;
181 hotspot->callback = block_hotspot_callback;
182 hotspot->destroy = NULL;
183 hotspot->data = block;
184 wl_list_insert(&output->hotspots, &hotspot->link);
99 185
100 // render background 186 double pos = *x;
101 if (block->background != 0x0) { 187 if (block->background) {
102 cairo_set_source_u32(window->cairo, block->background); 188 cairo_set_source_u32(cairo, block->background);
103 cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2); 189 cairo_rectangle(cairo, pos - 0.5 * output->scale,
104 cairo_fill(window->cairo); 190 output->scale, block_width, height);
191 cairo_fill(cairo);
105 } 192 }
106 193
107 // render top border 194 if (block->border && block->border_top > 0) {
108 if (block->border != 0 && block->border_top > 0) { 195 render_sharp_line(cairo, block->border,
109 render_sharp_line(window->cairo, block->border, 196 pos - 0.5 * output->scale, output->scale,
110 pos - 0.5, 197 block_width, block->border_top);
111 1,
112 block_width,
113 block->border_top);
114 } 198 }
115 199 if (block->border && block->border_bottom > 0) {
116 // render bottom border 200 render_sharp_line(cairo, block->border,
117 if (block->border != 0 && block->border_bottom > 0) { 201 pos - 0.5 * output->scale,
118 render_sharp_line(window->cairo, block->border, 202 height - output->scale - block->border_bottom,
119 pos - 0.5, 203 block_width, block->border_bottom);
120 (window->height * window->scale) - 1 - block->border_bottom,
121 block_width,
122 block->border_bottom);
123 } 204 }
124
125 // render left border
126 if (block->border != 0 && block->border_left > 0) { 205 if (block->border != 0 && block->border_left > 0) {
127 render_sharp_line(window->cairo, block->border, 206 render_sharp_line(cairo, block->border,
128 pos - 0.5, 207 pos - 0.5 * output->scale, output->scale,
129 1, 208 block->border_left, height);
130 block->border_left,
131 (window->height * window->scale) - 2);
132
133 pos += block->border_left + margin; 209 pos += block->border_left + margin;
134 } 210 }
135 211
136 // render text
137 double offset = 0; 212 double offset = 0;
138
139 if (strncmp(block->align, "left", 5) == 0) { 213 if (strncmp(block->align, "left", 5) == 0) {
140 offset = pos; 214 offset = pos;
141 } else if (strncmp(block->align, "right", 5) == 0) { 215 } else if (strncmp(block->align, "right", 5) == 0) {
142 offset = pos + width - textwidth; 216 offset = pos + width - text_width;
143 } else if (strncmp(block->align, "center", 6) == 0) { 217 } else if (strncmp(block->align, "center", 6) == 0) {
144 offset = pos + (width - textwidth) / 2; 218 offset = pos + (width - text_width) / 2;
145 } 219 }
146 220 cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0);
147 cairo_move_to(window->cairo, offset, margin); 221 uint32_t color = block->color ? *block->color : config->colors.statusline;
148 cairo_set_source_u32(window->cairo, block->color); 222 cairo_set_source_u32(cairo, color);
149 pango_printf(window->cairo, window->font, window->scale, 223 pango_printf(cairo, config->font, output->scale,
150 block->markup, "%s", block->full_text); 224 block->markup, "%s", block->full_text);
151
152 pos += width; 225 pos += width;
153 226
154 // render right border 227 if (block->border && block->border_right > 0) {
155 if (block->border != 0 && block->border_right > 0) {
156 pos += margin; 228 pos += margin;
157 229 render_sharp_line(cairo, block->border,
158 render_sharp_line(window->cairo, block->border, 230 pos - 0.5 * output->scale, output->scale,
159 pos - 0.5, 231 block->border_right, height);
160 1,
161 block->border_right,
162 (window->height * window->scale) - 2);
163
164 pos += block->border_right; 232 pos += block->border_right;
165 } 233 }
166 234
167 // render separator
168 if (!edge && block->separator) { 235 if (!edge && block->separator) {
169 if (is_focused) { 236 if (focused) {
170 cairo_set_source_u32(window->cairo, config->colors.focused_separator); 237 cairo_set_source_u32(cairo, config->colors.focused_separator);
171 } else { 238 } else {
172 cairo_set_source_u32(window->cairo, config->colors.separator); 239 cairo_set_source_u32(cairo, config->colors.separator);
173 } 240 }
174 if (config->sep_symbol) { 241 if (config->sep_symbol) {
175 offset = pos + (block->separator_block_width - sep_width) / 2; 242 offset = pos + (block->separator_block_width - sep_width) / 2;
176 cairo_move_to(window->cairo, offset, margin); 243 cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0);
177 pango_printf(window->cairo, window->font, window->scale, 244 pango_printf(cairo, config->font, output->scale, false,
178 false, "%s", config->sep_symbol); 245 "%s", config->sep_symbol);
179 } else { 246 } else {
180 cairo_set_line_width(window->cairo, 1); 247 cairo_set_line_width(cairo, 1);
181 cairo_move_to(window->cairo, pos + block->separator_block_width/2, 248 cairo_move_to(cairo,
182 margin); 249 pos + block->separator_block_width / 2, margin);
183 cairo_line_to(window->cairo, pos + block->separator_block_width/2, 250 cairo_line_to(cairo,
184 (window->height * window->scale) - margin); 251 pos + block->separator_block_width / 2, height - margin);
185 cairo_stroke(window->cairo); 252 cairo_stroke(cairo);
186 } 253 }
187 } 254 }
255 return surface_height;
256}
188 257
258static uint32_t render_status_line_i3bar(cairo_t *cairo,
259 struct swaybar_config *config, struct swaybar_output *output,
260 struct status_line *status, bool focused,
261 double *x, uint32_t surface_height) {
262 uint32_t max_height = 0;
263 bool edge = true;
264 struct i3bar_block *block;
265 wl_list_for_each(block, &status->blocks, link) {
266 uint32_t h = render_status_block(cairo, config, output,
267 block, x, surface_height, focused, edge);
268 max_height = h > max_height ? h : max_height;
269 edge = false;
270 }
271 return max_height;
189} 272}
190 273
191static const char *strip_workspace_name(bool strip_num, const char *ws_name) { 274static uint32_t render_status_line(cairo_t *cairo,
192 bool strip = false; 275 struct swaybar_config *config, struct swaybar_output *output,
193 int i; 276 struct status_line *status, bool focused,
194 277 double *x, uint32_t surface_height) {
195 if (strip_num) { 278 switch (status->protocol) {
196 int len = strlen(ws_name); 279 case PROTOCOL_ERROR:
197 for (i = 0; i < len; ++i) { 280 return render_status_line_error(cairo, output, config,
198 if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { 281 status->text, x, surface_height);
199 if (':' == ws_name[i] && i < len-1 && i > 0) { 282 case PROTOCOL_TEXT:
200 strip = true; 283 return render_status_line_text(cairo, output, config,
201 ++i; 284 status->text, focused, x, surface_height);
202 } 285 case PROTOCOL_I3BAR:
203 break; 286 return render_status_line_i3bar(cairo, config, output,
204 } 287 status, focused, x, surface_height);
205 } 288 case PROTOCOL_UNDEF:
289 return 0;
206 } 290 }
291 return 0;
292}
293
294static uint32_t render_binding_mode_indicator(cairo_t *cairo,
295 struct swaybar_output *output, struct swaybar_config *config,
296 const char *mode, double x, uint32_t surface_height) {
297 uint32_t height = surface_height * output->scale;
207 298
208 if (strip) { 299 int text_width, text_height;
209 return ws_name + i; 300 get_text_size(cairo, config->font, &text_width, &text_height,
301 output->scale, true, "%s", mode);
302
303 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
304 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale;
305 int border_width = BORDER_WIDTH * output->scale;
306
307 uint32_t ideal_height = text_height + ws_vertical_padding * 2
308 + border_width * 2;
309 uint32_t ideal_surface_height = ideal_height / output->scale;
310 if (surface_height < ideal_surface_height) {
311 return ideal_surface_height;
210 } 312 }
313 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
314
315 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
316 cairo_rectangle(cairo, x, 0, width, height);
317 cairo_fill(cairo);
318
319 cairo_set_source_u32(cairo, config->colors.binding_mode.border);
320 cairo_rectangle(cairo, x, 0, width, border_width);
321 cairo_fill(cairo);
322 cairo_rectangle(cairo, x, 0, border_width, height);
323 cairo_fill(cairo);
324 cairo_rectangle(cairo, x + width - border_width, 0, border_width, height);
325 cairo_fill(cairo);
326 cairo_rectangle(cairo, x, height - border_width, width, border_width);
327 cairo_fill(cairo);
328
329 double text_y = height / 2.0 - text_height / 2.0;
330 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
331 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
332 pango_printf(cairo, config->font, output->scale, true, "%s", mode);
333 return surface_height;
334}
211 335
336static const char *strip_workspace_number(const char *ws_name) {
337 size_t len = strlen(ws_name);
338 for (size_t i = 0; i < len; ++i) {
339 if (ws_name[i] < '0' || ws_name[i] > '9') {
340 if (':' == ws_name[i] && i < len - 1 && i > 0) {
341 return ws_name + i + 1;
342 }
343 return ws_name;
344 }
345 }
212 return ws_name; 346 return ws_name;
213} 347}
214 348
215void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { 349static void workspace_hotspot_callback(struct swaybar_output *output,
216 const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name); 350 int x, int y, uint32_t button, void *data) {
217 351 ipc_send_workspace_command(output->bar, (const char *)data);
218 get_text_size(window->cairo, window->font, width, height,
219 window->scale, true, "%s", stripped_name);
220 *width += 2 * ws_horizontal_padding;
221 *height += 2 * ws_vertical_padding;
222} 352}
223 353
224static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { 354static uint32_t render_workspace_button(cairo_t *cairo,
225 const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name); 355 struct swaybar_output *output, struct swaybar_config *config,
356 struct swaybar_workspace *ws, double *x, uint32_t surface_height) {
357 const char *name = ws->name;
358 if (config->strip_workspace_numbers) {
359 name = strip_workspace_number(ws->name);
360 }
226 361
227 struct box_colors box_colors; 362 struct box_colors box_colors;
228 if (ws->urgent) { 363 if (ws->urgent) {
@@ -235,133 +370,154 @@ static void render_workspace_button(struct window *window, struct config *config
235 box_colors = config->colors.inactive_workspace; 370 box_colors = config->colors.inactive_workspace;
236 } 371 }
237 372
238 int width, height; 373 uint32_t height = surface_height * output->scale;
239 workspace_button_size(window, stripped_name, &width, &height);
240 374
241 // background 375 int text_width, text_height;
242 cairo_set_source_u32(window->cairo, box_colors.background); 376 get_text_size(cairo, config->font, &text_width, &text_height,
243 cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); 377 output->scale, true, "%s", name);
244 cairo_fill(window->cairo); 378
245 379 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
246 // border 380 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale;
247 cairo_set_source_u32(window->cairo, box_colors.border); 381 int border_width = BORDER_WIDTH * output->scale;
248 cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); 382
249 cairo_stroke(window->cairo); 383 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
250 384 + border_width * 2;
251 // text 385 uint32_t ideal_surface_height = ideal_height / output->scale;
252 cairo_set_source_u32(window->cairo, box_colors.text); 386 if (surface_height < ideal_surface_height) {
253 cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); 387 return ideal_surface_height;
254 pango_printf(window->cairo, window->font, window->scale, 388 }
255 true, "%s", stripped_name);
256
257 *x += width + ws_spacing;
258}
259 389
260static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { 390 uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2;
261 int width, height; 391
262 get_text_size(window->cairo, window->font, &width, &height, 392 cairo_set_source_u32(cairo, box_colors.background);
263 window->scale, false, "%s", config->mode); 393 cairo_rectangle(cairo, *x, 0, width, height);
264 394 cairo_fill(cairo);
265 // background 395
266 cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); 396 cairo_set_source_u32(cairo, box_colors.border);
267 cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, 397 cairo_rectangle(cairo, *x, 0, width, border_width);
268 height + ws_vertical_padding * 2); 398 cairo_fill(cairo);
269 cairo_fill(window->cairo); 399 cairo_rectangle(cairo, *x, 0, border_width, height);
270 400 cairo_fill(cairo);
271 // border 401 cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height);
272 cairo_set_source_u32(window->cairo, config->colors.binding_mode.border); 402 cairo_fill(cairo);
273 cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, 403 cairo_rectangle(cairo, *x, height - border_width, width, border_width);
274 height + ws_vertical_padding * 2); 404 cairo_fill(cairo);
275 cairo_stroke(window->cairo); 405
276 406 double text_y = height / 2.0 - text_height / 2.0;
277 // text 407 cairo_set_source_u32(cairo, box_colors.text);
278 cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); 408 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
279 cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); 409 pango_printf(cairo, config->font, output->scale, true, "%s", name);
280 pango_printf(window->cairo, window->font, window->scale, 410
281 false, "%s", config->mode); 411 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
412 hotspot->x = *x;
413 hotspot->y = 0;
414 hotspot->width = width;
415 hotspot->height = height;
416 hotspot->callback = workspace_hotspot_callback;
417 hotspot->destroy = free;
418 hotspot->data = strdup(ws->name);
419 wl_list_insert(&output->hotspots, &hotspot->link);
420
421 *x += width;
422 return surface_height;
282} 423}
283 424
284void render(struct output *output, struct config *config, struct status_line *line) { 425static uint32_t render_to_cairo(cairo_t *cairo,
285 int i; 426 struct swaybar *bar, struct swaybar_output *output) {
286 427 struct swaybar_config *config = bar->config;
287 struct window *window = output->window;
288 cairo_t *cairo = window->cairo;
289 bool is_focused = output->focused;
290
291 // Clear
292 cairo_save(cairo);
293 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
294 cairo_paint(cairo);
295 cairo_restore(cairo);
296
297 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 428 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
298 429 if (output->focused) {
299 // Background
300 if (is_focused) {
301 cairo_set_source_u32(cairo, config->colors.focused_background); 430 cairo_set_source_u32(cairo, config->colors.focused_background);
302 } else { 431 } else {
303 cairo_set_source_u32(cairo, config->colors.background); 432 cairo_set_source_u32(cairo, config->colors.background);
304 } 433 }
305 cairo_paint(cairo); 434 cairo_paint(cairo);
306 435
307#ifdef ENABLE_TRAY 436 uint32_t max_height = 0;
308 uint32_t tray_width = tray_render(output, config); 437 /*
309#else 438 * Each render_* function takes the actual height of the bar, and returns
310 const uint32_t tray_width = window->width * window->scale; 439 * the ideal height. If the actual height is too short, the render function
311#endif 440 * can do whatever it wants - the buffer won't be committed. If the actual
312 441 * height is too tall, the render function should adapt its drawing to
313 // Command output 442 * utilize the available space.
314 if (is_focused) { 443 */
315 cairo_set_source_u32(cairo, config->colors.focused_statusline); 444 double x = output->width * output->scale;
316 } else { 445 if (bar->status) {
317 cairo_set_source_u32(cairo, config->colors.statusline); 446 uint32_t h = render_status_line(cairo, config, output,
318 } 447 bar->status, output->focused, &x, output->height);
319 448 max_height = h > max_height ? h : max_height;
320 int width, height; 449 }
321 450 x = 0;
322 if (line->protocol == TEXT) { 451 if (config->workspace_buttons) {
323 get_text_size(window->cairo, window->font, &width, &height, 452 struct swaybar_workspace *ws;
324 window->scale, config->pango_markup, "%s", line->text_line); 453 wl_list_for_each_reverse(ws, &output->workspaces, link) {
325 cairo_move_to(cairo, tray_width - margin - width, margin); 454 uint32_t h = render_workspace_button(cairo,
326 pango_printf(window->cairo, window->font, window->scale, 455 output, config, ws, &x, output->height);
327 config->pango_markup, "%s", line->text_line); 456 max_height = h > max_height ? h : max_height;
328 } else if (line->protocol == I3BAR && line->block_line) {
329 double pos = tray_width - 0.5;
330 bool edge = true;
331 for (i = line->block_line->length - 1; i >= 0; --i) {
332 struct status_block *block = line->block_line->items[i];
333 if (block->full_text && block->full_text[0]) {
334 render_block(window, config, block, &pos, edge, is_focused);
335 edge = false;
336 }
337 } 457 }
338 } 458 }
459 if (config->binding_mode_indicator && config->mode) {
460 uint32_t h = render_binding_mode_indicator(cairo,
461 output, config, config->mode, x, output->height);
462 max_height = h > max_height ? h : max_height;
463 }
339 464
340 cairo_set_line_width(cairo, 1.0); 465 return max_height > output->height ? max_height : output->height;
341 double x = 0.5; 466}
342 467
343 // Workspaces 468void render_frame(struct swaybar *bar, struct swaybar_output *output) {
344 if (config->workspace_buttons) { 469 struct swaybar_hotspot *hotspot, *tmp;
345 for (i = 0; i < output->workspaces->length; ++i) { 470 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) {
346 struct workspace *ws = output->workspaces->items[i]; 471 if (hotspot->destroy) {
347 render_workspace_button(window, config, ws, &x); 472 hotspot->destroy(hotspot->data);
348 } 473 }
474 wl_list_remove(&hotspot->link);
475 free(hotspot);
349 } 476 }
350 477
351 // binding mode indicator 478 cairo_surface_t *recorder = cairo_recording_surface_create(
352 if (config->mode && config->binding_mode_indicator) { 479 CAIRO_CONTENT_COLOR_ALPHA, NULL);
353 render_binding_mode_indicator(window, config, x); 480 cairo_t *cairo = cairo_create(recorder);
481 cairo_save(cairo);
482 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
483 cairo_paint(cairo);
484 cairo_restore(cairo);
485 uint32_t height = render_to_cairo(cairo, bar, output);
486 if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) {
487 height = bar->config->height;
354 } 488 }
355} 489 if (height != output->height) {
356 490 // Reconfigure surface
357void set_window_height(struct window *window, int height) { 491 zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height);
358 int text_width, text_height; 492 zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height);
359 get_text_size(window->cairo, window->font, 493 // TODO: this could infinite loop if the compositor assigns us a
360 &text_width, &text_height, window->scale, false, 494 // different height than what we asked for
361 "Test string for measuring purposes"); 495 wl_surface_commit(output->surface);
362 if (height > 0) { 496 wl_display_roundtrip(bar->display);
363 margin = (height - text_height) / 2; 497 } else {
364 ws_vertical_padding = margin - 1.5; 498 // Replay recording into shm and send it off
365 } 499 output->current_buffer = get_next_buffer(bar->shm,
366 window->height = (text_height + margin * 2) / window->scale; 500 output->buffers,
501 output->width * output->scale,
502 output->height * output->scale);
503 cairo_t *shm = output->current_buffer->cairo;
504
505 cairo_save(shm);
506 cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);
507 cairo_paint(shm);
508 cairo_restore(shm);
509
510 cairo_set_source_surface(shm, recorder, 0.0, 0.0);
511 cairo_paint(shm);
512
513 wl_surface_set_buffer_scale(output->surface, output->scale);
514 wl_surface_attach(output->surface,
515 output->current_buffer->buffer, 0, 0);
516 wl_surface_damage(output->surface, 0, 0,
517 output->width, output->height);
518 wl_surface_commit(output->surface);
519 wl_display_roundtrip(bar->display);
520 }
521 cairo_surface_destroy(recorder);
522 cairo_destroy(cairo);
367} 523}
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 87e90caf..8afe4707 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,530 +1,130 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE
2#include <fcntl.h>
3#include <json-c/json.h>
2#include <stdlib.h> 4#include <stdlib.h>
3#include <string.h> 5#include <string.h>
6#include <stdio.h>
4#include <unistd.h> 7#include <unistd.h>
5#include <json-c/json.h> 8#include <wlr/util/log.h>
6
7#include "swaybar/config.h" 9#include "swaybar/config.h"
8#include "swaybar/status_line.h" 10#include "swaybar/status_line.h"
9#include "log.h" 11#include "readline.h"
10#include "util.h"
11
12#define I3JSON_MAXDEPTH 4
13#define I3JSON_UNKNOWN 0
14#define I3JSON_ARRAY 1
15#define I3JSON_STRING 2
16
17struct {
18 int bufsize;
19 char *buffer;
20 char *line_start;
21 char *parserpos;
22 bool escape;
23 int depth;
24 int bar[I3JSON_MAXDEPTH+1];
25} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } };
26
27static char line[1024];
28static char line_rest[1024];
29
30static char event_buff[1024];
31
32static void free_status_block(void *item) {
33 if (!item) {
34 return;
35 }
36 struct status_block *sb = (struct status_block*)item;
37 if (sb->full_text) {
38 free(sb->full_text);
39 }
40 if (sb->short_text) {
41 free(sb->short_text);
42 }
43 if (sb->align) {
44 free(sb->align);
45 }
46 if (sb->name) {
47 free(sb->name);
48 }
49 if (sb->instance) {
50 free(sb->instance);
51 }
52 free(sb);
53}
54
55static void parse_json(struct bar *bar, const char *text) {
56 json_object *results = json_tokener_parse(text);
57 if (!results) {
58 sway_log(L_DEBUG, "Failed to parse json");
59 return;
60 }
61
62 if (json_object_array_length(results) < 1) {
63 return;
64 }
65
66 if (bar->status->block_line) {
67 list_foreach(bar->status->block_line, free_status_block);
68 list_free(bar->status->block_line);
69 }
70
71 bar->status->block_line = create_list();
72
73 int i;
74 for (i = 0; i < json_object_array_length(results); ++i) {
75 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
76 json_object *name, *instance, *separator, *separator_block_width;
77 json_object *background, *border, *border_top, *border_bottom;
78 json_object *border_left, *border_right, *markup;
79
80 json_object *json = json_object_array_get_idx(results, i);
81 if (!json) {
82 continue;
83 }
84
85 json_object_object_get_ex(json, "full_text", &full_text);
86 json_object_object_get_ex(json, "short_text", &short_text);
87 json_object_object_get_ex(json, "color", &color);
88 json_object_object_get_ex(json, "min_width", &min_width);
89 json_object_object_get_ex(json, "align", &align);
90 json_object_object_get_ex(json, "urgent", &urgent);
91 json_object_object_get_ex(json, "name", &name);
92 json_object_object_get_ex(json, "instance", &instance);
93 json_object_object_get_ex(json, "markup", &markup);
94 json_object_object_get_ex(json, "separator", &separator);
95 json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
96 json_object_object_get_ex(json, "background", &background);
97 json_object_object_get_ex(json, "border", &border);
98 json_object_object_get_ex(json, "border_top", &border_top);
99 json_object_object_get_ex(json, "border_bottom", &border_bottom);
100 json_object_object_get_ex(json, "border_left", &border_left);
101 json_object_object_get_ex(json, "border_right", &border_right);
102
103 struct status_block *new = calloc(1, sizeof(struct status_block));
104
105 if (full_text) {
106 new->full_text = strdup(json_object_get_string(full_text));
107 }
108
109 if (short_text) {
110 new->short_text = strdup(json_object_get_string(short_text));
111 }
112
113 if (color) {
114 new->color = parse_color(json_object_get_string(color));
115 } else {
116 new->color = bar->config->colors.statusline;
117 }
118
119 if (min_width) {
120 json_type type = json_object_get_type(min_width);
121 if (type == json_type_int) {
122 new->min_width = json_object_get_int(min_width);
123 } else if (type == json_type_string) {
124 /* the width will be calculated when rendering */
125 new->min_width = 0;
126 }
127 }
128
129 if (align) {
130 new->align = strdup(json_object_get_string(align));
131 } else {
132 new->align = strdup("left");
133 }
134
135 if (urgent) {
136 new->urgent = json_object_get_int(urgent);
137 }
138 12
139 if (name) { 13void status_error(struct status_line *status, const char *text) {
140 new->name = strdup(json_object_get_string(name)); 14 close(status->read_fd);
141 } 15 close(status->write_fd);
142 16 status->protocol = PROTOCOL_ERROR;
143 if (instance) { 17 status->text = text;
144 new->instance = strdup(json_object_get_string(instance));
145 }
146
147 if (markup) {
148 new->markup = false;
149 const char *markup_str = json_object_get_string(markup);
150 if (strcmp(markup_str, "pango") == 0) {
151 new->markup = true;
152 }
153 }
154
155 if (separator) {
156 new->separator = json_object_get_int(separator);
157 } else {
158 new->separator = true; // i3bar spec
159 }
160
161 if (separator_block_width) {
162 new->separator_block_width = json_object_get_int(separator_block_width);
163 } else {
164 new->separator_block_width = 9; // i3bar spec
165 }
166
167 // Airblader features
168 if (background) {
169 new->background = parse_color(json_object_get_string(background));
170 } else {
171 new->background = 0x0; // transparent
172 }
173
174 if (border) {
175 new->border = parse_color(json_object_get_string(border));
176 } else {
177 new->border = 0x0; // transparent
178 }
179
180 if (border_top) {
181 new->border_top = json_object_get_int(border_top);
182 } else {
183 new->border_top = 1;
184 }
185
186 if (border_bottom) {
187 new->border_bottom = json_object_get_int(border_bottom);
188 } else {
189 new->border_bottom = 1;
190 }
191
192 if (border_left) {
193 new->border_left = json_object_get_int(border_left);
194 } else {
195 new->border_left = 1;
196 }
197
198 if (border_right) {
199 new->border_right = json_object_get_int(border_right);
200 } else {
201 new->border_right = 1;
202 }
203
204 list_add(bar->status->block_line, new);
205 }
206
207 json_object_put(results);
208} 18}
209 19
210// continue parsing from last parserpos 20bool status_handle_readable(struct status_line *status) {
211static int i3json_parse(struct bar *bar) { 21 char *line;
212 char *c = i3json_state.parserpos; 22 switch (status->protocol) {
213 int handled = 0; 23 case PROTOCOL_ERROR:
214 while (*c) {
215 if (i3json_state.bar[i3json_state.depth] == I3JSON_STRING) {
216 if (!i3json_state.escape && *c == '"') {
217 --i3json_state.depth;
218 }
219 i3json_state.escape = !i3json_state.escape && *c == '\\';
220 } else {
221 switch (*c) {
222 case '[':
223 ++i3json_state.depth;
224 if (i3json_state.depth > I3JSON_MAXDEPTH) {
225 sway_abort("JSON too deep");
226 }
227 i3json_state.bar[i3json_state.depth] = I3JSON_ARRAY;
228 if (i3json_state.depth == 2) {
229 i3json_state.line_start = c;
230 }
231 break;
232 case ']':
233 if (i3json_state.bar[i3json_state.depth] != I3JSON_ARRAY) {
234 sway_abort("JSON malformed");
235 }
236 --i3json_state.depth;
237 if (i3json_state.depth == 1) {
238 // c[1] is valid since c[0] != '\0'
239 char p = c[1];
240 c[1] = '\0';
241 parse_json(bar, i3json_state.line_start);
242 c[1] = p;
243 ++handled;
244 i3json_state.line_start = c+1;
245 }
246 break;
247 case '"':
248 ++i3json_state.depth;
249 if (i3json_state.depth > I3JSON_MAXDEPTH) {
250 sway_abort("JSON too deep");
251 }
252 i3json_state.bar[i3json_state.depth] = I3JSON_STRING;
253 break;
254 }
255 }
256 ++c;
257 }
258 i3json_state.parserpos = c;
259 return handled;
260}
261
262// Read line from file descriptor, only show the line tail if it is too long.
263// In non-blocking mode treat "no more data" as a linebreak.
264// If data after a line break has been read, return it in rest.
265// If rest is non-empty, then use that as the start of the next line.
266static int read_line_tail(int fd, char *buf, int nbyte, char *rest) {
267 if (fd < 0 || !buf || !nbyte) {
268 return -1;
269 }
270 int l;
271 char *buffer = malloc(nbyte*2+1);
272 char *readpos = buffer;
273 char *lf;
274 // prepend old data to new line if necessary
275 if (rest) {
276 l = strlen(rest);
277 if (l > nbyte) {
278 strcpy(buffer, rest + l - nbyte);
279 readpos += nbyte;
280 } else if (l) {
281 strcpy(buffer, rest);
282 readpos += l;
283 }
284 }
285 // read until a linefeed is found or no more data is available
286 while ((l = read(fd, readpos, nbyte)) > 0) {
287 readpos[l] = '\0';
288 lf = strchr(readpos, '\n');
289 if (lf) {
290 // linefeed found, replace with \0
291 *lf = '\0';
292 // give data from the end of the line, try to fill the buffer
293 if (lf-buffer > nbyte) {
294 strcpy(buf, lf - nbyte + 1);
295 } else {
296 strcpy(buf, buffer);
297 }
298 // we may have read data from the next line, save it to rest
299 if (rest) {
300 rest[0] = '\0';
301 strcpy(rest, lf + 1);
302 }
303 free(buffer);
304 return strlen(buf);
305 } else {
306 // no linefeed found, slide data back.
307 int overflow = readpos - buffer + l - nbyte;
308 if (overflow > 0) {
309 memmove(buffer, buffer + overflow , nbyte + 1);
310 }
311 }
312 }
313 if (l < 0) {
314 free(buffer);
315 return l;
316 }
317 readpos[l]='\0';
318 if (rest) {
319 rest[0] = '\0';
320 }
321 if (nbyte < readpos - buffer + l - 1) {
322 memcpy(buf, readpos - nbyte + l + 1, nbyte);
323 } else {
324 strncpy(buf, buffer, nbyte);
325 }
326 buf[nbyte-1] = '\0';
327 free(buffer);
328 return strlen(buf);
329}
330
331// make sure that enough buffer space is available starting from parserpos
332static void i3json_ensure_free(int min_free) {
333 int _step = 10240;
334 int r = min_free % _step;
335 if (r) {
336 min_free += _step - r;
337 }
338 if (!i3json_state.buffer) {
339 i3json_state.buffer = malloc(min_free);
340 i3json_state.bufsize = min_free;
341 i3json_state.parserpos = i3json_state.buffer;
342 } else {
343 int len = 0;
344 int pos = 0;
345 if (i3json_state.line_start) {
346 len = strlen(i3json_state.line_start);
347 pos = i3json_state.parserpos - i3json_state.line_start;
348 if (i3json_state.line_start != i3json_state.buffer) {
349 memmove(i3json_state.buffer, i3json_state.line_start, len+1);
350 }
351 } else {
352 len = strlen(i3json_state.buffer);
353 }
354 if (i3json_state.bufsize < len+min_free) {
355 i3json_state.bufsize += min_free;
356 if (i3json_state.bufsize > 1024000) {
357 sway_abort("Status line json too long or malformed.");
358 }
359 i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize);
360 if (!i3json_state.buffer) {
361 sway_abort("Could not allocate json buffer");
362 }
363 }
364 if (i3json_state.line_start) {
365 i3json_state.line_start = i3json_state.buffer;
366 i3json_state.parserpos = i3json_state.buffer + pos;
367 } else {
368 i3json_state.parserpos = i3json_state.buffer;
369 }
370 }
371 if (!i3json_state.buffer) {
372 sway_abort("Could not allocate buffer.");
373 }
374}
375
376// append data and parse it.
377static int i3json_handle_data(struct bar *bar, char *data) {
378 int len = strlen(data);
379 i3json_ensure_free(len);
380 strcpy(i3json_state.parserpos, data);
381 return i3json_parse(bar);
382}
383
384// read data from fd and parse it.
385static int i3json_handle_fd(struct bar *bar) {
386 i3json_ensure_free(10240);
387 // get fresh data at the end of the buffer
388 int readlen = read(bar->status_read_fd, i3json_state.parserpos, 10239);
389 if (readlen < 0) {
390 return readlen;
391 }
392 i3json_state.parserpos[readlen] = '\0';
393 return i3json_parse(bar);
394}
395
396bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button) {
397 sway_log(L_DEBUG, "status_line_mouse_event.");
398 if (!bar->status->click_events) {
399 sway_log(L_DEBUG, "click_events are not enabled.");
400 return false; 24 return false;
401 } 25 case PROTOCOL_I3BAR:
402 26 if (i3bar_handle_readable(status) > 0) {
403 if (bar->status->protocol == I3BAR) {
404 sway_log(L_DEBUG, "Sending click event.");
405
406 // find clicked block
407 struct status_block *clicked_block = NULL;
408 struct status_block *current_block = NULL;
409 int num_blocks = bar->status->block_line->length;
410
411 if (num_blocks == 0) {
412 return false;
413 } else {
414 current_block = bar->status->block_line->items[0];
415 if (x < current_block->x) {
416 return false;
417 }
418 }
419
420 for (int i = 0; i < num_blocks; i++) {
421 current_block = bar->status->block_line->items[i];
422 if (x < (current_block->x + current_block->width)) {
423 clicked_block = current_block;
424 break;
425 }
426 }
427
428 if (!clicked_block || !clicked_block->name) {
429 return false;
430 }
431
432 // event example {"name":"capture","instance":"label","button":1,"x":3431,"y":18}
433
434 struct json_object *event_json = json_object_new_object();
435 json_object_object_add(event_json, "name", json_object_new_string(clicked_block->name));
436 if (clicked_block->instance) {
437 json_object_object_add(event_json, "instance", json_object_new_string(clicked_block->instance));
438 }
439 json_object_object_add(event_json, "button", json_object_new_int(button));
440 json_object_object_add(event_json, "x", json_object_new_int(x));
441 json_object_object_add(event_json, "y", json_object_new_int(y));
442
443 int len = snprintf(event_buff, sizeof(event_buff), "%s\n", json_object_to_json_string(event_json));
444
445 json_object_put(event_json);
446
447 if (len <= (int)sizeof(event_buff)) { // if not truncated
448 write(bar->status_write_fd, event_buff, len);
449 return true; 27 return true;
450 } 28 }
451 }
452
453 return false;
454}
455
456bool handle_status_line(struct bar *bar) {
457 bool dirty = false;
458
459 switch (bar->status->protocol) {
460 case I3BAR:
461 sway_log(L_DEBUG, "Got i3bar protocol.");
462 if (i3json_handle_fd(bar) > 0) {
463 dirty = true;
464 }
465 break; 29 break;
466 case TEXT: 30 case PROTOCOL_TEXT:
467 sway_log(L_DEBUG, "Got text protocol."); 31 line = read_line_buffer(status->read,
468 read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest); 32 status->text_state.buffer, status->text_state.buffer_size);
469 dirty = true; 33 if (!line) {
470 bar->status->text_line = line; 34 status_error(status, "[error reading from status command]");
471 break; 35 } else {
472 case UNDEF: 36 status->text = line;
473 sway_log(L_DEBUG, "Detecting protocol..."); 37 }
474 if (read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest) < 0) { 38 return true;
475 break; 39 case PROTOCOL_UNDEF:
40 line = read_line_buffer(status->read,
41 status->text_state.buffer, status->text_state.buffer_size);
42 if (!line) {
43 status_error(status, "[error reading from status command]");
44 return false;
476 } 45 }
477 dirty = true;
478 bar->status->text_line = line;
479 bar->status->protocol = TEXT;
480 if (line[0] == '{') { 46 if (line[0] == '{') {
481 // detect i3bar json protocol
482 json_object *proto = json_tokener_parse(line); 47 json_object *proto = json_tokener_parse(line);
483 if (proto) { 48 if (proto) {
484
485 json_object *version; 49 json_object *version;
486 if (json_object_object_get_ex(proto, "version", &version) 50 if (json_object_object_get_ex(proto, "version", &version)
487 && json_object_get_int(version) == 1 51 && json_object_get_int(version) == 1) {
488 ) { 52 wlr_log(L_DEBUG, "Switched to i3bar protocol.");
489 sway_log(L_DEBUG, "Switched to i3bar protocol."); 53 status->protocol = PROTOCOL_I3BAR;
490 bar->status->protocol = I3BAR;
491 } 54 }
492
493 json_object *click_events; 55 json_object *click_events;
494 if (json_object_object_get_ex(proto, "click_events", &click_events) 56 if (json_object_object_get_ex(
57 proto, "click_events", &click_events)
495 && json_object_get_boolean(click_events)) { 58 && json_object_get_boolean(click_events)) {
496 59 wlr_log(L_DEBUG, "Enabled click events.");
497 sway_log(L_DEBUG, "Enabling click events."); 60 status->i3bar_state.click_events = true;
498 bar->status->click_events = true;
499
500 const char *events_array = "[\n"; 61 const char *events_array = "[\n";
501 write(bar->status_write_fd, events_array, strlen(events_array)); 62 ssize_t len = strlen(events_array);
63 if (write(status->write_fd, events_array, len) != len) {
64 status_error(status,
65 "[failed to write to status command]");
66 }
502 } 67 }
503
504 i3json_handle_data(bar, line_rest);
505
506 json_object_put(proto); 68 json_object_put(proto);
507 } 69 }
70
71 status->protocol = PROTOCOL_I3BAR;
72 free(status->text_state.buffer);
73 wl_list_init(&status->blocks);
74 status->i3bar_state.buffer_size = 4096;
75 status->i3bar_state.buffer =
76 malloc(status->i3bar_state.buffer_size);
77 } else {
78 status->protocol = PROTOCOL_TEXT;
79 status->text = line;
508 } 80 }
509 break; 81 return true;
510 } 82 }
511 83 return false;
512 return dirty;
513} 84}
514 85
515struct status_line *init_status_line() { 86struct status_line *status_line_init(char *cmd) {
516 struct status_line *line = malloc(sizeof(struct status_line)); 87 struct status_line *status = calloc(1, sizeof(struct status_line));
517 line->block_line = create_list(); 88 status->text_state.buffer_size = 8192;
518 line->text_line = NULL; 89 status->text_state.buffer = malloc(status->text_state.buffer_size);
519 line->protocol = UNDEF;
520 line->click_events = false;
521 90
522 return line; 91 int pipe_read_fd[2];
523} 92 int pipe_write_fd[2];
93 if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) {
94 wlr_log(L_ERROR, "Unable to create pipes for status_command fork");
95 exit(1);
96 }
97
98 status->pid = fork();
99 if (status->pid == 0) {
100 dup2(pipe_read_fd[1], STDOUT_FILENO);
101 close(pipe_read_fd[0]);
102 close(pipe_read_fd[1]);
524 103
525void free_status_line(struct status_line *line) { 104 dup2(pipe_write_fd[0], STDIN_FILENO);
526 if (line->block_line) { 105 close(pipe_write_fd[0]);
527 list_foreach(line->block_line, free_status_block); 106 close(pipe_write_fd[1]);
528 list_free(line->block_line); 107
108 char *const _cmd[] = { "sh", "-c", cmd, NULL, };
109 execvp(_cmd[0], _cmd);
110 exit(1);
529 } 111 }
112
113 close(pipe_read_fd[1]);
114 status->read_fd = pipe_read_fd[0];
115 fcntl(status->read_fd, F_SETFL, O_NONBLOCK);
116 close(pipe_write_fd[0]);
117 status->write_fd = pipe_write_fd[1];
118 fcntl(status->write_fd, F_SETFL, O_NONBLOCK);
119
120 status->read = fdopen(status->read_fd, "r");
121 status->write = fdopen(status->write_fd, "w");
122 return status;
123}
124
125void status_line_free(struct status_line *status) {
126 close(status->read_fd);
127 close(status->write_fd);
128 kill(status->pid, SIGTERM);
129 free(status);
530} 130}
diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c
deleted file mode 100644
index 8e719fd9..00000000
--- a/swaybar/tray/dbus.c
+++ /dev/null
@@ -1,197 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <stdio.h>
3#include <stdlib.h>
4#include <stdint.h>
5#include <stdbool.h>
6#include <poll.h>
7#include <signal.h>
8#include <time.h>
9#include <dbus/dbus.h>
10#include "swaybar/tray/dbus.h"
11#include "swaybar/event_loop.h"
12#include "log.h"
13
14DBusConnection *conn = NULL;
15
16static void dispatch_watch(int fd, short mask, void *data) {
17 sway_log(L_DEBUG, "Dispatching watch");
18 DBusWatch *watch = data;
19
20 if (!dbus_watch_get_enabled(watch)) {
21 return;
22 }
23
24 uint32_t flags = 0;
25
26 if (mask & POLLIN) {
27 flags |= DBUS_WATCH_READABLE;
28 } if (mask & POLLOUT) {
29 flags |= DBUS_WATCH_WRITABLE;
30 } if (mask & POLLHUP) {
31 flags |= DBUS_WATCH_HANGUP;
32 } if (mask & POLLERR) {
33 flags |= DBUS_WATCH_ERROR;
34 }
35
36 dbus_watch_handle(watch, flags);
37}
38
39static dbus_bool_t add_watch(DBusWatch *watch, void *_data) {
40 if (!dbus_watch_get_enabled(watch)) {
41 // Watch should not be polled
42 return TRUE;
43 }
44
45 short mask = 0;
46 uint32_t flags = dbus_watch_get_flags(watch);
47
48 if (flags & DBUS_WATCH_READABLE) {
49 mask |= POLLIN;
50 } if (flags & DBUS_WATCH_WRITABLE) {
51 mask |= POLLOUT;
52 }
53
54 int fd = dbus_watch_get_unix_fd(watch);
55
56 sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd);
57 add_event(fd, mask, dispatch_watch, watch);
58
59 return TRUE;
60}
61
62static void remove_watch(DBusWatch *watch, void *_data) {
63 int fd = dbus_watch_get_unix_fd(watch);
64
65 remove_event(fd);
66}
67
68static void dispatch_timeout(timer_t timer, void *data) {
69 sway_log(L_DEBUG, "Dispatching DBus timeout");
70 DBusTimeout *timeout = data;
71
72 if (dbus_timeout_get_enabled(timeout)) {
73 dbus_timeout_handle(timeout);
74 }
75}
76
77static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) {
78 if (!dbus_timeout_get_enabled(timeout)) {
79 return TRUE;
80 }
81
82 timer_t *timer = malloc(sizeof(timer_t));
83 if (!timer) {
84 sway_log(L_ERROR, "Cannot allocate memory");
85 return FALSE;
86 }
87 struct sigevent ev = {
88 .sigev_notify = SIGEV_NONE,
89 };
90
91 if (timer_create(CLOCK_MONOTONIC, &ev, timer)) {
92 sway_log(L_ERROR, "Could not create DBus timer");
93 return FALSE;
94 }
95
96 int interval = dbus_timeout_get_interval(timeout);
97 int interval_sec = interval / 1000;
98 int interval_msec = (interval_sec * 1000) - interval;
99
100 struct timespec period = {
101 (time_t) interval_sec,
102 ((long) interval_msec) * 1000 * 1000,
103 };
104 struct itimerspec time = {
105 period,
106 period,
107 };
108
109 timer_settime(*timer, 0, &time, NULL);
110
111 dbus_timeout_set_data(timeout, timer, NULL);
112
113 sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec);
114 add_timer(*timer, dispatch_timeout, timeout);
115
116 return TRUE;
117}
118static void remove_timeout(DBusTimeout *timeout, void *_data) {
119 timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout);
120 sway_log(L_DEBUG, "Removing DBus timeout.");
121
122 if (timer) {
123 remove_timer(*timer);
124 timer_delete(*timer);
125 free(timer);
126 }
127}
128
129static bool should_dispatch = true;
130
131static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status,
132 void *_data) {
133 if (new_status == DBUS_DISPATCH_DATA_REMAINS) {
134 should_dispatch = true;
135 }
136}
137
138/* Public functions below */
139
140void dispatch_dbus() {
141 if (!should_dispatch || !conn) {
142 return;
143 }
144
145 DBusDispatchStatus status;
146
147 do {
148 status = dbus_connection_dispatch(conn);
149 } while (status == DBUS_DISPATCH_DATA_REMAINS);
150
151 if (status != DBUS_DISPATCH_COMPLETE) {
152 sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status);
153 }
154
155 should_dispatch = false;
156}
157
158int dbus_init() {
159 DBusError error;
160 dbus_error_init(&error);
161
162 conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
163 if (conn == NULL) {
164 sway_log(L_INFO, "Compiled with dbus support, but unable to connect to dbus");
165 sway_log(L_INFO, "swaybar will be unable to display tray icons.");
166 return -1;
167 }
168
169 dbus_connection_set_exit_on_disconnect(conn, FALSE);
170 if (dbus_error_is_set(&error)) {
171 sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message);
172 conn = NULL;
173 return -1;
174 }
175
176 sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn));
177
178 // Will be called if dispatch status changes
179 dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL);
180
181 if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
182 NULL, NULL, NULL)) {
183 dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
184 sway_log(L_ERROR, "Failed to activate DBUS watch functions");
185 return -1;
186 }
187
188 if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
189 NULL, NULL, NULL)) {
190 dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
191 dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL);
192 sway_log(L_ERROR, "Failed to activate DBUS timeout functions");
193 return -1;
194 }
195
196 return 0;
197}
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
deleted file mode 100644
index c146bf32..00000000
--- a/swaybar/tray/icon.c
+++ /dev/null
@@ -1,400 +0,0 @@
1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 200809L
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7#include <dirent.h>
8#include <sys/stat.h>
9#include <stdbool.h>
10#include <stdint.h>
11#include <limits.h>
12#include "swaybar/tray/icon.h"
13#include "swaybar/bar.h"
14#include "swaybar/config.h"
15#include "stringop.h"
16#include "log.h"
17
18/**
19 * REVIEW:
20 * This file repeats lots of "costly" operations that are the same for every
21 * icon. It's possible to create a dictionary or some other structure to cache
22 * these, though it may complicate things somewhat.
23 *
24 * Also parsing (index.theme) is currently pretty messy, so that could be made
25 * much better as well. Over all, things work, but are not optimal.
26 */
27
28/* Finds all themes that the given theme inherits */
29static list_t *find_inherits(const char *theme_dir) {
30 const char inherits[] = "Inherits";
31 const char index_name[] = "index.theme";
32 list_t *themes = create_list();
33 FILE *index = NULL;
34 char *path = malloc(strlen(theme_dir) + sizeof(index_name));
35 if (!path) {
36 goto fail;
37 }
38 if (!themes) {
39 goto fail;
40 }
41
42 strcpy(path, theme_dir);
43 strcat(path, index_name);
44
45 index = fopen(path, "r");
46 if (!index) {
47 goto fail;
48 }
49
50 char *buf = NULL;
51 size_t n = 0;
52 while (!feof(index) && getline(&buf, &n, index) != -1) {
53 if (n <= sizeof(inherits) + 1) {
54 continue;
55 }
56 if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) {
57 char *themestr = buf + sizeof(inherits);
58 themes = split_string(themestr, ",");
59 break;
60 }
61 }
62 free(buf);
63
64fail:
65 free(path);
66 if (index) {
67 fclose(index);
68 }
69 return themes;
70}
71
72static bool isdir(const char *path) {
73 struct stat statbuf;
74 if (stat(path, &statbuf) != -1) {
75 if (S_ISDIR(statbuf.st_mode)) {
76 return true;
77 }
78 }
79 return false;
80
81}
82
83/**
84 * Returns the directory of a given theme if it exists.
85 * The returned pointer must be freed.
86 */
87static char *find_theme_dir(const char *theme) {
88 char *basedir;
89 char *icon_dir;
90
91 if (!theme) {
92 return NULL;
93 }
94
95 if (!(icon_dir = malloc(1024))) {
96 sway_log(L_ERROR, "Out of memory!");
97 goto fail;
98 }
99
100 if ((basedir = getenv("HOME"))) {
101 if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) {
102 sway_log(L_ERROR, "Path too long to render");
103 // XXX perhaps just goto trying in /usr/share? This
104 // shouldn't happen anyway, but might with a long global
105 goto fail;
106 }
107
108 if (isdir(icon_dir)) {
109 return icon_dir;
110 }
111 }
112
113 if ((basedir = getenv("XDG_DATA_DIRS"))) {
114 if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
115 sway_log(L_ERROR, "Path too long to render");
116 // ditto
117 goto fail;
118 }
119
120 if (isdir(icon_dir)) {
121 return icon_dir;
122 }
123 }
124
125 // Spec says use "/usr/share/pixmaps/", but I see everything in
126 // "/usr/share/icons/" look it both, I suppose.
127 if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) {
128 sway_log(L_ERROR, "Path too long to render");
129 goto fail;
130 }
131 if (isdir(icon_dir)) {
132 return icon_dir;
133 }
134
135 if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) {
136 sway_log(L_ERROR, "Path too long to render");
137 goto fail;
138 }
139 if (isdir(icon_dir)) {
140 return icon_dir;
141 }
142
143fail:
144 free(icon_dir);
145 sway_log(L_ERROR, "Could not find dir for theme: %s", theme);
146 return NULL;
147}
148
149/**
150 * Returns all theme dirs needed to be looked in for an icon.
151 * Does not check for duplicates
152 */
153static list_t *find_all_theme_dirs(const char *theme) {
154 list_t *dirs = create_list();
155 if (!dirs) {
156 return NULL;
157 }
158 char *dir = find_theme_dir(theme);
159 if (dir) {
160 list_add(dirs, dir);
161 list_t *inherits = find_inherits(dir);
162 list_cat(dirs, inherits);
163 list_free(inherits);
164 }
165 dir = find_theme_dir("hicolor");
166 if (dir) {
167 list_add(dirs, dir);
168 }
169
170 return dirs;
171}
172
173struct subdir {
174 int size;
175 char name[];
176};
177
178static int subdir_str_cmp(const void *_subdir, const void *_str) {
179 const struct subdir *subdir = _subdir;
180 const char *str = _str;
181 return strcmp(subdir->name, str);
182}
183/**
184 * Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but
185 * generates a list of struct subdirs
186 */
187static list_t *split_subdirs(char *subdir_str) {
188 list_t *subdir_list = create_list();
189 char *copy = strdup(subdir_str);
190 if (!subdir_list || !copy) {
191 list_free(subdir_list);
192 free(copy);
193 return NULL;
194 }
195
196 char *token;
197 token = strtok(copy, ",");
198 while(token) {
199 int len = strlen(token) + 1;
200 struct subdir *subdir =
201 malloc(sizeof(struct subdir) + sizeof(char [len]));
202 if (!subdir) {
203 // Return what we have
204 return subdir_list;
205 }
206 subdir->size = 0;
207 strcpy(subdir->name, token);
208
209 list_add(subdir_list, subdir);
210
211 token = strtok(NULL, ",");
212 }
213 free(copy);
214
215 return subdir_list;
216}
217/**
218 * Returns a list of all subdirectories of a theme.
219 * Take note: the subdir names are all relative to `theme_dir` and must be
220 * combined with it to form a valid directory.
221 *
222 * Each member of the list is of type (struct subdir *) this struct contains
223 * the name of the subdir, along with size information. These must be freed
224 * bye the caller.
225 *
226 * This currently ignores min and max sizes of icons.
227 */
228static list_t* find_theme_subdirs(const char *theme_dir) {
229 const char index_name[] = "/index.theme";
230 list_t *dirs = NULL;
231 char *path = malloc(strlen(theme_dir) + sizeof(index_name));
232 FILE *index = NULL;
233 if (!path) {
234 sway_log(L_ERROR, "Failed to allocate memory");
235 goto fail;
236 }
237
238 strcpy(path, theme_dir);
239 strcat(path, index_name);
240
241 index = fopen(path, "r");
242 if (!index) {
243 sway_log(L_ERROR, "Could not open file: %s", path);
244 goto fail;
245 }
246
247 char *buf = NULL;
248 size_t n = 0;
249 const char directories[] = "Directories";
250 while (!feof(index) && getline(&buf, &n, index) != -1) {
251 if (n <= sizeof(directories) + 1) {
252 continue;
253 }
254 if (strncmp(directories, buf, sizeof(directories) - 1) == 0) {
255 char *dirstr = buf + sizeof(directories);
256 dirs = split_subdirs(dirstr);
257 break;
258 }
259 }
260 // Now, find the size of each dir
261 struct subdir *current_subdir = NULL;
262 const char size[] = "Size";
263 while (!feof(index) && getline(&buf, &n, index) != -1) {
264 if (buf[0] == '[') {
265 int len = strlen(buf);
266 if (buf[len-1] == '\n') {
267 len--;
268 }
269 // replace ']'
270 buf[len-1] = '\0';
271
272 int index;
273 if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) {
274 current_subdir = (dirs->items[index]);
275 }
276 }
277
278 if (strncmp(size, buf, sizeof(size) - 1) == 0) {
279 if (current_subdir) {
280 current_subdir->size = atoi(buf + sizeof(size));
281 }
282 }
283 }
284 free(buf);
285fail:
286 free(path);
287 if (index) {
288 fclose(index);
289 }
290 return dirs;
291}
292
293/* Returns the file of an icon given its name and size */
294static char *find_icon_file(const char *name, int size) {
295 int namelen = strlen(name);
296 list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme);
297 if (!dirs) {
298 return NULL;
299 }
300 int min_size_diff = INT_MAX;
301 char *current_file = NULL;
302
303 for (int i = 0; i < dirs->length; ++i) {
304 char *dir = dirs->items[i];
305 list_t *subdirs = find_theme_subdirs(dir);
306
307 if (!subdirs) {
308 continue;
309 }
310
311 for (int i = 0; i < subdirs->length; ++i) {
312 struct subdir *subdir = subdirs->items[i];
313
314 // Only use an unsized if we don't already have a
315 // canidate this should probably change to allow svgs
316 if (!subdir->size && current_file) {
317 continue;
318 }
319
320 int size_diff = abs(size - subdir->size);
321
322 if (size_diff >= min_size_diff) {
323 continue;
324 }
325
326 char *path = malloc(strlen(subdir->name) + strlen(dir) + 2);
327
328 strcpy(path, dir);
329 path[strlen(dir)] = '/';
330 strcpy(path + strlen(dir) + 1, subdir->name);
331
332 DIR *icons = opendir(path);
333 if (!icons) {
334 free(path);
335 continue;
336 }
337
338 struct dirent *direntry;
339 while ((direntry = readdir(icons)) != NULL) {
340 int len = strlen(direntry->d_name);
341 if (len <= namelen + 2) { //must have some ext
342 continue;
343 }
344 if (strncmp(direntry->d_name, name, namelen) == 0) {
345 char *ext = direntry->d_name + namelen + 1;
346#ifdef WITH_GDK_PIXBUF
347 if (strcmp(ext, "png") == 0 ||
348 strcmp(ext, "xpm") == 0 ||
349 strcmp(ext, "svg") == 0) {
350#else
351 if (strcmp(ext, "png") == 0) {
352#endif
353 free(current_file);
354 char *icon_path = malloc(strlen(path) + len + 2);
355
356 strcpy(icon_path, path);
357 icon_path[strlen(path)] = '/';
358 strcpy(icon_path + strlen(path) + 1, direntry->d_name);
359 current_file = icon_path;
360 min_size_diff = size_diff;
361 }
362 }
363 }
364 free(path);
365 closedir(icons);
366 }
367 free_flat_list(subdirs);
368 }
369 free_flat_list(dirs);
370
371 return current_file;
372}
373
374cairo_surface_t *find_icon(const char *name, int size) {
375 char *image_path = find_icon_file(name, size);
376 if (image_path == NULL) {
377 return NULL;
378 }
379
380 cairo_surface_t *image = NULL;
381#ifdef WITH_GDK_PIXBUF
382 GError *err = NULL;
383 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
384 if (!pixbuf) {
385 sway_log(L_ERROR, "Failed to load icon image: %s", err->message);
386 }
387 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
388 g_object_unref(pixbuf);
389#else
390 // TODO make svg work? cairo supports it. maybe remove gdk alltogether
391 image = cairo_image_surface_create_from_png(image_path);
392#endif //WITH_GDK_PIXBUF
393 if (!image) {
394 sway_log(L_ERROR, "Could not read icon image");
395 return NULL;
396 }
397
398 free(image_path);
399 return image;
400}
diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c
deleted file mode 100644
index c9d00657..00000000
--- a/swaybar/tray/sni.c
+++ /dev/null
@@ -1,481 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
5#include <stdint.h>
6#include <stdbool.h>
7#include <dbus/dbus.h>
8#include <arpa/inet.h>
9#include <netinet/in.h>
10#include "swaybar/tray/dbus.h"
11#include "swaybar/tray/sni.h"
12#include "swaybar/tray/icon.h"
13#include "swaybar/bar.h"
14#include "client/cairo.h"
15#include "log.h"
16
17// Not sure what this is but cairo needs it.
18static const cairo_user_data_key_t cairo_user_data_key;
19
20struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item,
21 int height) {
22 struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref));
23 if (!sni_ref) {
24 return NULL;
25 }
26 sni_ref->icon = cairo_image_surface_scale(item->image, height, height);
27 sni_ref->ref = item;
28
29 return sni_ref;
30}
31
32void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
33 if (!sni_ref) {
34 return;
35 }
36 cairo_surface_destroy(sni_ref->icon);
37 free(sni_ref);
38}
39
40/* Gets the pixmap of an icon */
41static void reply_icon(DBusPendingCall *pending, void *_data) {
42 struct StatusNotifierItem *item = _data;
43
44 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
45
46 if (!reply) {
47 sway_log(L_ERROR, "Did not get reply");
48 goto bail;
49 }
50
51 int message_type = dbus_message_get_type(reply);
52
53 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
54 char *msg;
55
56 dbus_message_get_args(reply, NULL,
57 DBUS_TYPE_STRING, &msg,
58 DBUS_TYPE_INVALID);
59
60 sway_log(L_ERROR, "Message is error: %s", msg);
61 goto bail;
62 }
63
64 DBusMessageIter iter;
65 DBusMessageIter variant; /* v[a(iiay)] */
66 DBusMessageIter array; /* a(iiay) */
67 DBusMessageIter d_struct; /* (iiay) */
68 DBusMessageIter icon; /* ay */
69
70 dbus_message_iter_init(reply, &iter);
71
72 // Each if here checks the types above before recursing
73 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
74 sway_log(L_ERROR, "Relpy type incorrect");
75 sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
76 dbus_message_iter_get_signature(&iter));
77 goto bail;
78 }
79 dbus_message_iter_recurse(&iter, &variant);
80
81 if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) {
82 sway_log(L_ERROR, "Relpy type incorrect");
83 sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"",
84 dbus_message_iter_get_signature(&variant));
85 goto bail;
86 }
87
88 if (dbus_message_iter_get_element_count(&variant) == 0) {
89 // Can't recurse if there are no items
90 sway_log(L_INFO, "Item has no icon");
91 goto bail;
92 }
93 dbus_message_iter_recurse(&variant, &array);
94
95 dbus_message_iter_recurse(&array, &d_struct);
96
97 int width;
98 dbus_message_iter_get_basic(&d_struct, &width);
99 dbus_message_iter_next(&d_struct);
100
101 int height;
102 dbus_message_iter_get_basic(&d_struct, &height);
103 dbus_message_iter_next(&d_struct);
104
105 int len = dbus_message_iter_get_element_count(&d_struct);
106
107 if (!len) {
108 sway_log(L_ERROR, "No icon data");
109 goto bail;
110 }
111
112 // Also implies len % 4 == 0, useful below
113 if (len != width * height * 4) {
114 sway_log(L_ERROR, "Incorrect array size passed");
115 goto bail;
116 }
117
118 dbus_message_iter_recurse(&d_struct, &icon);
119
120 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
121 // FIXME support a variable stride
122 // (works on my machine though for all tested widths)
123 if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
124 goto bail;
125 }
126
127 // Data is by reference, no need to free
128 uint8_t *message_data;
129 dbus_message_iter_get_fixed_array(&icon, &message_data, &len);
130
131 uint8_t *image_data = malloc(stride * height);
132 if (!image_data) {
133 sway_log(L_ERROR, "Could not allocate memory for icon");
134 goto bail;
135 }
136
137 // Transform from network byte order to host byte order
138 // Assumptions are safe because the equality above
139 uint32_t *network = (uint32_t *) message_data;
140 uint32_t *host = (uint32_t *)image_data;
141 for (int i = 0; i < width * height; ++i) {
142 host[i] = ntohl(network[i]);
143 }
144
145 cairo_surface_t *image = cairo_image_surface_create_for_data(
146 image_data, CAIRO_FORMAT_ARGB32,
147 width, height, stride);
148
149 if (image) {
150 if (item->image) {
151 cairo_surface_destroy(item->image);
152 }
153 item->image = image;
154 // Free the image data on surface destruction
155 cairo_surface_set_user_data(image,
156 &cairo_user_data_key,
157 image_data,
158 free);
159 item->dirty = true;
160 dirty = true;
161
162 dbus_message_unref(reply);
163 dbus_pending_call_unref(pending);
164 return;
165 } else {
166 sway_log(L_ERROR, "Could not create image surface");
167 free(image_data);
168 }
169
170bail:
171 if (reply) {
172 dbus_message_unref(reply);
173 }
174 dbus_pending_call_unref(pending);
175 sway_log(L_ERROR, "Could not get icon from item");
176 return;
177}
178static void send_icon_msg(struct StatusNotifierItem *item) {
179 DBusPendingCall *pending;
180 DBusMessage *message = dbus_message_new_method_call(
181 item->name,
182 "/StatusNotifierItem",
183 "org.freedesktop.DBus.Properties",
184 "Get");
185 const char *iface;
186 if (item->kde_special_snowflake) {
187 iface = "org.kde.StatusNotifierItem";
188 } else {
189 iface = "org.freedesktop.StatusNotifierItem";
190 }
191 const char *prop = "IconPixmap";
192
193 dbus_message_append_args(message,
194 DBUS_TYPE_STRING, &iface,
195 DBUS_TYPE_STRING, &prop,
196 DBUS_TYPE_INVALID);
197
198 bool status =
199 dbus_connection_send_with_reply(conn, message, &pending, -1);
200
201 dbus_message_unref(message);
202
203 if (!(pending || status)) {
204 sway_log(L_ERROR, "Could not get item icon");
205 return;
206 }
207
208 dbus_pending_call_set_notify(pending, reply_icon, item, NULL);
209}
210
211/* Get an icon by its name */
212static void reply_icon_name(DBusPendingCall *pending, void *_data) {
213 struct StatusNotifierItem *item = _data;
214
215 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
216
217 if (!reply) {
218 sway_log(L_INFO, "Got no icon name reply from item");
219 goto bail;
220 }
221
222 int message_type = dbus_message_get_type(reply);
223
224 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
225 char *msg;
226
227 dbus_message_get_args(reply, NULL,
228 DBUS_TYPE_STRING, &msg,
229 DBUS_TYPE_INVALID);
230
231 sway_log(L_INFO, "Could not get icon name: %s", msg);
232 goto bail;
233 }
234
235 DBusMessageIter iter; /* v[s] */
236 DBusMessageIter variant; /* s */
237
238 dbus_message_iter_init(reply, &iter);
239 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
240 sway_log(L_ERROR, "Relpy type incorrect");
241 sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
242 dbus_message_iter_get_signature(&iter));
243 goto bail;
244 }
245 dbus_message_iter_recurse(&iter, &variant);
246
247
248 if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
249 sway_log(L_ERROR, "Relpy type incorrect");
250 sway_log(L_ERROR, "Should be \"s\", is \"%s\"",
251 dbus_message_iter_get_signature(&iter));
252 goto bail;
253 }
254
255 char *icon_name;
256 dbus_message_iter_get_basic(&variant, &icon_name);
257
258 cairo_surface_t *image = find_icon(icon_name, 256);
259
260 if (image) {
261 sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name,
262 cairo_image_surface_get_width(image));
263 if (item->image) {
264 cairo_surface_destroy(item->image);
265 }
266 item->image = image;
267 item->dirty = true;
268 dirty = true;
269
270 dbus_message_unref(reply);
271 dbus_pending_call_unref(pending);
272 return;
273 }
274
275bail:
276 if (reply) {
277 dbus_message_unref(reply);
278 }
279 dbus_pending_call_unref(pending);
280 // Now try the pixmap
281 send_icon_msg(item);
282 return;
283}
284static void send_icon_name_msg(struct StatusNotifierItem *item) {
285 DBusPendingCall *pending;
286 DBusMessage *message = dbus_message_new_method_call(
287 item->name,
288 "/StatusNotifierItem",
289 "org.freedesktop.DBus.Properties",
290 "Get");
291 const char *iface;
292 if (item->kde_special_snowflake) {
293 iface = "org.kde.StatusNotifierItem";
294 } else {
295 iface = "org.freedesktop.StatusNotifierItem";
296 }
297 const char *prop = "IconName";
298
299 dbus_message_append_args(message,
300 DBUS_TYPE_STRING, &iface,
301 DBUS_TYPE_STRING, &prop,
302 DBUS_TYPE_INVALID);
303
304 bool status =
305 dbus_connection_send_with_reply(conn, message, &pending, -1);
306
307 dbus_message_unref(message);
308
309 if (!(pending || status)) {
310 sway_log(L_ERROR, "Could not get item icon name");
311 return;
312 }
313
314 dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL);
315}
316
317void get_icon(struct StatusNotifierItem *item) {
318 send_icon_name_msg(item);
319}
320
321void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
322 const char *iface =
323 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
324 : "org.freedesktop.StatusNotifierItem");
325 DBusMessage *message = dbus_message_new_method_call(
326 item->name,
327 "/StatusNotifierItem",
328 iface,
329 "Activate");
330
331 dbus_message_append_args(message,
332 DBUS_TYPE_INT32, &x,
333 DBUS_TYPE_INT32, &y,
334 DBUS_TYPE_INVALID);
335
336 dbus_connection_send(conn, message, NULL);
337
338 dbus_message_unref(message);
339}
340
341void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
342 const char *iface =
343 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
344 : "org.freedesktop.StatusNotifierItem");
345 DBusMessage *message = dbus_message_new_method_call(
346 item->name,
347 "/StatusNotifierItem",
348 iface,
349 "ContextMenu");
350
351 dbus_message_append_args(message,
352 DBUS_TYPE_INT32, &x,
353 DBUS_TYPE_INT32, &y,
354 DBUS_TYPE_INVALID);
355
356 dbus_connection_send(conn, message, NULL);
357
358 dbus_message_unref(message);
359}
360void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
361 const char *iface =
362 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
363 : "org.freedesktop.StatusNotifierItem");
364 DBusMessage *message = dbus_message_new_method_call(
365 item->name,
366 "/StatusNotifierItem",
367 iface,
368 "SecondaryActivate");
369
370 dbus_message_append_args(message,
371 DBUS_TYPE_INT32, &x,
372 DBUS_TYPE_INT32, &y,
373 DBUS_TYPE_INVALID);
374
375 dbus_connection_send(conn, message, NULL);
376
377 dbus_message_unref(message);
378}
379
380static void get_unique_name(struct StatusNotifierItem *item) {
381 // I think that we're fine being sync here becaues the message is
382 // directly to the message bus. Could be async though.
383 DBusMessage *message = dbus_message_new_method_call(
384 "org.freedesktop.DBus",
385 "/org/freedesktop/DBus",
386 "org.freedesktop.DBus",
387 "GetNameOwner");
388
389 dbus_message_append_args(message,
390 DBUS_TYPE_STRING, &item->name,
391 DBUS_TYPE_INVALID);
392
393 DBusMessage *reply = dbus_connection_send_with_reply_and_block(
394 conn, message, -1, NULL);
395
396 dbus_message_unref(message);
397
398 if (!reply) {
399 sway_log(L_ERROR, "Could not get unique name for item: %s",
400 item->name);
401 return;
402 }
403
404 char *unique_name;
405 if (!dbus_message_get_args(reply, NULL,
406 DBUS_TYPE_STRING, &unique_name,
407 DBUS_TYPE_INVALID)) {
408 sway_log(L_ERROR, "Error parsing method args");
409 } else {
410 if (item->unique_name) {
411 free(item->unique_name);
412 }
413 item->unique_name = strdup(unique_name);
414 }
415
416 dbus_message_unref(reply);
417}
418
419struct StatusNotifierItem *sni_create(const char *name) {
420 // Make sure `name` is well formed
421 if (!dbus_validate_bus_name(name, NULL)) {
422 sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name);
423 return NULL;
424 }
425
426 struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
427 item->name = strdup(name);
428 item->unique_name = NULL;
429 item->image = NULL;
430 item->dirty = false;
431
432 // If it doesn't use this name then assume that it uses the KDE spec
433 // This is because xembed-sni-proxy uses neither "org.freedesktop" nor
434 // "org.kde" and just gives us the items "unique name"
435 //
436 // We could use this to our advantage and fill out the "unique name"
437 // field with the given name if it is neither freedesktop or kde, but
438 // that's makes us rely on KDE hackyness which is bad practice
439 const char freedesktop_name[] = "org.freedesktop";
440 if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) {
441 item->kde_special_snowflake = true;
442 } else {
443 item->kde_special_snowflake = false;
444 }
445
446 get_icon(item);
447
448 get_unique_name(item);
449
450 return item;
451}
452/* Return 0 if `item` has a name of `str` */
453int sni_str_cmp(const void *_item, const void *_str) {
454 const struct StatusNotifierItem *item = _item;
455 const char *str = _str;
456
457 return strcmp(item->name, str);
458}
459/* Returns 0 if `item` has a unique name of `str` */
460int sni_uniq_cmp(const void *_item, const void *_str) {
461 const struct StatusNotifierItem *item = _item;
462 const char *str = _str;
463
464 if (!item->unique_name) {
465 return false;
466 }
467 return strcmp(item->unique_name, str);
468}
469void sni_free(struct StatusNotifierItem *item) {
470 if (!item) {
471 return;
472 }
473 free(item->name);
474 if (item->unique_name) {
475 free(item->unique_name);
476 }
477 if (item->image) {
478 cairo_surface_destroy(item->image);
479 }
480 free(item);
481}
diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c
deleted file mode 100644
index 86453e70..00000000
--- a/swaybar/tray/sni_watcher.c
+++ /dev/null
@@ -1,497 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <unistd.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <stdbool.h>
7#include <dbus/dbus.h>
8#include "swaybar/tray/dbus.h"
9#include "list.h"
10#include "log.h"
11
12static list_t *items = NULL;
13static list_t *hosts = NULL;
14
15/**
16 * Describes the function of the StatusNotifierWatcher
17 * See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/
18 *
19 * We also implement KDE's special snowflake protocol, it's like this but with
20 * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
21 */
22static const char *interface_xml =
23 "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
24 "'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>"
25 "<node>"
26 " <interface name='org.freedesktop.DBus.Introspectable'>"
27 " <method name='Introspect'>"
28 " <arg name='xml_data' direction='out' type='s'/>"
29 " </method>"
30 " </interface>"
31 " <interface name='org.freedesktop.DBus.Properties'>"
32 " <method name='Get'>"
33 " <arg name='interface' direction='in' type='s'/>"
34 " <arg name='propname' direction='in' type='s'/>"
35 " <arg name='value' direction='out' type='v'/>"
36 " </method>"
37 " <method name='Set'>"
38 " <arg name='interface' direction='in' type='s'/>"
39 " <arg name='propname' direction='in' type='s'/>"
40 " <arg name='value' direction='in' type='v'/>"
41 " </method>"
42 " <method name='GetAll'>"
43 " <arg name='interface' direction='in' type='s'/>"
44 " <arg name='props' direction='out' type='a{sv}'/>"
45 " </method>"
46 " </interface>"
47 " <interface name='org.freedesktop.StatusNotifierWatcher'>"
48 " <method name='RegisterStatusNotifierItem'>"
49 " <arg type='s' name='service' direction='in'/>"
50 " </method>"
51 " <method name='RegisterStatusNotifierHost'>"
52 " <arg type='s' name='service' direction='in'/>"
53 " </method>"
54 " <property name='RegisteredStatusNotifierItems' type='as' access='read'/>"
55 " <property name='IsStatusNotifierHostRegistered' type='b' access='read'/>"
56 " <property name='ProtocolVersion' type='i' access='read'/>"
57 " <signal name='StatusNotifierItemRegistered'>"
58 " <arg type='s' name='service' direction='out'/>"
59 " </signal>"
60 " <signal name='StatusNotifierItemUnregistered'>"
61 " <arg type='s' name='service' direction='out'/>"
62 " </signal>"
63 " <signal name='StatusNotifierHostRegistered'>"
64 " <arg type='' name='service' direction='out'/>"
65 " </signal>"
66 " </interface>"
67 "</node>";
68
69static void host_registered_signal(DBusConnection *connection) {
70 // Send one signal for each protocol
71 DBusMessage *signal = dbus_message_new_signal(
72 "/StatusNotifierWatcher",
73 "org.freedesktop.StatusNotifierWatcher",
74 "StatusNotifierHostRegistered");
75
76 dbus_connection_send(connection, signal, NULL);
77 dbus_message_unref(signal);
78
79
80 signal = dbus_message_new_signal(
81 "/StatusNotifierWatcher",
82 "org.kde.StatusNotifierWatcher",
83 "StatusNotifierHostRegistered");
84
85 dbus_connection_send(connection, signal, NULL);
86 dbus_message_unref(signal);
87}
88static void item_registered_signal(DBusConnection *connection, const char *name) {
89 DBusMessage *signal = dbus_message_new_signal(
90 "/StatusNotifierWatcher",
91 "org.freedesktop.StatusNotifierWatcher",
92 "StatusNotifierItemRegistered");
93 dbus_message_append_args(signal,
94 DBUS_TYPE_STRING, &name,
95 DBUS_TYPE_INVALID);
96 dbus_connection_send(connection, signal, NULL);
97 dbus_message_unref(signal);
98
99 signal = dbus_message_new_signal(
100 "/StatusNotifierWatcher",
101 "org.kde.StatusNotifierWatcher",
102 "StatusNotifierItemRegistered");
103 dbus_message_append_args(signal,
104 DBUS_TYPE_STRING, &name,
105 DBUS_TYPE_INVALID);
106 dbus_connection_send(connection, signal, NULL);
107 dbus_message_unref(signal);
108}
109static void item_unregistered_signal(DBusConnection *connection, const char *name) {
110 DBusMessage *signal = dbus_message_new_signal(
111 "/StatusNotifierWatcher",
112 "org.freedesktop.StatusNotifierWatcher",
113 "StatusNotifierItemUnregistered");
114 dbus_message_append_args(signal,
115 DBUS_TYPE_STRING, &name,
116 DBUS_TYPE_INVALID);
117 dbus_connection_send(connection, signal, NULL);
118 dbus_message_unref(signal);
119
120 signal = dbus_message_new_signal(
121 "/StatusNotifierWatcher",
122 "org.kde.StatusNotifierWatcher",
123 "StatusNotifierItemUnregistered");
124 dbus_message_append_args(signal,
125 DBUS_TYPE_STRING, &name,
126 DBUS_TYPE_INVALID);
127 dbus_connection_send(connection, signal, NULL);
128 dbus_message_unref(signal);
129}
130
131static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
132 DBusMessage *reply;
133
134 reply = dbus_message_new_method_return(request);
135 dbus_message_append_args(reply,
136 DBUS_TYPE_STRING, &interface_xml,
137 DBUS_TYPE_INVALID);
138 dbus_connection_send(connection, reply, NULL);
139 dbus_message_unref(reply);
140}
141
142static void register_item(DBusConnection *connection, DBusMessage *message) {
143 DBusError error;
144 char *name;
145
146 dbus_error_init(&error);
147 if (!dbus_message_get_args(message, &error,
148 DBUS_TYPE_STRING, &name,
149 DBUS_TYPE_INVALID)) {
150 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
151 }
152
153 sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
154
155 // Don't add duplicate or not real item
156 if (!dbus_validate_bus_name(name, NULL)) {
157 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
158 return;
159 }
160
161 if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
162 return;
163 }
164 if (!dbus_bus_name_has_owner(connection, name, &error)) {
165 return;
166 }
167
168 list_add(items, strdup(name));
169 item_registered_signal(connection, name);
170
171 // It's silly, but xembedsniproxy wants a reply for this function
172 DBusMessage *reply = dbus_message_new_method_return(message);
173 dbus_connection_send(connection, reply, NULL);
174 dbus_message_unref(reply);
175}
176
177static void register_host(DBusConnection *connection, DBusMessage *message) {
178 DBusError error;
179 char *name;
180
181 dbus_error_init(&error);
182 if (!dbus_message_get_args(message, &error,
183 DBUS_TYPE_STRING, &name,
184 DBUS_TYPE_INVALID)) {
185 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
186 }
187
188 sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
189
190 // Don't add duplicate or not real host
191 if (!dbus_validate_bus_name(name, NULL)) {
192 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
193 return;
194 }
195
196
197 if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) {
198 return;
199 }
200 if (!dbus_bus_name_has_owner(connection, name, &error)) {
201 return;
202 }
203
204 list_add(hosts, strdup(name));
205 host_registered_signal(connection);
206}
207
208static void get_property(DBusConnection *connection, DBusMessage *message) {
209 DBusError error;
210 char *interface;
211 char *property;
212
213 dbus_error_init(&error);
214 if (!dbus_message_get_args(message, &error,
215 DBUS_TYPE_STRING, &interface,
216 DBUS_TYPE_STRING, &property,
217 DBUS_TYPE_INVALID)) {
218 sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
219 return;
220 }
221
222 if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
223 sway_log(L_INFO, "Replying with items\n");
224 DBusMessage *reply;
225 reply = dbus_message_new_method_return(message);
226 DBusMessageIter iter;
227 DBusMessageIter sub;
228 DBusMessageIter subsub;
229
230 dbus_message_iter_init_append(reply, &iter);
231
232 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
233 "as", &sub);
234 dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
235 "s", &subsub);
236
237 for (int i = 0; i < items->length; ++i) {
238 dbus_message_iter_append_basic(&subsub,
239 DBUS_TYPE_STRING, &items->items[i]);
240 }
241
242 dbus_message_iter_close_container(&sub, &subsub);
243 dbus_message_iter_close_container(&iter, &sub);
244
245 dbus_connection_send(connection, reply, NULL);
246 dbus_message_unref(reply);
247 } else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) {
248 DBusMessage *reply;
249 DBusMessageIter iter;
250 DBusMessageIter sub;
251 int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
252
253 reply = dbus_message_new_method_return(message);
254
255 dbus_message_iter_init_append(reply, &iter);
256
257 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
258 "b", &sub);
259 dbus_message_iter_append_basic(&sub,
260 DBUS_TYPE_BOOLEAN, &registered);
261
262 dbus_message_iter_close_container(&iter, &sub);
263
264 dbus_connection_send(connection, reply, NULL);
265 dbus_message_unref(reply);
266 } else if (strcmp(property, "ProtocolVersion") == 0) {
267 DBusMessage *reply;
268 DBusMessageIter iter;
269 DBusMessageIter sub;
270 const int version = 0;
271
272 reply = dbus_message_new_method_return(message);
273
274 dbus_message_iter_init_append(reply, &iter);
275
276 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
277 "i", &sub);
278 dbus_message_iter_append_basic(&sub,
279 DBUS_TYPE_INT32, &version);
280
281 dbus_message_iter_close_container(&iter, &sub);
282 dbus_connection_send(connection, reply, NULL);
283 dbus_message_unref(reply);
284 }
285}
286
287static void set_property(DBusConnection *connection, DBusMessage *message) {
288 // All properties are read only and we don't allow new properties
289 return;
290}
291
292static void get_all(DBusConnection *connection, DBusMessage *message) {
293 DBusMessage *reply;
294 reply = dbus_message_new_method_return(message);
295 DBusMessageIter iter; /* a{v} */
296 DBusMessageIter arr;
297 DBusMessageIter dict;
298 DBusMessageIter sub;
299 DBusMessageIter subsub;
300 int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
301 const int version = 0;
302 const char *prop;
303
304 // Could clean this up with a function for each prop
305 dbus_message_iter_init_append(reply, &iter);
306 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
307 "{sv}", &arr);
308
309 prop = "RegisteredStatusNotifierItems";
310 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
311 NULL, &dict);
312 dbus_message_iter_append_basic(&dict,
313 DBUS_TYPE_STRING, &prop);
314 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
315 "as", &sub);
316 dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
317 "s", &subsub);
318 for (int i = 0; i < items->length; ++i) {
319 dbus_message_iter_append_basic(&subsub,
320 DBUS_TYPE_STRING, &items->items[i]);
321 }
322 dbus_message_iter_close_container(&sub, &subsub);
323 dbus_message_iter_close_container(&dict, &sub);
324 dbus_message_iter_close_container(&arr, &dict);
325
326 prop = "IsStatusNotifierHostRegistered";
327 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
328 NULL, &dict);
329 dbus_message_iter_append_basic(&dict,
330 DBUS_TYPE_STRING, &prop);
331 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
332 "b", &sub);
333 dbus_message_iter_append_basic(&sub,
334 DBUS_TYPE_BOOLEAN, &registered);
335 dbus_message_iter_close_container(&dict, &sub);
336 dbus_message_iter_close_container(&arr, &dict);
337
338 prop = "ProtocolVersion";
339 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
340 NULL, &dict);
341 dbus_message_iter_append_basic(&dict,
342 DBUS_TYPE_STRING, &prop);
343 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
344 "i", &sub);
345 dbus_message_iter_append_basic(&sub,
346 DBUS_TYPE_INT32, &version);
347 dbus_message_iter_close_container(&dict, &sub);
348 dbus_message_iter_close_container(&arr, &dict);
349
350 dbus_message_iter_close_container(&iter, &arr);
351
352 dbus_connection_send(connection, reply, NULL);
353 dbus_message_unref(reply);
354}
355
356static DBusHandlerResult message_handler(DBusConnection *connection,
357 DBusMessage *message, void *data) {
358 const char *interface_name = dbus_message_get_interface(message);
359 const char *member_name = dbus_message_get_member(message);
360
361 // In order of the xml above
362 if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 &&
363 strcmp(member_name, "Introspect") == 0) {
364 // We don't have an introspect for KDE
365 respond_to_introspect(connection, message);
366 return DBUS_HANDLER_RESULT_HANDLED;
367 } else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) {
368 if (strcmp(member_name, "Get") == 0) {
369 get_property(connection, message);
370 return DBUS_HANDLER_RESULT_HANDLED;
371 } else if (strcmp(member_name, "Set") == 0) {
372 set_property(connection, message);
373 return DBUS_HANDLER_RESULT_HANDLED;
374 } else if (strcmp(member_name, "GetAll") == 0) {
375 get_all(connection, message);
376 return DBUS_HANDLER_RESULT_HANDLED;
377 } else {
378 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
379 }
380 } else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 ||
381 strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) {
382 if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) {
383 register_item(connection, message);
384 return DBUS_HANDLER_RESULT_HANDLED;
385 } else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) {
386 register_host(connection, message);
387 return DBUS_HANDLER_RESULT_HANDLED;
388 } else {
389 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
390 }
391 }
392 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
393}
394
395static DBusHandlerResult signal_handler(DBusConnection *connection,
396 DBusMessage *message, void *_data) {
397 if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) {
398 // Only eat the message if it is name that we are watching
399 const char *name;
400 const char *old_owner;
401 const char *new_owner;
402 int index;
403 if (!dbus_message_get_args(message, NULL,
404 DBUS_TYPE_STRING, &name,
405 DBUS_TYPE_STRING, &old_owner,
406 DBUS_TYPE_STRING, &new_owner,
407 DBUS_TYPE_INVALID)) {
408 sway_log(L_ERROR, "Error getting LostName args");
409 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
410 }
411 if (strcmp(new_owner, "") != 0) {
412 // Name is not lost
413 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
414 }
415 if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) {
416 sway_log(L_INFO, "Status Notifier Item lost %s", name);
417 free(items->items[index]);
418 list_del(items, index);
419 item_unregistered_signal(connection, name);
420
421 return DBUS_HANDLER_RESULT_HANDLED;
422 }
423 if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) {
424 sway_log(L_INFO, "Status Notifier Host lost %s", name);
425 free(hosts->items[index]);
426 list_del(hosts, index);
427
428 return DBUS_HANDLER_RESULT_HANDLED;
429 }
430 }
431 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
432}
433
434static const DBusObjectPathVTable vtable = {
435 .message_function = message_handler,
436 .unregister_function = NULL,
437};
438
439int init_sni_watcher() {
440 DBusError error;
441 dbus_error_init(&error);
442 if (!conn) {
443 sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher");
444 return -1;
445 }
446
447 items = create_list();
448 hosts = create_list();
449
450 int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
451 DBUS_NAME_FLAG_REPLACE_EXISTING,
452 &error);
453 if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
454 sway_log(L_DEBUG, "Got watcher name");
455 } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
456 sway_log(L_INFO, "Could not get watcher name, it may start later");
457 }
458 if (dbus_error_is_set(&error)) {
459 sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message);
460 return -1;
461 }
462
463 status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher",
464 DBUS_NAME_FLAG_REPLACE_EXISTING,
465 &error);
466 if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
467 sway_log(L_DEBUG, "Got kde watcher name");
468 } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
469 sway_log(L_INFO, "Could not get kde watcher name, it may start later");
470 }
471 if (dbus_error_is_set(&error)) {
472 sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message);
473 return -1;
474 }
475
476 dbus_connection_try_register_object_path(conn,
477 "/StatusNotifierWatcher",
478 &vtable, NULL, &error);
479 if (dbus_error_is_set(&error)) {
480 sway_log(L_ERROR, "dbus_err: %s\n", error.message);
481 return -1;
482 }
483
484 dbus_bus_add_match(conn,
485 "type='signal',\
486 sender='org.freedesktop.DBus',\
487 interface='org.freedesktop.DBus',\
488 member='NameOwnerChanged'",
489 &error);
490
491 if (dbus_error_is_set(&error)) {
492 sway_log(L_ERROR, "DBus error getting match args: %s", error.message);
493 }
494
495 dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
496 return 0;
497}
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
deleted file mode 100644
index 91c3af06..00000000
--- a/swaybar/tray/tray.c
+++ /dev/null
@@ -1,398 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <unistd.h>
3#include <stdlib.h>
4#include <string.h>
5#include <sys/wait.h>
6#include <dbus/dbus.h>
7#include "swaybar/bar.h"
8#include "swaybar/tray/tray.h"
9#include "swaybar/tray/dbus.h"
10#include "swaybar/tray/sni.h"
11#include "swaybar/tray/sni_watcher.h"
12#include "swaybar/bar.h"
13#include "swaybar/config.h"
14#include "list.h"
15#include "log.h"
16
17struct tray *tray;
18
19static void register_host(char *name) {
20 DBusMessage *message;
21
22 message = dbus_message_new_method_call(
23 "org.freedesktop.StatusNotifierWatcher",
24 "/StatusNotifierWatcher",
25 "org.freedesktop.StatusNotifierWatcher",
26 "RegisterStatusNotifierHost");
27 if (!message) {
28 sway_log(L_ERROR, "Cannot allocate dbus method call");
29 return;
30 }
31
32 dbus_message_append_args(message,
33 DBUS_TYPE_STRING, &name,
34 DBUS_TYPE_INVALID);
35
36 dbus_connection_send(conn, message, NULL);
37
38 dbus_message_unref(message);
39}
40
41static void get_items_reply(DBusPendingCall *pending, void *_data) {
42 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
43
44 if (!reply) {
45 sway_log(L_ERROR, "Got no items reply from sni watcher");
46 goto bail;
47 }
48
49 int message_type = dbus_message_get_type(reply);
50
51 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
52 char *msg;
53
54 dbus_message_get_args(reply, NULL,
55 DBUS_TYPE_STRING, &msg,
56 DBUS_TYPE_INVALID);
57
58 sway_log(L_ERROR, "Message is error: %s", msg);
59 goto bail;
60 }
61
62 DBusMessageIter iter;
63 DBusMessageIter variant;
64 DBusMessageIter array;
65
66 dbus_message_iter_init(reply, &iter);
67 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
68 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
69 goto bail;
70 }
71 dbus_message_iter_recurse(&iter, &variant);
72 if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
73 dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
74 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
75 goto bail;
76 }
77
78 // Clear list
79 list_foreach(tray->items, (void (*)(void *))sni_free);
80 list_free(tray->items);
81 tray->items = create_list();
82
83 // O(n) function, could be faster dynamically reading values
84 int len = dbus_message_iter_get_element_count(&variant);
85
86 dbus_message_iter_recurse(&variant, &array);
87 for (int i = 0; i < len; i++) {
88 const char *name;
89 dbus_message_iter_get_basic(&array, &name);
90
91 struct StatusNotifierItem *item = sni_create(name);
92
93 if (item) {
94 sway_log(L_DEBUG, "Item registered with host: %s", name);
95 list_add(tray->items, item);
96 dirty = true;
97 }
98 }
99
100bail:
101 dbus_message_unref(reply);
102 dbus_pending_call_unref(pending);
103 return;
104}
105static void get_items() {
106 DBusPendingCall *pending;
107 DBusMessage *message = dbus_message_new_method_call(
108 "org.freedesktop.StatusNotifierWatcher",
109 "/StatusNotifierWatcher",
110 "org.freedesktop.DBus.Properties",
111 "Get");
112
113 const char *iface = "org.freedesktop.StatusNotifierWatcher";
114 const char *prop = "RegisteredStatusNotifierItems";
115 dbus_message_append_args(message,
116 DBUS_TYPE_STRING, &iface,
117 DBUS_TYPE_STRING, &prop,
118 DBUS_TYPE_INVALID);
119
120 bool status =
121 dbus_connection_send_with_reply(conn, message, &pending, -1);
122 dbus_message_unref(message);
123
124 if (!(pending || status)) {
125 sway_log(L_ERROR, "Could not get items");
126 return;
127 }
128
129 dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL);
130}
131
132static DBusHandlerResult signal_handler(DBusConnection *connection,
133 DBusMessage *message, void *_data) {
134 if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
135 "StatusNotifierItemRegistered")) {
136 const char *name;
137 if (!dbus_message_get_args(message, NULL,
138 DBUS_TYPE_STRING, &name,
139 DBUS_TYPE_INVALID)) {
140 sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args");
141 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
142 }
143
144 if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
145 struct StatusNotifierItem *item = sni_create(name);
146
147 if (item) {
148 list_add(tray->items, item);
149 dirty = true;
150 }
151 }
152
153 return DBUS_HANDLER_RESULT_HANDLED;
154 } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
155 "StatusNotifierItemUnregistered")) {
156 const char *name;
157 if (!dbus_message_get_args(message, NULL,
158 DBUS_TYPE_STRING, &name,
159 DBUS_TYPE_INVALID)) {
160 sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args");
161 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
162 }
163
164 int index;
165 if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
166 sni_free(tray->items->items[index]);
167 list_del(tray->items, index);
168 dirty = true;
169 } else {
170 // If it's not in our list, then our list is incorrect.
171 // Fetch all items again
172 sway_log(L_INFO, "Host item list incorrect, refreshing");
173 get_items();
174 }
175
176 return DBUS_HANDLER_RESULT_HANDLED;
177 } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem",
178 "NewIcon") || dbus_message_is_signal(message,
179 "org.kde.StatusNotifierItem", "NewIcon")) {
180 const char *name;
181 int index;
182 struct StatusNotifierItem *item;
183
184 name = dbus_message_get_sender(message);
185 if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) {
186 item = tray->items->items[index];
187 sway_log(L_INFO, "NewIcon signal from item %s", item->name);
188 get_icon(item);
189 }
190
191 return DBUS_HANDLER_RESULT_HANDLED;
192 }
193 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
194}
195
196static int init_host() {
197 tray = (struct tray *)malloc(sizeof(tray));
198
199 tray->items = create_list();
200
201 DBusError error;
202 dbus_error_init(&error);
203 char *name = NULL;
204 if (!conn) {
205 sway_log(L_ERROR, "Connection is null, cannot init SNI host");
206 goto err;
207 }
208 name = calloc(sizeof(char), 256);
209
210 if (!name) {
211 sway_log(L_ERROR, "Cannot allocate name");
212 goto err;
213 }
214
215 pid_t pid = getpid();
216 if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid)
217 >= 256) {
218 sway_log(L_ERROR, "Cannot get host name because string is too short."
219 "This should not happen");
220 goto err;
221 }
222
223 // We want to be the sole owner of this name
224 if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE,
225 &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
226 sway_log(L_ERROR, "Cannot get host name and start the tray");
227 goto err;
228 }
229 if (dbus_error_is_set(&error)) {
230 sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message);
231 goto err;
232 }
233 sway_log(L_DEBUG, "Got host name");
234
235 register_host(name);
236
237 get_items();
238
239 // Perhaps use addmatch helper functions like wlc does?
240 dbus_bus_add_match(conn,
241 "type='signal',\
242 sender='org.freedesktop.StatusNotifierWatcher',\
243 member='StatusNotifierItemRegistered'",
244 &error);
245 if (dbus_error_is_set(&error)) {
246 sway_log(L_ERROR, "dbus_err: %s", error.message);
247 goto err;
248 }
249 dbus_bus_add_match(conn,
250 "type='signal',\
251 sender='org.freedesktop.StatusNotifierWatcher',\
252 member='StatusNotifierItemUnregistered'",
253 &error);
254 if (dbus_error_is_set(&error)) {
255 sway_log(L_ERROR, "dbus_err: %s", error.message);
256 return -1;
257 }
258
259 // SNI matches
260 dbus_bus_add_match(conn,
261 "type='signal',\
262 interface='org.freedesktop.StatusNotifierItem',\
263 member='NewIcon'",
264 &error);
265 if (dbus_error_is_set(&error)) {
266 sway_log(L_ERROR, "dbus_err %s", error.message);
267 goto err;
268 }
269 dbus_bus_add_match(conn,
270 "type='signal',\
271 interface='org.kde.StatusNotifierItem',\
272 member='NewIcon'",
273 &error);
274 if (dbus_error_is_set(&error)) {
275 sway_log(L_ERROR, "dbus_err %s", error.message);
276 goto err;
277 }
278
279 dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
280
281 free(name);
282 return 0;
283
284err:
285 // TODO better handle errors
286 free(name);
287 return -1;
288}
289
290void tray_mouse_event(struct output *output, int x, int y,
291 uint32_t button, uint32_t state) {
292
293 struct window *window = output->window;
294 uint32_t tray_padding = swaybar.config->tray_padding;
295 int tray_width = window->width * window->scale;
296
297 for (int i = 0; i < output->items->length; ++i) {
298 struct sni_icon_ref *item =
299 output->items->items[i];
300 int icon_width = cairo_image_surface_get_width(item->icon);
301
302 tray_width -= tray_padding;
303 if (x <= tray_width && x >= tray_width - icon_width) {
304 if (button == swaybar.config->activate_button) {
305 sni_activate(item->ref, x, y);
306 } else if (button == swaybar.config->context_button) {
307 sni_context_menu(item->ref, x, y);
308 } else if (button == swaybar.config->secondary_button) {
309 sni_secondary(item->ref, x, y);
310 }
311 break;
312 }
313 tray_width -= icon_width;
314 }
315}
316
317uint32_t tray_render(struct output *output, struct config *config) {
318 struct window *window = output->window;
319 cairo_t *cairo = window->cairo;
320
321 // Tray icons
322 uint32_t tray_padding = config->tray_padding;
323 uint32_t tray_width = window->width * window->scale;
324 const int item_size = (window->height * window->scale) - (2 * tray_padding);
325
326 if (item_size < 0) {
327 // Can't render items if the padding is too large
328 return tray_width;
329 }
330
331 if (config->tray_output && strcmp(config->tray_output, output->name) != 0) {
332 return tray_width;
333 }
334
335 for (int i = 0; i < tray->items->length; ++i) {
336 struct StatusNotifierItem *item =
337 tray->items->items[i];
338 if (!item->image) {
339 continue;
340 }
341
342 struct sni_icon_ref *render_item = NULL;
343 int j;
344 for (j = i; j < output->items->length; ++j) {
345 struct sni_icon_ref *ref =
346 output->items->items[j];
347 if (ref->ref == item) {
348 render_item = ref;
349 break;
350 } else {
351 sni_icon_ref_free(ref);
352 list_del(output->items, j);
353 }
354 }
355
356 if (!render_item) {
357 render_item = sni_icon_ref_create(item, item_size);
358 list_add(output->items, render_item);
359 } else if (item->dirty) {
360 // item needs re-render
361 sni_icon_ref_free(render_item);
362 output->items->items[j] = render_item =
363 sni_icon_ref_create(item, item_size);
364 }
365
366 tray_width -= tray_padding;
367 tray_width -= item_size;
368
369 cairo_operator_t op = cairo_get_operator(cairo);
370 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
371 cairo_set_source_surface(cairo, render_item->icon, tray_width, tray_padding);
372 cairo_rectangle(cairo, tray_width, tray_padding, item_size, item_size);
373 cairo_fill(cairo);
374 cairo_set_operator(cairo, op);
375
376 item->dirty = false;
377 }
378
379
380 if (tray_width != window->width * window->scale) {
381 tray_width -= tray_padding;
382 }
383
384 return tray_width;
385}
386
387void init_tray(struct bar *bar) {
388 if (!bar->config->tray_output || strcmp(bar->config->tray_output, "none") != 0) {
389 /* Connect to the D-Bus */
390 dbus_init();
391
392 /* Start the SNI watcher */
393 init_sni_watcher();
394
395 /* Start the SNI host */
396 init_host();
397 }
398}
diff --git a/swaybg/CMakeLists.txt b/swaybg/CMakeLists.txt
deleted file mode 100644
index f8cad404..00000000
--- a/swaybg/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
1include_directories(
2 ${PROTOCOLS_INCLUDE_DIRS}
3 ${WAYLAND_INCLUDE_DIR}
4 ${CAIRO_INCLUDE_DIRS}
5 ${PANGO_INCLUDE_DIRS}
6 ${XKBCOMMON_INCLUDE_DIRS}
7)
8
9add_executable(swaybg
10 main.c
11)
12
13target_link_libraries(swaybg
14 sway-common
15 sway-wayland
16 ${WAYLAND_CLIENT_LIBRARIES}
17 ${WAYLAND_CURSOR_LIBRARIES}
18 ${CAIRO_LIBRARIES}
19 ${PANGO_LIBRARIES}
20 m
21)
22
23if (WITH_GDK_PIXBUF)
24 include_directories(
25 ${GDK_PIXBUF_INCLUDE_DIRS}
26 )
27 target_link_libraries(swaybg
28 ${GDK_PIXBUF_LIBRARIES}
29 )
30endif()
31
32install(
33 TARGETS swaybg
34 RUNTIME
35 DESTINATION bin
36 COMPONENT runtime
37)
diff --git a/swaybg/main.c b/swaybg/main.c
index 2fdd4220..679b8c20 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -1,44 +1,55 @@
1#include "wayland-desktop-shell-client-protocol.h" 1#include <assert.h>
2#include <ctype.h>
2#include <stdbool.h> 3#include <stdbool.h>
3#include <stdio.h> 4#include <stdio.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <ctype.h>
6#include <wayland-client.h>
7#include <time.h>
8#include <string.h> 6#include <string.h>
9#include "client/window.h" 7#include <time.h>
10#include "client/registry.h" 8#include <wayland-client.h>
11#include "client/cairo.h" 9#include <wlr/util/log.h>
12#include "log.h" 10#include "background-image.h"
13#include "list.h" 11#include "pool-buffer.h"
12#include "cairo.h"
14#include "util.h" 13#include "util.h"
14#include "wlr-layer-shell-unstable-v1-client-protocol.h"
15 15
16list_t *surfaces; 16struct swaybg_args {
17struct registry *registry; 17 int output_idx;
18 const char *path;
19 enum background_mode mode;
20};
18 21
19enum scaling_mode { 22struct swaybg_context {
20 SCALING_MODE_STRETCH, 23 uint32_t color;
21 SCALING_MODE_FILL, 24 cairo_surface_t *image;
22 SCALING_MODE_FIT,
23 SCALING_MODE_CENTER,
24 SCALING_MODE_TILE,
25}; 25};
26 26
27void sway_terminate(int exit_code) { 27struct swaybg_state {
28 int i; 28 const struct swaybg_args *args;
29 for (i = 0; i < surfaces->length; ++i) { 29 struct swaybg_context context;
30 struct window *window = surfaces->items[i]; 30
31 window_teardown(window); 31 struct wl_display *display;
32 } 32 struct wl_compositor *compositor;
33 list_free(surfaces); 33 struct zwlr_layer_shell_v1 *layer_shell;
34 registry_teardown(registry); 34 struct wl_shm *shm;
35 exit(exit_code); 35
36} 36 struct wl_output *output;
37 struct wl_surface *surface;
38 struct wl_region *input_region;
39 struct zwlr_layer_surface_v1 *layer_surface;
40
41 bool run_display;
42 uint32_t width, height;
43 int32_t scale;
44 struct pool_buffer buffers[2];
45 struct pool_buffer *current_buffer;
46};
37 47
38bool is_valid_color(const char *color) { 48bool is_valid_color(const char *color) {
39 int len = strlen(color); 49 int len = strlen(color);
40 if (len != 7 || color[0] != '#') { 50 if (len != 7 || color[0] != '#') {
41 sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color); 51 wlr_log(L_ERROR, "%s is not a valid color for swaybg. "
52 "Color should be specified as #rrggbb (no alpha).", color);
42 return false; 53 return false;
43 } 54 }
44 55
@@ -52,163 +63,181 @@ bool is_valid_color(const char *color) {
52 return true; 63 return true;
53} 64}
54 65
55int main(int argc, const char **argv) { 66static void render_frame(struct swaybg_state *state) {
56 init_log(L_INFO); 67 int buffer_width = state->width * state->scale,
57 surfaces = create_list(); 68 buffer_height = state->height * state->scale;
58 registry = registry_poll(); 69 state->current_buffer = get_next_buffer(state->shm,
59 70 state->buffers, buffer_width, buffer_height);
60 if (argc != 4) { 71 cairo_t *cairo = state->current_buffer->cairo;
61 sway_abort("Do not run this program manually. See man 5 sway and look for output options."); 72 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
73 cairo_set_source_u32(cairo, state->context.color);
74 cairo_paint(cairo);
75 } else {
76 render_background_image(cairo, state->context.image,
77 state->args->mode, buffer_width, buffer_height);
62 } 78 }
63 79
64 if (!registry->desktop_shell) { 80 wl_surface_set_buffer_scale(state->surface, state->scale);
65 sway_abort("swaybg requires the compositor to support the desktop-shell extension."); 81 wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0);
82 wl_surface_damage(state->surface, 0, 0, state->width, state->height);
83 wl_surface_commit(state->surface);
84}
85
86static bool prepare_context(struct swaybg_state *state) {
87 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
88 state->context.color = parse_color(state->args->path);
89 return is_valid_color(state->args->path);
66 } 90 }
91 if (!(state->context.image = load_background_image(state->args->path))) {
92 return false;
93 }
94 return true;
95}
67 96
68 int desired_output = atoi(argv[1]); 97static void layer_surface_configure(void *data,
69 sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); 98 struct zwlr_layer_surface_v1 *surface,
70 int i; 99 uint32_t serial, uint32_t width, uint32_t height) {
71 struct output_state *output = registry->outputs->items[desired_output]; 100 struct swaybg_state *state = data;
72 struct window *window = window_setup(registry, 101 state->width = width;
73 output->width, output->height, output->scale, false); 102 state->height = height;
74 if (!window) { 103 zwlr_layer_surface_v1_ack_configure(surface, serial);
75 sway_abort("Failed to create surfaces."); 104 render_frame(state);
105}
106
107static void layer_surface_closed(void *data,
108 struct zwlr_layer_surface_v1 *surface) {
109 struct swaybg_state *state = data;
110 zwlr_layer_surface_v1_destroy(state->layer_surface);
111 wl_surface_destroy(state->surface);
112 wl_region_destroy(state->input_region);
113 state->run_display = false;
114}
115
116struct zwlr_layer_surface_v1_listener layer_surface_listener = {
117 .configure = layer_surface_configure,
118 .closed = layer_surface_closed,
119};
120
121static void output_geometry(void *data, struct wl_output *output, int32_t x,
122 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
123 const char *make, const char *model, int32_t transform) {
124 // Who cares
125}
126
127static void output_mode(void *data, struct wl_output *output, uint32_t flags,
128 int32_t width, int32_t height, int32_t refresh) {
129 // Who cares
130}
131
132static void output_done(void *data, struct wl_output *output) {
133 // Who cares
134}
135
136static void output_scale(void *data, struct wl_output *output, int32_t factor) {
137 struct swaybg_state *state = data;
138 state->scale = factor;
139 if (state->run_display) {
140 render_frame(state);
76 } 141 }
77 desktop_shell_set_background(registry->desktop_shell, output->output, window->surface); 142}
78 window_make_shell(window);
79 list_add(surfaces, window);
80
81 if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) {
82 cairo_set_source_u32(window->cairo, parse_color(argv[2]));
83 cairo_paint(window->cairo);
84 window_render(window);
85 } else {
86#ifdef WITH_GDK_PIXBUF
87 GError *err = NULL;
88 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err);
89 if (!pixbuf) {
90 sway_abort("Failed to load background image.");
91 }
92 cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
93 g_object_unref(pixbuf);
94#else
95 cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
96#endif //WITH_GDK_PIXBUF
97 if (!image) {
98 sway_abort("Failed to read background image.");
99 }
100 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
101 sway_abort("Failed to read background image: %s."
102#ifndef WITH_GDK_PIXBUF
103 "\nSway was compiled without gdk_pixbuf support, so only"
104 "\nPNG images can be loaded. This is the likely cause."
105#endif //WITH_GDK_PIXBUF
106 , cairo_status_to_string(cairo_surface_status(image)));
107 }
108 double width = cairo_image_surface_get_width(image);
109 double height = cairo_image_surface_get_height(image);
110
111 const char *scaling_mode_str = argv[3];
112 enum scaling_mode scaling_mode = SCALING_MODE_STRETCH;
113 if (strcmp(scaling_mode_str, "stretch") == 0) {
114 scaling_mode = SCALING_MODE_STRETCH;
115 } else if (strcmp(scaling_mode_str, "fill") == 0) {
116 scaling_mode = SCALING_MODE_FILL;
117 } else if (strcmp(scaling_mode_str, "fit") == 0) {
118 scaling_mode = SCALING_MODE_FIT;
119 } else if (strcmp(scaling_mode_str, "center") == 0) {
120 scaling_mode = SCALING_MODE_CENTER;
121 } else if (strcmp(scaling_mode_str, "tile") == 0) {
122 scaling_mode = SCALING_MODE_TILE;
123 } else {
124 sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
125 }
126 143
127 int wwidth = window->width * window->scale; 144struct wl_output_listener output_listener = {
128 int wheight = window->height * window->scale; 145 .geometry = output_geometry,
129 146 .mode = output_mode,
130 for (i = 0; i < surfaces->length; ++i) { 147 .done = output_done,
131 struct window *window = surfaces->items[i]; 148 .scale = output_scale,
132 if (window_prerender(window) && window->cairo) { 149};
133 switch (scaling_mode) { 150
134 case SCALING_MODE_STRETCH: 151static void handle_global(void *data, struct wl_registry *registry,
135 cairo_scale(window->cairo, 152 uint32_t name, const char *interface, uint32_t version) {
136 (double) wwidth / width, 153 struct swaybg_state *state = data;
137 (double) wheight / height); 154 if (strcmp(interface, wl_compositor_interface.name) == 0) {
138 cairo_set_source_surface(window->cairo, image, 0, 0); 155 state->compositor = wl_registry_bind(registry, name,
139 break; 156 &wl_compositor_interface, 3);
140 case SCALING_MODE_FILL: 157 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
141 { 158 state->shm = wl_registry_bind(registry, name,
142 double window_ratio = (double) wwidth / wheight; 159 &wl_shm_interface, 1);
143 double bg_ratio = width / height; 160 } else if (strcmp(interface, wl_output_interface.name) == 0) {
144 161 static int output_idx = 0;
145 if (window_ratio > bg_ratio) { 162 if (output_idx == state->args->output_idx) {
146 double scale = (double) wwidth / width; 163 state->output = wl_registry_bind(registry, name,
147 cairo_scale(window->cairo, scale, scale); 164 &wl_output_interface, 3);
148 cairo_set_source_surface(window->cairo, image, 165 wl_output_add_listener(state->output, &output_listener, state);
149 0,
150 (double) wheight/2 / scale - height/2);
151 } else {
152 double scale = (double) wheight / height;
153 cairo_scale(window->cairo, scale, scale);
154 cairo_set_source_surface(window->cairo, image,
155 (double) wwidth/2 / scale - width/2,
156 0);
157 }
158 break;
159 }
160 case SCALING_MODE_FIT:
161 {
162 double window_ratio = (double) wwidth / wheight;
163 double bg_ratio = width / height;
164
165 if (window_ratio > bg_ratio) {
166 double scale = (double) wheight / height;
167 cairo_scale(window->cairo, scale, scale);
168 cairo_set_source_surface(window->cairo, image,
169 (double) wwidth/2 / scale - width/2,
170 0);
171 } else {
172 double scale = (double) wwidth / width;
173 cairo_scale(window->cairo, scale, scale);
174 cairo_set_source_surface(window->cairo, image,
175 0,
176 (double) wheight/2 / scale - height/2);
177 }
178 break;
179 }
180 case SCALING_MODE_CENTER:
181 cairo_set_source_surface(window->cairo, image,
182 (double) wwidth/2 - width/2,
183 (double) wheight/2 - height/2);
184 break;
185 case SCALING_MODE_TILE:
186 {
187 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
188 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
189 cairo_set_source(window->cairo, pattern);
190 break;
191 }
192 default:
193 sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str);
194 }
195
196 cairo_paint(window->cairo);
197
198 window_render(window);
199 }
200 } 166 }
167 output_idx++;
168 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
169 state->layer_shell = wl_registry_bind(
170 registry, name, &zwlr_layer_shell_v1_interface, 1);
171 }
172}
201 173
202 cairo_surface_destroy(image); 174static void handle_global_remove(void *data, struct wl_registry *registry,
175 uint32_t name) {
176 // who cares
177}
178
179static const struct wl_registry_listener registry_listener = {
180 .global = handle_global,
181 .global_remove = handle_global_remove,
182};
183
184int main(int argc, const char **argv) {
185 struct swaybg_args args = {0};
186 struct swaybg_state state = {0};
187 state.args = &args;
188 wlr_log_init(L_DEBUG, NULL);
189
190 if (argc != 4) {
191 wlr_log(L_ERROR, "Do not run this program manually. "
192 "See man 5 sway and look for output options.");
193 return 1;
203 } 194 }
195 args.output_idx = atoi(argv[1]);
196 args.path = argv[2];
204 197
205 while (wl_display_dispatch(registry->display) != -1); 198 args.mode = parse_background_mode(argv[3]);
199 if (args.mode == BACKGROUND_MODE_INVALID) {
200 return 1;
201 }
202 if (!prepare_context(&state)) {
203 return 1;
204 }
206 205
207 for (i = 0; i < surfaces->length; ++i) { 206 assert(state.display = wl_display_connect(NULL));
208 struct window *window = surfaces->items[i]; 207
209 window_teardown(window); 208 struct wl_registry *registry = wl_display_get_registry(state.display);
209 wl_registry_add_listener(registry, &registry_listener, &state);
210 wl_display_roundtrip(state.display);
211 assert(state.compositor && state.layer_shell && state.output && state.shm);
212
213 // Second roundtrip to get output properties
214 wl_display_roundtrip(state.display);
215
216 assert(state.surface = wl_compositor_create_surface(state.compositor));
217
218 assert(state.input_region = wl_compositor_create_region(state.compositor));
219 wl_surface_set_input_region(state.surface, state.input_region);
220
221 state.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
222 state.layer_shell, state.surface, state.output,
223 ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");
224 assert(state.layer_surface);
225
226 zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0);
227 zwlr_layer_surface_v1_set_anchor(state.layer_surface,
228 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
229 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
230 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
231 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
232 zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1);
233 zwlr_layer_surface_v1_add_listener(state.layer_surface,
234 &layer_surface_listener, &state);
235 wl_surface_commit(state.surface);
236 wl_display_roundtrip(state.display);
237
238 state.run_display = true;
239 while (wl_display_dispatch(state.display) != -1 && state.run_display) {
240 // This space intentionally left blank
210 } 241 }
211 list_free(surfaces);
212 registry_teardown(registry);
213 return 0; 242 return 0;
214} 243}
diff --git a/swaybg/meson.build b/swaybg/meson.build
new file mode 100644
index 00000000..8704de6d
--- /dev/null
+++ b/swaybg/meson.build
@@ -0,0 +1,18 @@
1executable(
2 'swaybg',
3 'main.c',
4 include_directories: [sway_inc],
5 dependencies: [
6 cairo,
7 client_protos,
8 gdk_pixbuf,
9 jsonc,
10 math,
11 pango,
12 pangocairo,
13 wayland_client,
14 wlroots,
15 ],
16 link_with: [lib_sway_common, lib_sway_client],
17 install: true
18)
diff --git a/swaygrab/CMakeLists.txt b/swaygrab/CMakeLists.txt
deleted file mode 100644
index 42806cae..00000000
--- a/swaygrab/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
1include_directories(
2 ${JSONC_INCLUDE_DIRS}
3 ${WLC_INCLUDE_DIRS}
4 ${XKBCOMMON_INCLUDE_DIRS}
5)
6
7add_executable(swaygrab
8 main.c
9 json.c
10)
11
12target_link_libraries(swaygrab
13 sway-common
14 ${JSONC_LIBRARIES}
15 rt
16 m
17)
18
19install(
20 TARGETS swaygrab
21 RUNTIME
22 DESTINATION bin
23 COMPONENT runtime
24)
25
26if (A2X_FOUND)
27 add_manpage(swaygrab 1)
28endif()
diff --git a/swaylock/CMakeLists.txt b/swaylock/CMakeLists.txt
deleted file mode 100644
index 90b54a72..00000000
--- a/swaylock/CMakeLists.txt
+++ /dev/null
@@ -1,51 +0,0 @@
1include_directories(
2 ${PROTOCOLS_INCLUDE_DIRS}
3 ${WAYLAND_INCLUDE_DIR}
4 ${CAIRO_INCLUDE_DIRS}
5 ${PANGO_INCLUDE_DIRS}
6 ${PAM_INCLUDE_DIRS}
7 ${JSONC_INCLUDE_DIRS}
8 ${XKBCOMMON_INCLUDE_DIRS}
9)
10
11add_executable(swaylock
12 main.c
13)
14
15target_link_libraries(swaylock
16 sway-common
17 sway-wayland
18 ${WAYLAND_CLIENT_LIBRARIES}
19 ${WAYLAND_CURSOR_LIBRARIES}
20 ${CAIRO_LIBRARIES}
21 ${PANGO_LIBRARIES}
22 ${PAM_LIBRARIES}
23 ${JSONC_LIBRARIES}
24 m
25)
26
27if (WITH_GDK_PIXBUF)
28 include_directories(
29 ${GDK_PIXBUF_INCLUDE_DIRS}
30 )
31 target_link_libraries(swaylock
32 ${GDK_PIXBUF_LIBRARIES}
33 )
34endif()
35
36install(
37 TARGETS swaylock
38 RUNTIME
39 DESTINATION bin
40 COMPONENT runtime
41)
42
43install(
44 FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock
45 DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d/
46 COMPONENT data
47)
48
49if (A2X_FOUND)
50 add_manpage(swaylock 1)
51endif()
diff --git a/swaylock/main.c b/swaylock/main.c
index c2615951..4c6b44c6 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -1,387 +1,166 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 700
2#include "wayland-swaylock-client-protocol.h" 2#define _POSIX_C_SOURCE 200112L
3#include <xkbcommon/xkbcommon.h> 3#include <assert.h>
4#include <xkbcommon/xkbcommon-names.h> 4#include <ctype.h>
5#include <security/pam_appl.h> 5#include <fcntl.h>
6#include <json-c/json.h> 6#include <getopt.h>
7#include <stdbool.h>
7#include <stdio.h> 8#include <stdio.h>
8#include <stdlib.h> 9#include <stdlib.h>
9#include <string.h> 10#include <string.h>
10#include <sys/types.h> 11#include <sys/stat.h>
11#include <pwd.h> 12#include <time.h>
12#include <getopt.h>
13#include <signal.h>
14#include <stdbool.h>
15#include <unistd.h> 13#include <unistd.h>
16#include "client/window.h" 14#include <wayland-client.h>
17#include "client/registry.h" 15#include <wlr/util/log.h>
18#include "client/cairo.h" 16#include "swaylock/seat.h"
19#include "swaylock/swaylock.h" 17#include "swaylock/swaylock.h"
20#include "ipc-client.h" 18#include "background-image.h"
21#include "log.h" 19#include "pool-buffer.h"
20#include "cairo.h"
22#include "util.h" 21#include "util.h"
22#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
23#include "wlr-layer-shell-unstable-v1-client-protocol.h"
23 24
24struct registry *registry; 25static void daemonize() {
25struct render_data render_data; 26 int fds[2];
26struct lock_config *config; 27 if (pipe(fds) != 0) {
27bool show_indicator = true; 28 wlr_log(L_ERROR, "Failed to pipe");
28
29void wl_dispatch_events() {
30 wl_display_flush(registry->display);
31 if (wl_display_dispatch(registry->display) == -1) {
32 sway_log(L_ERROR, "failed to run wl_display_dispatch");
33 exit(1); 29 exit(1);
34 } 30 }
31 if (fork() == 0) {
32 close(fds[0]);
33 int devnull = open("/dev/null", O_RDWR);
34 dup2(STDOUT_FILENO, devnull);
35 dup2(STDERR_FILENO, devnull);
36 uint8_t success = 0;
37 if (chdir("/") != 0) {
38 write(fds[1], &success, 1);
39 exit(1);
40 }
41 success = 1;
42 if (write(fds[1], &success, 1) != 1) {
43 exit(1);
44 }
45 close(fds[1]);
46 } else {
47 close(fds[1]);
48 uint8_t success;
49 if (read(fds[0], &success, 1) != 1 || !success) {
50 wlr_log(L_ERROR, "Failed to daemonize");
51 exit(1);
52 }
53 close(fds[0]);
54 exit(0);
55 }
35} 56}
36 57
37void sigalarm_handler(int sig) { 58static void layer_surface_configure(void *data,
38 signal(SIGALRM, SIG_IGN); 59 struct zwlr_layer_surface_v1 *layer_surface,
39 // Hide typing indicator 60 uint32_t serial, uint32_t width, uint32_t height) {
40 render_data.auth_state = AUTH_STATE_IDLE; 61 struct swaylock_surface *surface = data;
41 render(&render_data, config); 62 surface->width = width;
42 wl_display_flush(registry->display); 63 surface->height = height;
43 signal(SIGALRM, sigalarm_handler); 64 zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
65 render_frame(surface);
44} 66}
45 67
46void sway_terminate(int exit_code) { 68static void layer_surface_closed(void *data,
47 int i; 69 struct zwlr_layer_surface_v1 *layer_surface) {
48 for (i = 0; i < render_data.surfaces->length; ++i) { 70 struct swaylock_surface *surface = data;
49 struct window *window = render_data.surfaces->items[i]; 71 zwlr_layer_surface_v1_destroy(surface->layer_surface);
50 window_teardown(window); 72 wl_surface_destroy(surface->surface);
51 } 73 surface->state->run_display = false;
52 list_free(render_data.surfaces);
53 if (registry) {
54 registry_teardown(registry);
55 }
56 exit(exit_code);
57} 74}
58 75
59char *password; 76static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
60int password_size; 77 .configure = layer_surface_configure,
61enum line_source line_source = LINE_SOURCE_DEFAULT; 78 .closed = layer_surface_closed,
62 79};
63struct lock_config *init_config() {
64 struct lock_config *config = calloc(1, sizeof(struct lock_config));
65
66 config->font = strdup("sans-serif");
67 config->colors.text = 0x000000FF;
68
69 config->colors.line = 0x000000FF;
70 config->colors.separator = 0x000000FF;
71
72 config->colors.input_cursor = 0x33DB00FF;
73 config->colors.backspace_cursor = 0xDB3300FF;
74
75 config->colors.normal.inner_ring = 0x000000BF;
76 config->colors.normal.outer_ring = 0x337D00FF;
77
78 config->colors.validating.inner_ring = 0x0072FFBF;
79 config->colors.validating.outer_ring = 0x3300FAFF;
80 80
81 config->colors.invalid.inner_ring = 0xFA0000BF; 81static void output_geometry(void *data, struct wl_output *output, int32_t x,
82 config->colors.invalid.outer_ring = 0x7D3300FF; 82 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
83 83 const char *make, const char *model, int32_t transform) {
84 config->radius = 50; 84 // Who cares
85 config->thickness = 10;
86
87 return config;
88} 85}
89 86
90void free_config(struct lock_config *config) { 87static void output_mode(void *data, struct wl_output *output, uint32_t flags,
91 free(config->font); 88 int32_t width, int32_t height, int32_t refresh) {
92 free(config); 89 // Who cares
93} 90}
94 91
95int function_conversation(int num_msg, const struct pam_message **msg, 92static void output_done(void *data, struct wl_output *output) {
96 struct pam_response **resp, void *appdata_ptr) { 93 // Who cares
97
98 const char* msg_style_names[] = {
99 NULL,
100 "PAM_PROMPT_ECHO_OFF",
101 "PAM_PROMPT_ECHO_ON",
102 "PAM_ERROR_MSG",
103 "PAM_TEXT_INFO",
104 };
105
106 /* PAM expects an array of responses, one for each message */
107 struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response));
108 *resp = pam_reply;
109
110 for(int i=0; i<num_msg; ++i) {
111 sway_log(L_DEBUG, "msg[%d]: (%s) %s", i,
112 msg_style_names[msg[i]->msg_style],
113 msg[i]->msg);
114
115 switch (msg[i]->msg_style) {
116 case PAM_PROMPT_ECHO_OFF:
117 case PAM_PROMPT_ECHO_ON:
118 pam_reply[i].resp = password;
119 break;
120
121 case PAM_ERROR_MSG:
122 case PAM_TEXT_INFO:
123 break;
124 }
125 }
126
127 return PAM_SUCCESS;
128} 94}
129 95
130/** 96static void output_scale(void *data, struct wl_output *output, int32_t factor) {
131 * Note: PAM will free() 'password' during the process 97 struct swaylock_surface *surface = data;
132 */ 98 surface->scale = factor;
133bool verify_password() { 99 if (surface->state->run_display) {
134 struct passwd *passwd = getpwuid(getuid()); 100 render_frames(surface->state);
135 char *username = passwd->pw_name;
136
137 const struct pam_conv local_conversation = { function_conversation, NULL };
138 pam_handle_t *local_auth_handle = NULL;
139 int pam_err;
140 if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
141 sway_abort("PAM returned %d\n", pam_err);
142 } 101 }
143 if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
144 return false;
145 }
146 if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
147 return false;
148 }
149 return true;
150} 102}
151 103
152void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { 104struct wl_output_listener output_listener = {
153 int redraw_screen = 0; 105 .geometry = output_geometry,
154 char *password_realloc; 106 .mode = output_mode,
155 int i; 107 .done = output_done,
156 108 .scale = output_scale,
157 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { 109};
158 switch (sym) { 110
159 case XKB_KEY_KP_Enter: 111static void handle_global(void *data, struct wl_registry *registry,
160 case XKB_KEY_Return: 112 uint32_t name, const char *interface, uint32_t version) {
161 render_data.auth_state = AUTH_STATE_VALIDATING; 113 struct swaylock_state *state = data;
162 114 if (strcmp(interface, wl_compositor_interface.name) == 0) {
163 render(&render_data, config); 115 state->compositor = wl_registry_bind(registry, name,
164 // Make sure our render call will actually be displayed on the screen 116 &wl_compositor_interface, 3);
165 wl_dispatch_events(); 117 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
166 118 state->shm = wl_registry_bind(registry, name,
167 if (verify_password()) { 119 &wl_shm_interface, 1);
168 exit(0); 120 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
169 } 121 struct wl_seat *seat = wl_registry_bind(
170 122 registry, name, &wl_seat_interface, 1);
171 render_data.auth_state = AUTH_STATE_INVALID; 123 wl_seat_add_listener(seat, &seat_listener, state);
172 redraw_screen = 1; 124 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
173 125 state->layer_shell = wl_registry_bind(
174 password_size = 1024; 126 registry, name, &zwlr_layer_shell_v1_interface, 1);
175 password = malloc(password_size); 127 } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) {
176 password[0] = '\0'; 128 state->input_inhibit_manager = wl_registry_bind(
177 break; 129 registry, name, &zwlr_input_inhibit_manager_v1_interface, 1);
178 case XKB_KEY_BackSpace: 130 } else if (strcmp(interface, wl_output_interface.name) == 0) {
179 i = strlen(password); 131 struct swaylock_surface *surface =
180 if (i > 0) { 132 calloc(1, sizeof(struct swaylock_surface));
181 password[i - 1] = '\0'; 133 surface->state = state;
182 render_data.auth_state = AUTH_STATE_BACKSPACE; 134 surface->output = wl_registry_bind(registry, name,
183 redraw_screen = 1; 135 &wl_output_interface, 3);
184 } 136 wl_output_add_listener(surface->output, &output_listener, surface);
185 break; 137 wl_list_insert(&state->surfaces, &surface->link);
186 case XKB_KEY_Control_L:
187 case XKB_KEY_Control_R:
188 case XKB_KEY_Shift_L:
189 case XKB_KEY_Shift_R:
190 case XKB_KEY_Caps_Lock:
191 case XKB_KEY_Shift_Lock:
192 case XKB_KEY_Meta_L:
193 case XKB_KEY_Meta_R:
194 case XKB_KEY_Alt_L:
195 case XKB_KEY_Alt_R:
196 case XKB_KEY_Super_L:
197 case XKB_KEY_Super_R:
198 case XKB_KEY_Hyper_L:
199 case XKB_KEY_Hyper_R:
200 break; // don't draw screen on modifier keys
201 case XKB_KEY_Escape:
202 case XKB_KEY_u:
203 case XKB_KEY_U:
204 // clear password buffer on ctrl-u (or escape for i3lock compatibility)
205 if (sym == XKB_KEY_Escape || xkb_state_mod_name_is_active(registry->input->xkb.state,
206 XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) {
207 render_data.auth_state = AUTH_STATE_BACKSPACE;
208 redraw_screen = 1;
209
210 password_size = 1024;
211 free(password);
212 password = malloc(password_size);
213 password[0] = '\0';
214 break;
215 }
216 /* fallthrough */
217 default:
218 render_data.auth_state = AUTH_STATE_INPUT;
219 redraw_screen = 1;
220 i = strlen(password);
221 if (i + 1 == password_size) {
222 password_size += 1024;
223 password_realloc = realloc(password, password_size);
224 // reset password if realloc fails.
225 if (password_realloc == NULL) {
226 password_size = 1024;
227 free(password);
228 password = malloc(password_size);
229 password[0] = '\0';
230 break;
231 } else {
232 password = password_realloc;
233 }
234 }
235 password[i] = (char)codepoint;
236 password[i + 1] = '\0';
237 break;
238 }
239 if (redraw_screen) {
240 render(&render_data, config);
241 wl_dispatch_events();
242 // Hide the indicator after a couple of seconds
243 alarm(5);
244 }
245 } 138 }
246} 139}
247 140
248void render_color(struct window *window, uint32_t color) { 141static void handle_global_remove(void *data, struct wl_registry *registry,
249 cairo_set_source_u32(window->cairo, color); 142 uint32_t name) {
250 cairo_paint(window->cairo); 143 // who cares
251}
252
253void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) {
254 double width = cairo_image_surface_get_width(image);
255 double height = cairo_image_surface_get_height(image);
256 int wwidth = window->width * window->scale;
257 int wheight = window->height * window->scale;
258
259 switch (scaling_mode) {
260 case SCALING_MODE_STRETCH:
261 cairo_scale(window->cairo,
262 (double) wwidth / width,
263 (double) wheight / height);
264 cairo_set_source_surface(window->cairo, image, 0, 0);
265 break;
266 case SCALING_MODE_FILL:
267 {
268 double window_ratio = (double) wwidth / wheight;
269 double bg_ratio = width / height;
270
271 if (window_ratio > bg_ratio) {
272 double scale = (double) wwidth / width;
273 cairo_scale(window->cairo, scale, scale);
274 cairo_set_source_surface(window->cairo, image,
275 0,
276 (double) wheight/2 / scale - height/2);
277 } else {
278 double scale = (double) wheight / height;
279 cairo_scale(window->cairo, scale, scale);
280 cairo_set_source_surface(window->cairo, image,
281 (double) wwidth/2 / scale - width/2,
282 0);
283 }
284 break;
285 }
286 case SCALING_MODE_FIT:
287 {
288 double window_ratio = (double) wwidth / wheight;
289 double bg_ratio = width / height;
290
291 if (window_ratio > bg_ratio) {
292 double scale = (double) wheight / height;
293 cairo_scale(window->cairo, scale, scale);
294 cairo_set_source_surface(window->cairo, image,
295 (double) wwidth/2 / scale - width/2,
296 0);
297 } else {
298 double scale = (double) wwidth / width;
299 cairo_scale(window->cairo, scale, scale);
300 cairo_set_source_surface(window->cairo, image,
301 0,
302 (double) wheight/2 / scale - height/2);
303 }
304 break;
305 }
306 case SCALING_MODE_CENTER:
307 cairo_set_source_surface(window->cairo, image,
308 (double) wwidth/2 - width/2,
309 (double) wheight/2 - height/2);
310 break;
311 case SCALING_MODE_TILE:
312 {
313 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
314 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
315 cairo_set_source(window->cairo, pattern);
316 break;
317 }
318 }
319
320 cairo_paint(window->cairo);
321} 144}
322 145
323cairo_surface_t *load_image(char *image_path) { 146static const struct wl_registry_listener registry_listener = {
324 cairo_surface_t *image = NULL; 147 .global = handle_global,
325 148 .global_remove = handle_global_remove,
326#ifdef WITH_GDK_PIXBUF 149};
327 GError *err = NULL;
328 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
329 if (!pixbuf) {
330 sway_abort("Failed to load background image: %s", err->message);
331 }
332 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
333 g_object_unref(pixbuf);
334#else
335 image = cairo_image_surface_create_from_png(image_path);
336#endif //WITH_GDK_PIXBUF
337 if (!image) {
338 sway_abort("Failed to read background image.");
339 }
340 150
341 return image; 151static struct swaylock_state state;
342}
343 152
344int main(int argc, char **argv) { 153int main(int argc, char **argv) {
345 const char *scaling_mode_str = "fit", *socket_path = NULL;
346 int i;
347 void *images = NULL;
348 config = init_config();
349
350 render_data.num_images = 0;
351 render_data.color_set = 0;
352 render_data.color = 0xFFFFFFFF;
353 render_data.auth_state = AUTH_STATE_IDLE;
354
355 init_log(L_INFO);
356 // Install SIGALARM handler (for hiding the typing indicator)
357 signal(SIGALRM, sigalarm_handler);
358
359 static struct option long_options[] = { 154 static struct option long_options[] = {
360 {"help", no_argument, NULL, 'h'}, 155 {"help", no_argument, NULL, 'h'},
361 {"color", required_argument, NULL, 'c'}, 156 {"color", required_argument, NULL, 'c'},
362 {"image", required_argument, NULL, 'i'}, 157 {"image", required_argument, NULL, 'i'},
363 {"scaling", required_argument, NULL, 0}, 158 {"scaling", required_argument, NULL, 's'},
364 {"tiling", no_argument, NULL, 't'}, 159 {"tiling", no_argument, NULL, 't'},
365 {"version", no_argument, NULL, 'v'}, 160 {"version", no_argument, NULL, 'v'},
366 {"socket", required_argument, NULL, 'p'}, 161 {"socket", required_argument, NULL, 'p'},
367 {"no-unlock-indicator", no_argument, NULL, 'u'}, 162 {"no-unlock-indicator", no_argument, NULL, 'u'},
368 {"daemonize", no_argument, NULL, 'f'}, 163 {"daemonize", no_argument, NULL, 'f'},
369 {"font", required_argument, NULL, 0},
370 {"line-uses-ring", no_argument, NULL, 'r'},
371 {"line-uses-inside", no_argument, NULL, 's'},
372 {"textcolor", required_argument, NULL, 0},
373 {"insidevercolor", required_argument, NULL, 0},
374 {"insidewrongcolor", required_argument, NULL, 0},
375 {"insidecolor", required_argument, NULL, 0},
376 {"ringvercolor", required_argument, NULL, 0},
377 {"ringwrongcolor", required_argument, NULL, 0},
378 {"ringcolor", required_argument, NULL, 0},
379 {"linecolor", required_argument, NULL, 0},
380 {"separatorcolor", required_argument, NULL, 0},
381 {"keyhlcolor", required_argument, NULL, 0},
382 {"bshlcolor", required_argument, NULL, 0},
383 {"indicator-radius", required_argument, NULL, 0},
384 {"indicator-thickness", required_argument, NULL, 0},
385 {0, 0, 0, 0} 164 {0, 0, 0, 0}
386 }; 165 };
387 166
@@ -390,415 +169,124 @@ int main(int argc, char **argv) {
390 "\n" 169 "\n"
391 " -h, --help Show help message and quit.\n" 170 " -h, --help Show help message and quit.\n"
392 " -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\n" 171 " -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\n"
393 " --scaling Scaling mode: stretch, fill, fit, center, tile.\n" 172 " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n"
394 " -t, --tiling Same as --scaling=tile.\n" 173 " -t, --tiling Same as --scaling=tile.\n"
395 " -v, --version Show the version number and quit.\n" 174 " -v, --version Show the version number and quit.\n"
396 " -i, --image [<output>:]<path> Display the given image.\n" 175 " -i, --image [<output>:]<path> Display the given image.\n"
397 " -u, --no-unlock-indicator Disable the unlock indicator.\n" 176 " -u, --no-unlock-indicator Disable the unlock indicator.\n"
398 " -f, --daemonize Detach from the controlling terminal.\n" 177 " -f, --daemonize Detach from the controlling terminal.\n"
399 " --socket <socket> Use the specified socket.\n" 178 " --socket <socket> Use the specified socket.\n";
400 " For more information see `man swaylock`\n";
401 179
402 180 struct swaylock_args args = {
403 registry = registry_poll(); 181 .mode = BACKGROUND_MODE_SOLID_COLOR,
182 .color = 0xFFFFFFFF,
183 .show_indicator = true,
184 };
185 cairo_surface_t *background_image = NULL;
186 state.args = args;
187 wlr_log_init(L_DEBUG, NULL);
404 188
405 int c; 189 int c;
406 while (1) { 190 while (1) {
407 int option_index = 0; 191 int option_index = 0;
408 c = getopt_long(argc, argv, "hc:i:srtvuf", long_options, &option_index); 192 c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index);
409 if (c == -1) { 193 if (c == -1) {
410 break; 194 break;
411 } 195 }
412 switch (c) { 196 switch (c) {
413 case 'c': 197 case 'c': {
414 { 198 state.args.color = parse_color(optarg);
415 render_data.color = parse_color(optarg); 199 state.args.mode = BACKGROUND_MODE_SOLID_COLOR;
416 render_data.color_set = 1;
417 break; 200 break;
418 } 201 }
419 case 'i': 202 case 'i':
420 { 203 // TODO: Multiple background images (bleh)
421 char *image_path = strchr(optarg, ':'); 204 background_image = load_background_image(optarg);
422 if (image_path == NULL) { 205 if (!background_image) {
423 if (render_data.num_images == 0) { 206 return 1;
424 // Provided image without output
425 render_data.image = load_image(optarg);
426 render_data.num_images = -1;
427 } else {
428 sway_log(L_ERROR, "output must be defined for all --images or no --images");
429 exit(EXIT_FAILURE);
430 }
431 } else {
432 // Provided image for all outputs
433 if (render_data.num_images == 0) {
434 images = calloc(registry->outputs->length, sizeof(char*) * 2);
435 } else if (render_data.num_images == -1) {
436 sway_log(L_ERROR, "output must be defined for all --images or no --images");
437 exit(EXIT_FAILURE);
438 }
439
440 image_path[0] = '\0';
441 ((char**) images)[render_data.num_images * 2] = optarg;
442 ((char**) images)[render_data.num_images++ * 2 + 1] = ++image_path;
443 } 207 }
208 state.args.mode = BACKGROUND_MODE_FILL;
444 break; 209 break;
445 } 210 case 's':
446 case 't': 211 state.args.mode = parse_background_mode(optarg);
447 scaling_mode_str = "tile"; 212 if (state.args.mode == BACKGROUND_MODE_INVALID) {
213 return 1;
214 }
448 break; 215 break;
449 case 'p': 216 case 't':
450 socket_path = optarg; 217 state.args.mode = BACKGROUND_MODE_TILE;
451 break; 218 break;
452 case 'v': 219 case 'v':
453 fprintf(stdout, "swaylock version " SWAY_VERSION "\n"); 220#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE
454 exit(EXIT_SUCCESS); 221 fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n",
455 break; 222 SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH);
223#else
224 fprintf(stdout, "version unknown\n");
225#endif
226 return 0;
456 case 'u': 227 case 'u':
457 show_indicator = false; 228 state.args.show_indicator = false;
458 break;
459 case 'f': {
460 pid_t t = fork();
461 if (t == -1) {
462 sway_log(L_ERROR, "daemon call failed");
463 exit(EXIT_FAILURE);
464 } else if (t > 0) {
465 exit(0);
466 }
467 break; 229 break;
468 } 230 case 'f':
469 case 'r': 231 daemonize();
470 if (line_source != LINE_SOURCE_DEFAULT) {
471 sway_log(L_ERROR, "line source options conflict");
472 exit(EXIT_FAILURE);
473 }
474 line_source = LINE_SOURCE_RING;
475 break;
476 case 's':
477 if (line_source != LINE_SOURCE_DEFAULT) {
478 sway_log(L_ERROR, "line source options conflict");
479 exit(EXIT_FAILURE);
480 }
481 line_source = LINE_SOURCE_INSIDE;
482 break;
483 case 0:
484 if (strcmp(long_options[option_index].name, "font") == 0) {
485 free(config->font);
486 config->font = strdup(optarg);
487 } else if (strcmp(long_options[option_index].name, "scaling") == 0) {
488 scaling_mode_str = optarg;
489 } else if (strcmp(long_options[option_index].name, "textcolor") == 0) {
490 config->colors.text = parse_color(optarg);
491 } else if (strcmp(long_options[option_index].name, "insidevercolor") == 0) {
492 config->colors.validating.inner_ring = parse_color(optarg);
493 } else if (strcmp(long_options[option_index].name, "insidewrongcolor") == 0) {
494 config->colors.invalid.inner_ring = parse_color(optarg);
495 } else if (strcmp(long_options[option_index].name, "insidecolor") == 0) {
496 config->colors.normal.inner_ring = parse_color(optarg);
497 } else if (strcmp(long_options[option_index].name, "ringvercolor") == 0) {
498 config->colors.validating.outer_ring = parse_color(optarg);
499 } else if (strcmp(long_options[option_index].name, "ringwrongcolor") == 0) {
500 config->colors.invalid.outer_ring = parse_color(optarg);
501 } else if (strcmp(long_options[option_index].name, "ringcolor") == 0) {
502 config->colors.normal.outer_ring = parse_color(optarg);
503 } else if (strcmp(long_options[option_index].name, "linecolor") == 0) {
504 config->colors.line = parse_color(optarg);
505 } else if (strcmp(long_options[option_index].name, "separatorcolor") == 0) {
506 config->colors.separator = parse_color(optarg);
507 } else if (strcmp(long_options[option_index].name, "keyhlcolor") == 0) {
508 config->colors.input_cursor = parse_color(optarg);
509 } else if (strcmp(long_options[option_index].name, "bshlcolor") == 0) {
510 config->colors.backspace_cursor = parse_color(optarg);
511 } else if (strcmp(long_options[option_index].name, "indicator-radius") == 0) {
512 config->radius = atoi(optarg);
513 } else if (strcmp(long_options[option_index].name, "indicator-thickness") == 0) {
514 config->thickness = atoi(optarg);
515 }
516 break; 232 break;
517 default: 233 default:
518 fprintf(stderr, "%s", usage); 234 fprintf(stderr, "%s", usage);
519 exit(EXIT_FAILURE); 235 return 1;
520 }
521 }
522
523 render_data.scaling_mode = SCALING_MODE_STRETCH;
524 if (strcmp(scaling_mode_str, "stretch") == 0) {
525 render_data.scaling_mode = SCALING_MODE_STRETCH;
526 } else if (strcmp(scaling_mode_str, "fill") == 0) {
527 render_data.scaling_mode = SCALING_MODE_FILL;
528 } else if (strcmp(scaling_mode_str, "fit") == 0) {
529 render_data.scaling_mode = SCALING_MODE_FIT;
530 } else if (strcmp(scaling_mode_str, "center") == 0) {
531 render_data.scaling_mode = SCALING_MODE_CENTER;
532 } else if (strcmp(scaling_mode_str, "tile") == 0) {
533 render_data.scaling_mode = SCALING_MODE_TILE;
534 } else {
535 sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
536 }
537
538 password_size = 1024;
539 password = malloc(password_size);
540 password[0] = '\0';
541 render_data.surfaces = create_list();
542 if (!socket_path) {
543 socket_path = get_socketpath();
544 if (!socket_path) {
545 sway_abort("Unable to retrieve socket path");
546 } 236 }
547 } 237 }
548 238
549 if (!registry) { 239 wl_list_init(&state.surfaces);
550 sway_abort("Unable to connect to wayland compositor"); 240 state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
551 } 241 state.display = wl_display_connect(NULL);
552 242 assert(state.display);
553 if (!registry->swaylock) { 243
554 sway_abort("swaylock requires the compositor to support the swaylock extension."); 244 struct wl_registry *registry = wl_display_get_registry(state.display);
555 } 245 wl_registry_add_listener(registry, &registry_listener, &state);
556 246 wl_display_roundtrip(state.display);
557 if (registry->pointer) { 247 assert(state.compositor && state.layer_shell && state.shm);
558 // We don't want swaylock to have a pointer 248 if (!state.input_inhibit_manager) {
559 wl_pointer_destroy(registry->pointer); 249 wlr_log(L_ERROR, "Compositor does not support the input inhibitor "
560 registry->pointer = NULL; 250 "protocol, refusing to run insecurely");
561 } 251 }
562 252
563 for (i = 0; i < registry->outputs->length; ++i) { 253 if (wl_list_empty(&state.surfaces)) {
564 struct output_state *output = registry->outputs->items[i]; 254 wlr_log(L_DEBUG, "Exiting - no outputs to show on.");
565 struct window *window = window_setup(registry, 255 return 0;
566 output->width, output->height, output->scale, true);
567 if (!window) {
568 sway_abort("Failed to create surfaces.");
569 }
570 list_add(render_data.surfaces, window);
571 }
572
573 registry->input->notify = notify_key;
574
575 // Different background for the output
576 if (render_data.num_images >= 1) {
577 char **displays_paths = images;
578 render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*));
579
580 int socketfd = ipc_open_socket(socket_path);
581 uint32_t len = 0;
582 char *outputs = ipc_single_command(socketfd, IPC_GET_OUTPUTS, "", &len);
583 struct json_object *json_outputs = json_tokener_parse(outputs);
584
585 for (i = 0; i < registry->outputs->length; ++i) {
586 if (displays_paths[i * 2] != NULL) {
587 for (int j = 0;; ++j) {
588 if (j >= json_object_array_length(json_outputs)) {
589 sway_log(L_ERROR, "%s is not an extant output", displays_paths[i * 2]);
590 exit(EXIT_FAILURE);
591 }
592
593 struct json_object *dsp_name, *at_j = json_object_array_get_idx(json_outputs, j);
594 if (!json_object_object_get_ex(at_j, "name", &dsp_name)) {
595 sway_abort("output doesn't have a name field");
596 }
597 if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) {
598 render_data.images[j] = load_image(displays_paths[i * 2 + 1]);
599 break;
600 }
601 }
602 }
603 }
604
605 json_object_put(json_outputs);
606 close(socketfd);
607 free(displays_paths);
608 } 256 }
609 257
610 render(&render_data, config); 258 struct swaylock_surface *surface;
611 bool locked = false; 259 wl_list_for_each(surface, &state.surfaces, link) {
612 while (wl_display_dispatch(registry->display) != -1) { 260 surface->image = background_image;
613 if (!locked) { 261
614 for (i = 0; i < registry->outputs->length; ++i) { 262 surface->surface = wl_compositor_create_surface(state.compositor);
615 struct output_state *output = registry->outputs->items[i]; 263 assert(surface->surface);
616 struct window *window = render_data.surfaces->items[i]; 264
617 lock_set_lock_surface(registry->swaylock, output->output, window->surface); 265 surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
618 } 266 state.layer_shell, surface->surface, surface->output,
619 locked = true; 267 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen");
620 } 268 assert(surface->layer_surface);
269
270 zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0);
271 zwlr_layer_surface_v1_set_anchor(surface->layer_surface,
272 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
273 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
274 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
275 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
276 zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1);
277 zwlr_layer_surface_v1_set_keyboard_interactivity(
278 surface->layer_surface, true);
279 zwlr_layer_surface_v1_add_listener(surface->layer_surface,
280 &layer_surface_listener, surface);
281 wl_surface_commit(surface->surface);
282 wl_display_roundtrip(state.display);
621 } 283 }
622 284
623 // Free surfaces 285 zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
624 if (render_data.num_images == -1) {
625 cairo_surface_destroy(render_data.image);
626 } else if (render_data.num_images >= 1) {
627 for (i = 0; i < registry->outputs->length; ++i) {
628 if (render_data.images[i] != NULL) {
629 cairo_surface_destroy(render_data.images[i]);
630 }
631 }
632 free(render_data.images);
633 }
634 286
635 for (i = 0; i < render_data.surfaces->length; ++i) { 287 state.run_display = true;
636 struct window *window = render_data.surfaces->items[i]; 288 while (wl_display_dispatch(state.display) != -1 && state.run_display) {
637 window_teardown(window); 289 // This space intentionally left blank
638 } 290 }
639 list_free(render_data.surfaces);
640 registry_teardown(registry);
641
642 free_config(config);
643
644 return 0; 291 return 0;
645} 292}
646
647void render(struct render_data *render_data, struct lock_config *config) {
648 int i;
649 for (i = 0; i < render_data->surfaces->length; ++i) {
650 sway_log(L_DEBUG, "Render surface %d of %d", i, render_data->surfaces->length);
651 struct window *window = render_data->surfaces->items[i];
652 if (!window_prerender(window) || !window->cairo) {
653 continue;
654 }
655 int wwidth = window->width * window->scale;
656 int wheight = window->height * window->scale;
657
658 cairo_save(window->cairo);
659 cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR);
660 cairo_paint(window->cairo);
661 cairo_restore(window->cairo);
662
663 // Reset the transformation matrix
664 cairo_identity_matrix(window->cairo);
665
666 if (render_data->num_images == 0 || render_data->color_set) {
667 render_color(window, render_data->color);
668 }
669
670 if (render_data->num_images == -1) {
671 // One background for all
672 render_image(window, render_data->image, render_data->scaling_mode);
673 } else if (render_data->num_images >= 1) {
674 // Different backgrounds
675 if (render_data->images[i] != NULL) {
676 render_image(window, render_data->images[i], render_data->scaling_mode);
677 }
678 }
679
680 // Reset the transformation matrix again
681 cairo_identity_matrix(window->cairo);
682
683 // Draw specific values (copied from i3)
684 const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
685 const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
686
687 // Add visual indicator
688 if (show_indicator && render_data->auth_state != AUTH_STATE_IDLE) {
689 // Draw circle
690 cairo_set_line_width(window->cairo, config->thickness);
691 cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, 0, 2 * M_PI);
692 switch (render_data->auth_state) {
693 case AUTH_STATE_INPUT:
694 case AUTH_STATE_BACKSPACE: {
695 cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring);
696 cairo_fill_preserve(window->cairo);
697 cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring);
698 cairo_stroke(window->cairo);
699 } break;
700 case AUTH_STATE_VALIDATING: {
701 cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring);
702 cairo_fill_preserve(window->cairo);
703 cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring);
704 cairo_stroke(window->cairo);
705 } break;
706 case AUTH_STATE_INVALID: {
707 cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring);
708 cairo_fill_preserve(window->cairo);
709 cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring);
710 cairo_stroke(window->cairo);
711 } break;
712 default: break;
713 }
714
715 // Draw a message
716 char *text = NULL;
717 cairo_set_source_u32(window->cairo, config->colors.text);
718 cairo_select_font_face(window->cairo, config->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
719 cairo_set_font_size(window->cairo, config->radius/3.0f);
720 switch (render_data->auth_state) {
721 case AUTH_STATE_VALIDATING:
722 text = "verifying";
723 break;
724 case AUTH_STATE_INVALID:
725 text = "wrong";
726 break;
727 default: break;
728 }
729
730 if (text) {
731 cairo_text_extents_t extents;
732 double x, y;
733
734 cairo_text_extents(window->cairo, text, &extents);
735 x = wwidth/2 - ((extents.width/2) + extents.x_bearing);
736 y = wheight/2 - ((extents.height/2) + extents.y_bearing);
737
738 cairo_move_to(window->cairo, x, y);
739 cairo_show_text(window->cairo, text);
740 cairo_close_path(window->cairo);
741 cairo_new_sub_path(window->cairo);
742 }
743
744 // Typing indicator: Highlight random part on keypress
745 if (render_data->auth_state == AUTH_STATE_INPUT || render_data->auth_state == AUTH_STATE_BACKSPACE) {
746 static double highlight_start = 0;
747 highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5;
748 cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE);
749 if (render_data->auth_state == AUTH_STATE_INPUT) {
750 cairo_set_source_u32(window->cairo, config->colors.input_cursor);
751 } else {
752 cairo_set_source_u32(window->cairo, config->colors.backspace_cursor);
753 }
754 cairo_stroke(window->cairo);
755
756 // Draw borders
757 cairo_set_source_u32(window->cairo, config->colors.separator);
758 cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS);
759 cairo_stroke(window->cairo);
760
761 cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start + TYPE_INDICATOR_RANGE, (highlight_start + TYPE_INDICATOR_RANGE) + TYPE_INDICATOR_BORDER_THICKNESS);
762 cairo_stroke(window->cairo);
763 }
764
765 switch(line_source) {
766 case LINE_SOURCE_RING:
767 switch(render_data->auth_state) {
768 case AUTH_STATE_VALIDATING:
769 cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring);
770 break;
771 case AUTH_STATE_INVALID:
772 cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring);
773 break;
774 default:
775 cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring);
776 }
777 break;
778 case LINE_SOURCE_INSIDE:
779 switch(render_data->auth_state) {
780 case AUTH_STATE_VALIDATING:
781 cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring);
782 break;
783 case AUTH_STATE_INVALID:
784 cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring);
785 break;
786 default:
787 cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring);
788 break;
789 }
790 break;
791 default:
792 cairo_set_source_u32(window->cairo, config->colors.line);
793 break;
794 }
795 // Draw inner + outer border of the circle
796 cairo_set_line_width(window->cairo, 2.0);
797 cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius - config->thickness/2, 0, 2*M_PI);
798 cairo_stroke(window->cairo);
799 cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius + config->thickness/2, 0, 2*M_PI);
800 cairo_stroke(window->cairo);
801 }
802 window_render(window);
803 }
804}
diff --git a/swaylock/meson.build b/swaylock/meson.build
new file mode 100644
index 00000000..3cde47a4
--- /dev/null
+++ b/swaylock/meson.build
@@ -0,0 +1,23 @@
1executable(
2 'swaylock', [
3 'main.c',
4 'password.c',
5 'render.c',
6 'seat.c'
7 ],
8 include_directories: [sway_inc],
9 dependencies: [
10 cairo,
11 client_protos,
12 gdk_pixbuf,
13 libpam,
14 math,
15 pango,
16 pangocairo,
17 xkbcommon,
18 wayland_client,
19 wlroots,
20 ],
21 link_with: [lib_sway_common, lib_sway_client],
22 install: true
23)
diff --git a/swaylock/password.c b/swaylock/password.c
new file mode 100644
index 00000000..1839f991
--- /dev/null
+++ b/swaylock/password.c
@@ -0,0 +1,126 @@
1#include <assert.h>
2#include <pwd.h>
3#include <security/pam_appl.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <wlr/util/log.h>
7#include <xkbcommon/xkbcommon.h>
8#include "swaylock/swaylock.h"
9#include "swaylock/seat.h"
10#include "unicode.h"
11
12static int function_conversation(int num_msg, const struct pam_message **msg,
13 struct pam_response **resp, void *data) {
14 struct swaylock_password *pw = data;
15 /* PAM expects an array of responses, one for each message */
16 struct pam_response *pam_reply = calloc(
17 num_msg, sizeof(struct pam_response));
18 *resp = pam_reply;
19 for (int i = 0; i < num_msg; ++i) {
20 switch (msg[i]->msg_style) {
21 case PAM_PROMPT_ECHO_OFF:
22 case PAM_PROMPT_ECHO_ON:
23 pam_reply[i].resp = pw->buffer;
24 break;
25 case PAM_ERROR_MSG:
26 case PAM_TEXT_INFO:
27 break;
28 }
29 }
30 return PAM_SUCCESS;
31}
32
33static bool attempt_password(struct swaylock_password *pw) {
34 struct passwd *passwd = getpwuid(getuid());
35 char *username = passwd->pw_name;
36 const struct pam_conv local_conversation = {
37 function_conversation, pw
38 };
39 pam_handle_t *local_auth_handle = NULL;
40 int pam_err;
41 if ((pam_err = pam_start("swaylock", username,
42 &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
43 wlr_log(L_ERROR, "PAM returned error %d", pam_err);
44 }
45 if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
46 wlr_log(L_ERROR, "pam_authenticate failed");
47 goto fail;
48 }
49 if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
50 wlr_log(L_ERROR, "pam_end failed");
51 goto fail;
52 }
53 // PAM frees this
54 pw->buffer = NULL;
55 pw->len = pw->size = 0;
56 return true;
57fail:
58 // PAM frees this
59 pw->buffer = NULL;
60 pw->len = pw->size = 0;
61 return false;
62}
63
64static bool backspace(struct swaylock_password *pw) {
65 if (pw->len != 0) {
66 pw->buffer[--pw->len] = 0;
67 return true;
68 }
69 return false;
70}
71
72static void append_ch(struct swaylock_password *pw, uint32_t codepoint) {
73 if (!pw->buffer) {
74 pw->size = 8;
75 if (!(pw->buffer = malloc(pw->size))) {
76 // TODO: Display error
77 return;
78 }
79 pw->buffer[0] = 0;
80 }
81 size_t utf8_size = utf8_chsize(codepoint);
82 if (pw->len + utf8_size + 1 >= pw->size) {
83 size_t size = pw->size * 2;
84 char *buffer = realloc(pw->buffer, size);
85 if (!buffer) {
86 // TODO: Display error
87 return;
88 }
89 pw->size = size;
90 pw->buffer = buffer;
91 }
92 utf8_encode(&pw->buffer[pw->len], codepoint);
93 pw->buffer[pw->len + utf8_size] = 0;
94 pw->len += utf8_size;
95}
96
97void swaylock_handle_key(struct swaylock_state *state,
98 xkb_keysym_t keysym, uint32_t codepoint) {
99 switch (keysym) {
100 case XKB_KEY_KP_Enter: /* fallthrough */
101 case XKB_KEY_Return:
102 state->auth_state = AUTH_STATE_VALIDATING;
103 render_frames(state);
104 wl_display_roundtrip(state->display);
105 if (attempt_password(&state->password)) {
106 state->run_display = false;
107 break;
108 }
109 state->auth_state = AUTH_STATE_INVALID;
110 render_frames(state);
111 break;
112 case XKB_KEY_BackSpace:
113 if (backspace(&state->password)) {
114 state->auth_state = AUTH_STATE_BACKSPACE;
115 render_frames(state);
116 }
117 break;
118 default:
119 if (codepoint) {
120 append_ch(&state->password, codepoint);
121 state->auth_state = AUTH_STATE_INPUT;
122 render_frames(state);
123 }
124 break;
125 }
126}
diff --git a/swaylock/render.c b/swaylock/render.c
new file mode 100644
index 00000000..cd387be5
--- /dev/null
+++ b/swaylock/render.c
@@ -0,0 +1,150 @@
1#define _POSIX_C_SOURCE 199506L
2#include <math.h>
3#include <stdlib.h>
4#include <wayland-client.h>
5#include "cairo.h"
6#include "background-image.h"
7#include "swaylock/swaylock.h"
8
9#define M_PI 3.14159265358979323846
10const int ARC_RADIUS = 50;
11const int ARC_THICKNESS = 10;
12const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
13const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
14
15void render_frame(struct swaylock_surface *surface) {
16 struct swaylock_state *state = surface->state;
17
18 int buffer_width = surface->width * surface->scale;
19 int buffer_height = surface->height * surface->scale;
20
21 surface->current_buffer = get_next_buffer(state->shm,
22 surface->buffers, buffer_width, buffer_height);
23 cairo_t *cairo = surface->current_buffer->cairo;
24 cairo_identity_matrix(cairo);
25
26 if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) {
27 cairo_set_source_u32(cairo, state->args.color);
28 cairo_paint(cairo);
29 } else {
30 render_background_image(cairo, surface->image,
31 state->args.mode, buffer_width, buffer_height);
32 }
33 cairo_identity_matrix(cairo);
34
35 int arc_radius = ARC_RADIUS * surface->scale;
36 int arc_thickness = ARC_THICKNESS * surface->scale;
37 float type_indicator_border_thickness =
38 TYPE_INDICATOR_BORDER_THICKNESS * surface->scale;
39
40 if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) {
41 // Draw circle
42 cairo_set_line_width(cairo, arc_thickness);
43 cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI);
44 switch (state->auth_state) {
45 case AUTH_STATE_INPUT:
46 case AUTH_STATE_BACKSPACE: {
47 cairo_set_source_rgba(cairo, 0, 0, 0, 0.75);
48 cairo_fill_preserve(cairo);
49 cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0);
50 cairo_stroke(cairo);
51 } break;
52 case AUTH_STATE_VALIDATING: {
53 cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75);
54 cairo_fill_preserve(cairo);
55 cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255);
56 cairo_stroke(cairo);
57 } break;
58 case AUTH_STATE_INVALID: {
59 cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75);
60 cairo_fill_preserve(cairo);
61 cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0);
62 cairo_stroke(cairo);
63 } break;
64 default: break;
65 }
66
67 // Draw a message
68 char *text = NULL;
69 cairo_set_source_rgb(cairo, 0, 0, 0);
70 cairo_select_font_face(cairo, "sans-serif",
71 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
72 cairo_set_font_size(cairo, arc_radius / 3.0f);
73 switch (state->auth_state) {
74 case AUTH_STATE_VALIDATING:
75 text = "verifying";
76 break;
77 case AUTH_STATE_INVALID:
78 text = "wrong";
79 break;
80 default: break;
81 }
82
83 if (text) {
84 cairo_text_extents_t extents;
85 double x, y;
86 cairo_text_extents(cairo, text, &extents);
87 x = (buffer_width / 2) -
88 (extents.width / 2 + extents.x_bearing);
89 y = (buffer_height / 2) -
90 (extents.height / 2 + extents.y_bearing);
91
92 cairo_move_to(cairo, x, y);
93 cairo_show_text(cairo, text);
94 cairo_close_path(cairo);
95 cairo_new_sub_path(cairo);
96 }
97
98 // Typing indicator: Highlight random part on keypress
99 if (state->auth_state == AUTH_STATE_INPUT
100 || state->auth_state == AUTH_STATE_BACKSPACE) {
101 static double highlight_start = 0;
102 highlight_start +=
103 (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5;
104 cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
105 arc_radius, highlight_start,
106 highlight_start + TYPE_INDICATOR_RANGE);
107 if (state->auth_state == AUTH_STATE_INPUT) {
108 cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0);
109 } else {
110 cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0);
111 }
112 cairo_stroke(cairo);
113
114 // Draw borders
115 cairo_set_source_rgb(cairo, 0, 0, 0);
116 cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
117 arc_radius, highlight_start,
118 highlight_start + type_indicator_border_thickness);
119 cairo_stroke(cairo);
120
121 cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
122 arc_radius, highlight_start + TYPE_INDICATOR_RANGE,
123 highlight_start + TYPE_INDICATOR_RANGE +
124 type_indicator_border_thickness);
125 cairo_stroke(cairo);
126 }
127
128 // Draw inner + outer border of the circle
129 cairo_set_source_rgb(cairo, 0, 0, 0);
130 cairo_set_line_width(cairo, 2.0 * surface->scale);
131 cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
132 arc_radius - arc_thickness / 2, 0, 2 * M_PI);
133 cairo_stroke(cairo);
134 cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
135 arc_radius + arc_thickness / 2, 0, 2 * M_PI);
136 cairo_stroke(cairo);
137 }
138
139 wl_surface_set_buffer_scale(surface->surface, surface->scale);
140 wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0);
141 wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height);
142 wl_surface_commit(surface->surface);
143}
144
145void render_frames(struct swaylock_state *state) {
146 struct swaylock_surface *surface;
147 wl_list_for_each(surface, &state->surfaces, link) {
148 render_frame(surface);
149 }
150}
diff --git a/swaylock/seat.c b/swaylock/seat.c
new file mode 100644
index 00000000..21db7c4f
--- /dev/null
+++ b/swaylock/seat.c
@@ -0,0 +1,190 @@
1#include <assert.h>
2#include <stdlib.h>
3#include <sys/mman.h>
4#include <unistd.h>
5#include <wlr/util/log.h>
6#include <xkbcommon/xkbcommon.h>
7#include "swaylock/swaylock.h"
8#include "swaylock/seat.h"
9
10const char *XKB_MASK_NAMES[MASK_LAST] = {
11 XKB_MOD_NAME_SHIFT,
12 XKB_MOD_NAME_CAPS,
13 XKB_MOD_NAME_CTRL,
14 XKB_MOD_NAME_ALT,
15 "Mod2",
16 "Mod3",
17 XKB_MOD_NAME_LOGO,
18 "Mod5",
19};
20
21const enum mod_bit XKB_MODS[MASK_LAST] = {
22 MOD_SHIFT,
23 MOD_CAPS,
24 MOD_CTRL,
25 MOD_ALT,
26 MOD_MOD2,
27 MOD_MOD3,
28 MOD_LOGO,
29 MOD_MOD5
30};
31
32static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
33 uint32_t format, int32_t fd, uint32_t size) {
34 struct swaylock_state *state = data;
35 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
36 close(fd);
37 wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format);
38 exit(1);
39 }
40 char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
41 if (map_shm == MAP_FAILED) {
42 close(fd);
43 wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting");
44 exit(1);
45 }
46 struct xkb_keymap *keymap = xkb_keymap_new_from_string(
47 state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
48 munmap(map_shm, size);
49 close(fd);
50 assert(keymap);
51 struct xkb_state *xkb_state = xkb_state_new(keymap);
52 assert(xkb_state);
53 xkb_keymap_unref(state->xkb.keymap);
54 xkb_state_unref(state->xkb.state);
55 state->xkb.keymap = keymap;
56 state->xkb.state = xkb_state;
57}
58
59static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
60 uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
61 // Who cares
62}
63
64static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
65 uint32_t serial, struct wl_surface *surface) {
66 // Who cares
67}
68
69static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
70 uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) {
71 struct swaylock_state *state = data;
72 enum wl_keyboard_key_state key_state = _key_state;
73 xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8);
74 uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ?
75 key + 8 : 0;
76 uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode);
77 if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) {
78 swaylock_handle_key(state, sym, codepoint);
79 }
80}
81
82static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
83 uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
84 uint32_t mods_locked, uint32_t group) {
85 struct swaylock_state *state = data;
86 xkb_state_update_mask(state->xkb.state,
87 mods_depressed, mods_latched, mods_locked, 0, 0, group);
88 xkb_mod_mask_t mask = xkb_state_serialize_mods(state->xkb.state,
89 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
90 state->xkb.modifiers = 0;
91 for (uint32_t i = 0; i < MASK_LAST; ++i) {
92 if (mask & state->xkb.masks[i]) {
93 state->xkb.modifiers |= XKB_MODS[i];
94 }
95 }
96}
97
98static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
99 int32_t rate, int32_t delay) {
100 // TODO
101}
102
103static const struct wl_keyboard_listener keyboard_listener = {
104 .keymap = keyboard_keymap,
105 .enter = keyboard_enter,
106 .leave = keyboard_leave,
107 .key = keyboard_key,
108 .modifiers = keyboard_modifiers,
109 .repeat_info = keyboard_repeat_info,
110};
111
112static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
113 uint32_t serial, struct wl_surface *surface,
114 wl_fixed_t surface_x, wl_fixed_t surface_y) {
115 wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0);
116}
117
118static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
119 uint32_t serial, struct wl_surface *surface) {
120 // Who cares
121}
122
123static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
124 uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
125 // Who cares
126}
127
128static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
129 uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
130 // Who cares
131}
132
133static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
134 uint32_t time, uint32_t axis, wl_fixed_t value) {
135 // Who cares
136}
137
138static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
139 // Who cares
140}
141
142static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
143 uint32_t axis_source) {
144 // Who cares
145}
146
147static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
148 uint32_t time, uint32_t axis) {
149 // Who cares
150}
151
152static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
153 uint32_t axis, int32_t discrete) {
154 // Who cares
155}
156
157static const struct wl_pointer_listener pointer_listener = {
158 .enter = wl_pointer_enter,
159 .leave = wl_pointer_leave,
160 .motion = wl_pointer_motion,
161 .button = wl_pointer_button,
162 .axis = wl_pointer_axis,
163 .frame = wl_pointer_frame,
164 .axis_source = wl_pointer_axis_source,
165 .axis_stop = wl_pointer_axis_stop,
166 .axis_discrete = wl_pointer_axis_discrete,
167};
168
169static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
170 enum wl_seat_capability caps) {
171 struct swaylock_state *state = data;
172 if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
173 struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat);
174 wl_pointer_add_listener(pointer, &pointer_listener, NULL);
175 }
176 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
177 struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat);
178 wl_keyboard_add_listener(keyboard, &keyboard_listener, state);
179 }
180}
181
182static void seat_handle_name(void *data, struct wl_seat *wl_seat,
183 const char *name) {
184 // Who cares
185}
186
187const struct wl_seat_listener seat_listener = {
188 .capabilities = seat_handle_capabilities,
189 .name = seat_handle_name,
190};
diff --git a/swaymsg/CMakeLists.txt b/swaymsg/CMakeLists.txt
deleted file mode 100644
index b428a409..00000000
--- a/swaymsg/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
1add_executable(swaymsg
2 main.c
3)
4
5include_directories(
6 ${JSONC_INCLUDE_DIRS}
7)
8
9target_link_libraries(swaymsg
10 sway-common
11 ${JSONC_LIBRARIES}
12)
13
14install(
15 TARGETS swaymsg
16 RUNTIME
17 DESTINATION bin
18 COMPONENT runtime
19)
20
21if (A2X_FOUND)
22 add_manpage(swaymsg 1)
23endif()
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 2f9cfb14..c9be3a86 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -61,83 +61,112 @@ static void pretty_print_workspace(json_object *w) {
61 ); 61 );
62} 62}
63 63
64static void pretty_print_input(json_object *i) { 64static const char *pretty_type_name(const char *name) {
65 json_object *id, *name, *size, *caps; 65 // TODO these constants probably belong in the common lib
66 json_object_object_get_ex(i, "identifier", &id);
67 json_object_object_get_ex(i, "name", &name);
68 json_object_object_get_ex(i, "size", &size);
69 json_object_object_get_ex(i, "capabilities", &caps);
70
71 printf( "Input device %s\n Type: ", json_object_get_string(name));
72
73 struct { 66 struct {
74 const char *a; 67 const char *a;
75 const char *b; 68 const char *b;
76 } cap_names[] = { 69 } type_names[] = {
77 { "keyboard", "Keyboard" }, 70 { "keyboard", "Keyboard" },
78 { "pointer", "Mouse" }, 71 { "pointer", "Mouse" },
79 { "touch", "Touch" },
80 { "tablet_tool", "Tablet tool" },
81 { "tablet_pad", "Tablet pad" }, 72 { "tablet_pad", "Tablet pad" },
82 { "gesture", "Gesture" }, 73 { "tablet_tool", "Tablet tool" },
83 { "switch", "Switch" }, 74 { "touch", "Touch" },
84 }; 75 };
85 76
86 size_t len = json_object_array_length(caps); 77 for (size_t i = 0; i < sizeof(type_names) / sizeof(type_names[0]); ++i) {
87 if (len == 0) { 78 if (strcmp(type_names[i].a, name) == 0) {
88 printf("Unknown"); 79 return type_names[i].b;
89 }
90
91 json_object *cap;
92 for (size_t i = 0; i < len; ++i) {
93 cap = json_object_array_get_idx(caps, i);
94 const char *cap_s = json_object_get_string(cap);
95 const char *_name = NULL;
96 for (size_t j = 0; j < sizeof(cap_names) / sizeof(cap_names[0]); ++i) {
97 if (strcmp(cap_names[i].a, cap_s) == 0) {
98 _name = cap_names[i].b;
99 break;
100 }
101 } 80 }
102 printf("%s%s", _name ? _name : cap_s, len > 1 && i != len - 1 ? ", " : "");
103 } 81 }
104 printf("\n Sway ID: %s\n", json_object_get_string(id)); 82
105 if (size) { 83 return name;
106 json_object *width, *height; 84}
107 json_object_object_get_ex(size, "width", &width); 85
108 json_object_object_get_ex(size, "height", &height); 86static void pretty_print_input(json_object *i) {
109 printf(" Size: %lfmm x %lfmm\n", 87 json_object *id, *name, *type, *product, *vendor;
110 json_object_get_double(width), json_object_get_double(height)); 88 json_object_object_get_ex(i, "identifier", &id);
111 } 89 json_object_object_get_ex(i, "name", &name);
112 printf("\n"); 90 json_object_object_get_ex(i, "type", &type);
91 json_object_object_get_ex(i, "product", &product);
92 json_object_object_get_ex(i, "vendor", &vendor);
93
94 const char *fmt =
95 "Input device: %s\n"
96 " Type: %s\n"
97 " Identifier: %s\n"
98 " Product ID: %d\n"
99 " Vendor ID: %d\n\n";
100
101
102 printf(fmt, json_object_get_string(name),
103 pretty_type_name(json_object_get_string(type)),
104 json_object_get_string(id),
105 json_object_get_int(product),
106 json_object_get_int(vendor));
113} 107}
114 108
115static void pretty_print_output(json_object *o) { 109static void pretty_print_output(json_object *o) {
116 json_object *name, *rect, *focused, *active, *ws, *scale; 110 json_object *name, *rect, *focused, *active, *ws;
117 json_object_object_get_ex(o, "name", &name); 111 json_object_object_get_ex(o, "name", &name);
118 json_object_object_get_ex(o, "rect", &rect); 112 json_object_object_get_ex(o, "rect", &rect);
119 json_object_object_get_ex(o, "focused", &focused); 113 json_object_object_get_ex(o, "focused", &focused);
120 json_object_object_get_ex(o, "active", &active); 114 json_object_object_get_ex(o, "active", &active);
121 json_object_object_get_ex(o, "current_workspace", &ws); 115 json_object_object_get_ex(o, "current_workspace", &ws);
116 json_object *make, *model, *serial, *scale, *refresh, *transform;
117 json_object_object_get_ex(o, "make", &make);
118 json_object_object_get_ex(o, "model", &model);
119 json_object_object_get_ex(o, "serial", &serial);
122 json_object_object_get_ex(o, "scale", &scale); 120 json_object_object_get_ex(o, "scale", &scale);
121 json_object_object_get_ex(o, "refresh", &refresh);
122 json_object_object_get_ex(o, "transform", &transform);
123 json_object *x, *y, *width, *height; 123 json_object *x, *y, *width, *height;
124 json_object_object_get_ex(rect, "x", &x); 124 json_object_object_get_ex(rect, "x", &x);
125 json_object_object_get_ex(rect, "y", &y); 125 json_object_object_get_ex(rect, "y", &y);
126 json_object_object_get_ex(rect, "width", &width); 126 json_object_object_get_ex(rect, "width", &width);
127 json_object_object_get_ex(rect, "height", &height); 127 json_object_object_get_ex(rect, "height", &height);
128 json_object *modes;
129 json_object_object_get_ex(o, "modes", &modes);
130
128 printf( 131 printf(
129 "Output %s%s%s\n" 132 "Output %s '%s %s %s'%s%s\n"
130 " Geometry: %dx%d @ %d,%d\n" 133 " Current mode: %dx%d @ %f Hz\n"
134 " Position: %d,%d\n"
131 " Scale factor: %dx\n" 135 " Scale factor: %dx\n"
132 " Workspace: %s\n\n", 136 " Transform: %s\n"
137 " Workspace: %s\n",
133 json_object_get_string(name), 138 json_object_get_string(name),
139 json_object_get_string(make),
140 json_object_get_string(model),
141 json_object_get_string(serial),
134 json_object_get_boolean(focused) ? " (focused)" : "", 142 json_object_get_boolean(focused) ? " (focused)" : "",
135 !json_object_get_boolean(active) ? " (inactive)" : "", 143 !json_object_get_boolean(active) ? " (inactive)" : "",
136 json_object_get_int(width), json_object_get_int(height), 144 json_object_get_int(width), json_object_get_int(height),
145 (float)json_object_get_int(refresh) / 1000,
137 json_object_get_int(x), json_object_get_int(y), 146 json_object_get_int(x), json_object_get_int(y),
138 json_object_get_int(scale), 147 json_object_get_int(scale),
148 json_object_get_string(transform),
139 json_object_get_string(ws) 149 json_object_get_string(ws)
140 ); 150 );
151
152 size_t modes_len = json_object_array_length(modes);
153 if (modes_len > 0) {
154 printf(" Available modes:\n");
155 for (size_t i = 0; i < modes_len; ++i) {
156 json_object *mode = json_object_array_get_idx(modes, i);
157
158 json_object *mode_width, *mode_height, *mode_refresh;
159 json_object_object_get_ex(mode, "width", &mode_width);
160 json_object_object_get_ex(mode, "height", &mode_height);
161 json_object_object_get_ex(mode, "refresh", &mode_refresh);
162
163 printf(" %dx%d @ %f Hz\n", json_object_get_int(mode_width),
164 json_object_get_int(mode_height),
165 (float)json_object_get_int(mode_refresh) / 1000);
166 }
167 }
168
169 printf("\n");
141} 170}
142 171
143static void pretty_print_version(json_object *v) { 172static void pretty_print_version(json_object *v) {
@@ -149,7 +178,7 @@ static void pretty_print_version(json_object *v) {
149static void pretty_print_clipboard(json_object *v) { 178static void pretty_print_clipboard(json_object *v) {
150 if (success(v, true)) { 179 if (success(v, true)) {
151 if (json_object_is_type(v, json_type_array)) { 180 if (json_object_is_type(v, json_type_array)) {
152 for (int i = 0; i < json_object_array_length(v); ++i) { 181 for (size_t i = 0; i < json_object_array_length(v); ++i) {
153 json_object *o = json_object_array_get_idx(v, i); 182 json_object *o = json_object_array_get_idx(v, i);
154 printf("%s\n", json_object_get_string(o)); 183 printf("%s\n", json_object_get_string(o));
155 } 184 }
@@ -225,7 +254,7 @@ int main(int argc, char **argv) {
225 char *socket_path = NULL; 254 char *socket_path = NULL;
226 char *cmdtype = NULL; 255 char *cmdtype = NULL;
227 256
228 init_log(L_INFO); 257 wlr_log_init(L_INFO, NULL);
229 258
230 static struct option long_options[] = { 259 static struct option long_options[] = {
231 {"help", no_argument, NULL, 'h'}, 260 {"help", no_argument, NULL, 'h'},
@@ -314,9 +343,11 @@ int main(int argc, char **argv) {
314 } 343 }
315 free(cmdtype); 344 free(cmdtype);
316 345
317 char *command = strdup(""); 346 char *command = NULL;
318 if (optind < argc) { 347 if (optind < argc) {
319 command = join_args(argv + optind, argc - optind); 348 command = join_args(argv + optind, argc - optind);
349 } else {
350 command = strdup("");
320 } 351 }
321 352
322 int ret = 0; 353 int ret = 0;
@@ -341,7 +372,7 @@ int main(int argc, char **argv) {
341 } else { 372 } else {
342 pretty_print(type, obj); 373 pretty_print(type, obj);
343 } 374 }
344 free(obj); 375 json_object_put(obj);
345 } 376 }
346 } 377 }
347 close(socketfd); 378 close(socketfd);
diff --git a/swaymsg/meson.build b/swaymsg/meson.build
new file mode 100644
index 00000000..8638b838
--- /dev/null
+++ b/swaymsg/meson.build
@@ -0,0 +1,8 @@
1executable(
2 'swaymsg',
3 'main.c',
4 include_directories: [sway_inc],
5 dependencies: [jsonc, wlroots],
6 link_with: [lib_sway_common],
7 install: true
8)
diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt
deleted file mode 100644
index 91fd7fff..00000000
--- a/wayland/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
1include_directories(
2 ${PROTOCOLS_INCLUDE_DIRS}
3 ${PANGO_INCLUDE_DIRS}
4 ${XKBCOMMON_INCLUDE_DIRS}
5 ${WAYLAND_INCLUDE_DIR}
6 ${EPOLLSHIM_INCLUDE_DIRS}
7)
8
9add_library(sway-wayland STATIC
10 buffers.c
11 pango.c
12 registry.c
13 window.c
14 cairo.c
15 )
16
17target_link_libraries(sway-wayland
18 sway-common
19 sway-protocols
20 ${PANGO_LIBRARIES}
21 ${XKBCOMMON_LIBRARIES}
22 ${EPOLLSHIM_LIBRARIES}
23 )
24
25if (WITH_GDK_PIXBUF)
26 include_directories(
27 ${GDK_PIXBUF_INCLUDE_DIRS}
28 )
29 target_link_libraries(sway-wayland
30 ${GDK_PIXBUF_LIBRARIES}
31 )
32endif()
diff --git a/wayland/registry.c b/wayland/registry.c
deleted file mode 100644
index bbb43ad9..00000000
--- a/wayland/registry.c
+++ /dev/null
@@ -1,293 +0,0 @@
1#include <wayland-client.h>
2#include <xkbcommon/xkbcommon.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6#include <sys/mman.h>
7#include <sys/types.h>
8#include <sys/timerfd.h>
9#include "wayland-desktop-shell-client-protocol.h"
10#include "wayland-swaylock-client-protocol.h"
11#include "client/registry.h"
12#include "stringop.h"
13#include "log.h"
14
15static void display_handle_mode(void *data, struct wl_output *wl_output,
16 uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
17 struct output_state *state = data;
18 if (flags & WL_OUTPUT_MODE_CURRENT) {
19 state->flags = flags;
20 state->width = width;
21 state->height = height;
22 sway_log(L_DEBUG, "Got mode %dx%d:0x%X for output %p",
23 width, height, flags, data);
24 }
25}
26
27static void display_handle_geometry(void *data, struct wl_output *wl_output,
28 int32_t x, int32_t y, int32_t physical_width, int32_t physical_height,
29 int32_t subpixel, const char *make, const char *model, int32_t transform) {
30 // this space intentionally left blank
31}
32
33static void display_handle_done(void *data, struct wl_output *wl_output) {
34 // this space intentionally left blank
35}
36
37static void display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) {
38 struct output_state *state = data;
39 state->scale = factor;
40 sway_log(L_DEBUG, "Got scale factor %d for output %p", factor, data);
41}
42
43static const struct wl_output_listener output_listener = {
44 .mode = display_handle_mode,
45 .geometry = display_handle_geometry,
46 .done = display_handle_done,
47 .scale = display_handle_scale
48};
49
50const char *XKB_MASK_NAMES[MASK_LAST] = {
51 XKB_MOD_NAME_SHIFT,
52 XKB_MOD_NAME_CAPS,
53 XKB_MOD_NAME_CTRL,
54 XKB_MOD_NAME_ALT,
55 "Mod2",
56 "Mod3",
57 XKB_MOD_NAME_LOGO,
58 "Mod5",
59};
60
61const enum mod_bit XKB_MODS[MASK_LAST] = {
62 MOD_SHIFT,
63 MOD_CAPS,
64 MOD_CTRL,
65 MOD_ALT,
66 MOD_MOD2,
67 MOD_MOD3,
68 MOD_LOGO,
69 MOD_MOD5
70};
71
72static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
73 uint32_t format, int fd, uint32_t size) {
74 // Keyboard errors are abort-worthy because you wouldn't be able to unlock your screen otherwise.
75
76 struct registry *registry = data;
77 if (!data) {
78 close(fd);
79 return;
80 }
81
82 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
83 close(fd);
84 sway_abort("Unknown keymap format %d, aborting", format);
85 }
86
87 char *map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
88 if (map_str == MAP_FAILED) {
89 close(fd);
90 sway_abort("Unable to initialized shared keyboard memory, aborting");
91 }
92
93 struct xkb_keymap *keymap = xkb_keymap_new_from_string(registry->input->xkb.context,
94 map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
95 munmap(map_str, size);
96 close(fd);
97
98 if (!keymap) {
99 sway_abort("Failed to compile keymap, aborting");
100 }
101
102 struct xkb_state *state = xkb_state_new(keymap);
103 if (!state) {
104 xkb_keymap_unref(keymap);
105 sway_abort("Failed to create xkb state, aborting");
106 }
107
108 xkb_keymap_unref(registry->input->xkb.keymap);
109 xkb_state_unref(registry->input->xkb.state);
110 registry->input->xkb.keymap = keymap;
111 registry->input->xkb.state = state;
112
113 int i;
114 for (i = 0; i < MASK_LAST; ++i) {
115 registry->input->xkb.masks[i] = 1 << xkb_keymap_mod_get_index(registry->input->xkb.keymap, XKB_MASK_NAMES[i]);
116 }
117}
118
119static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
120 uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
121 // this space intentionally left blank
122}
123
124static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
125 uint32_t serial, struct wl_surface *surface) {
126 // this space intentionally left blank
127}
128
129static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
130 uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) {
131 struct registry *registry = data;
132 enum wl_keyboard_key_state state = state_w;
133
134 if (!registry->input->xkb.state) {
135 return;
136 }
137
138 xkb_keysym_t sym = xkb_state_key_get_one_sym(registry->input->xkb.state, key + 8);
139 registry->input->sym = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? sym : XKB_KEY_NoSymbol);
140 registry->input->code = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? key + 8 : 0);
141 uint32_t codepoint = xkb_state_key_get_utf32(registry->input->xkb.state, registry->input->code);
142 if (registry->input->notify) {
143 registry->input->notify(state, sym, key, codepoint);
144 }
145}
146
147static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
148 uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
149 uint32_t mods_locked, uint32_t group) {
150 struct registry *registry = data;
151
152 if (!registry->input->xkb.keymap) {
153 return;
154 }
155
156 xkb_state_update_mask(registry->input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
157 xkb_mod_mask_t mask = xkb_state_serialize_mods(registry->input->xkb.state,
158 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
159
160 registry->input->modifiers = 0;
161 for (uint32_t i = 0; i < MASK_LAST; ++i) {
162 if (mask & registry->input->xkb.masks[i]) {
163 registry->input->modifiers |= XKB_MODS[i];
164 }
165 }
166}
167
168static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
169 int32_t rate, int32_t delay) {
170 // this space intentionally left blank
171}
172
173static const struct wl_keyboard_listener keyboard_listener = {
174 .keymap = keyboard_handle_keymap,
175 .enter = keyboard_handle_enter,
176 .leave = keyboard_handle_leave,
177 .key = keyboard_handle_key,
178 .modifiers = keyboard_handle_modifiers,
179 .repeat_info = keyboard_handle_repeat_info
180};
181
182static void seat_handle_capabilities(void *data, struct wl_seat *seat,
183 enum wl_seat_capability caps) {
184 struct registry *reg = data;
185
186 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !reg->pointer) {
187 reg->pointer = wl_seat_get_pointer(reg->seat);
188 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && reg->pointer) {
189 wl_pointer_destroy(reg->pointer);
190 reg->pointer = NULL;
191 }
192
193 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !reg->keyboard) {
194 reg->keyboard = wl_seat_get_keyboard(reg->seat);
195 wl_keyboard_add_listener(reg->keyboard, &keyboard_listener, reg);
196 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && reg->keyboard) {
197 wl_keyboard_destroy(reg->keyboard);
198 reg->keyboard = NULL;
199 }
200}
201
202static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) {
203 // this space intentionally left blank
204}
205
206static const struct wl_seat_listener seat_listener = {
207 .capabilities = seat_handle_capabilities,
208 .name = seat_handle_name,
209};
210
211static void registry_global(void *data, struct wl_registry *registry,
212 uint32_t name, const char *interface, uint32_t version) {
213 struct registry *reg = data;
214
215 if (strcmp(interface, wl_compositor_interface.name) == 0) {
216 reg->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version);
217 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
218 reg->shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
219 } else if (strcmp(interface, wl_shell_interface.name) == 0) {
220 reg->shell = wl_registry_bind(registry, name, &wl_shell_interface, version);
221 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
222 reg->seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
223 wl_seat_add_listener(reg->seat, &seat_listener, reg);
224 } else if (strcmp(interface, wl_output_interface.name) == 0) {
225 struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, version);
226 struct output_state *ostate = malloc(sizeof(struct output_state));
227 ostate->output = output;
228 ostate->scale = 1;
229 wl_output_add_listener(output, &output_listener, ostate);
230 list_add(reg->outputs, ostate);
231 } else if (strcmp(interface, desktop_shell_interface.name) == 0) {
232 reg->desktop_shell = wl_registry_bind(registry, name, &desktop_shell_interface, version);
233 } else if (strcmp(interface, lock_interface.name) == 0) {
234 reg->swaylock = wl_registry_bind(registry, name, &lock_interface, version);
235 }
236}
237
238static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
239 // this space intentionally left blank
240}
241
242static const struct wl_registry_listener registry_listener = {
243 .global = registry_global,
244 .global_remove = registry_global_remove
245};
246
247struct registry *registry_poll(void) {
248 struct registry *registry = malloc(sizeof(struct registry));
249 memset(registry, 0, sizeof(struct registry));
250 registry->outputs = create_list();
251 registry->input = calloc(sizeof(struct input), 1);
252 registry->input->xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
253
254 registry->display = wl_display_connect(NULL);
255 if (!registry->display) {
256 sway_log(L_ERROR, "Error opening display");
257 registry_teardown(registry);
258 return NULL;
259 }
260
261 struct wl_registry *reg = wl_display_get_registry(registry->display);
262 wl_registry_add_listener(reg, &registry_listener, registry);
263 wl_display_dispatch(registry->display);
264 wl_display_roundtrip(registry->display);
265 wl_registry_destroy(reg);
266
267 return registry;
268}
269
270void registry_teardown(struct registry *registry) {
271 if (registry->pointer) {
272 wl_pointer_destroy(registry->pointer);
273 }
274 if (registry->seat) {
275 wl_seat_destroy(registry->seat);
276 }
277 if (registry->shell) {
278 wl_shell_destroy(registry->shell);
279 }
280 if (registry->shm) {
281 wl_shm_destroy(registry->shm);
282 }
283 if (registry->compositor) {
284 wl_compositor_destroy(registry->compositor);
285 }
286 if (registry->display) {
287 wl_display_disconnect(registry->display);
288 }
289 if (registry->outputs) {
290 free_flat_list(registry->outputs);
291 }
292 free(registry);
293}
diff --git a/wayland/window.c b/wayland/window.c
deleted file mode 100644
index 8a506656..00000000
--- a/wayland/window.c
+++ /dev/null
@@ -1,177 +0,0 @@
1#include <wayland-client.h>
2#include <wayland-cursor.h>
3#include "wayland-xdg-shell-client-protocol.h"
4#include "wayland-desktop-shell-client-protocol.h"
5#include <cairo/cairo.h>
6#include <pango/pangocairo.h>
7#include <stdlib.h>
8#include <string.h>
9#include <stdio.h>
10#include <unistd.h>
11#include <errno.h>
12#include <sys/mman.h>
13#include "client/window.h"
14#include "client/buffer.h"
15#include "list.h"
16#include "log.h"
17
18static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
19 uint32_t serial, struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w) {
20 struct window *window = data;
21 if (window->registry->pointer) {
22 struct wl_cursor_image *image = window->cursor.cursor->images[0];
23 wl_pointer_set_cursor(pointer, serial, window->cursor.surface, image->hotspot_x, image->hotspot_y);
24 }
25}
26
27static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
28 uint32_t serial, struct wl_surface *surface) {
29}
30
31static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
32 uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) {
33 struct window *window = data;
34
35 window->pointer_input.last_x = wl_fixed_to_int(sx_w);
36 window->pointer_input.last_y = wl_fixed_to_int(sy_w);
37}
38
39static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
40 uint32_t time, uint32_t button, uint32_t state_w) {
41 struct window *window = data;
42 struct pointer_input *input = &window->pointer_input;
43
44 if (window->pointer_input.notify_button) {
45 window->pointer_input.notify_button(window, input->last_x, input->last_y, button, state_w);
46 }
47}
48
49static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
50 uint32_t time, uint32_t axis, wl_fixed_t value) {
51 struct window *window = data;
52 enum scroll_direction direction;
53
54 switch (axis) {
55 case 0:
56 direction = wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN;
57 break;
58 case 1:
59 direction = wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT;
60 break;
61 default:
62 sway_log(L_DEBUG, "Unexpected axis value on mouse scroll");
63 return;
64 }
65
66 if (window->pointer_input.notify_scroll) {
67 window->pointer_input.notify_scroll(window, direction);
68 }
69}
70
71static const struct wl_pointer_listener pointer_listener = {
72 .enter = pointer_handle_enter,
73 .leave = pointer_handle_leave,
74 .motion = pointer_handle_motion,
75 .button = pointer_handle_button,
76 .axis = pointer_handle_axis
77};
78
79void shell_surface_configure(void *data, struct wl_shell_surface *wl_shell_surface,
80 uint32_t edges, int32_t width, int32_t height) {
81 struct window *window = data;
82 window->width = width;
83 window->height = height;
84}
85
86static const struct wl_shell_surface_listener surface_listener = {
87 .configure = shell_surface_configure
88};
89
90void window_make_shell(struct window *window) {
91 window->shell_surface = wl_shell_get_shell_surface(window->registry->shell, window->surface);
92 wl_shell_surface_add_listener(window->shell_surface, &surface_listener, window);
93 wl_shell_surface_set_toplevel(window->shell_surface);
94}
95
96struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
97 int32_t scale, bool shell_surface) {
98 struct window *window = malloc(sizeof(struct window));
99 memset(window, 0, sizeof(struct window));
100 window->width = width;
101 window->height = height;
102 window->scale = scale;
103 window->registry = registry;
104 window->font = "monospace 10";
105
106 window->surface = wl_compositor_create_surface(registry->compositor);
107 if (shell_surface) {
108 window_make_shell(window);
109 }
110 if (registry->pointer) {
111 wl_pointer_add_listener(registry->pointer, &pointer_listener, window);
112 }
113
114 get_next_buffer(window);
115
116 if (registry->pointer) {
117 char *cursor_theme = getenv("SWAY_CURSOR_THEME");
118 if (!cursor_theme) {
119 cursor_theme = "default";
120 }
121 char *cursor_size = getenv("SWAY_CURSOR_SIZE");
122 if (!cursor_size) {
123 cursor_size = "16";
124 }
125
126 sway_log(L_DEBUG, "Cursor scale: %d", scale);
127 window->cursor.cursor_theme = wl_cursor_theme_load(cursor_theme,
128 atoi(cursor_size) * scale, registry->shm);
129 window->cursor.cursor = wl_cursor_theme_get_cursor(window->cursor.cursor_theme, "left_ptr");
130 window->cursor.surface = wl_compositor_create_surface(registry->compositor);
131
132 struct wl_cursor_image *image = window->cursor.cursor->images[0];
133 struct wl_buffer *cursor_buf = wl_cursor_image_get_buffer(image);
134 wl_surface_attach(window->cursor.surface, cursor_buf, 0, 0);
135 wl_surface_set_buffer_scale(window->cursor.surface, scale);
136 wl_surface_damage(window->cursor.surface, 0, 0,
137 image->width, image->height);
138 wl_surface_commit(window->cursor.surface);
139 }
140
141 return window;
142}
143
144static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) {
145 struct window *window = data;
146 wl_callback_destroy(callback);
147 window->frame_cb = NULL;
148}
149
150static const struct wl_callback_listener listener = {
151 frame_callback
152};
153
154int window_prerender(struct window *window) {
155 if (window->frame_cb) {
156 return 0;
157 }
158
159 get_next_buffer(window);
160 return 1;
161}
162
163int window_render(struct window *window) {
164 window->frame_cb = wl_surface_frame(window->surface);
165 wl_callback_add_listener(window->frame_cb, &listener, window);
166
167 wl_surface_attach(window->surface, window->buffer->buffer, 0, 0);
168 wl_surface_set_buffer_scale(window->surface, window->scale);
169 wl_surface_damage(window->surface, 0, 0, window->width, window->height);
170 wl_surface_commit(window->surface);
171
172 return 1;
173}
174
175void window_teardown(struct window *window) {
176 // TODO
177}