diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-04-12 20:19:54 -0400 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-04-12 20:19:54 -0400 |
commit | cd1b32453a9296c18b28bff71607aeb22987b5cd (patch) | |
tree | c653c6d525b471914c01a9d7ae543f521b6138ed | |
parent | Merge pull request #1634 from aleksander/master (diff) | |
parent | Fix separator height calculation (diff) | |
download | sway-cd1b32453a9296c18b28bff71607aeb22987b5cd.tar.gz sway-cd1b32453a9296c18b28bff71607aeb22987b5cd.tar.zst sway-cd1b32453a9296c18b28bff71607aeb22987b5cd.zip |
Merge branch 'wlroots'
281 files changed, 14666 insertions, 20887 deletions
@@ -1,24 +1,29 @@ | |||
1 | # vim: ft=yaml ts=2 sw=2 et : | 1 | # vim: ft=yaml ts=2 sw=2 et : |
2 | image: archlinux | 2 | image: archlinux |
3 | packages: | 3 | packages: |
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 | ||
13 | sources: | 15 | sources: |
14 | - https://git.sr.ht/~sircmpwn/sway | 16 | - https://github.com/swaywm/sway |
17 | - https://github.com/swaywm/wlroots | ||
15 | tasks: | 18 | tasks: |
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 | ||
@@ -1,7 +1,3 @@ | |||
1 | CMakeCache.txt | ||
2 | CMakeFiles | ||
3 | Makefile | ||
4 | cmake_install.cmake | ||
5 | install_manifest.txt | 1 | install_manifest.txt |
6 | *.swp | 2 | *.swp |
7 | *.o | 3 | *.o |
@@ -12,3 +8,5 @@ build/ | |||
12 | .lvimrc | 8 | .lvimrc |
13 | config-debug | 9 | config-debug |
14 | wayland-*-protocol.* | 10 | wayland-*-protocol.* |
11 | /subprojects/wlroots | ||
12 | subprojects | ||
diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3eeb1682..00000000 --- a/.travis.yml +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | sudo: required | ||
2 | |||
3 | language: c | ||
4 | |||
5 | compiler: | ||
6 | - gcc | ||
7 | - clang | ||
8 | |||
9 | env: | ||
10 | - BUILD_TYPE=Release | ||
11 | - BUILD_TYPE=Debug | ||
12 | - BUILD_TYPE=ASAN | ||
13 | |||
14 | arch: | ||
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 | |||
30 | script: | ||
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 | ||
15 | find_program( A2X_PROGRAM a2x ) | ||
16 | |||
17 | # Found something, attempt to try and use it... | ||
18 | if( 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() | ||
30 | endif() | ||
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 | ||
34 | if( 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() | ||
63 | endif() | ||
64 | |||
65 | # If we've found a command that works, check the version | ||
66 | if( A2X_COMMAND ) | ||
67 | string(REGEX REPLACE ".*a2x[^0-9.]*\([0-9.]+\).*" "\\1" A2X_VERSION "${_OUT}") | ||
68 | endif() | ||
69 | |||
70 | # Generate the *_FOUND as necessary, etc. | ||
71 | include( FindPackageHandleStandardArgs ) | ||
72 | find_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 | |||
11 | find_package(PkgConfig) | ||
12 | |||
13 | if(Cairo_FIND_REQUIRED) | ||
14 | set(_pkgconfig_REQUIRED "REQUIRED") | ||
15 | else(Cairo_FIND_REQUIRED) | ||
16 | set(_pkgconfig_REQUIRED "") | ||
17 | endif(Cairo_FIND_REQUIRED) | ||
18 | |||
19 | if(CAIRO_MIN_VERSION) | ||
20 | pkg_check_modules(CAIRO ${_pkgconfig_REQUIRED} cairo>=${CAIRO_MIN_VERSION}) | ||
21 | else(CAIRO_MIN_VERSION) | ||
22 | pkg_check_modules(CAIRO ${_pkgconfig_REQUIRED} cairo) | ||
23 | endif(CAIRO_MIN_VERSION) | ||
24 | |||
25 | if(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND) | ||
26 | find_path(CAIRO_INCLUDE_DIRS cairo.h) | ||
27 | find_library(CAIRO_LIBRARIES cairo) | ||
28 | else(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}) | ||
38 | endif(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND) | ||
39 | |||
40 | include(FindPackageHandleStandardArgs) | ||
41 | find_package_handle_standard_args(CAIRO DEFAULT_MSG CAIRO_LIBRARIES CAIRO_INCLUDE_DIRS) | ||
42 | mark_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 | |||
31 | FIND_PACKAGE(PkgConfig) | ||
32 | PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1) | ||
33 | |||
34 | FIND_LIBRARY(DBUS_LIBRARIES | ||
35 | NAMES dbus-1 | ||
36 | HINTS ${PC_DBUS_LIBDIR} | ||
37 | ${PC_DBUS_LIBRARY_DIRS} | ||
38 | ) | ||
39 | |||
40 | FIND_PATH(DBUS_INCLUDE_DIR | ||
41 | NAMES dbus/dbus.h | ||
42 | HINTS ${PC_DBUS_INCLUDEDIR} | ||
43 | ${PC_DBUS_INCLUDE_DIRS} | ||
44 | ) | ||
45 | |||
46 | GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH) | ||
47 | FIND_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 | |||
56 | SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) | ||
57 | |||
58 | INCLUDE(FindPackageHandleStandardArgs) | ||
59 | FIND_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 | |||
8 | find_path(EPOLLSHIM_INCLUDE_DIRS NAMES sys/epoll.h sys/timerfd.h HINTS /usr/local/include/libepoll-shim) | ||
9 | find_library(EPOLLSHIM_LIBRARIES NAMES epoll-shim libepoll-shim HINTS /usr/local/lib) | ||
10 | |||
11 | if (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES) | ||
12 | set(EPOLLSHIM_FOUND TRUE) | ||
13 | endif (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES) | ||
14 | |||
15 | include(FindPackageHandleStandardArgs) | ||
16 | find_package_handle_standard_args(EPOLLSHIM DEFAULT_MSG EPOLLSHIM_LIBRARIES EPOLLSHIM_INCLUDE_DIRS) | ||
17 | mark_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 | |||
11 | find_package(PkgConfig) | ||
12 | |||
13 | if(GdkPixbuf_FIND_REQUIRED) | ||
14 | set(_pkgconfig_REQUIRED "REQUIRED") | ||
15 | else(GdkPixbuf_FIND_REQUIRED) | ||
16 | set(_pkgconfig_REQUIRED "") | ||
17 | endif(GdkPixbuf_FIND_REQUIRED) | ||
18 | |||
19 | if(GDK_PIXBUF_MIN_VERSION) | ||
20 | pkg_check_modules(GDK_PIXBUF ${_pkgconfig_REQUIRED} "gdk-pixbuf-2.0>=${GDK_PIXBUF_MIN_VERSION}") | ||
21 | else(GDK_PIXBUF_MIN_VERSION) | ||
22 | pkg_check_modules(GDK_PIXBUF ${_pkgconfig_REQUIRED} "gdk-pixbuf-2.0") | ||
23 | endif(GDK_PIXBUF_MIN_VERSION) | ||
24 | |||
25 | if(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) | ||
28 | else(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}) | ||
39 | endif(NOT GDK_PIXBUF_FOUND AND NOT PKG_CONFIG_FOUND) | ||
40 | |||
41 | include(FindPackageHandleStandardArgs) | ||
42 | find_package_handle_standard_args(GDK_PIXBUF DEFAULT_MSG GDK_PIXBUF_LIBRARIES GDK_PIXBUF_INCLUDE_DIRS) | ||
43 | mark_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 | |||
10 | find_package(PkgConfig) | ||
11 | |||
12 | if (JsonC_FIND_REQUIRED) | ||
13 | set(_pkgconfig_REQUIRED "REQUIRED") | ||
14 | else() | ||
15 | set(_pkgconfig_REQUIRED "") | ||
16 | endif() | ||
17 | |||
18 | if(JsonC_FIND_VERSION) | ||
19 | pkg_check_modules(PC_JSONC ${_pkgconfig_REQUIRED} json-c=${JsonC_FIND_VERSION}) | ||
20 | else() | ||
21 | pkg_check_modules(PC_JSONC ${_pkgconfig_REQUIRED} json-c) | ||
22 | endif() | ||
23 | |||
24 | find_path(JSONC_INCLUDE_DIRS NAMES json-c/json.h HINTS ${PC_JSONC_INCLUDE_DIRS}) | ||
25 | find_library(JSONC_LIBRARIES NAMES json-c HINTS ${PC_JSONC_LIBRARY_DIRS}) | ||
26 | include(FindPackageHandleStandardArgs) | ||
27 | |||
28 | find_package_handle_standard_args(JSONC DEFAULT_MSG JSONC_LIBRARIES JSONC_INCLUDE_DIRS) | ||
29 | mark_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 | |||
35 | include(FeatureSummary) | ||
36 | set_package_properties(LibInput PROPERTIES | ||
37 | URL "http://freedesktop.org/wiki/Software/libinput/" | ||
38 | DESCRIPTION "Library to handle input devices") | ||
39 | |||
40 | find_package(PkgConfig) | ||
41 | pkg_check_modules(PC_INPUT QUIET libinput) | ||
42 | find_library(LIBINPUT_LIBRARIES NAMES input HINTS ${PC_INPUT_LIBRARY_DIRS}) | ||
43 | find_path(LIBINPUT_INCLUDE_DIRS libinput.h HINTS ${PC_INPUT_INCLUDE_DIRS}) | ||
44 | |||
45 | set(LIBINPUT_VERSION ${PC_INPUT_VERSION}) | ||
46 | string(REPLACE "." ";" VERSION_LIST "${PC_INPUT_VERSION}") | ||
47 | |||
48 | LIST(LENGTH VERSION_LIST n) | ||
49 | if (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) | ||
53 | endif () | ||
54 | |||
55 | # This is compatible with libinput-version.h that exists in upstream | ||
56 | # but isn't in distribution (probably forgotten) | ||
57 | set(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 | |||
63 | include(FindPackageHandleStandardArgs) | ||
64 | find_package_handle_standard_args(LIBINPUT DEFAULT_MSG LIBINPUT_INCLUDE_DIRS LIBINPUT_LIBRARIES) | ||
65 | mark_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 | |||
34 | include(FeatureSummary) | ||
35 | set_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 | |||
39 | find_package(PkgConfig) | ||
40 | pkg_check_modules(PC_CAP QUIET Libcap) | ||
41 | find_library(Libcap_LIBRARIES NAMES cap HINTS ${PC_CAP_LIBRARY_DIRS}) | ||
42 | find_path(Libcap_INCLUDE_DIRS sys/capability.h HINTS ${PC_CAP_INCLUDE_DIRS}) | ||
43 | |||
44 | set(Libcap_VERSION ${PC_CAP_VERSION}) | ||
45 | string(REPLACE "." ";" VERSION_LIST "${PC_CAP_VERSION}") | ||
46 | |||
47 | LIST(LENGTH VERSION_LIST n) | ||
48 | if (n EQUAL 2) | ||
49 | list(GET VERSION_LIST 0 Libcap_VERSION_MAJOR) | ||
50 | list(GET VERSION_LIST 1 Libcap_VERSION_MINOR) | ||
51 | endif () | ||
52 | |||
53 | include(FindPackageHandleStandardArgs) | ||
54 | find_package_handle_standard_args(Libcap DEFAULT_MSG Libcap_INCLUDE_DIRS Libcap_LIBRARIES) | ||
55 | mark_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 | |||
8 | if (PAM_INCLUDE_DIR AND PAM_LIBRARY) | ||
9 | set(PAM_FIND_QUIETLY TRUE) | ||
10 | endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) | ||
11 | |||
12 | find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h) | ||
13 | find_library(PAM_LIBRARY pam) | ||
14 | find_library(DL_LIBRARY dl) | ||
15 | |||
16 | if (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) | ||
27 | endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) | ||
28 | |||
29 | if (PAM_FOUND) | ||
30 | if (NOT PAM_FIND_QUIETLY) | ||
31 | message(STATUS "Found PAM: ${PAM_LIBRARIES}") | ||
32 | endif (NOT PAM_FIND_QUIETLY) | ||
33 | else (PAM_FOUND) | ||
34 | if (PAM_FIND_REQUIRED) | ||
35 | message(FATAL_ERROR "PAM was not found") | ||
36 | endif(PAM_FIND_REQUIRED) | ||
37 | endif (PAM_FOUND) | ||
38 | |||
39 | mark_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. | ||
19 | FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h) | ||
20 | |||
21 | # Look for the library. | ||
22 | FIND_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. | ||
25 | INCLUDE(FindPackageHandleStandardArgs) | ||
26 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR) | ||
27 | |||
28 | # Copy the results to the output variables. | ||
29 | IF(PCRE_FOUND) | ||
30 | SET(PCRE_LIBRARIES ${PCRE_LIBRARY}) | ||
31 | SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR}) | ||
32 | ELSE(PCRE_FOUND) | ||
33 | SET(PCRE_LIBRARIES) | ||
34 | SET(PCRE_INCLUDE_DIRS) | ||
35 | ENDIF(PCRE_FOUND) | ||
36 | |||
37 | MARK_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 | |||
11 | find_package(PkgConfig) | ||
12 | |||
13 | if(Pango_FIND_REQUIRED) | ||
14 | set(_pkgconfig_REQUIRED "REQUIRED") | ||
15 | else(Pango_FIND_REQUIRED) | ||
16 | set(_pkgconfig_REQUIRED "") | ||
17 | endif(Pango_FIND_REQUIRED) | ||
18 | |||
19 | if(PANGO_MIN_VERSION) | ||
20 | pkg_check_modules(PANGO ${_pkgconfig_REQUIRED} "pango>=${PANGO_MIN_VERSION}" "pangocairo>=${PANGO_MIN_VERSION}") | ||
21 | else(PANGO_MIN_VERSION) | ||
22 | pkg_check_modules(PANGO ${_pkgconfig_REQUIRED} pango pangocairo) | ||
23 | endif(PANGO_MIN_VERSION) | ||
24 | |||
25 | if(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND) | ||
26 | find_path(PANGO_INCLUDE_DIRS pango.h) | ||
27 | find_library(PANGO_LIBRARIES pango pangocairo) | ||
28 | else(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}) | ||
38 | endif(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND) | ||
39 | |||
40 | include(FindPackageHandleStandardArgs) | ||
41 | find_package_handle_standard_args(PANGO DEFAULT_MSG PANGO_LIBRARIES PANGO_INCLUDE_DIRS) | ||
42 | mark_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 | |||
11 | find_package(PkgConfig) | ||
12 | pkg_check_modules(PC_WLC QUIET wlc) | ||
13 | find_path(WLC_INCLUDE_DIRS NAMES wlc/wlc.h HINTS ${PC_WLC_INCLUDE_DIRS}) | ||
14 | find_library(WLC_LIBRARIES NAMES wlc HINTS ${PC_WLC_LIBRARY_DIRS}) | ||
15 | |||
16 | set(WLC_DEFINITIONS ${PC_WLC_CFLAGS_OTHER}) | ||
17 | |||
18 | include(FindPackageHandleStandardArgs) | ||
19 | find_package_handle_standard_args(wlc DEFAULT_MSG WLC_LIBRARIES WLC_INCLUDE_DIRS) | ||
20 | mark_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 | |||
22 | IF (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 | |||
67 | ENDIF () | ||
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 | |||
9 | find_package(PkgConfig) | ||
10 | pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon) | ||
11 | find_path(XKBCOMMON_INCLUDE_DIRS NAMES xkbcommon/xkbcommon.h HINTS ${PC_XKBCOMMON_INCLUDE_DIRS}) | ||
12 | find_library(XKBCOMMON_LIBRARIES NAMES xkbcommon HINTS ${PC_XKBCOMMON_LIBRARY_DIRS}) | ||
13 | |||
14 | set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER}) | ||
15 | |||
16 | include(FindPackageHandleStandardArgs) | ||
17 | find_package_handle_standard_args(XKBCOMMON DEFAULT_MSG XKBCOMMON_LIBRARIES XKBCOMMON_INCLUDE_DIRS) | ||
18 | mark_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 @@ | |||
1 | find_package(A2X) | ||
2 | |||
3 | if (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() | ||
33 | endif() | ||
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 | |||
33 | find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner) | ||
34 | |||
35 | # wayland_add_protocol_client(outfiles inputfile basename) | ||
36 | function(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) | ||
55 | endfunction() | ||
56 | |||
57 | # wayland_add_protocol_server(outfiles inputfile basename) | ||
58 | function(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) | ||
77 | endfunction() | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index dc521570..00000000 --- a/CMakeLists.txt +++ /dev/null | |||
@@ -1,161 +0,0 @@ | |||
1 | cmake_minimum_required(VERSION 3.1.0) | ||
2 | |||
3 | project(sway C) | ||
4 | |||
5 | add_compile_options(-g) | ||
6 | set(CMAKE_C_STANDARD 99) | ||
7 | set(CMAKE_C_EXTENSIONS OFF) | ||
8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) | ||
9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) | ||
10 | add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-result -Werror) | ||
11 | |||
12 | # Add Address Sanitiezed build type | ||
13 | set(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 ) | ||
17 | mark_as_advanced( | ||
18 | CMAKE_C_FLAGS_ASAN | ||
19 | CMAKE_EXE_LINKER_FLAGS_DEBUG | ||
20 | CMAKE_SHARED_LINKER_FLAGS_DEBUG | ||
21 | ) | ||
22 | |||
23 | list(INSERT CMAKE_MODULE_PATH 0 | ||
24 | ${CMAKE_CURRENT_SOURCE_DIR}/CMake | ||
25 | ) | ||
26 | |||
27 | if (VERSION) | ||
28 | add_definitions(-DSWAY_VERSION=\"${VERSION}\") | ||
29 | else() | ||
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}\\\")\"") | ||
45 | endif() | ||
46 | |||
47 | option(enable-swaylock "Enables the swaylock utility" YES) | ||
48 | option(enable-swaybg "Enables the wallpaper utility" YES) | ||
49 | option(enable-swaybar "Enables the swaybar utility" YES) | ||
50 | option(enable-swaygrab "Enables the swaygrab utility" YES) | ||
51 | option(enable-swaymsg "Enables the swaymsg utility" YES) | ||
52 | option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" YES) | ||
53 | option(enable-tray "Enables the swaybar tray" YES) | ||
54 | option(zsh-completions "Zsh shell completions" NO) | ||
55 | option(default-wallpaper "Installs the default wallpaper" YES) | ||
56 | option(LD_LIBRARY_PATH "Configure sway's default LD_LIBRARY_PATH") | ||
57 | |||
58 | if (LD_LIBRARY_PATH) | ||
59 | add_definitions(-D_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}") | ||
60 | endif() | ||
61 | |||
62 | find_package(JsonC 0.12.1 REQUIRED) | ||
63 | find_package(PCRE REQUIRED) | ||
64 | find_package(WLC REQUIRED) | ||
65 | find_package(Wayland REQUIRED) | ||
66 | find_package(XKBCommon REQUIRED) | ||
67 | find_package(Cairo REQUIRED) | ||
68 | find_package(Pango REQUIRED) | ||
69 | find_package(GdkPixbuf) | ||
70 | find_package(PAM) | ||
71 | find_package(DBus 1.10) | ||
72 | |||
73 | find_package(LibInput REQUIRED) | ||
74 | |||
75 | if (CMAKE_SYSTEM_NAME STREQUAL Linux) | ||
76 | find_package(Libcap REQUIRED) | ||
77 | endif (CMAKE_SYSTEM_NAME STREQUAL Linux) | ||
78 | |||
79 | if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) | ||
80 | find_package(EpollShim REQUIRED) | ||
81 | endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) | ||
82 | |||
83 | include(FeatureSummary) | ||
84 | include(Manpage) | ||
85 | include(GNUInstallDirs) | ||
86 | |||
87 | if (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() | ||
94 | else() | ||
95 | message(STATUS "Building without gdk-pixbuf, only png images supported.") | ||
96 | endif() | ||
97 | |||
98 | if (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() | ||
105 | else() | ||
106 | message(STATUS "Building without the tray.") | ||
107 | endif() | ||
108 | |||
109 | include_directories(include) | ||
110 | |||
111 | add_subdirectory(protocols) | ||
112 | add_subdirectory(common) | ||
113 | add_subdirectory(wayland) | ||
114 | |||
115 | add_subdirectory(sway) | ||
116 | if(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() | ||
122 | endif() | ||
123 | if(enable-swaymsg) | ||
124 | add_subdirectory(swaymsg) | ||
125 | endif() | ||
126 | if(enable-swaygrab) | ||
127 | add_subdirectory(swaygrab) | ||
128 | endif() | ||
129 | if(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() | ||
135 | endif() | ||
136 | if(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() | ||
142 | endif() | ||
143 | if(zsh-completions) | ||
144 | add_subdirectory(completions/zsh) | ||
145 | endif() | ||
146 | install( | ||
147 | FILES ${CMAKE_CURRENT_SOURCE_DIR}/sway.desktop | ||
148 | DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/wayland-sessions | ||
149 | COMPONENT data | ||
150 | ) | ||
151 | |||
152 | if(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 | ) | ||
159 | endif() | ||
160 | |||
161 | feature_summary(WHAT ALL) | ||
@@ -22,14 +22,3 @@ assertion fails. | |||
22 | 22 | ||
23 | Outside of the compositor (swaymsg, swaybar, etc), using `assert.h` is | 23 | Outside of the compositor (swaymsg, swaybar, etc), using `assert.h` is |
24 | permitted. | 24 | permitted. |
25 | |||
26 | ## Building against a local wlc | ||
27 | |||
28 | 1. Build wlc as described [here](https://github.com/Cloudef/wlc#building) | ||
29 | 2. Inside your sway source folder, tell `cmake` to use your local version of wlc: | ||
30 | |||
31 | ```bash | ||
32 | cmake \ | ||
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 | ||
3 | Der Fortschritt dieser Ãœbersetzung kann [hier](https://github.com/swaywm/sway/issues/1318) | 3 | Der Fortschritt dieser Ãœbersetzung kann [hier](https://github.com/swaywm/sway/issues/1318) |
4 | eingesehen werden. | 4 | eingesehen werden. |
@@ -52,7 +52,7 @@ schau im IRC-Channel vorbei oder schreibe eine e-Mail an sir@cmpwn.com (nur engl | |||
52 | 52 | ||
53 | Abhängigkeiten: | 53 | Abhä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 | ||
75 | Führe diese Befehle aus: | 75 | Fü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 | ||
83 | In Systemen mit logind musst du `sway` einige Capabilities geben: | 81 | In 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/) |
4 | compatible avec i3, **en cours de développement**. | 4 | compatible 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 | ||
48 | Installez les dépendances : | 48 | Installez 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 | ||
70 | Exécutez ces commandes : | 70 | Exé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 | ||
78 | Sur les systèmes avec logind, vous devez définir quelques caps sur le binaire : | 76 | Sur 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 | ||
49 | Installa queste dipendenze: | 49 | Installa 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 | ||
71 | Esegui questi comandi: | 71 | Esegui 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 | ||
79 | Per i sistemi con logind, devi impostare un paio di caps sull'eseguibile: | 77 | Per 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"ã¯**開発ä¸**ã® |
4 | i3互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚ | 4 | i3互æ›ãª[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 | ||
69 | logindを使用ã—ã¦ã„るシステムã§ã¯ã€ãƒã‚¤ãƒŠãƒªã«ã„ãã¤ã‹ã®ã‚±ãƒ¼ãƒ‘ビリティをè¨å®šã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™: | 67 | logindを使用ã—ã¦ã„るシステムã§ã¯ã€ãƒã‚¤ãƒŠãƒªã«ã„ãã¤ã‹ã®ã‚±ãƒ¼ãƒ‘ビリティをè¨å®šã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™: |
70 | 68 | ||
@@ -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 | ||
54 | Install dependencies: | 54 | Install 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 | ||
79 | Run these commands: | 79 | Run 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 | ||
87 | On systems with logind, you need to set a few caps on the binary: | 85 | On 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/) |
4 | compatÃvel com o i3. Leia o [FAQ](https://github.com/swaywm/sway/wiki). Participe do | 4 | compatÃ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 | ||
55 | Antes de iniciar a compilação, instale as dependências: | 55 | Antes 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 | ||
77 | Para compilar, execute estes comandos: | 77 | Para 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 | ||
85 | Em sistemas com logind, configure as seguintes capacidades para o arquivo binário: | 83 | Em 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" на данный момент **(в разработке)** |
4 | i3-ÑовмеÑтимый [Wayland](http://wayland.freedesktop.org/) композитор. | 4 | i3-ÑовмеÑтимый [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 @@ | |||
1 | lib_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 | ||
15 | static int create_pool_file(size_t size, char **name) { | 14 | static 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 | ||
44 | static void buffer_release(void *data, struct wl_buffer *wl_buffer) { | 43 | static 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 | ||
53 | static struct buffer *create_buffer(struct window *window, struct buffer *buf, | 52 | static 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 | ||
88 | static void destroy_buffer(struct buffer *buffer) { | 82 | void 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 | ||
104 | struct buffer *get_next_buffer(struct window *window) { | 98 | struct 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 @@ | |||
1 | include_directories( | ||
2 | ${WLC_INCLUDE_DIRS} | ||
3 | ${XKBCOMMON_INCLUDE_DIRS} | ||
4 | ) | ||
5 | |||
6 | add_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 | |||
15 | target_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 | |||
7 | enum 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 | |||
25 | cairo_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 | |||
56 | void 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 | ||
3 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { | 8 | void 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 | ||
11 | cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) { | 16 | cairo_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 | |||
41 | cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { | 34 | cairo_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'}; | |||
14 | static const size_t ipc_header_size = sizeof(ipc_magic)+8; | 14 | static const size_t ipc_header_size = sizeof(ipc_magic)+8; |
15 | 15 | ||
16 | char *get_socketpath(void) { | 16 | char *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 | ||
26 | int ipc_open_socket(const char *socket_path) { | 44 | int ipc_open_socket(const char *socket_path) { |
@@ -79,7 +97,7 @@ struct ipc_response *ipc_recv_response(int socketfd) { | |||
79 | error_2: | 97 | error_2: |
80 | free(response); | 98 | free(response); |
81 | error_1: | 99 | error_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 | ||
15 | static int colored = 1; | 6 | void sway_terminate(int code); |
16 | static log_importance_t loglevel_default = L_ERROR; | ||
17 | static log_importance_t v = L_SILENT; | ||
18 | 7 | ||
19 | static const char *verbosity_colors[] = { | 8 | void _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 | }; | ||
25 | static const char verbosity_chars[] = { | ||
26 | [L_SILENT] = '\0', | ||
27 | [L_ERROR ] = 'E', | ||
28 | [L_INFO ] = 'I', | ||
29 | [L_DEBUG ] = 'D', | ||
30 | }; | ||
31 | |||
32 | void 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 | |||
41 | void set_log_level(log_importance_t verbosity) { | ||
42 | v = verbosity; | ||
43 | } | ||
44 | |||
45 | log_importance_t get_log_level(void) { | ||
46 | return v; | ||
47 | } | ||
48 | |||
49 | void reset_log_level(void) { | ||
50 | v = loglevel_default; | ||
51 | } | ||
52 | |||
53 | bool toggle_debug_logging(void) { | ||
54 | v = (v == L_DEBUG) ? loglevel_default : L_DEBUG; | ||
55 | return (v == L_DEBUG); | ||
56 | } | ||
57 | |||
58 | void sway_log_colors(int mode) { | ||
59 | colored = (mode == 1) ? 1 : 0; | ||
60 | } | ||
61 | |||
62 | void _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 | |||
113 | void _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 | |||
121 | void _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 | ||
129 | void sway_log_errno(log_importance_t verbosity, char* format, ...) { | 16 | bool _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 | |||
157 | bool _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 @@ | |||
1 | lib_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 | ||
11 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, | 11 | PangoLayout *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 | ||
34 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | 40 | void 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 | ||
55 | void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...) { | 57 | void 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 | |||
5 | size_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 | |||
16 | static const uint8_t masks[] = { | ||
17 | 0x7F, | ||
18 | 0x1F, | ||
19 | 0x0F, | ||
20 | 0x07, | ||
21 | 0x03, | ||
22 | 0x01 | ||
23 | }; | ||
24 | |||
25 | uint32_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 | |||
51 | size_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 | |||
79 | static 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 | |||
93 | int 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 | ||
44 | uint32_t get_modifier_mask_by_name(const char *name) { | 44 | uint32_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 @@ | |||
1 | install( | ||
2 | FILES _sway _swaymsg _swaygrab _swaylock | ||
3 | DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/zsh/site-functions/ | ||
4 | ) | ||
@@ -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/) |
24 | output * bg __DATADIR__/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill | 24 | output * 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 | ||
197 | include __SYSCONFDIR__/sway/config.d/* | 197 | include @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 | |||
5 | enum 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 | |||
15 | enum background_mode parse_background_mode(const char *mode); | ||
16 | cairo_surface_t *load_background_image(const char *path); | ||
17 | void 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 | ||
7 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color); | 6 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color); |
8 | 7 | ||
9 | cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height); | 8 | cairo_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 | ||
14 | cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf); | 15 | cairo_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 | |||
6 | struct 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 | |||
10 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text, | ||
11 | int32_t scale, bool markup); | ||
12 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | ||
13 | int32_t scale, bool markup, const char *fmt, ...); | ||
14 | void 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 | |||
10 | enum 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 | |||
21 | enum 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 | |||
33 | struct output_state { | ||
34 | struct wl_output *output; | ||
35 | uint32_t flags; | ||
36 | uint32_t width, height; | ||
37 | uint32_t scale; | ||
38 | }; | ||
39 | |||
40 | struct 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 | |||
47 | struct 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 | |||
58 | struct 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 | |||
72 | struct registry *registry_poll(void); | ||
73 | void 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 | |||
12 | struct window; | ||
13 | |||
14 | struct 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 | |||
23 | struct 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 | |||
30 | enum scroll_direction { | ||
31 | SCROLL_UP, | ||
32 | SCROLL_DOWN, | ||
33 | SCROLL_LEFT, | ||
34 | SCROLL_RIGHT, | ||
35 | }; | ||
36 | |||
37 | struct 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 | |||
45 | struct 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 | |||
60 | struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height, | ||
61 | int32_t scale, bool shell_surface); | ||
62 | void window_teardown(struct window *state); | ||
63 | int window_prerender(struct window *state); | ||
64 | int window_render(struct window *state); | ||
65 | void 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 | ||
5 | typedef enum { | 6 | void _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 | |||
12 | void init_log(log_importance_t verbosity); | ||
13 | void set_log_level(log_importance_t verbosity); | ||
14 | log_importance_t get_log_level(void); | ||
15 | void reset_log_level(void); | ||
16 | // returns whether debug logging is on after switching. | ||
17 | bool toggle_debug_logging(void); | ||
18 | void sway_log_colors(int mode); | ||
19 | void sway_log_errno(log_importance_t verbosity, char* format, ...) __attribute__((format(printf,2,3))); | ||
20 | |||
21 | void _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 | ||
25 | bool _sway_assert(bool condition, const char *filename, int line, const char* format, ...) __attribute__((format(printf,4,5))); | 10 | bool _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 | |||
29 | void _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 | ||
37 | void error_handler(int sig); | 14 | void 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 | |||
9 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | ||
10 | const char *text, int32_t scale, bool markup); | ||
11 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | ||
12 | int32_t scale, bool markup, const char *fmt, ...); | ||
13 | void 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 | |||
9 | struct 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 | |||
18 | struct pool_buffer *get_next_buffer(struct wl_shm *shm, | ||
19 | struct pool_buffer pool[static 2], uint32_t width, uint32_t height); | ||
20 | void 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 | |||
4 | void 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 | */ | ||
9 | struct border { | ||
10 | unsigned char *buffer; | ||
11 | struct wlc_geometry geometry; | ||
12 | }; | ||
13 | |||
14 | /** | ||
15 | * Clear border buffer. | ||
16 | */ | ||
17 | void border_clear(struct border *border); | ||
18 | |||
19 | /** | ||
20 | * Recursively update all of the borders within a container. | ||
21 | */ | ||
22 | void update_container_border(swayc_t *container); | ||
23 | |||
24 | void render_view_borders(wlc_handle view); | ||
25 | int get_font_text_height(const char *font); | ||
26 | bool 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" |
9 | extern 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 | ||
49 | struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val); | 45 | struct cmd_results *checkarg(int argc, const char *name, |
50 | struct cmd_results *add_color(const char*, char*, const char*); | 46 | enum expected_args type, int val); |
51 | void input_cmd_apply(struct input_config *input); | ||
52 | void hide_view_in_scratchpad(swayc_t *sp_view); | ||
53 | |||
54 | swayc_t *sp_view; | ||
55 | int sp_index; | ||
56 | 47 | ||
57 | /** | 48 | /** |
58 | * Parse and handles a command. | 49 | * Parse and executes a command. |
59 | */ | 50 | */ |
60 | struct cmd_results *handle_command(char *command, enum command_context context); | 51 | struct 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 | */ |
70 | struct cmd_results *config_commands_command(char *exec); | 61 | struct 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 | */ |
85 | const char *cmd_results_to_json(struct cmd_results *results); | 75 | const char *cmd_results_to_json(struct cmd_results *results); |
86 | 76 | ||
87 | void remove_view_from_scratchpad(swayc_t *); | 77 | struct 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 | ||
93 | typedef struct cmd_results *sway_cmd(int argc, char **argv); | 80 | typedef struct cmd_results *sway_cmd(int argc, char **argv); |
94 | 81 | ||
@@ -108,6 +95,7 @@ sway_cmd cmd_commands; | |||
108 | sway_cmd cmd_debuglog; | 95 | sway_cmd cmd_debuglog; |
109 | sway_cmd cmd_default_border; | 96 | sway_cmd cmd_default_border; |
110 | sway_cmd cmd_default_floating_border; | 97 | sway_cmd cmd_default_floating_border; |
98 | sway_cmd cmd_default_orientation; | ||
111 | sway_cmd cmd_exec; | 99 | sway_cmd cmd_exec; |
112 | sway_cmd cmd_exec_always; | 100 | sway_cmd cmd_exec_always; |
113 | sway_cmd cmd_exit; | 101 | sway_cmd cmd_exit; |
@@ -126,6 +114,7 @@ sway_cmd cmd_gaps; | |||
126 | sway_cmd cmd_hide_edge_borders; | 114 | sway_cmd cmd_hide_edge_borders; |
127 | sway_cmd cmd_include; | 115 | sway_cmd cmd_include; |
128 | sway_cmd cmd_input; | 116 | sway_cmd cmd_input; |
117 | sway_cmd cmd_seat; | ||
129 | sway_cmd cmd_ipc; | 118 | sway_cmd cmd_ipc; |
130 | sway_cmd cmd_kill; | 119 | sway_cmd cmd_kill; |
131 | sway_cmd cmd_layout; | 120 | sway_cmd cmd_layout; |
@@ -134,10 +123,10 @@ sway_cmd cmd_mark; | |||
134 | sway_cmd cmd_mode; | 123 | sway_cmd cmd_mode; |
135 | sway_cmd cmd_mouse_warping; | 124 | sway_cmd cmd_mouse_warping; |
136 | sway_cmd cmd_move; | 125 | sway_cmd cmd_move; |
126 | sway_cmd cmd_opacity; | ||
137 | sway_cmd cmd_new_float; | 127 | sway_cmd cmd_new_float; |
138 | sway_cmd cmd_new_window; | 128 | sway_cmd cmd_new_window; |
139 | sway_cmd cmd_no_focus; | 129 | sway_cmd cmd_no_focus; |
140 | sway_cmd cmd_orientation; | ||
141 | sway_cmd cmd_output; | 130 | sway_cmd cmd_output; |
142 | sway_cmd cmd_permit; | 131 | sway_cmd cmd_permit; |
143 | sway_cmd cmd_reject; | 132 | sway_cmd cmd_reject; |
@@ -153,6 +142,7 @@ sway_cmd cmd_splith; | |||
153 | sway_cmd cmd_splitt; | 142 | sway_cmd cmd_splitt; |
154 | sway_cmd cmd_splitv; | 143 | sway_cmd cmd_splitv; |
155 | sway_cmd cmd_sticky; | 144 | sway_cmd cmd_sticky; |
145 | sway_cmd cmd_swaybg_command; | ||
156 | sway_cmd cmd_unmark; | 146 | sway_cmd cmd_unmark; |
157 | sway_cmd cmd_workspace; | 147 | sway_cmd cmd_workspace; |
158 | sway_cmd cmd_ws_auto_back_and_forth; | 148 | sway_cmd cmd_ws_auto_back_and_forth; |
@@ -195,17 +185,28 @@ sway_cmd bar_colors_cmd_statusline; | |||
195 | sway_cmd bar_colors_cmd_focused_statusline; | 185 | sway_cmd bar_colors_cmd_focused_statusline; |
196 | sway_cmd bar_colors_cmd_urgent_workspace; | 186 | sway_cmd bar_colors_cmd_urgent_workspace; |
197 | 187 | ||
188 | sway_cmd input_cmd_seat; | ||
198 | sway_cmd input_cmd_accel_profile; | 189 | sway_cmd input_cmd_accel_profile; |
199 | sway_cmd input_cmd_click_method; | 190 | sway_cmd input_cmd_click_method; |
200 | sway_cmd input_cmd_drag_lock; | 191 | sway_cmd input_cmd_drag_lock; |
201 | sway_cmd input_cmd_dwt; | 192 | sway_cmd input_cmd_dwt; |
202 | sway_cmd input_cmd_events; | 193 | sway_cmd input_cmd_events; |
203 | sway_cmd input_cmd_left_handed; | 194 | sway_cmd input_cmd_left_handed; |
195 | sway_cmd input_cmd_map_to_output; | ||
204 | sway_cmd input_cmd_middle_emulation; | 196 | sway_cmd input_cmd_middle_emulation; |
205 | sway_cmd input_cmd_natural_scroll; | 197 | sway_cmd input_cmd_natural_scroll; |
206 | sway_cmd input_cmd_pointer_accel; | 198 | sway_cmd input_cmd_pointer_accel; |
207 | sway_cmd input_cmd_scroll_method; | 199 | sway_cmd input_cmd_scroll_method; |
208 | sway_cmd input_cmd_tap; | 200 | sway_cmd input_cmd_tap; |
201 | sway_cmd input_cmd_xkb_layout; | ||
202 | sway_cmd input_cmd_xkb_model; | ||
203 | sway_cmd input_cmd_xkb_options; | ||
204 | sway_cmd input_cmd_xkb_rules; | ||
205 | sway_cmd input_cmd_xkb_variant; | ||
206 | |||
207 | sway_cmd seat_cmd_attach; | ||
208 | sway_cmd seat_cmd_fallback; | ||
209 | sway_cmd seat_cmd_cursor; | ||
209 | 210 | ||
210 | sway_cmd cmd_ipc_cmd; | 211 | sway_cmd cmd_ipc_cmd; |
211 | sway_cmd cmd_ipc_events; | 212 | sway_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 | */ |
48 | struct sway_mode { | 48 | struct 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 | */ |
56 | struct input_config { | 57 | struct 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 | */ | ||
87 | struct 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 | */ | ||
95 | struct 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 | ||
328 | void pid_workspace_add(struct pid_workspace *pw); | 355 | void 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 | */ |
350 | void free_config(struct sway_config *config); | 377 | void free_config(struct sway_config *config); |
378 | |||
379 | void config_clear_handler_context(struct sway_config *config); | ||
380 | |||
381 | void 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); | |||
356 | struct cmd_results *check_security_config(); | 388 | struct cmd_results *check_security_config(); |
357 | 389 | ||
358 | int input_identifier_cmp(const void *item, const void *data); | 390 | int input_identifier_cmp(const void *item, const void *data); |
391 | |||
392 | struct input_config *new_input_config(const char* identifier); | ||
393 | |||
359 | void merge_input_config(struct input_config *dst, struct input_config *src); | 394 | void merge_input_config(struct input_config *dst, struct input_config *src); |
360 | void apply_input_config(struct input_config *ic, struct libinput_device *dev); | 395 | |
396 | struct input_config *copy_input_config(struct input_config *ic); | ||
397 | |||
361 | void free_input_config(struct input_config *ic); | 398 | void free_input_config(struct input_config *ic); |
362 | 399 | ||
400 | void apply_input_config(struct input_config *input); | ||
401 | |||
402 | int seat_name_cmp(const void *item, const void *data); | ||
403 | |||
404 | struct seat_config *new_seat_config(const char* name); | ||
405 | |||
406 | void merge_seat_config(struct seat_config *dst, struct seat_config *src); | ||
407 | |||
408 | struct seat_config *copy_seat_config(struct seat_config *seat); | ||
409 | |||
410 | void free_seat_config(struct seat_config *ic); | ||
411 | |||
412 | struct seat_attachment_config *seat_attachment_config_new(); | ||
413 | |||
414 | struct seat_attachment_config *seat_config_get_attachment( | ||
415 | struct seat_config *seat_config, char *identifier); | ||
416 | |||
417 | void apply_seat_config(struct seat_config *seat); | ||
418 | |||
363 | int output_name_cmp(const void *item, const void *data); | 419 | int output_name_cmp(const void *item, const void *data); |
420 | |||
421 | void output_get_identifier(char *identifier, size_t len, | ||
422 | struct sway_output *output); | ||
423 | |||
424 | struct output_config *new_output_config(const char *name); | ||
425 | |||
364 | void merge_output_config(struct output_config *dst, struct output_config *src); | 426 | void 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 | */ | ||
367 | void apply_output_config(struct output_config *oc, swayc_t *output); | ||
368 | void free_output_config(struct output_config *oc); | ||
369 | 427 | ||
370 | /** | 428 | void apply_output_config(struct output_config *oc, |
371 | * Updates the list of active bar modifiers | 429 | struct sway_container *output); |
372 | */ | 430 | |
373 | void update_active_bar_modifiers(void); | 431 | void free_output_config(struct output_config *oc); |
374 | 432 | ||
375 | int workspace_output_cmp_workspace(const void *a, const void *b); | 433 | int workspace_output_cmp_workspace(const void *a, const void *b); |
376 | 434 | ||
377 | int sway_binding_cmp(const void *a, const void *b); | 435 | int sway_binding_cmp(const void *a, const void *b); |
436 | |||
378 | int sway_binding_cmp_qsort(const void *a, const void *b); | 437 | int sway_binding_cmp_qsort(const void *a, const void *b); |
438 | |||
379 | int sway_binding_cmp_keys(const void *a, const void *b); | 439 | int sway_binding_cmp_keys(const void *a, const void *b); |
440 | |||
380 | void free_sway_binding(struct sway_binding *sb); | 441 | void free_sway_binding(struct sway_binding *sb); |
381 | struct sway_binding *sway_binding_dup(struct sway_binding *sb); | ||
382 | 442 | ||
383 | int sway_mouse_binding_cmp(const void *a, const void *b); | 443 | struct sway_binding *sway_binding_dup(struct sway_binding *sb); |
384 | int sway_mouse_binding_cmp_qsort(const void *a, const void *b); | ||
385 | int sway_mouse_binding_cmp_buttons(const void *a, const void *b); | ||
386 | void free_sway_mouse_binding(struct sway_mouse_binding *smb); | ||
387 | 444 | ||
388 | void load_swaybars(); | 445 | void load_swaybars(); |
446 | |||
447 | void invoke_swaybar(struct bar_config *bar); | ||
448 | |||
389 | void terminate_swaybg(pid_t pid); | 449 | void terminate_swaybg(pid_t pid); |
390 | 450 | ||
391 | /** | ||
392 | * Allocate and initialize default bar configuration. | ||
393 | */ | ||
394 | struct bar_config *default_bar_config(void); | 451 | struct bar_config *default_bar_config(void); |
395 | 452 | ||
396 | /** | 453 | void free_bar_config(struct bar_config *bar); |
397 | * Global config singleton. | 454 | |
398 | */ | 455 | /* Global config singleton. */ |
399 | extern struct sway_config *config; | 456 | extern struct sway_config *config; |
400 | 457 | ||
401 | /** | 458 | /* Config file currently being read */ |
402 | * Config file currently being read. | ||
403 | */ | ||
404 | extern const char *current_config_path; | 459 | extern 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 | |||
9 | typedef struct sway_container swayc_t; | ||
10 | |||
11 | extern swayc_t root_container; | ||
12 | extern 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 | */ | ||
20 | enum 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 | */ | ||
33 | enum 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 | |||
54 | enum 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 | */ | ||
65 | struct 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 | |||
175 | enum visibility_mask { | ||
176 | VISIBLE = true | ||
177 | } visible; | ||
178 | |||
179 | /** | ||
180 | * Allocates a new output container. | ||
181 | */ | ||
182 | swayc_t *new_output(wlc_handle handle); | ||
183 | /** | ||
184 | * Allocates a new workspace container. | ||
185 | */ | ||
186 | swayc_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 | */ | ||
194 | swayc_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 | */ | ||
200 | swayc_t *new_view(swayc_t *sibling, wlc_handle handle); | ||
201 | /** | ||
202 | * Allocates a new floating view in the active workspace. | ||
203 | */ | ||
204 | swayc_t *new_floating_view(wlc_handle handle); | ||
205 | |||
206 | void floating_view_sane_size(swayc_t *view); | ||
207 | |||
208 | /** | ||
209 | * Frees an output's container. | ||
210 | */ | ||
211 | swayc_t *destroy_output(swayc_t *output); | ||
212 | /** | ||
213 | * Destroys a workspace container and returns the parent pointer, or NULL. | ||
214 | */ | ||
215 | swayc_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 | */ | ||
220 | swayc_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 | */ | ||
225 | swayc_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 | */ | ||
231 | swayc_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 | */ | ||
235 | swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types); | ||
236 | /** | ||
237 | * Finds a parent with the given swayc_layout. | ||
238 | */ | ||
239 | swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts); | ||
240 | /** | ||
241 | * Finds the bottom-most focused container of a type. | ||
242 | */ | ||
243 | swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types); | ||
244 | /** | ||
245 | * Finds the bottom-most focused container of a layout. | ||
246 | */ | ||
247 | swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts); | ||
248 | |||
249 | /** | ||
250 | * Gets the swayc_t associated with a wlc_handle. | ||
251 | */ | ||
252 | swayc_t *swayc_by_handle(wlc_handle handle); | ||
253 | /** | ||
254 | * Gets the named swayc_t. | ||
255 | */ | ||
256 | swayc_t *swayc_by_name(const char *name); | ||
257 | /** | ||
258 | * Gets the active output's container. | ||
259 | */ | ||
260 | swayc_t *swayc_active_output(void); | ||
261 | /** | ||
262 | * Gets the active workspace's container. | ||
263 | */ | ||
264 | swayc_t *swayc_active_workspace(void); | ||
265 | /** | ||
266 | * Gets the workspace for the given view container. | ||
267 | */ | ||
268 | swayc_t *swayc_active_workspace_for(swayc_t *view); | ||
269 | /** | ||
270 | * Finds the container currently underneath the pointer. | ||
271 | */ | ||
272 | swayc_t *container_under_pointer(void); | ||
273 | /** | ||
274 | * Finds the first container following a callback. | ||
275 | */ | ||
276 | swayc_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 | */ | ||
281 | bool swayc_is_fullscreen(swayc_t *view); | ||
282 | /** | ||
283 | * Returns true if this view is focused. | ||
284 | */ | ||
285 | bool swayc_is_active(swayc_t *view); | ||
286 | /** | ||
287 | * Returns true if the parent is an ancestor of the child. | ||
288 | */ | ||
289 | bool swayc_is_parent_of(swayc_t *parent, swayc_t *child); | ||
290 | /** | ||
291 | * Returns true if the child is a desecendant of the parent. | ||
292 | */ | ||
293 | bool swayc_is_child_of(swayc_t *child, swayc_t *parent); | ||
294 | |||
295 | /** | ||
296 | * Returns true if this container is an empty workspace. | ||
297 | */ | ||
298 | bool 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 | */ | ||
304 | swayc_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 | */ | ||
310 | swayc_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 | */ | ||
318 | int swayc_gap(swayc_t *container); | ||
319 | |||
320 | /** | ||
321 | * Maps a container's children over a function. | ||
322 | */ | ||
323 | void 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 | */ | ||
331 | void set_view_visibility(swayc_t *view, void *data); | ||
332 | /** | ||
333 | * Set the gaps value for a view. | ||
334 | */ | ||
335 | void set_gaps(swayc_t *view, void *amount); | ||
336 | /** | ||
337 | * Add to the gaps value for a view. | ||
338 | */ | ||
339 | void add_gaps(swayc_t *view, void *amount); | ||
340 | |||
341 | /** | ||
342 | * Issue wlc calls to make the visibility of a container consistent. | ||
343 | */ | ||
344 | void update_visibility(swayc_t *container); | ||
345 | |||
346 | /** | ||
347 | * Close all child views of container | ||
348 | */ | ||
349 | void 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 | */ | ||
355 | swayc_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. |
34 | list_t *criteria_for(swayc_t *cont); | 34 | list_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. |
37 | list_t *container_for(list_t *tokens); | 37 | list_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 |
40 | bool criteria_any(swayc_t *cont, list_t *criteria); | 40 | bool 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 | |||
4 | extern bool enable_debug_tree; | ||
5 | void 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 | |||
3 | void 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 | |||
9 | struct 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 | |||
18 | struct 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 | |||
32 | struct desktop_shell_state { | ||
33 | list_t *backgrounds; | ||
34 | list_t *panels; | ||
35 | list_t *lock_surfaces; | ||
36 | bool is_locked; | ||
37 | }; | ||
38 | |||
39 | struct swaylock_state { | ||
40 | bool active; | ||
41 | wlc_handle output; | ||
42 | wlc_resource surface; | ||
43 | }; | ||
44 | |||
45 | struct decoration_state { | ||
46 | list_t *csd_resources; | ||
47 | }; | ||
48 | |||
49 | extern struct desktop_shell_state desktop_shell; | ||
50 | extern struct decoration_state decoration_state; | ||
51 | |||
52 | void register_extensions(void); | ||
53 | |||
54 | void 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 | ||
3 | enum 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 | |||
24 | swayc_t *get_focused_container(swayc_t *parent); | ||
25 | swayc_t *get_focused_view(swayc_t *parent); | ||
26 | swayc_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 | ||
30 | swayc_t *get_focused_view_include_floating(swayc_t *parent); | ||
31 | |||
32 | bool set_focused_container(swayc_t *container); | ||
33 | bool 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 | |||
38 | extern bool locked_container_focus; | ||
39 | |||
40 | // Prevents wss from being destroyed on focus switch | ||
41 | extern bool suspend_workspace_cleanup; | ||
42 | |||
43 | bool 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 | |||
7 | void register_wlc_handlers(); | ||
8 | |||
9 | extern 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 | |||
8 | struct input_config *new_input_config(const char* identifier); | ||
9 | |||
10 | char* libinput_dev_unique_id(struct libinput_device *dev); | ||
11 | |||
12 | /** | ||
13 | * Global input device list. | ||
14 | */ | ||
15 | extern 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 | */ | ||
21 | extern 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 | |||
6 | struct 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 | |||
30 | void sway_cursor_destroy(struct sway_cursor *cursor); | ||
31 | struct sway_cursor *sway_cursor_create(struct sway_seat *seat); | ||
32 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time); | ||
33 | void 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 | */ | ||
13 | extern struct sway_input_manager *input_manager; | ||
14 | |||
15 | struct 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 | |||
22 | struct 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 | |||
34 | struct sway_input_manager *input_manager_create(struct sway_server *server); | ||
35 | |||
36 | bool input_manager_has_focus(struct sway_input_manager *input, | ||
37 | struct sway_container *container); | ||
38 | |||
39 | void input_manager_set_focus(struct sway_input_manager *input, | ||
40 | struct sway_container *container); | ||
41 | |||
42 | void input_manager_configure_xcursor(struct sway_input_manager *input); | ||
43 | |||
44 | void input_manager_apply_input_config(struct sway_input_manager *input, | ||
45 | struct input_config *input_config); | ||
46 | |||
47 | void input_manager_apply_seat_config(struct sway_input_manager *input, | ||
48 | struct seat_config *seat_config); | ||
49 | |||
50 | struct sway_seat *input_manager_get_default_seat( | ||
51 | struct sway_input_manager *input); | ||
52 | |||
53 | struct 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 | */ | ||
59 | struct sway_seat *input_manager_current_seat(struct sway_input_manager *input); | ||
60 | |||
61 | struct 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 | |||
8 | struct 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 | |||
23 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | ||
24 | struct sway_seat_device *device); | ||
25 | |||
26 | void sway_keyboard_configure(struct sway_keyboard *keyboard); | ||
27 | |||
28 | void 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 | |||
8 | struct 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 | |||
15 | struct 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 | |||
24 | struct 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 | |||
46 | struct sway_seat *seat_create(struct sway_input_manager *input, | ||
47 | const char *seat_name); | ||
48 | |||
49 | void seat_destroy(struct sway_seat *seat); | ||
50 | |||
51 | void seat_add_device(struct sway_seat *seat, | ||
52 | struct sway_input_device *device); | ||
53 | |||
54 | void seat_configure_device(struct sway_seat *seat, | ||
55 | struct sway_input_device *device); | ||
56 | |||
57 | void seat_remove_device(struct sway_seat *seat, | ||
58 | struct sway_input_device *device); | ||
59 | |||
60 | void seat_configure_xcursor(struct sway_seat *seat); | ||
61 | |||
62 | void seat_set_focus(struct sway_seat *seat, struct sway_container *container); | ||
63 | |||
64 | void seat_set_focus_warp(struct sway_seat *seat, | ||
65 | struct sway_container *container, bool warp); | ||
66 | |||
67 | void seat_set_focus_surface(struct sway_seat *seat, | ||
68 | struct wlr_surface *surface); | ||
69 | |||
70 | void seat_set_focus_layer(struct sway_seat *seat, | ||
71 | struct wlr_layer_surface *layer); | ||
72 | |||
73 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
74 | struct wl_client *client); | ||
75 | |||
76 | struct 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 | */ | ||
87 | struct 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 | */ | ||
94 | struct 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 | */ | ||
101 | void 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 | |||
105 | void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); | ||
106 | |||
107 | struct seat_config *seat_get_config(struct sway_seat *seat); | ||
108 | |||
109 | bool 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 | ||
10 | bool check_key(uint32_t key_sym, uint32_t key_code); | ||
11 | |||
12 | // returns true if key_sym matches latest released key. | ||
13 | bool check_released_key(uint32_t key_sym); | ||
14 | |||
15 | // sets a key as pressed | ||
16 | void press_key(uint32_t key_sym, uint32_t key_code); | ||
17 | |||
18 | // unsets a key as pressed | ||
19 | void release_key(uint32_t key_sym, uint32_t key_code); | ||
20 | |||
21 | |||
22 | /* Pointer state */ | ||
23 | |||
24 | enum 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 | |||
32 | enum pointer_mode { | ||
33 | // Target | ||
34 | M_FLOATING = 1, | ||
35 | M_TILING = 2, | ||
36 | // Action | ||
37 | M_DRAGGING = 4, | ||
38 | M_RESIZING = 8, | ||
39 | }; | ||
40 | |||
41 | struct pointer_button_state { | ||
42 | bool held; | ||
43 | // state at the point it was pressed | ||
44 | int x, y; | ||
45 | swayc_t *view; | ||
46 | }; | ||
47 | |||
48 | extern 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 | |||
66 | enum modifier_state { | ||
67 | MOD_STATE_UNCHANGED = 0, | ||
68 | MOD_STATE_PRESSED = 1, | ||
69 | MOD_STATE_RELEASED = 2 | ||
70 | }; | ||
71 | |||
72 | void pointer_position_set(double new_x, double new_y, bool force_focus); | ||
73 | void 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 | ||
77 | void pointer_mode_set(uint32_t button, bool condition); | ||
78 | |||
79 | // Update mode in mouse motion | ||
80 | void pointer_mode_update(void); | ||
81 | |||
82 | // Reset mode on any keypress; | ||
83 | void pointer_mode_reset(void); | ||
84 | |||
85 | void 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 | */ | ||
94 | uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod); | ||
95 | |||
96 | /** | ||
97 | * Update the current modifiers state to new_state. | ||
98 | */ | ||
99 | void 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 | ||
8 | json_object *ipc_json_get_version(); | 7 | json_object *ipc_json_get_version(); |
8 | |||
9 | json_object *ipc_json_describe_container(struct sway_container *c); | ||
10 | json_object *ipc_json_describe_container_recursive(struct sway_container *c); | ||
11 | json_object *ipc_json_describe_input(struct sway_input_device *device); | ||
9 | json_object *ipc_json_describe_bar_config(struct bar_config *bar); | 12 | json_object *ipc_json_describe_bar_config(struct bar_config *bar); |
10 | json_object *ipc_json_describe_container(swayc_t *c); | ||
11 | json_object *ipc_json_describe_container_recursive(swayc_t *c); | ||
12 | json_object *ipc_json_describe_window(swayc_t *c); | ||
13 | json_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> | 7 | struct sway_server; |
5 | 8 | ||
6 | #include "container.h" | 9 | void ipc_init(struct sway_server *server); |
7 | #include "config.h" | ||
8 | #include "ipc.h" | ||
9 | 10 | ||
10 | void ipc_init(void); | ||
11 | void ipc_terminate(void); | 11 | void ipc_terminate(void); |
12 | |||
12 | struct sockaddr_un *ipc_user_sockaddr(void); | 13 | struct sockaddr_un *ipc_user_sockaddr(void); |
13 | 14 | ||
14 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change); | 15 | void ipc_event_workspace(struct sway_container *old, |
16 | struct sway_container *new, const char *change); | ||
17 | void ipc_event_window(struct sway_container *window, const char *change); | ||
15 | void ipc_event_barconfig_update(struct bar_config *bar); | 18 | void ipc_event_barconfig_update(struct bar_config *bar); |
16 | /** | ||
17 | * Send IPC mode event to all listening clients | ||
18 | */ | ||
19 | void ipc_event_mode(const char *mode); | 19 | void ipc_event_mode(const char *mode); |
20 | /** | ||
21 | * Send IPC window change event | ||
22 | */ | ||
23 | void 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 | */ | ||
29 | void ipc_event_modifier(uint32_t modifier, const char *state); | ||
30 | /** | ||
31 | * Send IPC keyboard binding event. | ||
32 | */ | ||
33 | void ipc_event_binding_keyboard(struct sway_binding *sb); | ||
34 | const char *swayc_type_string(enum swayc_types type); | ||
35 | |||
36 | /** | ||
37 | * Send pixel data to registered clients. | ||
38 | */ | ||
39 | void 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 | |||
8 | struct 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 | |||
22 | struct sway_output; | ||
23 | void 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 | |||
10 | extern list_t *scratchpad; | ||
11 | |||
12 | extern int min_sane_w; | ||
13 | extern int min_sane_h; | ||
14 | |||
15 | // Set initial values for root_container | ||
16 | void init_layout(void); | ||
17 | |||
18 | // Returns the index of child for its parent | ||
19 | int 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 | ||
23 | void 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 | ||
27 | void 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 | ||
31 | void add_floating(swayc_t *ws, swayc_t *child); | ||
32 | |||
33 | // insert child after sibling in parents children. | ||
34 | swayc_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 | ||
39 | swayc_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 | ||
43 | swayc_t *remove_child(swayc_t *child); | ||
44 | |||
45 | // 2 containers are swapped, they inherit eachothers focus | ||
46 | void swap_container(swayc_t *a, swayc_t *b); | ||
47 | |||
48 | // 2 Containers geometry are swapped, used with `swap_container` | ||
49 | void swap_geometry(swayc_t *a, swayc_t *b); | ||
50 | |||
51 | void move_container(swayc_t* container, enum movement_direction direction, int move_amt); | ||
52 | void move_container_to(swayc_t* container, swayc_t* destination); | ||
53 | void move_workspace_to(swayc_t* workspace, swayc_t* destination); | ||
54 | |||
55 | // Layout | ||
56 | /** | ||
57 | * Update child container geometries when switching between layouts. | ||
58 | */ | ||
59 | void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout); | ||
60 | void update_geometry(swayc_t *view); | ||
61 | void arrange_windows(swayc_t *container, double width, double height); | ||
62 | void arrange_backgrounds(void); | ||
63 | |||
64 | swayc_t *get_focused_container(swayc_t *parent); | ||
65 | swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir); | ||
66 | swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit); | ||
67 | |||
68 | void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge); | ||
69 | |||
70 | void layout_log(const swayc_t *c, int depth); | ||
71 | void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) __attribute__((format(printf,3,4))); | ||
72 | |||
73 | /** | ||
74 | * Get default layout. | ||
75 | */ | ||
76 | enum swayc_layouts default_layout(swayc_t *output); | ||
77 | |||
78 | bool is_auto_layout(enum swayc_layouts layout); | ||
79 | int auto_group_start_index(const swayc_t *container, int index); | ||
80 | int auto_group_end_index(const swayc_t *container, int index); | ||
81 | size_t auto_group_count(const swayc_t *container); | ||
82 | size_t auto_group_index(const swayc_t *container, int index); | ||
83 | bool 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" | 10 | struct sway_server; |
5 | #include "focus.h" | 11 | struct sway_container; |
6 | 12 | ||
7 | // Position is absolute coordinates on the edge where the adjacent output | 13 | struct sway_output { |
8 | // should be searched for. | 14 | struct wlr_output *wlr_output; |
9 | swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos); | 15 | struct sway_container *swayc; |
10 | swayc_t *swayc_opposite_output(enum movement_direction dir, const struct wlc_point *abs_pos); | 16 | struct sway_server *server; |
11 | swayc_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 |
14 | void 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; |
18 | void 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; |
21 | void sort_workspaces(swayc_t *output); | 25 | struct wl_listener mode; |
26 | struct wl_listener transform; | ||
27 | struct wl_listener scale; | ||
22 | 28 | ||
23 | void 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 | |||
35 | void output_damage_whole(struct sway_output *output); | ||
36 | |||
37 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
38 | struct wlr_surface *surface, bool whole); | ||
39 | |||
40 | void output_damage_view(struct sway_output *output, struct sway_view *view, | ||
41 | bool whole); | ||
42 | |||
43 | void output_damage_whole_container(struct sway_output *output, | ||
44 | struct sway_container *con); | ||
45 | |||
46 | struct 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 | |||
15 | struct 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 | |||
44 | struct sway_server server; | ||
45 | |||
46 | bool server_init(struct sway_server *server); | ||
47 | void server_fini(struct sway_server *server); | ||
48 | void server_run(struct sway_server *server); | ||
49 | |||
50 | void handle_new_output(struct wl_listener *listener, void *data); | ||
51 | |||
52 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); | ||
53 | void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); | ||
54 | void handle_xwayland_surface(struct wl_listener *listener, void *data); | ||
55 | void 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 | |||
9 | extern struct sway_container root_container; | ||
10 | |||
11 | struct sway_view; | ||
12 | struct 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 | */ | ||
20 | enum 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 | |||
31 | enum sway_container_layout { | ||
32 | L_NONE, | ||
33 | L_HORIZ, | ||
34 | L_VERT, | ||
35 | L_STACKED, | ||
36 | L_TABBED, | ||
37 | L_FLOATING, | ||
38 | }; | ||
39 | |||
40 | enum sway_container_border { | ||
41 | B_NONE, | ||
42 | B_PIXEL, | ||
43 | B_NORMAL, | ||
44 | }; | ||
45 | |||
46 | struct sway_root; | ||
47 | struct sway_output; | ||
48 | struct sway_view; | ||
49 | |||
50 | struct 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 | |||
94 | struct sway_container *container_create(enum sway_container_type type); | ||
95 | |||
96 | const char *container_type_to_str(enum sway_container_type type); | ||
97 | |||
98 | struct 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 | */ | ||
104 | struct 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 | */ | ||
110 | struct 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 | */ | ||
116 | struct 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 | */ | ||
124 | struct sway_container *container_view_create( | ||
125 | struct sway_container *sibling, struct sway_view *sway_view); | ||
126 | |||
127 | struct sway_container *container_destroy(struct sway_container *container); | ||
128 | |||
129 | struct sway_container *container_close(struct sway_container *container); | ||
130 | |||
131 | void 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 | */ | ||
139 | struct 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 | */ | ||
145 | struct 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 | */ | ||
153 | struct 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 | */ | ||
160 | void 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 | */ | ||
166 | void 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 | */ | ||
172 | bool 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 | */ | ||
178 | bool container_has_child(struct sway_container *con, | ||
179 | struct sway_container *child); | ||
180 | |||
181 | void container_create_notify(struct sway_container *container); | ||
182 | |||
183 | void container_damage_whole(struct sway_container *container); | ||
184 | |||
185 | bool container_reap_empty(struct sway_container *con); | ||
186 | |||
187 | struct sway_container *container_reap_empty_recursive( | ||
188 | struct sway_container *con); | ||
189 | |||
190 | struct 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 | |||
7 | enum movement_direction { | ||
8 | MOVE_LEFT, | ||
9 | MOVE_RIGHT, | ||
10 | MOVE_UP, | ||
11 | MOVE_DOWN, | ||
12 | MOVE_PARENT, | ||
13 | MOVE_CHILD, | ||
14 | }; | ||
15 | |||
16 | enum resize_edge { | ||
17 | RESIZE_EDGE_LEFT, | ||
18 | RESIZE_EDGE_RIGHT, | ||
19 | RESIZE_EDGE_TOP, | ||
20 | RESIZE_EDGE_BOTTOM, | ||
21 | }; | ||
22 | |||
23 | struct sway_container; | ||
24 | |||
25 | struct 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 | |||
39 | void layout_init(void); | ||
40 | |||
41 | void container_add_child(struct sway_container *parent, | ||
42 | struct sway_container *child); | ||
43 | |||
44 | struct sway_container *container_add_sibling(struct sway_container *parent, | ||
45 | struct sway_container *child); | ||
46 | |||
47 | struct sway_container *container_remove_child(struct sway_container *child); | ||
48 | |||
49 | struct sway_container *container_replace_child(struct sway_container *child, | ||
50 | struct sway_container *new_child); | ||
51 | |||
52 | struct sway_container *container_set_layout(struct sway_container *container, | ||
53 | enum sway_container_layout layout); | ||
54 | |||
55 | void container_move_to(struct sway_container* container, | ||
56 | struct sway_container* destination); | ||
57 | |||
58 | void container_move(struct sway_container *container, | ||
59 | enum movement_direction dir, int move_amt); | ||
60 | |||
61 | enum sway_container_layout container_get_default_layout( | ||
62 | struct sway_container *con); | ||
63 | |||
64 | void container_sort_workspaces(struct sway_container *output); | ||
65 | |||
66 | void arrange_windows(struct sway_container *container, | ||
67 | double width, double height); | ||
68 | |||
69 | struct sway_container *container_get_in_direction(struct sway_container | ||
70 | *container, struct sway_seat *seat, enum movement_direction dir); | ||
71 | |||
72 | struct sway_container *container_split(struct sway_container *child, | ||
73 | enum sway_container_layout layout); | ||
74 | |||
75 | void 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 | |||
10 | struct sway_container; | ||
11 | |||
12 | enum sway_view_type { | ||
13 | SWAY_VIEW_WL_SHELL, | ||
14 | SWAY_VIEW_XDG_SHELL_V6, | ||
15 | SWAY_VIEW_XWAYLAND, | ||
16 | }; | ||
17 | |||
18 | enum sway_view_prop { | ||
19 | VIEW_PROP_TITLE, | ||
20 | VIEW_PROP_APP_ID, | ||
21 | VIEW_PROP_CLASS, | ||
22 | VIEW_PROP_INSTANCE, | ||
23 | }; | ||
24 | |||
25 | struct 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 | |||
37 | struct 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 | |||
59 | struct 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 | |||
74 | struct 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 | |||
89 | struct 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 | |||
102 | struct 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 | |||
114 | struct sway_view_child; | ||
115 | |||
116 | struct 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 | */ | ||
123 | struct 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 | |||
135 | struct 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 | |||
145 | const char *view_get_title(struct sway_view *view); | ||
146 | |||
147 | const char *view_get_app_id(struct sway_view *view); | ||
148 | |||
149 | const char *view_get_class(struct sway_view *view); | ||
150 | |||
151 | const char *view_get_instance(struct sway_view *view); | ||
152 | |||
153 | void view_configure(struct sway_view *view, double ox, double oy, int width, | ||
154 | int height); | ||
155 | |||
156 | void view_set_activated(struct sway_view *view, bool activated); | ||
157 | |||
158 | void view_close(struct sway_view *view); | ||
159 | |||
160 | void view_damage(struct sway_view *view, bool whole); | ||
161 | |||
162 | void view_for_each_surface(struct sway_view *view, | ||
163 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
164 | |||
165 | // view implementation | ||
166 | |||
167 | void view_init(struct sway_view *view, enum sway_view_type type, | ||
168 | const struct sway_view_impl *impl); | ||
169 | |||
170 | void view_destroy(struct sway_view *view); | ||
171 | |||
172 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); | ||
173 | |||
174 | void view_unmap(struct sway_view *view); | ||
175 | |||
176 | void view_update_position(struct sway_view *view, double ox, double oy); | ||
177 | |||
178 | void view_update_size(struct sway_view *view, int width, int height); | ||
179 | |||
180 | void 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 | |||
184 | void 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 | |||
6 | extern char *prev_workspace_name; | ||
7 | |||
8 | char *workspace_next_name(const char *output_name); | ||
9 | |||
10 | bool workspace_switch(struct sway_container *workspace); | ||
11 | |||
12 | struct sway_container *workspace_by_number(const char* name); | ||
13 | |||
14 | struct sway_container *workspace_by_name(const char*); | ||
15 | |||
16 | struct sway_container *workspace_output_next(struct sway_container *current); | ||
17 | |||
18 | struct sway_container *workspace_next(struct sway_container *current); | ||
19 | |||
20 | struct sway_container *workspace_output_prev(struct sway_container *current); | ||
21 | |||
22 | struct sway_container *workspace_prev(struct sway_container *current); | ||
23 | |||
24 | bool 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 | |||
9 | extern char *prev_workspace_name; | ||
10 | |||
11 | char *workspace_next_name(const char *output_name); | ||
12 | swayc_t *workspace_create(const char*); | ||
13 | swayc_t *workspace_by_name(const char*); | ||
14 | swayc_t *workspace_by_number(const char*); | ||
15 | bool workspace_switch(swayc_t*); | ||
16 | swayc_t *workspace_output_next(); | ||
17 | swayc_t *workspace_next(); | ||
18 | swayc_t *workspace_output_prev(); | ||
19 | swayc_t *workspace_prev(); | ||
20 | swayc_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" | 6 | struct swaybar_config; |
5 | #include "client/window.h" | 7 | struct swaybar_output; |
6 | #include "list.h" | 8 | struct swaybar_workspace; |
7 | 9 | ||
8 | struct bar { | 10 | struct 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 | |||
19 | struct 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 | |||
28 | struct 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 | ||
21 | struct output { | 46 | struct 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 | ||
33 | struct workspace { | 67 | struct 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 */ | 76 | void bar_setup(struct swaybar *bar, |
42 | extern struct bar swaybar; | 77 | const char *socket_path, |
43 | 78 | const char *bar_id); | |
44 | /** True if sway needs to render */ | 79 | void bar_run(struct swaybar *bar); |
45 | extern bool dirty; | 80 | void bar_teardown(struct swaybar *bar); |
46 | |||
47 | /** | ||
48 | * Setup bar. | ||
49 | */ | ||
50 | void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id); | ||
51 | |||
52 | /** | ||
53 | * Create new output struct from name. | ||
54 | */ | ||
55 | struct output *new_output(const char *name); | ||
56 | 81 | ||
57 | /** | 82 | #endif |
58 | * Bar mainloop. | ||
59 | */ | ||
60 | void bar_run(struct bar *bar); | ||
61 | |||
62 | /** | ||
63 | * free workspace list. | ||
64 | */ | ||
65 | void free_workspaces(list_t *workspaces); | ||
66 | |||
67 | /** | ||
68 | * Teardown bar. | ||
69 | */ | ||
70 | void 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 | */ | ||
13 | struct box_colors { | 8 | struct 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 | /** | 14 | struct config_output { |
20 | * Swaybar config. | 15 | struct wl_list link; |
21 | */ | 16 | char *name; |
22 | struct config { | 17 | size_t index; |
18 | }; | ||
19 | |||
20 | struct 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 | /** | 53 | struct swaybar_config *init_config(); |
67 | * Parse position top|bottom|left|right. | 54 | void free_config(struct swaybar_config *config); |
68 | */ | ||
69 | uint32_t parse_position(const char *position); | 55 | uint32_t parse_position(const char *position); |
70 | 56 | ||
71 | /** | 57 | #endif |
72 | * Parse font. | ||
73 | */ | ||
74 | char *parse_font(const char *font); | ||
75 | |||
76 | /** | ||
77 | * Initialize default sway config. | ||
78 | */ | ||
79 | struct config *init_config(); | ||
80 | |||
81 | /** | ||
82 | * Free config struct. | ||
83 | */ | ||
84 | void 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); | |||
23 | void event_loop_poll(); | 22 | void event_loop_poll(); |
24 | 23 | ||
25 | void init_event_loop(); | 24 | void 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" | 6 | void ipc_initialize(struct swaybar *bar, const char *bar_id); |
5 | 7 | bool handle_ipc_readable(struct swaybar *bar); | |
6 | /** | 8 | void ipc_get_workspaces(struct swaybar *bar); |
7 | * Initialize ipc connection to sway and get sway state, outputs, bar_config. | 9 | void ipc_send_workspace_command(struct swaybar *bar, const char *ws); |
8 | */ | ||
9 | void ipc_bar_init(struct bar *bar, const char *bar_id); | ||
10 | |||
11 | /** | ||
12 | * Handle ipc event from sway. | ||
13 | */ | ||
14 | bool handle_ipc_event(struct bar *bar); | ||
15 | |||
16 | |||
17 | /** | ||
18 | * Send workspace command to sway | ||
19 | */ | ||
20 | void 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" | 4 | struct swaybar; |
5 | #include "bar.h" | 5 | struct swaybar_output; |
6 | struct swaybar_config; | ||
6 | 7 | ||
7 | /** | 8 | void render_frame(struct swaybar *bar, struct swaybar_output *output); |
8 | * Render swaybar. | ||
9 | */ | ||
10 | void 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 | */ | ||
15 | void set_window_height(struct window *window, int height); | ||
16 | |||
17 | /** | ||
18 | * Compute the size of a workspace name | ||
19 | */ | ||
20 | void 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 | ||
10 | typedef enum {UNDEF, TEXT, I3BAR} command_protocol; | 8 | enum status_protocol { |
9 | PROTOCOL_UNDEF, | ||
10 | PROTOCOL_ERROR, | ||
11 | PROTOCOL_TEXT, | ||
12 | PROTOCOL_I3BAR, | ||
13 | }; | ||
14 | |||
15 | struct text_protocol_state { | ||
16 | char *buffer; | ||
17 | size_t buffer_size; | ||
18 | }; | ||
11 | 19 | ||
12 | struct status_line { | 20 | enum 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 | |||
26 | struct 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 | ||
19 | struct status_block { | 37 | struct 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 | /** | 56 | struct status_line { |
42 | * Initialize status line struct. | 57 | pid_t pid; |
43 | */ | 58 | int read_fd, write_fd; |
44 | struct 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 |
49 | bool 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 | }; |
54 | bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button); | ||
55 | 68 | ||
56 | /** | 69 | struct status_line *status_line_init(char *cmd); |
57 | * Free status line struct. | 70 | void status_error(struct status_line *status, const char *text); |
58 | */ | 71 | bool status_handle_readable(struct status_line *status); |
59 | void free_status_line(struct status_line *line); | 72 | void status_line_free(struct status_line *status); |
73 | bool i3bar_handle_readable(struct status_line *status); | ||
74 | void 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 | |||
5 | enum 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 | |||
16 | enum 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 | |||
28 | struct 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 | |||
36 | extern 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> | |
6 | enum 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 | ||
14 | enum auth_state { | 12 | enum 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 | ||
22 | enum line_source { | 20 | struct swaylock_args { |
23 | LINE_SOURCE_DEFAULT, | ||
24 | LINE_SOURCE_RING, | ||
25 | LINE_SOURCE_INSIDE, | ||
26 | }; | ||
27 | |||
28 | struct 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 | ||
41 | struct lock_colors { | 26 | struct 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 | ||
46 | struct lock_config { | 32 | struct 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; | 46 | struct 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 | ||
64 | void render(struct render_data* render_data, struct lock_config *config); | 59 | void swaylock_handle_key(struct swaylock_state *state, |
60 | xkb_keysym_t keysym, uint32_t codepoint); | ||
61 | void render_frame(struct swaylock_surface *surface); | ||
62 | void 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 | */ | ||
15 | uint32_t utf8_decode(const char **str); | ||
16 | |||
17 | /** | ||
18 | * Encodes a character as UTF-8 and returns the length of that character. | ||
19 | */ | ||
20 | size_t utf8_encode(char *str, uint32_t ch); | ||
21 | |||
22 | /** | ||
23 | * Returns the size of the next UTF-8 character | ||
24 | */ | ||
25 | int utf8_size(const char *str); | ||
26 | |||
27 | /** | ||
28 | * Returns the size of a UTF-8 character | ||
29 | */ | ||
30 | size_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 @@ | |||
1 | project( | ||
2 | 'sway', | ||
3 | 'c', | ||
4 | license: 'MIT', | ||
5 | default_options: [ | ||
6 | 'c_std=c11', | ||
7 | 'warning_level=2', | ||
8 | 'werror=true', | ||
9 | ], | ||
10 | ) | ||
11 | |||
12 | add_project_arguments('-Wno-unused-parameter', language: 'c') | ||
13 | add_project_arguments('-Wno-unused-function', language: 'c') | ||
14 | add_project_arguments('-Wno-unused-result', language: 'c') | ||
15 | |||
16 | cc = meson.get_compiler('c') | ||
17 | |||
18 | is_freebsd = host_machine.system().startswith('freebsd') | ||
19 | datadir = get_option('datadir') | ||
20 | sysconfdir = get_option('sysconfdir') | ||
21 | prefix = get_option('prefix') | ||
22 | |||
23 | jsonc = dependency('json-c', version: '>=0.13') | ||
24 | pcre = dependency('libpcre') | ||
25 | wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) | ||
26 | wayland_server = dependency('wayland-server') | ||
27 | wayland_client = dependency('wayland-client') | ||
28 | wayland_cursor = dependency('wayland-cursor') | ||
29 | wayland_egl = dependency('wayland-egl') | ||
30 | wayland_protos = dependency('wayland-protocols') | ||
31 | xkbcommon = dependency('xkbcommon') | ||
32 | cairo = dependency('cairo') | ||
33 | pango = dependency('pango') | ||
34 | pangocairo = dependency('pangocairo') | ||
35 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) | ||
36 | pixman = dependency('pixman-1') | ||
37 | libcap = dependency('libcap') | ||
38 | libinput = dependency('libinput', version: '>=1.6.0') | ||
39 | libpam = cc.find_library('libpam') | ||
40 | math = cc.find_library('m') | ||
41 | rt = cc.find_library('rt') | ||
42 | git = find_program('git', required: false) | ||
43 | a2x = find_program('a2x', required: false) | ||
44 | |||
45 | conf_data = configuration_data() | ||
46 | |||
47 | if gdk_pixbuf.found() | ||
48 | conf_data.set('HAVE_GDK_PIXBUF', true) | ||
49 | endif | ||
50 | |||
51 | if 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 | ||
81 | endif | ||
82 | |||
83 | add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c') | ||
84 | |||
85 | version = get_option('sway_version') | ||
86 | if version != '' | ||
87 | version = '"@0@"'.format(version) | ||
88 | else | ||
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) | ||
96 | endif | ||
97 | add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') | ||
98 | |||
99 | sway_inc = include_directories('include') | ||
100 | |||
101 | subdir('include') | ||
102 | subdir('protocols') | ||
103 | subdir('common') | ||
104 | subdir('sway') | ||
105 | subdir('swaymsg') | ||
106 | |||
107 | subdir('client') | ||
108 | subdir('swaybg') | ||
109 | subdir('swaybar') | ||
110 | subdir('swaylock') | ||
111 | |||
112 | config = configuration_data() | ||
113 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) | ||
114 | config.set('datadir', join_paths(prefix, datadir)) | ||
115 | config.set('prefix', prefix) | ||
116 | |||
117 | configure_file( | ||
118 | configuration: config, | ||
119 | input: 'config.in', | ||
120 | output: '@BASENAME@', | ||
121 | install_dir: sysconfdir + '/sway' | ||
122 | ) | ||
123 | |||
124 | if 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 | ) | ||
131 | else | ||
132 | configure_file( | ||
133 | configuration: config, | ||
134 | input: 'security.d/00-defaults.in', | ||
135 | output: '@BASENAME@', | ||
136 | install_dir: sysconfdir + '/sway/security.d' | ||
137 | ) | ||
138 | endif | ||
139 | |||
140 | install_data( | ||
141 | 'sway.desktop', | ||
142 | install_dir: datadir + '/wayland-sessions' | ||
143 | ) | ||
144 | |||
145 | if (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) | ||
159 | endif | ||
160 | |||
161 | if (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) | ||
171 | endif | ||
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 @@ | |||
1 | option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') | ||
2 | option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') | ||
3 | option('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 @@ | |||
1 | include(Wayland) | ||
2 | |||
3 | WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-xdg-shell | ||
4 | xdg-shell.xml | ||
5 | xdg-shell | ||
6 | ) | ||
7 | WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-desktop-shell | ||
8 | desktop-shell.xml | ||
9 | desktop-shell | ||
10 | ) | ||
11 | WAYLAND_ADD_PROTOCOL_SERVER(proto-server-desktop-shell | ||
12 | desktop-shell.xml | ||
13 | desktop-shell | ||
14 | ) | ||
15 | |||
16 | WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-swaylock | ||
17 | swaylock.xml | ||
18 | swaylock | ||
19 | ) | ||
20 | WAYLAND_ADD_PROTOCOL_SERVER(proto-server-swaylock | ||
21 | swaylock.xml | ||
22 | swaylock | ||
23 | ) | ||
24 | |||
25 | WAYLAND_ADD_PROTOCOL_SERVER(proto-server-gamma-control | ||
26 | gamma-control.xml | ||
27 | gamma-control | ||
28 | ) | ||
29 | |||
30 | WAYLAND_ADD_PROTOCOL_SERVER(proto-server-server-decoration | ||
31 | server-decoration.xml | ||
32 | server-decoration | ||
33 | ) | ||
34 | |||
35 | add_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 | |||
45 | set(PROTOCOLS_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/protocols PARENT_SCOPE) | ||
46 | INCLUDE_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 @@ | |||
1 | wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') | ||
2 | |||
3 | wayland_scanner = find_program('wayland-scanner') | ||
4 | |||
5 | wayland_scanner_code = generator( | ||
6 | wayland_scanner, | ||
7 | output: '@BASENAME@-protocol.c', | ||
8 | arguments: ['code', '@INPUT@', '@OUTPUT@'], | ||
9 | ) | ||
10 | |||
11 | wayland_scanner_client = generator( | ||
12 | wayland_scanner, | ||
13 | output: '@BASENAME@-client-protocol.h', | ||
14 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'], | ||
15 | ) | ||
16 | |||
17 | wayland_scanner_server = generator( | ||
18 | wayland_scanner, | ||
19 | output: '@BASENAME@-protocol.h', | ||
20 | arguments: ['server-header', '@INPUT@', '@OUTPUT@'], | ||
21 | ) | ||
22 | |||
23 | client_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 | |||
29 | server_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 | |||
35 | client_protos_src = [] | ||
36 | client_protos_headers = [] | ||
37 | |||
38 | server_protos_src = [] | ||
39 | server_protos_headers = [] | ||
40 | |||
41 | foreach 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) | ||
45 | endforeach | ||
46 | |||
47 | foreach 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) | ||
51 | endforeach | ||
52 | |||
53 | lib_client_protos = static_library( | ||
54 | 'client_protos', | ||
55 | client_protos_src + client_protos_headers, | ||
56 | dependencies: [wayland_client] | ||
57 | ) # for the include directory | ||
58 | |||
59 | client_protos = declare_dependency( | ||
60 | link_with: lib_client_protos, | ||
61 | sources: client_protos_headers, | ||
62 | ) | ||
63 | |||
64 | lib_server_protos = static_library( | ||
65 | 'server_protos', | ||
66 | server_protos_src + server_protos_headers, | ||
67 | dependencies: [wayland_client] | ||
68 | ) # for the include directory | ||
69 | |||
70 | server_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 |
12 | permit * fullscreen keyboard mouse | 12 | permit * fullscreen keyboard mouse |
13 | permit __PREFIX__/bin/swaylock lock | 13 | permit @prefix@/bin/swaylock lock |
14 | permit __PREFIX__/bin/swaybg background | 14 | permit @prefix@/bin/swaybg background |
15 | permit __PREFIX__/bin/swaygrab screenshot | 15 | permit @prefix@/bin/swaygrab screenshot |
16 | permit __PREFIX__/bin/swaybar panel | 16 | permit @prefix@/bin/swaybar panel |
17 | 17 | ||
18 | # Configures enabled IPC features for specific programs | 18 | # Configures enabled IPC features for specific programs |
19 | ipc __PREFIX__/bin/swaymsg { | 19 | ipc @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 | ||
27 | ipc __PREFIX__/bin/swaybar { | 27 | ipc @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 | ||
39 | ipc __PREFIX__/bin/swaygrab { | 39 | ipc @prefix@/bin/swaygrab { |
40 | outputs enabled | 40 | outputs enabled |
41 | tree enabled | 41 | tree enabled |
42 | } | 42 | } |
43 | 43 | ||
44 | ipc __PREFIX__/bin/swaylock { | 44 | ipc @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 @@ | |||
1 | include_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 | |||
13 | file(GLOB cmds | ||
14 | "commands/*.c" | ||
15 | "commands/bar/*.c" | ||
16 | "commands/input/*.c" | ||
17 | ) | ||
18 | |||
19 | add_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 | |||
42 | add_definitions( | ||
43 | -DSYSCONFDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}" | ||
44 | ) | ||
45 | |||
46 | target_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 | |||
61 | if (CMAKE_SYSTEM_NAME STREQUAL Linux) | ||
62 | target_link_libraries(sway cap) | ||
63 | endif (CMAKE_SYSTEM_NAME STREQUAL Linux) | ||
64 | |||
65 | install( | ||
66 | TARGETS sway | ||
67 | RUNTIME | ||
68 | DESTINATION bin | ||
69 | COMPONENT runtime | ||
70 | ) | ||
71 | |||
72 | add_custom_target(configs ALL) | ||
73 | |||
74 | function(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}) | ||
92 | endfunction() | ||
93 | |||
94 | add_config(config config sway) | ||
95 | add_config(00-defaults security.d/00-defaults sway/security.d) | ||
96 | |||
97 | if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) | ||
98 | add_config(10-freebsd security.d/10-freebsd sway/security.d) | ||
99 | endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) | ||
100 | |||
101 | if (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) | ||
107 | endif() | ||
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 | |||
15 | void 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 | |||
25 | void border_clear(struct border *border) { | ||
26 | if (border && border->buffer) { | ||
27 | free(border->buffer); | ||
28 | border->buffer = NULL; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | static 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 | */ | ||
73 | static 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 | |||
99 | int 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 | |||
109 | static 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 | |||
175 | static 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 | */ | ||
250 | static 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 | |||
321 | void 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 | |||
351 | static 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 | |||
457 | cleanup: | ||
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 | |||
469 | void 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 | |||
480 | void 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 | |||
498 | bool 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 | ||
39 | struct cmd_handler { | 18 | struct cmd_handler { |
@@ -41,10 +20,6 @@ struct cmd_handler { | |||
41 | sway_cmd *handle; | 20 | sway_cmd *handle; |
42 | }; | 21 | }; |
43 | 22 | ||
44 | int sp_index = 0; | ||
45 | |||
46 | swayc_t *current_container = NULL; | ||
47 | |||
48 | // Returns error object, or NULL if check succeeds. | 23 | // Returns error object, or NULL if check succeeds. |
49 | struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) { | 24 | struct 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 | ||
87 | void hide_view_in_scratchpad(swayc_t *sp_view) { | 62 | void 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 | |||
104 | void 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 | ||
140 | void remove_view_from_scratchpad(swayc_t *view) { | 78 | void 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 */ |
156 | static struct cmd_handler handlers[] = { | 95 | static 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 | ||
224 | static struct cmd_handler bar_handlers[] = { | 112 | static 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 | */ | ||
256 | struct 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 | |||
285 | static 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 | |||
299 | static struct cmd_handler bar_colors_handlers[] = { | 139 | static 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 | ||
313 | static struct cmd_handler ipc_handlers[] = { | 153 | /* Config-time only commands. Keep alphabetized */ |
314 | { "*", cmd_ipc_cmd }, | 154 | static 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 | ||
325 | static struct cmd_handler ipc_event_handlers[] = { | 160 | /* Runtime-only commands. Keep alphabetized */ |
326 | { "*", cmd_ipc_event_cmd }, | 161 | static 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 | ||
335 | static int handler_compare(const void *_a, const void *_b) { | 176 | static 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 | ||
183 | static 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 | ||
204 | static struct cmd_handler seat_handlers[] = { | ||
205 | { "attach", seat_cmd_attach }, | ||
206 | { "cursor", seat_cmd_cursor }, | ||
207 | { "fallback", seat_cmd_fallback }, | ||
208 | }; | ||
209 | |||
341 | static struct cmd_handler *find_handler(char *line, enum cmd_status block) { | 210 | static 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 | ||
373 | struct cmd_results *handle_command(char *_exec, enum command_context context) { | 262 | struct 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: | 386 | cleanup: |
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) |
510 | struct cmd_results *config_command(char *exec, enum cmd_status block) { | 401 | struct 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 | ||
632 | struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) { | 524 | struct 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) { | |||
669 | const char *cmd_results_to_json(struct cmd_results *results) { | 562 | const 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 | */ | ||
587 | struct 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 | |||
9 | struct 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 | ||
9 | struct cmd_results *cmd_bar(int argc, char **argv) { | 8 | struct 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 | ||
5 | struct cmd_results *bar_cmd_activate_button(int argc, char **argv) { | 5 | struct 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 | ||
6 | struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { | 6 | struct 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 | ||
9 | struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { | 9 | struct 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 | ||
4 | static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) { | 4 | static 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 | ||
25 | static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) { | 20 | static 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 | ||
71 | struct cmd_results *bar_colors_cmd_background(int argc, char **argv) { | 60 | struct 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 | ||
75 | struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) { | 65 | struct 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 | ||
79 | struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) { | 70 | struct 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 | ||
106 | struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) { | 97 | struct 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 | ||
110 | struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) { | 102 | struct 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 | ||
114 | struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) { | 107 | struct 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 | ||
118 | struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) { | 112 | struct 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 | ||
122 | struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { | 117 | struct 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 | ||
5 | struct cmd_results *bar_cmd_context_button(int argc, char **argv) { | 5 | struct 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 | ||
9 | static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const char *hidden_state) { | 9 | static 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 | ||
5 | struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { | 5 | struct 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 | ||
5 | struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) { | 5 | struct 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 | ||
6 | struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { | 6 | struct 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 | ||
5 | struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { | 5 | struct 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 | ||
6 | struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { | 6 | struct 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 | ||
12 | int binding_order = 0; | 16 | int binding_order = 0; |
13 | 17 | ||
18 | void 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 | */ | ||
35 | bool 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 | |||
14 | struct cmd_results *cmd_bindsym(int argc, char **argv) { | 72 | struct 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 | |||
9 | struct 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 | |||
5 | static 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 | |||
35 | struct cmd_results *cmd_client_focused(int argc, char **argv) { | ||
36 | return parse_border_color(&config->border_colors.focused, "client.focused", argc, argv); | ||
37 | } | ||
38 | |||
39 | struct 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 | |||
43 | struct cmd_results *cmd_client_unfocused(int argc, char **argv) { | ||
44 | return parse_border_color(&config->border_colors.unfocused, "client.unfocused", argc, argv); | ||
45 | } | ||
46 | |||
47 | struct cmd_results *cmd_client_urgent(int argc, char **argv) { | ||
48 | return parse_border_color(&config->border_colors.urgent, "client.urgent", argc, argv); | ||
49 | } | ||
50 | |||
51 | struct cmd_results *cmd_client_placeholder(int argc, char **argv) { | ||
52 | return parse_border_color(&config->border_colors.placeholder, "client.placeholder", argc, argv); | ||
53 | } | ||
54 | |||
55 | struct 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 | |||
7 | static 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 | |||
19 | struct 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 | |||
8 | struct 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 | |||
6 | struct 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 | |||
7 | struct 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 | |||
7 | struct 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 | ||
5 | struct cmd_results *cmd_orientation(int argc, char **argv) { | 5 | struct 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 | ||
4 | void sway_terminate(int exit_code); | 5 | void sway_terminate(int exit_code); |
5 | 6 | ||
6 | struct cmd_results *cmd_exit(int argc, char **argv) { | 7 | struct 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 | |||
10 | struct 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 | |||
6 | struct 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 | |||
6 | struct 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 | |||
10 | struct 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 | |||
8 | struct 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" | 9 | static 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 | ||
11 | struct cmd_results *cmd_focus(int argc, char **argv) { | 30 | struct 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 | |||
8 | struct 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 | |||
9 | struct 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 | |||
5 | struct 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 | |||
10 | struct 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 | |||
11 | struct 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 | |||
5 | struct 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 | ||
7 | struct cmd_results *cmd_input(int argc, char **argv) { | 7 | struct 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 | ||
6 | struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { | 7 | struct 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 | ||
7 | struct cmd_results *input_cmd_click_method(int argc, char **argv) { | 8 | struct 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 | ||
6 | struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { | 7 | struct 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 | ||
6 | struct cmd_results *input_cmd_dwt(int argc, char **argv) { | 7 | struct 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 | ||
7 | struct cmd_results *input_cmd_events(int argc, char **argv) { | 8 | struct 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 | ||
6 | struct cmd_results *input_cmd_left_handed(int argc, char **argv) { | 7 | struct 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 | |||
9 | struct 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 | ||
6 | struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { | 7 | struct 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 | ||
6 | struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { | 7 | struct 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 | ||
6 | struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { | 7 | struct 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 | ||
6 | struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { | 7 | struct 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 | ||
7 | struct cmd_results *input_cmd_tap(int argc, char **argv) { | 8 | struct 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 | |||
7 | struct 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 | |||
7 | struct 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 | |||
7 | struct 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 | |||
7 | struct 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 | |||
7 | struct 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 | |||
11 | static struct ipc_policy *current_policy = NULL; | ||
12 | |||
13 | struct 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 | |||
46 | struct 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 | |||
64 | struct 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 | |||
115 | struct 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 | ||
5 | struct cmd_results *cmd_kill(int argc, char **argv) { | 9 | struct 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 | */ | ||
10 | static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv); | ||
11 | 7 | ||
12 | struct cmd_results *cmd_layout(int argc, char **argv) { | 8 | struct 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 | |||
77 | static 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 | |||
6 | struct 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 | |||
8 | static 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 | |||
17 | struct 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 | |||
16 | static 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 | |||
22 | static 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 | |||
50 | static 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 | |||
133 | static 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 | ||
12 | struct cmd_results *cmd_move(int argc, char **argv) { | 158 | struct 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 | |||
4 | struct 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 | |||
4 | struct 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 | |||
9 | struct 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 | |||
7 | static 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 | |||
16 | struct 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 | ||
23 | struct cmd_results *cmd_output(int argc, char **argv) { | 23 | static 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; | 67 | static 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 | |||
104 | static 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 | |||
120 | static 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 | |||
152 | static 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 | |||
233 | struct 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 | |||
9 | static 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 | |||
43 | struct 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 | |||
78 | struct 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 | ||
5 | struct cmd_results *cmd_reload(int argc, char **argv) { | 5 | struct 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 | ||
15 | enum resize_dim_types { | 12 | static const int MIN_SANE_W = 100, MIN_SANE_H = 60; |
16 | RESIZE_DIM_PX, | ||
17 | RESIZE_DIM_PPT, | ||
18 | RESIZE_DIM_DEFAULT, | ||
19 | }; | ||
20 | |||
21 | static 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); | 14 | enum 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); | 21 | enum resize_axis { |
44 | } | 22 | RESIZE_AXIS_HORIZONTAL, |
23 | RESIZE_AXIS_VERTICAL, | ||
24 | RESIZE_AXIS_INVALID, | ||
25 | }; | ||
45 | 26 | ||
46 | return true; | 27 | static 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 | ||
52 | static bool resize_floating(int amount, bool use_width) { | 40 | static 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; | 50 | static 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 | ||
66 | static bool resize_tiled(int amount, bool use_width) { | 54 | static 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 | } | 58 | static 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 | |||
200 | static 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); | |
213 | static 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 | ||
227 | static 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 | ||
262 | static 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); | 188 | static 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 | ||
326 | struct cmd_results *cmd_resize(int argc, char **argv) { | 213 | struct 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 | |||
9 | static 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 | |||
37 | struct 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 | |||
5 | struct 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 | |||
7 | struct 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 | |||
10 | struct 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 | |||
13 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, | ||
14 | char *action, char *button_str, uint32_t time); | ||
15 | |||
16 | static 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 | |||
20 | struct 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 | |||
65 | static 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 | |||
7 | struct 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 | ||
18 | void 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 | |||
17 | struct cmd_results *cmd_set(int argc, char **argv) { | 27 | struct 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 | |||
5 | struct 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 | |||
5 | struct 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 | ||
11 | static struct cmd_results *_do_split(int argc, char **argv, int layout) { | 11 | static 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 | ||
57 | struct cmd_results *cmd_split(int argc, char **argv) { | 20 | struct 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 | ||
83 | struct cmd_results *cmd_splitv(int argc, char **argv) { | 48 | struct 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 | ||
87 | struct cmd_results *cmd_splith(int argc, char **argv) { | 56 | struct 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 | ||
91 | struct cmd_results *cmd_splitt(int argc, char **argv) { | 64 | struct 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 | |||
5 | struct 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 | |||
6 | struct 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 | |||
7 | struct 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 | |||
5 | struct 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 | ||
31 | struct sway_config *config = NULL; | 33 | struct sway_config *config = NULL; |
32 | 34 | ||
33 | static void terminate_swaybar(pid_t pid); | ||
34 | |||
35 | static 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 | |||
44 | static 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 | |||
53 | static void free_mode(struct sway_mode *mode) { | 35 | static 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 | |||
66 | static 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 | ||
118 | void 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 | |||
126 | void 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 | |||
136 | static 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 | |||
145 | static 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]); |
164 | void 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 | |||
195 | void 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 | |||
205 | void free_command_policy(struct command_policy *policy) { | ||
206 | if (!policy) { | ||
207 | return; | ||
208 | } | ||
209 | free(policy->command); | ||
210 | free(policy); | ||
211 | } | ||
212 | |||
213 | void free_feature_policy(struct feature_policy *policy) { | ||
214 | if (!policy) { | ||
215 | return; | ||
216 | } | ||
217 | free(policy->program); | ||
218 | free(policy); | ||
219 | } | 55 | } |
220 | 56 | ||
221 | void free_config(struct sway_config *config) { | 57 | void 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 | 116 | static void destroy_removed_seats(struct sway_config *old_config, | |
294 | static 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 | ||
298 | static void config_defaults(struct sway_config *config) { | 133 | static 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 | ||
406 | static int compare_modifiers(const void *left, const void *right) { | 244 | static 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 | |||
413 | void 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 | ||
431 | static char *get_config_path(void) { | 248 | static 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) { | |||
472 | const char *current_config_path; | 290 | const char *current_config_path; |
473 | 291 | ||
474 | static bool load_config(const char *path, struct sway_config *config) { | 292 | static 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 | ||
505 | static int qstrcmp(const void* a, const void* b) { | ||
506 | return strcmp(*((char**) a), *((char**) b)); | ||
507 | } | ||
508 | |||
509 | bool load_main_config(const char *file, bool is_active) { | 323 | bool 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 | ||
595 | static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) { | 411 | static 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 | ||
684 | struct cmd_results *check_security_config() { | 504 | void 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 | ||
693 | bool read_config(FILE *file, struct sway_config *config) { | 511 | bool 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 | ||
841 | int 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 | |||
847 | int 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 | |||
854 | void 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 | |||
893 | void 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 | |||
932 | static 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 | |||
997 | static 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 | |||
1007 | void 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 | |||
1017 | static 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 | |||
1030 | void 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 | |||
1067 | void 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 | |||
1120 | void 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 | |||
1200 | char *do_var_replacement(char *str) { | 672 | char *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 | |||
1251 | int 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 | |||
1297 | int 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 | |||
1306 | int sway_binding_cmp_qsort(const void *a, const void *b) { | ||
1307 | return sway_binding_cmp(*(void **)a, *(void **)b); | ||
1308 | } | ||
1309 | |||
1310 | void 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 | |||
1323 | int 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 | |||
1334 | int 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 | |||
1343 | int sway_mouse_binding_cmp_qsort(const void *a, const void *b) { | ||
1344 | return sway_mouse_binding_cmp(*(void **)a, *(void **)b); | ||
1345 | } | ||
1346 | |||
1347 | void free_sway_mouse_binding(struct sway_mouse_binding *binding) { | ||
1348 | if (binding->command) { | ||
1349 | free(binding->command); | ||
1350 | } | ||
1351 | free(binding); | ||
1352 | } | ||
1353 | |||
1354 | struct 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 | |||
1379 | struct 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 | |||
1440 | cleanup: | ||
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 | |||
18 | static 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 | |||
29 | void 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 | |||
71 | struct 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; | ||
151 | cleanup: | ||
152 | free_bar_config(bar); | ||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | void 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 | |||
206 | static 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 | |||
217 | void 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 | |||
8 | struct 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 | |||
36 | void 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 | |||
97 | struct 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 | |||
107 | void free_input_config(struct input_config *ic) { | ||
108 | if (!ic) { | ||
109 | return; | ||
110 | } | ||
111 | free(ic->identifier); | ||
112 | free(ic); | ||
113 | } | ||
114 | |||
115 | int 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 | |||
14 | int 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 | |||
21 | void 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 | |||
28 | struct 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 | |||
47 | void 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 | |||
86 | static 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 | |||
113 | void 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 | |||
123 | void 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 | |||
205 | void 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 | |||
7 | struct 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 | |||
33 | struct 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 | |||
43 | static void seat_attachment_config_free( | ||
44 | struct seat_attachment_config *attachment) { | ||
45 | free(attachment->identifier); | ||
46 | free(attachment); | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | static 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 | |||
62 | static 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 | |||
67 | void 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 | |||
102 | struct 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 | |||
113 | void 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 | |||
129 | int 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 | |||
135 | struct 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 | |||
22 | static 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 | |||
43 | static 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 | |||
95 | static 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 | |||
121 | swayc_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 | |||
217 | swayc_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 | |||
242 | swayc_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 | |||
292 | swayc_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 | |||
332 | swayc_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 | |||
389 | void 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 | |||
431 | swayc_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 | |||
458 | swayc_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 | |||
501 | swayc_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 | |||
514 | swayc_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 | |||
532 | swayc_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 | |||
560 | static 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 | |||
567 | swayc_t *swayc_by_name(const char *name) { | ||
568 | return swayc_by_test(&root_container, test_name, (void *)name); | ||
569 | } | ||
570 | |||
571 | swayc_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 | |||
584 | swayc_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 | |||
597 | swayc_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 | |||
610 | swayc_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 | |||
624 | static 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 | |||
655 | swayc_t *swayc_by_handle(wlc_handle handle) { | ||
656 | return _swayc_by_handle_helper(handle, &root_container); | ||
657 | } | ||
658 | |||
659 | swayc_t *swayc_active_output(void) { | ||
660 | return root_container.focused; | ||
661 | } | ||
662 | |||
663 | swayc_t *swayc_active_workspace(void) { | ||
664 | return root_container.focused ? root_container.focused->focused : NULL; | ||
665 | } | ||
666 | |||
667 | swayc_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 | |||
688 | static 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 | |||
700 | swayc_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 | |||
765 | swayc_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 | |||
801 | bool swayc_is_fullscreen(swayc_t *view) { | ||
802 | return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN); | ||
803 | } | ||
804 | |||
805 | bool swayc_is_active(swayc_t *view) { | ||
806 | return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED); | ||
807 | } | ||
808 | |||
809 | bool 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 | |||
819 | bool swayc_is_child_of(swayc_t *child, swayc_t *parent) { | ||
820 | return swayc_is_parent_of(parent, child); | ||
821 | } | ||
822 | |||
823 | bool swayc_is_empty_workspace(swayc_t *container) { | ||
824 | return container->type == C_WORKSPACE && container->children->length == 0; | ||
825 | } | ||
826 | |||
827 | int 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 | |||
850 | void 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 | |||
869 | void 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 | |||
900 | void 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 | |||
931 | void 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 | |||
941 | void 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 | |||
953 | static void close_view(swayc_t *container, void *data) { | ||
954 | if (container->type == C_VIEW) { | ||
955 | wlc_view_close(container->handle); | ||
956 | } | ||
957 | } | ||
958 | |||
959 | void close_views(swayc_t *container) { | ||
960 | container_map(container, close_view, NULL); | ||
961 | } | ||
962 | |||
963 | swayc_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 | |||
978 | swayc_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 | |||
988 | swayc_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 | ||
13 | enum criteria_type { // *must* keep in sync with criteria_strings[] | 14 | enum 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 | ||
29 | static const char * const criteria_strings[CRIT_LAST] = { | 31 | static 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. |
103 | static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) { | 106 | static 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). |
271 | static bool criteria_test(swayc_t *cont, list_t *tokens) { | 275 | static 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 | ||
406 | bool criteria_any(swayc_t *cont, list_t *criteria) { | 401 | bool 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 | ||
416 | list_t *criteria_for(swayc_t *cont) { | 411 | list_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 | ||
432 | static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) { | 427 | static 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 | ||
438 | list_t *container_for(list_t *tokens) { | 434 | list_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 | |||
15 | static 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 | |||
33 | static 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 | |||
72 | bool enable_debug_tree = false; | ||
73 | |||
74 | void 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 */ | ||
16 | static 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 | } | ||
53 | void 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 | |||
77 | const 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. | ||
87 | void 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 | |||
5 | void 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 | |||
17 | static 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 | |||
79 | static 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 | |||
158 | void 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 | |||
219 | static 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 | |||
227 | static 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 | |||
250 | static 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 | |||
260 | static 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 | |||
282 | static 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 | |||
293 | static 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 | |||
299 | void 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 | |||
26 | struct 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 | */ | ||
40 | static 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 | */ | ||
60 | struct root_geometry { | ||
61 | double x, y; | ||
62 | int width, height; | ||
63 | float rotation; | ||
64 | }; | ||
65 | |||
66 | static 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 | |||
102 | static 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 | |||
114 | static 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 | |||
126 | static 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 | |||
139 | static 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 | |||
154 | static 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 | |||
161 | struct render_data { | ||
162 | struct root_geometry root_geo; | ||
163 | struct sway_output *output; | ||
164 | float alpha; | ||
165 | }; | ||
166 | |||
167 | static 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 | |||
204 | static 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 | |||
211 | static 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 | |||
218 | static 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 | |||
229 | static void render_container(struct sway_output *output, | ||
230 | struct sway_container *con) { | ||
231 | container_descendants(con, C_VIEW, render_container_iterator, output); | ||
232 | } | ||
233 | |||
234 | static 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 | |||
250 | static 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 | |||
288 | renderer_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 | |||
301 | struct send_frame_done_data { | ||
302 | struct root_geometry root_geo; | ||
303 | struct sway_output *output; | ||
304 | struct timespec *when; | ||
305 | }; | ||
306 | |||
307 | static 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 | |||
318 | static 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 | |||
324 | static 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 | |||
330 | static 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 | |||
340 | static 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 | |||
346 | static 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 | |||
369 | static 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 | |||
397 | void output_damage_whole(struct sway_output *output) { | ||
398 | wlr_output_damage_add_whole(output->damage); | ||
399 | } | ||
400 | |||
401 | struct damage_data { | ||
402 | struct root_geometry root_geo; | ||
403 | struct sway_output *output; | ||
404 | bool whole; | ||
405 | }; | ||
406 | |||
407 | static 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 | |||
448 | void 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 | |||
459 | void 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 | |||
474 | static 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 | |||
485 | void 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 | |||
500 | static 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 | |||
506 | static 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 | |||
511 | static 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 | |||
517 | static 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 | |||
523 | static 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 | |||
529 | void 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 | |||
14 | static 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 | |||
23 | static 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 | |||
37 | static 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 | |||
49 | static 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 | |||
57 | static 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 | |||
67 | static const struct sway_view_impl view_impl = { | ||
68 | .get_prop = get_prop, | ||
69 | .configure = configure, | ||
70 | .close = _close, | ||
71 | .destroy = destroy, | ||
72 | }; | ||
73 | |||
74 | static 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 | |||
85 | static 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 | |||
91 | void 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 | |||
14 | static const struct sway_view_child_impl popup_impl; | ||
15 | |||
16 | static 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 | |||
28 | static const struct sway_view_child_impl popup_impl = { | ||
29 | .destroy = popup_destroy, | ||
30 | }; | ||
31 | |||
32 | static struct sway_xdg_popup_v6 *popup_create( | ||
33 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view); | ||
34 | |||
35 | static 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 | |||
42 | static 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 | |||
47 | static 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 | |||
52 | static 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 | |||
74 | static 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 | |||
83 | static 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 | |||
97 | static 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 | |||
111 | static 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 | |||
121 | static 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 | |||
130 | static 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 | |||
140 | static 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 | |||
152 | static 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 | |||
161 | static 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 | |||
173 | static 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 | |||
180 | static 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 | |||
190 | static 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 | |||
207 | static 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 | |||
213 | void 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 | |||
18 | static 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 | |||
28 | static 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 | |||
47 | static 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 | |||
73 | static 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 | |||
82 | static 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 | |||
95 | static 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 | |||
122 | static 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 | |||
131 | static 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 | |||
145 | static 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 | |||
176 | static 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 | |||
184 | static 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 | |||
191 | static 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 | |||
203 | static 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 | |||
211 | static 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 | |||
222 | static 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 | |||
229 | static 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 | |||
245 | static 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 | |||
256 | static 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 | |||
268 | void 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 | |||
16 | struct desktop_shell_state desktop_shell; | ||
17 | struct decoration_state decoration_state; | ||
18 | |||
19 | static 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 | |||
38 | void 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 | |||
50 | void 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 | |||
63 | void 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 | |||
87 | static 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 | |||
115 | static 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 | |||
138 | static 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 | |||
142 | static void desktop_unlock(struct wl_client *client, struct wl_resource *resource) { | ||
143 | sway_log(L_ERROR, "desktop_unlock is not currently supported"); | ||
144 | } | ||
145 | |||
146 | static 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 | |||
150 | static void desktop_ready(struct wl_client *client, struct wl_resource *resource) { | ||
151 | // nop | ||
152 | } | ||
153 | |||
154 | static 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 | |||
167 | static 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 | |||
177 | static 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 | |||
192 | static 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 | |||
226 | static 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 | |||
231 | static struct lock_interface swaylock_implementation = { | ||
232 | .set_lock_surface = set_lock_surface, | ||
233 | .unlock = unlock | ||
234 | }; | ||
235 | |||
236 | static 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 | |||
251 | static void gamma_control_destroy(struct wl_client *client, struct wl_resource *res) { | ||
252 | wl_resource_destroy(res); | ||
253 | } | ||
254 | |||
255 | static 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 | |||
275 | static void gamma_control_reset_gamma(struct wl_client *client, | ||
276 | struct wl_resource *resource) { | ||
277 | // This space intentionally left blank | ||
278 | } | ||
279 | |||
280 | static 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 | |||
286 | static void gamma_control_manager_destroy(struct wl_client *client, | ||
287 | struct wl_resource *res) { | ||
288 | wl_resource_destroy(res); | ||
289 | } | ||
290 | |||
291 | static 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 | |||
304 | static struct gamma_control_manager_interface gamma_manager_implementation = { | ||
305 | .destroy = gamma_control_manager_destroy, | ||
306 | .get_gamma_control = gamma_control_manager_get | ||
307 | }; | ||
308 | |||
309 | static 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 | |||
323 | static void server_decoration_release(struct wl_client *client, | ||
324 | struct wl_resource *resource) { | ||
325 | wl_resource_destroy(resource); | ||
326 | } | ||
327 | |||
328 | void 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 | |||
339 | static 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 | |||
358 | static struct org_kde_kwin_server_decoration_interface server_decoration_implementation = { | ||
359 | .release = server_decoration_release, | ||
360 | .request_mode = server_decoration_request_mode, | ||
361 | }; | ||
362 | |||
363 | static 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 | ||
375 | static struct org_kde_kwin_server_decoration_manager_interface server_decoration_manager_implementation = { | ||
376 | .create = server_decoration_manager_create, | ||
377 | }; | ||
378 | |||
379 | static 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 | |||
395 | void 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 | |||
13 | bool locked_container_focus = false; | ||
14 | bool suspend_workspace_cleanup = false; | ||
15 | |||
16 | // switches parent focus to c. will switch it accordingly | ||
17 | static 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 | |||
61 | bool 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 | |||
89 | swayc_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 | |||
99 | bool 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 | |||
191 | bool 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 | |||
236 | swayc_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 | |||
250 | swayc_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 | |||
260 | swayc_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 | |||
37 | static 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 | |||
48 | static 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 | |||
59 | static 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 | |||
95 | static 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 | |||
100 | static 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 | |||
109 | static 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 | |||
115 | static 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 | |||
126 | static 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 | |||
159 | static 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 | |||
170 | static 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 | |||
193 | static 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 | |||
212 | static void handle_output_post_render(wlc_handle output) { | ||
213 | ipc_get_pixels(output); | ||
214 | } | ||
215 | |||
216 | static void handle_view_pre_render(wlc_handle view) { | ||
217 | render_view_borders(view); | ||
218 | } | ||
219 | |||
220 | static 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 | |||
236 | static 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 | |||
247 | static 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 | |||
268 | static 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 | |||
340 | static 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 | |||
532 | static 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 | |||
610 | static void handle_view_focus(wlc_handle view, bool focus) { | ||
611 | return; | ||
612 | } | ||
613 | |||
614 | static 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 | |||
638 | static 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 | |||
674 | static 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 | |||
701 | static 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 | |||
728 | static 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 | |||
749 | static 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 | |||
769 | static 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 | |||
781 | static 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 | |||
866 | static 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 | |||
930 | static 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 | |||
946 | static 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 | |||
1082 | bool 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 | |||
1107 | static 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 | |||
1123 | void 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 | |||
13 | struct 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 | |||
41 | char *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 | |||
68 | list_t *input_devices = NULL; | ||
69 | struct 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 | |||
17 | static 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 | */ | ||
39 | static 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 | |||
121 | void 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 | |||
152 | static 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 | |||
160 | static 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 | |||
169 | void 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 | |||
207 | static 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 | |||
214 | static 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 | |||
221 | static 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 | |||
227 | static 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 | |||
233 | static 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 | |||
240 | static 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 | |||
258 | static 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 | |||
266 | static 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 | |||
289 | static 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 | |||
314 | void 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 | |||
324 | struct 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 | |||
19 | static const char *default_seat = "seat0"; | ||
20 | |||
21 | // TODO make me not global | ||
22 | struct sway_input_manager *input_manager; | ||
23 | |||
24 | struct input_config *current_input_config = NULL; | ||
25 | struct seat_config *current_seat_config = NULL; | ||
26 | |||
27 | struct 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 | |||
35 | struct 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 | |||
47 | static 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 | |||
73 | static 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 | |||
84 | static 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 | |||
96 | static 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 | |||
180 | static 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 | |||
204 | static 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 | |||
267 | static 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 | |||
276 | static 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 | |||
293 | struct 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 | |||
322 | bool 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 | |||
334 | void 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 | |||
342 | void 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 | |||
359 | void 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 | |||
417 | void 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 | |||
424 | struct 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 | |||
435 | struct 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 | |||
10 | static 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 | |||
26 | static 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 | |||
36 | static 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 | |||
46 | static 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 | |||
57 | static 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 | |||
65 | static 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 | |||
80 | static 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 | |||
92 | static 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 | */ | ||
113 | static 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 | */ | ||
140 | static 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 | |||
174 | static 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 | */ | ||
265 | static 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 | */ | ||
290 | static 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 | */ | ||
313 | static 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 | |||
326 | static 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 | |||
399 | static 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 | |||
410 | struct 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 | |||
427 | void 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 | |||
497 | void 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 | |||
23 | static 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 | |||
35 | void 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 | |||
45 | static struct sway_seat_container *seat_container_from_container( | ||
46 | struct sway_seat *seat, struct sway_container *con); | ||
47 | |||
48 | static 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 | |||
66 | static 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 | |||
90 | static 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 | |||
110 | void 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 | |||
124 | struct 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 | |||
129 | static 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 | |||
168 | static 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 | |||
197 | static 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 | |||
203 | static 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 | |||
217 | struct 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 | |||
262 | static 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 | |||
289 | static 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 | |||
296 | static 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 | |||
316 | static 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 | |||
323 | static 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 | |||
335 | void 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 | |||
360 | void 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 | |||
384 | void 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 | |||
399 | void 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 | |||
434 | bool 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 | |||
440 | void 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 | |||
538 | void seat_set_focus(struct sway_seat *seat, | ||
539 | struct sway_container *container) { | ||
540 | seat_set_focus_warp(seat, container, true); | ||
541 | } | ||
542 | |||
543 | void 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 | |||
566 | void 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 | |||
588 | void 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 | |||
633 | struct 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 | |||
638 | struct 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 | |||
645 | struct 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 | |||
652 | void 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 | |||
665 | struct 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 | |||
10 | struct 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 | |||
22 | static struct key_state key_state_array[KEY_STATE_MAX_LENGTH]; | ||
23 | |||
24 | static struct key_state last_released; | ||
25 | |||
26 | static uint32_t modifiers_state; | ||
27 | |||
28 | void 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 | |||
41 | uint32_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 | |||
57 | void modifiers_state_update(uint32_t new_state) { | ||
58 | modifiers_state = new_state; | ||
59 | } | ||
60 | |||
61 | static 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 | |||
82 | bool 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 | |||
86 | bool 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 | |||
92 | void 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 | |||
107 | void 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 | |||
120 | struct pointer_state pointer_state; | ||
121 | |||
122 | static 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 | |||
146 | static struct { | ||
147 | bool left; | ||
148 | bool top; | ||
149 | } lock; | ||
150 | |||
151 | // initial set/unset | ||
152 | |||
153 | static 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 | |||
161 | static 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 | |||
186 | static 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 | |||
195 | static 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 | |||
205 | void 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 | |||
228 | void 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 | |||
234 | static 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 | |||
256 | static 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 | |||
284 | void 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 | |||
341 | void 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 | |||
475 | void 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" | |
11 | static 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 | |||
14 | static 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)); | 32 | json_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 | ||
31 | static json_object *ipc_json_create_rect_from_geometry(struct wlc_geometry g) { | 47 | static 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 | ||
42 | static const char *ipc_json_border_description(swayc_t *c) { | 58 | static 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 | ||
63 | static const char *ipc_json_layout_description(enum swayc_layouts l) { | 63 | static 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 | ||
97 | static float ipc_json_child_percentage(swayc_t *c) { | 85 | static 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 | |||
124 | static 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 | ||
135 | static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) { | 141 | static 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); |
146 | static 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 | ||
156 | static void ipc_json_describe_view(swayc_t *c, json_object *object) { | 155 | static 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 | ||
196 | static void ipc_json_describe_root(swayc_t *c, json_object *object) { | 169 | static 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 | ||
201 | json_object *ipc_json_describe_container(swayc_t *c) { | 174 | json_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 | ||
252 | json_object *ipc_json_describe_input(struct libinput_device *device) { | 218 | json_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 | ||
313 | json_object *ipc_json_get_version() { | 233 | static 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); | 249 | json_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 | ||
328 | json_object *ipc_json_describe_bar_config(struct bar_config *bar) { | 270 | json_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 | |||
454 | json_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__ | ||
22 | struct 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 | ||
39 | static int ipc_socket = -1; | 28 | static int ipc_socket = -1; |
40 | static struct wlc_event_source *ipc_event_source = NULL; | 29 | static struct wl_event_source *ipc_event_source = NULL; |
41 | static struct sockaddr_un *ipc_sockaddr = NULL; | 30 | static struct sockaddr_un *ipc_sockaddr = NULL; |
42 | static list_t *ipc_client_list = NULL; | 31 | static list_t *ipc_client_list = NULL; |
43 | 32 | ||
44 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | 33 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; |
45 | 34 | ||
46 | struct ipc_client { | 35 | struct 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 | ||
59 | static list_t *ipc_get_pixel_requests = NULL; | ||
60 | |||
61 | struct get_pixels_request { | ||
62 | struct ipc_client *client; | ||
63 | wlc_handle output; | ||
64 | struct wlc_geometry geo; | ||
65 | }; | ||
66 | |||
67 | struct 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 | |||
80 | struct sockaddr_un *ipc_user_sockaddr(void); | 49 | struct sockaddr_un *ipc_user_sockaddr(void); |
81 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 50 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
82 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | 51 | int 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); | |||
84 | void ipc_client_disconnect(struct ipc_client *client); | 53 | void ipc_client_disconnect(struct ipc_client *client); |
85 | void ipc_client_handle_command(struct ipc_client *client); | 54 | void ipc_client_handle_command(struct ipc_client *client); |
86 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); | 55 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); |
87 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data); | ||
88 | void ipc_get_outputs_callback(swayc_t *container, void *data); | ||
89 | static void ipc_get_marks_callback(swayc_t *container, void *data); | ||
90 | 56 | ||
91 | void ipc_init(void) { | 57 | void 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 | ||
124 | void ipc_terminate(void) { | 90 | void 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 | ||
160 | static 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 | |||
176 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 126 | int 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 | ||
232 | static const int ipc_header_size = sizeof(ipc_magic)+8; | 180 | static const int ipc_header_size = sizeof(ipc_magic) + 8; |
233 | 181 | ||
234 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | 182 | int 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 | ||
244 | static 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 | |||
254 | static 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 | |||
269 | void 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 | |||
298 | void 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 | |||
312 | void 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 | |||
324 | void 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 | |||
296 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | 337 | int 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 | ||
360 | bool output_by_name_test(swayc_t *view, void *data) { | 403 | static 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 | |||
368 | void 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 | |||
415 | static 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 | |||
423 | static 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 | |||
438 | static 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 | |||
501 | error:; | ||
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 | |||
508 | release: | ||
509 | release_clipboard_request(req); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static 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 | ||
528 | bool 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 | |||
556 | void 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 | |||
663 | data_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 | |||
670 | cleanup: | ||
671 | list_free(requested); | ||
672 | free(types); | ||
673 | } | 430 | } |
674 | 431 | ||
675 | void ipc_client_handle_command(struct ipc_client *client) { | 432 | void 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 | ||
934 | exit_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 | ||
938 | exit_cleanup: | 611 | exit_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 | |||
988 | void 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 | |||
1000 | void 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 | |||
1006 | static 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 | |||
1016 | void 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 | |||
1055 | void 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 | |||
1079 | void 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 | |||
1091 | void 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 | |||
1100 | void 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 | |||
1111 | void 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 | |||
1125 | static 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 | |||
1137 | void 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 | |||
18 | swayc_t root_container; | ||
19 | swayc_t *current_focus; | ||
20 | list_t *scratchpad; | ||
21 | |||
22 | int min_sane_h = 60; | ||
23 | int min_sane_w = 100; | ||
24 | |||
25 | void 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 | |||
37 | int 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 | |||
61 | void 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 | |||
75 | static double *get_height(swayc_t *cont) { | ||
76 | return &cont->height; | ||
77 | } | ||
78 | |||
79 | static double *get_width(swayc_t *cont) { | ||
80 | return &cont->width; | ||
81 | } | ||
82 | |||
83 | void 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 | |||
138 | void 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 | |||
153 | swayc_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 | |||
180 | swayc_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 | |||
215 | swayc_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 | |||
289 | void 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 | |||
320 | void 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 | |||
335 | static 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 | |||
353 | void 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 | |||
538 | void 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 | |||
583 | void 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 | |||
608 | static 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 | |||
630 | static 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 | |||
673 | void 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 | |||
708 | static 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 | |||
747 | void 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 | */ | ||
936 | static 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); | ||
940 | static 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); | ||
944 | static void apply_tabbed_or_stacked_layout(swayc_t *container, double x, | ||
945 | double y, double width, | ||
946 | double height); | ||
947 | |||
948 | static 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 | |||
953 | static 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 | |||
1127 | void 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 | |||
1179 | void 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 | |||
1232 | void 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 | |||
1252 | void 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 | |||
1376 | void 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 | |||
1382 | void 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 | */ | ||
1393 | static 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 | |||
1434 | swayc_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 | |||
1566 | swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) { | ||
1567 | return get_swayc_in_direction_under(container, dir, NULL); | ||
1568 | } | ||
1569 | |||
1570 | void 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 | |||
1596 | enum 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 | |||
1608 | bool 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 | */ | ||
1615 | static 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 | */ | ||
1625 | static 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 | */ | ||
1632 | size_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 | */ | ||
1639 | size_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 | */ | ||
1648 | int 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 | */ | ||
1673 | int 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 | */ | ||
1700 | size_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 | */ | ||
1734 | bool 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 | ||
32 | static bool terminate_request = false; | 29 | static bool terminate_request = false; |
33 | static int exit_value = 0; | 30 | static int exit_value = 0; |
31 | struct sway_server server; | ||
34 | 32 | ||
35 | void sway_terminate(int exit_code) { | 33 | void 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 | ||
41 | void sig_handler(int signal) { | 39 | void 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 | ||
46 | static 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 | |||
56 | void detect_raspi() { | 44 | void 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 | ||
176 | void run_as_ipc_client(char *command, char *socket_path) { | 111 | void run_as_ipc_client(char *command, char *socket_path) { |
@@ -184,27 +119,15 @@ void run_as_ipc_client(char *command, char *socket_path) { | |||
184 | static void log_env() { | 119 | static 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 | ||
238 | static void log_kernel() { | 161 | static 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 | ||
222 | static 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 | |||
298 | int main(int argc, char **argv) { | 253 | int 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 @@ | |||
1 | sway_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 | |||
108 | sway_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 | |||
124 | executable( | ||
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 | |||
8 | void 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 | |||
15 | swayc_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 | |||
50 | swayc_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. | ||
102 | swayc_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 | |||
229 | void 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 | |||
251 | void 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 | |||
257 | static 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 | |||
275 | void 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 | |||
11 | static 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 | |||
45 | struct 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 | |||
73 | struct 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 | ||
101 | struct command_policy *alloc_command_policy(const char *command) { | 6 | struct 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 | |||
115 | static 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 | |||
143 | struct 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 | |||
163 | uint32_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 | |||
180 | uint32_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 | |||
197 | uint32_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 | |||
213 | const 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 | |||
28 | static 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 | |||
44 | bool 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 | |||
127 | void server_fini(struct sway_server *server) { | ||
128 | // TODO | ||
129 | } | ||
130 | |||
131 | void 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 | ///// |
2 | vim:set ts=4 sw=4 tw=82 noet: | 2 | vim:set ft=asciidoc ts=4 sw=4 tw=82 noet: |
3 | ///// | 3 | ///// |
4 | sway-input (5) | 4 | sway-input (5) |
5 | ============== | 5 | ============== |
@@ -11,12 +11,57 @@ sway-input - input configuration file and commands | |||
11 | Description | 11 | Description |
12 | ----------- | 12 | ----------- |
13 | 13 | ||
14 | Sway allows for configuration of libinput devices within the sway configuration file. | 14 | Sway allows for configuration of devices within the sway configuration file. |
15 | sway-input commands must be used inside an _input { }_ block in the config. | 15 | sway-input commands must be used inside an _input { }_ block in the config. |
16 | To obtain a list of available device identifiers, run **swaymsg -t get_inputs**. | 16 | To obtain a list of available device identifiers, run **swaymsg -t get_inputs**. |
17 | 17 | ||
18 | Commands | 18 | Input Commands |
19 | -------- | 19 | -------------- |
20 | |||
21 | Keyboard Configuration | ||
22 | ~~~~~~~~~~~~~~~~~~~~~~ | ||
23 | |||
24 | For 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 | |||
43 | Mapping 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 | |||
63 | Libinput 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 | ||
101 | Seat Configuration | ||
102 | ------------------ | ||
103 | |||
104 | Configure options for multiseat mode. sway-seat commands must be used inside a | ||
105 | _seat { }_ block in the config. | ||
106 | |||
107 | A _seat_ is a collection of input devices that act independently of each other. | ||
108 | Seats are identified by name and the default seat is _seat0_ if no seats are | ||
109 | configured. Each seat has an independent keyboard focus and a separate cursor that | ||
110 | is controlled by the pointer devices of the seat. This is useful for multiple | ||
111 | people using the desktop at the same time with their own devices (each sitting in | ||
112 | their 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 | |||
56 | See Also | 122 | See 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 | ///// |
2 | vim:set ts=4 sw=4 tw=82 noet: | 2 | vim: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 | |||
46 | The following commands cannot be used directly in the configuration file. | 49 | The following commands cannot be used directly in the configuration file. |
47 | They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). | 50 | They 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 | |||
21 | static list_t *bfs_queue; | ||
22 | |||
23 | static 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 | |||
36 | const 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 | |||
53 | void 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 | |||
62 | static 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 | |||
72 | struct 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 | |||
95 | static 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 | |||
128 | static 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 | |||
165 | static 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 | |||
207 | static void container_root_finish(struct sway_container *con) { | ||
208 | wlr_log(L_ERROR, "TODO: destroy the root container"); | ||
209 | } | ||
210 | |||
211 | bool 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 | |||
240 | struct 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 | |||
252 | struct 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 | |||
263 | struct 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 | |||
304 | static 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 | |||
310 | struct 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 | |||
327 | struct 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 | |||
354 | void 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 | |||
368 | struct 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 | |||
388 | struct 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 | |||
402 | struct 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 | |||
465 | void 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 | |||
480 | void 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 | |||
504 | bool 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 | |||
515 | static bool find_child_func(struct sway_container *con, void *data) { | ||
516 | struct sway_container *child = data; | ||
517 | return con == child; | ||
518 | } | ||
519 | |||
520 | bool 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 | |||
528 | void 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 | |||
20 | struct sway_container root_container; | ||
21 | |||
22 | static 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 | |||
36 | struct 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 | |||
49 | void 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 | |||
68 | static 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 | |||
85 | void 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 | |||
97 | struct 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 | |||
113 | void 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 | |||
122 | struct 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 | |||
134 | void 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 | |||
169 | static 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 | |||
191 | static 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 | |||
205 | static 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 | |||
221 | static 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 */ | ||
226 | static 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. */ | ||
234 | static 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 | |||
258 | void 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 | |||
456 | enum 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 | |||
479 | static 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 | |||
497 | void container_sort_workspaces(struct sway_container *output) { | ||
498 | list_stable_sort(output->children, sort_workspace_cmp_qsort); | ||
499 | } | ||
500 | |||
501 | static 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 | |||
506 | static 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 | |||
511 | void 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 | |||
610 | static 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 | |||
667 | void 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 | */ | ||
727 | static 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 | |||
780 | static 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 | |||
800 | static 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 | |||
813 | struct 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 | |||
930 | struct 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 | |||
959 | struct 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 | |||
1011 | void 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 | |||
9 | struct 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 | |||
10 | void 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 | |||
17 | void 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 | |||
35 | const 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 | |||
42 | const 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 | |||
49 | const 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 | |||
56 | const 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 | |||
63 | void 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 | |||
70 | void 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 | |||
76 | void view_close(struct sway_view *view) { | ||
77 | if (view->impl->close) { | ||
78 | view->impl->close(view); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | void 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 | |||
91 | static 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 | |||
100 | void 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 | |||
109 | static void view_subsurface_create(struct sway_view *view, | ||
110 | struct wlr_subsurface *subsurface); | ||
111 | |||
112 | static void view_init_subsurfaces(struct sway_view *view, | ||
113 | struct wlr_surface *surface); | ||
114 | |||
115 | static 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 | |||
123 | static 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 | |||
129 | static 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 | |||
135 | static 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 | |||
165 | void 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 | |||
193 | void 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 | |||
213 | void 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 | |||
224 | void 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 | |||
236 | static 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 | |||
246 | static 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 | |||
254 | static 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 | |||
262 | static 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 | |||
269 | static 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 | |||
276 | static 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 | |||
284 | void 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 | |||
316 | void 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 | |||
17 | static 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 | |||
44 | struct 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 | |||
69 | char *prev_workspace_name = NULL; | ||
70 | struct workspace_by_number_data { | ||
71 | int len; | ||
72 | const char *cset; | ||
73 | const char *name; | ||
74 | }; | ||
75 | |||
76 | void next_name_map(struct sway_container *ws, void *data) { | ||
77 | int *count = data; | ||
78 | ++count; | ||
79 | } | ||
80 | |||
81 | static 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 | |||
96 | char *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 | |||
204 | static 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 | |||
213 | struct 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 | |||
223 | static 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 | |||
228 | struct 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 | */ | ||
257 | struct 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 | */ | ||
289 | struct 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 | |||
328 | struct sway_container *workspace_output_next(struct sway_container *current) { | ||
329 | return workspace_output_prev_next_impl(current, true); | ||
330 | } | ||
331 | |||
332 | struct sway_container *workspace_next(struct sway_container *current) { | ||
333 | return workspace_prev_next_impl(current, true); | ||
334 | } | ||
335 | |||
336 | struct sway_container *workspace_output_prev(struct sway_container *current) { | ||
337 | return workspace_output_prev_next_impl(current, false); | ||
338 | } | ||
339 | |||
340 | struct sway_container *workspace_prev(struct sway_container *current) { | ||
341 | return workspace_prev_next_impl(current, false); | ||
342 | } | ||
343 | |||
344 | bool 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 | |||
393 | bool 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 | |||
24 | char *prev_workspace_name = NULL; | ||
25 | struct workspace_by_number_data { | ||
26 | int len; | ||
27 | const char *cset; | ||
28 | const char *name; | ||
29 | }; | ||
30 | |||
31 | static 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 | |||
45 | char *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 | |||
133 | swayc_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 | |||
158 | static bool _workspace_by_name(swayc_t *view, void *data) { | ||
159 | return (view->type == C_WORKSPACE) && | ||
160 | (strcasecmp(view->name, (char *) data) == 0); | ||
161 | } | ||
162 | |||
163 | swayc_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 | |||
184 | static 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 | } | ||
192 | swayc_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 | */ | ||
206 | swayc_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 | */ | ||
227 | swayc_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 | |||
256 | swayc_t *workspace_output_next() { | ||
257 | return workspace_output_prev_next_impl(swayc_active_output(), true); | ||
258 | } | ||
259 | |||
260 | swayc_t *workspace_next() { | ||
261 | return workspace_prev_next_impl(swayc_active_workspace(), true); | ||
262 | } | ||
263 | |||
264 | swayc_t *workspace_output_prev() { | ||
265 | return workspace_output_prev_next_impl(swayc_active_output(), false); | ||
266 | } | ||
267 | |||
268 | swayc_t *workspace_prev() { | ||
269 | return workspace_prev_next_impl(swayc_active_workspace(), false); | ||
270 | } | ||
271 | |||
272 | bool 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 | |||
325 | swayc_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 @@ | |||
1 | include_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 | ) | ||
10 | if (ENABLE_TRAY) | ||
11 | file(GLOB tray | ||
12 | tray/*.c | ||
13 | ) | ||
14 | endif() | ||
15 | |||
16 | add_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 | |||
27 | target_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 | |||
38 | if (WITH_GDK_PIXBUF) | ||
39 | include_directories( | ||
40 | ${GDK_PIXBUF_INCLUDE_DIRS} | ||
41 | ) | ||
42 | endif() | ||
43 | |||
44 | target_link_libraries(swaybar rt) | ||
45 | |||
46 | install( | ||
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 | ||
31 | static void bar_init(struct bar *bar) { | 32 | static 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 | ||
37 | static void spawn_status_cmd_proc(struct bar *bar) { | 37 | static 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 | ||
83 | struct output *new_output(const char *name) { | 65 | static 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 | ||
95 | static void mouse_button_notify(struct window *window, int x, int y, | 75 | static 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; | 81 | struct 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 | |
86 | static 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) { | 114 | static 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 | ||
120 | static 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 | ||
147 | static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) { | 127 | static 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) { | 152 | static 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 | ||
204 | void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) { | 186 | static 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); | 190 | static 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 */ | 195 | static 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 */ | 200 | static 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 */ | 205 | struct 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 | |||
217 | static 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 | 226 | static 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 | ||
259 | bool dirty = true; | 231 | const struct wl_seat_listener seat_listener = { |
232 | .capabilities = seat_handle_capabilities, | ||
233 | .name = seat_handle_name, | ||
234 | }; | ||
260 | 235 | ||
261 | static void respond_ipc(int fd, short mask, void *_bar) { | 236 | static 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 | ||
267 | static void respond_command(int fd, short mask, void *_bar) { | 242 | static 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 | ||
272 | static void respond_output(int fd, short mask, void *_output) { | 247 | static 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 | |
251 | static 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 | ||
279 | void bar_run(struct bar *bar) { | 260 | struct 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; | 267 | static 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; | 280 | static 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 | ||
312 | void free_workspaces(list_t *workspaces) { | 316 | static 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 | ||
322 | static void free_output(struct output *output) { | 328 | static 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 | |||
333 | static 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); | 340 | void 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 | ||
337 | static 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, ®istry_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 | ||
345 | static 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 | ||
358 | void bar_teardown(struct bar *bar) { | 404 | static 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) { | 412 | static 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) { | 419 | static 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 */ | 426 | void 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) { | 437 | static 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) { | 446 | void 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 | ||
8 | uint32_t parse_position(const char *position) { | 7 | uint32_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 | ||
22 | char *parse_font(const char *font) { | 25 | struct 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 | |||
33 | struct 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 | ||
91 | void free_config(struct config *config) { | 72 | void 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 | ||
12 | struct event_item { | 11 | struct 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 | ||
17 | struct timer_item { | 16 | struct 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() { | |||
138 | void init_event_loop() { | 137 | void 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 | |||
10 | static 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 | |||
23 | static 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 | |||
110 | bool 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 | |||
192 | void 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 | |||
11 | void ipc_send_workspace_command(const char *workspace_name) { | ||
12 | uint32_t size = strlen("workspace \"\"") + strlen(workspace_name) + 1; | ||
13 | 10 | ||
11 | void 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); | 19 | char *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 | ||
20 | static void ipc_parse_config(struct config *config, const char *payload) { | 28 | static 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 | |||
132 | static 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 | ||
252 | static void ipc_update_workspaces(struct bar *bar) { | 219 | static 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 | ||
228 | void 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 | ||
309 | void ipc_bar_init(struct bar *bar, const char *bar_id) { | 277 | static 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\" ]"; | 312 | void 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 | ||
370 | bool handle_ipc_event(struct bar *bar) { | 326 | bool 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 */ | 11 | static struct swaybar swaybar; |
12 | struct bar swaybar; | ||
13 | 12 | ||
14 | void sway_terminate(int exit_code) { | 13 | void sig_handler(int signal) { |
15 | bar_teardown(&swaybar); | 14 | bar_teardown(&swaybar); |
16 | exit(exit_code); | 15 | exit(0); |
17 | } | 16 | } |
18 | 17 | ||
19 | void sig_handler(int signal) { | 18 | void sway_terminate(int code) { |
20 | bar_teardown(&swaybar); | 19 | bar_teardown(&swaybar); |
21 | exit(0); | 20 | exit(code); |
22 | } | 21 | } |
23 | 22 | ||
24 | int main(int argc, char **argv) { | 23 | int 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 @@ | |||
1 | executable( | ||
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 | 17 | static const int WS_HORIZONTAL_PADDING = 5; |
15 | #include "log.h" | 18 | static const double WS_VERTICAL_PADDING = 1.5; |
16 | 19 | static const double BORDER_WIDTH = 1; | |
17 | 20 | ||
18 | /* internal spacing */ | 21 | static uint32_t render_status_line_error(cairo_t *cairo, |
19 | static int margin = 3; | 22 | struct swaybar_output *output, struct swaybar_config *config, |
20 | static int ws_horizontal_padding = 5; | 23 | const char *error, double *x, uint32_t surface_height) { |
21 | static double ws_vertical_padding = 1.5; | 24 | if (!error) { |
22 | static 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; |
30 | static 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 | |||
53 | static 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 | |||
87 | static 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 | ||
56 | static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { | 111 | static 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; | 118 | static 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 | ||
258 | static 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 | ||
191 | static const char *strip_workspace_name(bool strip_num, const char *ws_name) { | 274 | static 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 | |||
294 | static 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 | ||
336 | static 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 | ||
215 | void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { | 349 | static 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 | ||
224 | static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { | 354 | static 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 | ||
260 | static 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 | ||
284 | void render(struct output *output, struct config *config, struct status_line *line) { | 425 | static 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 | 468 | void 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 | |
357 | void 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 | |||
17 | struct { | ||
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 | |||
27 | static char line[1024]; | ||
28 | static char line_rest[1024]; | ||
29 | |||
30 | static char event_buff[1024]; | ||
31 | |||
32 | static 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 | |||
55 | static 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) { | 13 | void 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 | 20 | bool status_handle_readable(struct status_line *status) { |
211 | static 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. | ||
266 | static 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 | ||
332 | static 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. | ||
377 | static 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. | ||
385 | static 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 | |||
396 | bool 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 | |||
456 | bool 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 | ||
515 | struct status_line *init_status_line() { | 86 | struct 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 | ||
525 | void 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 | |||
125 | void 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 | |||
14 | DBusConnection *conn = NULL; | ||
15 | |||
16 | static 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 | |||
39 | static 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 | |||
62 | static void remove_watch(DBusWatch *watch, void *_data) { | ||
63 | int fd = dbus_watch_get_unix_fd(watch); | ||
64 | |||
65 | remove_event(fd); | ||
66 | } | ||
67 | |||
68 | static 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 | |||
77 | static 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 | } | ||
118 | static 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 | |||
129 | static bool should_dispatch = true; | ||
130 | |||
131 | static 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 | |||
140 | void 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 | |||
158 | int 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 */ | ||
29 | static 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 | |||
64 | fail: | ||
65 | free(path); | ||
66 | if (index) { | ||
67 | fclose(index); | ||
68 | } | ||
69 | return themes; | ||
70 | } | ||
71 | |||
72 | static 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 | */ | ||
87 | static 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 | |||
143 | fail: | ||
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 | */ | ||
153 | static 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 | |||
173 | struct subdir { | ||
174 | int size; | ||
175 | char name[]; | ||
176 | }; | ||
177 | |||
178 | static 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 | */ | ||
187 | static 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 | */ | ||
228 | static 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); | ||
285 | fail: | ||
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 */ | ||
294 | static 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 | |||
374 | cairo_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. | ||
18 | static const cairo_user_data_key_t cairo_user_data_key; | ||
19 | |||
20 | struct 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 | |||
32 | void 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 */ | ||
41 | static 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 | |||
170 | bail: | ||
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 | } | ||
178 | static 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 */ | ||
212 | static 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 | |||
275 | bail: | ||
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 | } | ||
284 | static 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 | |||
317 | void get_icon(struct StatusNotifierItem *item) { | ||
318 | send_icon_name_msg(item); | ||
319 | } | ||
320 | |||
321 | void 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 | |||
341 | void 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 | } | ||
360 | void 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 | |||
380 | static 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 | |||
419 | struct 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` */ | ||
453 | int 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` */ | ||
460 | int 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 | } | ||
469 | void 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 | |||
12 | static list_t *items = NULL; | ||
13 | static 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 | */ | ||
22 | static 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 | |||
69 | static 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 | } | ||
88 | static 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 | } | ||
109 | static 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 | |||
131 | static 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 | |||
142 | static 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 | |||
177 | static 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 | |||
208 | static 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, ®istered); | ||
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 | |||
287 | static void set_property(DBusConnection *connection, DBusMessage *message) { | ||
288 | // All properties are read only and we don't allow new properties | ||
289 | return; | ||
290 | } | ||
291 | |||
292 | static 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, ®istered); | ||
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 | |||
356 | static 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 | |||
395 | static 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 | |||
434 | static const DBusObjectPathVTable vtable = { | ||
435 | .message_function = message_handler, | ||
436 | .unregister_function = NULL, | ||
437 | }; | ||
438 | |||
439 | int 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 | |||
17 | struct tray *tray; | ||
18 | |||
19 | static 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 | |||
41 | static 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 | |||
100 | bail: | ||
101 | dbus_message_unref(reply); | ||
102 | dbus_pending_call_unref(pending); | ||
103 | return; | ||
104 | } | ||
105 | static 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 | |||
132 | static 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 | |||
196 | static 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 | |||
284 | err: | ||
285 | // TODO better handle errors | ||
286 | free(name); | ||
287 | return -1; | ||
288 | } | ||
289 | |||
290 | void 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 | |||
317 | uint32_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 | |||
387 | void 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 @@ | |||
1 | include_directories( | ||
2 | ${PROTOCOLS_INCLUDE_DIRS} | ||
3 | ${WAYLAND_INCLUDE_DIR} | ||
4 | ${CAIRO_INCLUDE_DIRS} | ||
5 | ${PANGO_INCLUDE_DIRS} | ||
6 | ${XKBCOMMON_INCLUDE_DIRS} | ||
7 | ) | ||
8 | |||
9 | add_executable(swaybg | ||
10 | main.c | ||
11 | ) | ||
12 | |||
13 | target_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 | |||
23 | if (WITH_GDK_PIXBUF) | ||
24 | include_directories( | ||
25 | ${GDK_PIXBUF_INCLUDE_DIRS} | ||
26 | ) | ||
27 | target_link_libraries(swaybg | ||
28 | ${GDK_PIXBUF_LIBRARIES} | ||
29 | ) | ||
30 | endif() | ||
31 | |||
32 | install( | ||
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 | ||
16 | list_t *surfaces; | 16 | struct swaybg_args { |
17 | struct registry *registry; | 17 | int output_idx; |
18 | const char *path; | ||
19 | enum background_mode mode; | ||
20 | }; | ||
18 | 21 | ||
19 | enum scaling_mode { | 22 | struct 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 | ||
27 | void sway_terminate(int exit_code) { | 27 | struct 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 | ||
38 | bool is_valid_color(const char *color) { | 48 | bool 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 | ||
55 | int main(int argc, const char **argv) { | 66 | static 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 | |||
86 | static 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]); | 97 | static 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 | |||
107 | static 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 | |||
116 | struct zwlr_layer_surface_v1_listener layer_surface_listener = { | ||
117 | .configure = layer_surface_configure, | ||
118 | .closed = layer_surface_closed, | ||
119 | }; | ||
120 | |||
121 | static 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 | |||
127 | static 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 | |||
132 | static void output_done(void *data, struct wl_output *output) { | ||
133 | // Who cares | ||
134 | } | ||
135 | |||
136 | static 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; | 144 | struct 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: | 151 | static 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); | 174 | static void handle_global_remove(void *data, struct wl_registry *registry, |
175 | uint32_t name) { | ||
176 | // who cares | ||
177 | } | ||
178 | |||
179 | static const struct wl_registry_listener registry_listener = { | ||
180 | .global = handle_global, | ||
181 | .global_remove = handle_global_remove, | ||
182 | }; | ||
183 | |||
184 | int 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, ®istry_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 @@ | |||
1 | executable( | ||
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 @@ | |||
1 | include_directories( | ||
2 | ${JSONC_INCLUDE_DIRS} | ||
3 | ${WLC_INCLUDE_DIRS} | ||
4 | ${XKBCOMMON_INCLUDE_DIRS} | ||
5 | ) | ||
6 | |||
7 | add_executable(swaygrab | ||
8 | main.c | ||
9 | json.c | ||
10 | ) | ||
11 | |||
12 | target_link_libraries(swaygrab | ||
13 | sway-common | ||
14 | ${JSONC_LIBRARIES} | ||
15 | rt | ||
16 | m | ||
17 | ) | ||
18 | |||
19 | install( | ||
20 | TARGETS swaygrab | ||
21 | RUNTIME | ||
22 | DESTINATION bin | ||
23 | COMPONENT runtime | ||
24 | ) | ||
25 | |||
26 | if (A2X_FOUND) | ||
27 | add_manpage(swaygrab 1) | ||
28 | endif() | ||
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 @@ | |||
1 | include_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 | |||
11 | add_executable(swaylock | ||
12 | main.c | ||
13 | ) | ||
14 | |||
15 | target_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 | |||
27 | if (WITH_GDK_PIXBUF) | ||
28 | include_directories( | ||
29 | ${GDK_PIXBUF_INCLUDE_DIRS} | ||
30 | ) | ||
31 | target_link_libraries(swaylock | ||
32 | ${GDK_PIXBUF_LIBRARIES} | ||
33 | ) | ||
34 | endif() | ||
35 | |||
36 | install( | ||
37 | TARGETS swaylock | ||
38 | RUNTIME | ||
39 | DESTINATION bin | ||
40 | COMPONENT runtime | ||
41 | ) | ||
42 | |||
43 | install( | ||
44 | FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock | ||
45 | DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d/ | ||
46 | COMPONENT data | ||
47 | ) | ||
48 | |||
49 | if (A2X_FOUND) | ||
50 | add_manpage(swaylock 1) | ||
51 | endif() | ||
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 | ||
24 | struct registry *registry; | 25 | static void daemonize() { |
25 | struct render_data render_data; | 26 | int fds[2]; |
26 | struct lock_config *config; | 27 | if (pipe(fds) != 0) { |
27 | bool show_indicator = true; | 28 | wlr_log(L_ERROR, "Failed to pipe"); |
28 | |||
29 | void 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 | ||
37 | void sigalarm_handler(int sig) { | 58 | static 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 | ||
46 | void sway_terminate(int exit_code) { | 68 | static 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 | ||
59 | char *password; | 76 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { |
60 | int password_size; | 77 | .configure = layer_surface_configure, |
61 | enum line_source line_source = LINE_SOURCE_DEFAULT; | 78 | .closed = layer_surface_closed, |
62 | 79 | }; | |
63 | struct 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; | 81 | static 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 | ||
90 | void free_config(struct lock_config *config) { | 87 | static 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 | ||
95 | int function_conversation(int num_msg, const struct pam_message **msg, | 92 | static 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 | /** | 96 | static 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; |
133 | bool 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 | ||
152 | void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { | 104 | struct 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: | 111 | static 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 | ||
248 | void render_color(struct window *window, uint32_t color) { | 141 | static 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 | |||
253 | void 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 | ||
323 | cairo_surface_t *load_image(char *image_path) { | 146 | static 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; | 151 | static struct swaylock_state state; |
342 | } | ||
343 | 152 | ||
344 | int main(int argc, char **argv) { | 153 | int 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, ®istry_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 | |||
647 | void 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 @@ | |||
1 | executable( | ||
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 | |||
12 | static 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 | |||
33 | static 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; | ||
57 | fail: | ||
58 | // PAM frees this | ||
59 | pw->buffer = NULL; | ||
60 | pw->len = pw->size = 0; | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | static 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 | |||
72 | static 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 | |||
97 | void 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 | ||
10 | const int ARC_RADIUS = 50; | ||
11 | const int ARC_THICKNESS = 10; | ||
12 | const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; | ||
13 | const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; | ||
14 | |||
15 | void 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 | |||
145 | void 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 | |||
10 | const 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 | |||
21 | const 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 | |||
32 | static 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 | |||
59 | static 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 | |||
64 | static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, | ||
65 | uint32_t serial, struct wl_surface *surface) { | ||
66 | // Who cares | ||
67 | } | ||
68 | |||
69 | static 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 | |||
82 | static 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 | |||
98 | static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, | ||
99 | int32_t rate, int32_t delay) { | ||
100 | // TODO | ||
101 | } | ||
102 | |||
103 | static 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 | |||
112 | static 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 | |||
118 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, | ||
119 | uint32_t serial, struct wl_surface *surface) { | ||
120 | // Who cares | ||
121 | } | ||
122 | |||
123 | static 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 | |||
128 | static 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 | |||
133 | static 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 | |||
138 | static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { | ||
139 | // Who cares | ||
140 | } | ||
141 | |||
142 | static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, | ||
143 | uint32_t axis_source) { | ||
144 | // Who cares | ||
145 | } | ||
146 | |||
147 | static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, | ||
148 | uint32_t time, uint32_t axis) { | ||
149 | // Who cares | ||
150 | } | ||
151 | |||
152 | static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, | ||
153 | uint32_t axis, int32_t discrete) { | ||
154 | // Who cares | ||
155 | } | ||
156 | |||
157 | static 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 | |||
169 | static 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 | |||
182 | static void seat_handle_name(void *data, struct wl_seat *wl_seat, | ||
183 | const char *name) { | ||
184 | // Who cares | ||
185 | } | ||
186 | |||
187 | const 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 @@ | |||
1 | add_executable(swaymsg | ||
2 | main.c | ||
3 | ) | ||
4 | |||
5 | include_directories( | ||
6 | ${JSONC_INCLUDE_DIRS} | ||
7 | ) | ||
8 | |||
9 | target_link_libraries(swaymsg | ||
10 | sway-common | ||
11 | ${JSONC_LIBRARIES} | ||
12 | ) | ||
13 | |||
14 | install( | ||
15 | TARGETS swaymsg | ||
16 | RUNTIME | ||
17 | DESTINATION bin | ||
18 | COMPONENT runtime | ||
19 | ) | ||
20 | |||
21 | if (A2X_FOUND) | ||
22 | add_manpage(swaymsg 1) | ||
23 | endif() | ||
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 | ||
64 | static void pretty_print_input(json_object *i) { | 64 | static 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); | 86 | static 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 | ||
115 | static void pretty_print_output(json_object *o) { | 109 | static 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 | ||
143 | static void pretty_print_version(json_object *v) { | 172 | static void pretty_print_version(json_object *v) { |
@@ -149,7 +178,7 @@ static void pretty_print_version(json_object *v) { | |||
149 | static void pretty_print_clipboard(json_object *v) { | 178 | static 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 @@ | |||
1 | executable( | ||
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 @@ | |||
1 | include_directories( | ||
2 | ${PROTOCOLS_INCLUDE_DIRS} | ||
3 | ${PANGO_INCLUDE_DIRS} | ||
4 | ${XKBCOMMON_INCLUDE_DIRS} | ||
5 | ${WAYLAND_INCLUDE_DIR} | ||
6 | ${EPOLLSHIM_INCLUDE_DIRS} | ||
7 | ) | ||
8 | |||
9 | add_library(sway-wayland STATIC | ||
10 | buffers.c | ||
11 | pango.c | ||
12 | registry.c | ||
13 | window.c | ||
14 | cairo.c | ||
15 | ) | ||
16 | |||
17 | target_link_libraries(sway-wayland | ||
18 | sway-common | ||
19 | sway-protocols | ||
20 | ${PANGO_LIBRARIES} | ||
21 | ${XKBCOMMON_LIBRARIES} | ||
22 | ${EPOLLSHIM_LIBRARIES} | ||
23 | ) | ||
24 | |||
25 | if (WITH_GDK_PIXBUF) | ||
26 | include_directories( | ||
27 | ${GDK_PIXBUF_INCLUDE_DIRS} | ||
28 | ) | ||
29 | target_link_libraries(sway-wayland | ||
30 | ${GDK_PIXBUF_LIBRARIES} | ||
31 | ) | ||
32 | endif() | ||
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 | |||
15 | static 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 | |||
27 | static 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 | |||
33 | static void display_handle_done(void *data, struct wl_output *wl_output) { | ||
34 | // this space intentionally left blank | ||
35 | } | ||
36 | |||
37 | static 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 | |||
43 | static 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 | |||
50 | const 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 | |||
61 | const 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 | |||
72 | static 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 | |||
119 | static 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 | |||
124 | static 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 | |||
129 | static 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 | |||
147 | static 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 | |||
168 | static 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 | |||
173 | static 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 | |||
182 | static 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 | |||
202 | static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) { | ||
203 | // this space intentionally left blank | ||
204 | } | ||
205 | |||
206 | static const struct wl_seat_listener seat_listener = { | ||
207 | .capabilities = seat_handle_capabilities, | ||
208 | .name = seat_handle_name, | ||
209 | }; | ||
210 | |||
211 | static 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 | |||
238 | static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { | ||
239 | // this space intentionally left blank | ||
240 | } | ||
241 | |||
242 | static const struct wl_registry_listener registry_listener = { | ||
243 | .global = registry_global, | ||
244 | .global_remove = registry_global_remove | ||
245 | }; | ||
246 | |||
247 | struct 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, ®istry_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 | |||
270 | void 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 | |||
18 | static 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 | |||
27 | static void pointer_handle_leave(void *data, struct wl_pointer *pointer, | ||
28 | uint32_t serial, struct wl_surface *surface) { | ||
29 | } | ||
30 | |||
31 | static 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 | |||
39 | static 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 | |||
49 | static 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 | |||
71 | static 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 | |||
79 | void 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 | |||
86 | static const struct wl_shell_surface_listener surface_listener = { | ||
87 | .configure = shell_surface_configure | ||
88 | }; | ||
89 | |||
90 | void 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 | |||
96 | struct 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 | |||
144 | static 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 | |||
150 | static const struct wl_callback_listener listener = { | ||
151 | frame_callback | ||
152 | }; | ||
153 | |||
154 | int 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 | |||
163 | int 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 | |||
175 | void window_teardown(struct window *window) { | ||
176 | // TODO | ||
177 | } | ||