aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.build.yml21
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml31
-rw-r--r--CMake/FindA2X.cmake75
-rw-r--r--CMake/FindCairo.cmake42
-rw-r--r--CMake/FindDBus.cmake59
-rw-r--r--CMake/FindEpollShim.cmake17
-rw-r--r--CMake/FindGdkPixbuf.cmake43
-rw-r--r--CMake/FindJsonC.cmake29
-rw-r--r--CMake/FindLibInput.cmake66
-rw-r--r--CMake/FindLibcap.cmake56
-rw-r--r--CMake/FindPAM.cmake245
-rw-r--r--CMake/FindPCRE.cmake37
-rw-r--r--CMake/FindPango.cmake42
-rw-r--r--CMake/FindWLC.cmake20
-rw-r--r--CMake/FindWayland.cmake67
-rw-r--r--CMake/FindXKBCommon.cmake19
-rw-r--r--CMake/Manpage.cmake33
-rw-r--r--CMake/Wayland.cmake77
-rw-r--r--CMakeLists.txt161
-rw-r--r--HACKING.md11
-rw-r--r--README.de.md14
-rw-r--r--README.el.md14
-rw-r--r--README.fr.md14
-rw-r--r--README.it.md14
-rw-r--r--README.ja.md14
-rw-r--r--README.md40
-rw-r--r--README.pt.md14
-rw-r--r--README.ru.md14
-rw-r--r--README.uk.md14
-rw-r--r--client/meson.build16
-rw-r--r--client/pool-buffer.c (renamed from wayland/buffers.c)58
-rw-r--r--common/CMakeLists.txt15
-rw-r--r--common/background-image.c119
-rw-r--r--common/cairo.c (renamed from wayland/cairo.c)45
-rw-r--r--common/ipc-client.c34
-rw-r--r--common/log.c151
-rw-r--r--common/meson.build23
-rw-r--r--common/pango.c (renamed from wayland/pango.c)33
-rw-r--r--common/readline.c4
-rw-r--r--common/unicode.c101
-rw-r--r--common/util.c24
-rw-r--r--completions/zsh/CMakeLists.txt4
-rw-r--r--config.in20
-rw-r--r--include/background-image.h20
-rw-r--r--include/cairo.h (renamed from include/client/cairo.h)10
-rw-r--r--include/client/buffer.h8
-rw-r--r--include/client/pango.h16
-rw-r--r--include/client/registry.h75
-rw-r--r--include/client/window.h67
-rw-r--r--include/ipc.h1
-rw-r--r--include/log.h33
-rw-r--r--include/meson.build1
-rw-r--r--include/pango.h16
-rw-r--r--include/pool-buffer.h22
-rw-r--r--include/sway.h6
-rw-r--r--include/sway/border.h28
-rw-r--r--include/sway/commands.h45
-rw-r--r--include/sway/config.h159
-rw-r--r--include/sway/container.h357
-rw-r--r--include/sway/criteria.h8
-rw-r--r--include/sway/debug.h7
-rw-r--r--include/sway/desktop.h4
-rw-r--r--include/sway/extensions.h56
-rw-r--r--include/sway/focus.h45
-rw-r--r--include/sway/handlers.h11
-rw-r--r--include/sway/input.h23
-rw-r--r--include/sway/input/cursor.h36
-rw-r--r--include/sway/input/input-manager.h64
-rw-r--r--include/sway/input/keyboard.h30
-rw-r--r--include/sway/input/seat.h111
-rw-r--r--include/sway/input_state.h102
-rw-r--r--include/sway/ipc-json.h13
-rw-r--r--include/sway/ipc-server.h38
-rw-r--r--include/sway/layers.h25
-rw-r--r--include/sway/layout.h85
-rw-r--r--include/sway/output.h53
-rw-r--r--include/sway/server.h57
-rw-r--r--include/sway/tree/container.h192
-rw-r--r--include/sway/tree/layout.h78
-rw-r--r--include/sway/tree/output.h0
-rw-r--r--include/sway/tree/view.h186
-rw-r--r--include/sway/tree/workspace.h26
-rw-r--r--include/sway/workspace.h22
-rw-r--r--include/swaybar/bar.h110
-rw-r--r--include/swaybar/config.h59
-rw-r--r--include/swaybar/event_loop.h4
-rw-r--r--include/swaybar/ipc.h26
-rw-r--r--include/swaybar/render.h22
-rw-r--r--include/swaybar/status_line.h78
-rw-r--r--include/swaylock/seat.h38
-rw-r--r--include/swaylock/swaylock.h98
-rw-r--r--include/unicode.h33
-rw-r--r--meson.build171
-rw-r--r--meson_options.txt3
-rw-r--r--protocols/CMakeLists.txt46
-rw-r--r--protocols/desktop-shell.xml138
-rw-r--r--protocols/gamma-control.xml57
-rw-r--r--protocols/meson.build73
-rw-r--r--protocols/server-decoration.xml94
-rw-r--r--protocols/swaylock.xml18
-rw-r--r--protocols/wlr-input-inhibitor-unstable-v1.xml67
-rw-r--r--protocols/wlr-layer-shell-unstable-v1.xml281
-rw-r--r--protocols/xdg-shell.xml430
-rw-r--r--security.d/00-defaults.in18
-rw-r--r--sway/CMakeLists.txt107
-rw-r--r--sway/border.c510
-rw-r--r--sway/commands.c480
-rw-r--r--sway/commands/assign.c57
-rw-r--r--sway/commands/bar.c12
-rw-r--r--sway/commands/bar/activate_button.c22
-rw-r--r--sway/commands/bar/binding_mode_indicator.c20
-rw-r--r--sway/commands/bar/bindsym.c39
-rw-r--r--sway/commands/bar/colors.c51
-rw-r--r--sway/commands/bar/context_button.c22
-rw-r--r--sway/commands/bar/font.c13
-rw-r--r--sway/commands/bar/height.c5
-rw-r--r--sway/commands/bar/hidden_state.c24
-rw-r--r--sway/commands/bar/icon_theme.c21
-rw-r--r--sway/commands/bar/id.c7
-rw-r--r--sway/commands/bar/mode.c15
-rw-r--r--sway/commands/bar/modifier.c8
-rw-r--r--sway/commands/bar/output.c15
-rw-r--r--sway/commands/bar/pango_markup.c11
-rw-r--r--sway/commands/bar/position.c29
-rw-r--r--sway/commands/bar/secondary_button.c22
-rw-r--r--sway/commands/bar/separator_symbol.c9
-rw-r--r--sway/commands/bar/status_command.c9
-rw-r--r--sway/commands/bar/strip_workspace_numbers.c18
-rw-r--r--sway/commands/bar/swaybar_command.c9
-rw-r--r--sway/commands/bar/tray_output.c25
-rw-r--r--sway/commands/bar/tray_padding.c29
-rw-r--r--sway/commands/bar/workspace_buttons.c15
-rw-r--r--sway/commands/bar/wrap_scroll.c12
-rw-r--r--sway/commands/bind.c139
-rw-r--r--sway/commands/border.c65
-rw-r--r--sway/commands/client.c72
-rw-r--r--sway/commands/clipboard.c38
-rw-r--r--sway/commands/commands.c26
-rw-r--r--sway/commands/debuglog.c27
-rw-r--r--sway/commands/default_border.c44
-rw-r--r--sway/commands/default_floating_border.c45
-rw-r--r--sway/commands/default_orientation.c (renamed from sway/commands/orientation.c)8
-rw-r--r--sway/commands/exec.c4
-rw-r--r--sway/commands/exec_always.c22
-rw-r--r--sway/commands/exit.c9
-rw-r--r--sway/commands/floating.c81
-rw-r--r--sway/commands/floating_maximum_size.c38
-rw-r--r--sway/commands/floating_minimum_size.c38
-rw-r--r--sway/commands/floating_mod.c42
-rw-r--r--sway/commands/floating_scroll.c46
-rw-r--r--sway/commands/focus.c134
-rw-r--r--sway/commands/focus_follows_mouse.c1
-rw-r--r--sway/commands/font.c27
-rw-r--r--sway/commands/for_window.c41
-rw-r--r--sway/commands/force_focus_wrapping.c13
-rw-r--r--sway/commands/fullscreen.c60
-rw-r--r--sway/commands/gaps.c166
-rw-r--r--sway/commands/hide_edge_borders.c27
-rw-r--r--sway/commands/input.c100
-rw-r--r--sway/commands/input/accel_profile.c13
-rw-r--r--sway/commands/input/click_method.c17
-rw-r--r--sway/commands/input/drag_lock.c16
-rw-r--r--sway/commands/input/dwt.c13
-rw-r--r--sway/commands/input/events.c22
-rw-r--r--sway/commands/input/left_handed.c16
-rw-r--r--sway/commands/input/map_to_output.c27
-rw-r--r--sway/commands/input/middle_emulation.c19
-rw-r--r--sway/commands/input/natural_scroll.c16
-rw-r--r--sway/commands/input/pointer_accel.c16
-rw-r--r--sway/commands/input/scroll_method.c16
-rw-r--r--sway/commands/input/tap.c17
-rw-r--r--sway/commands/input/xkb_layout.c26
-rw-r--r--sway/commands/input/xkb_model.c26
-rw-r--r--sway/commands/input/xkb_options.c26
-rw-r--r--sway/commands/input/xkb_rules.c26
-rw-r--r--sway/commands/input/xkb_variant.c26
-rw-r--r--sway/commands/ipc.c162
-rw-r--r--sway/commands/kill.c16
-rw-r--r--sway/commands/layout.c173
-rw-r--r--sway/commands/log_colors.c22
-rw-r--r--sway/commands/mark.c87
-rw-r--r--sway/commands/mode.c38
-rw-r--r--sway/commands/mouse_warping.c4
-rw-r--r--sway/commands/move.c328
-rw-r--r--sway/commands/new_float.c8
-rw-r--r--sway/commands/new_window.c8
-rw-r--r--sway/commands/no_focus.c41
-rw-r--r--sway/commands/opacity.c36
-rw-r--r--sway/commands/output.c429
-rw-r--r--sway/commands/permit.c108
-rw-r--r--sway/commands/reload.c4
-rw-r--r--sway/commands/resize.c515
-rw-r--r--sway/commands/scratchpad.c72
-rw-r--r--sway/commands/seamless_mouse.c13
-rw-r--r--sway/commands/seat.c61
-rw-r--r--sway/commands/seat/attach.c28
-rw-r--r--sway/commands/seat/cursor.c89
-rw-r--r--sway/commands/seat/fallback.c32
-rw-r--r--sway/commands/set.c13
-rw-r--r--sway/commands/show_marks.c13
-rw-r--r--sway/commands/smart_gaps.c20
-rw-r--r--sway/commands/split.c103
-rw-r--r--sway/commands/sticky.c25
-rw-r--r--sway/commands/swaybg_command.c20
-rw-r--r--sway/commands/unmark.c31
-rw-r--r--sway/commands/workspace.c48
-rw-r--r--sway/commands/workspace_layout.c40
-rw-r--r--sway/commands/ws_auto_back_and_forth.c (renamed from sway/commands/workspace_auto_back_and_forth.c)8
-rw-r--r--sway/config.c1027
-rw-r--r--sway/config/bar.c240
-rw-r--r--sway/config/input.c119
-rw-r--r--sway/config/output.c213
-rw-r--r--sway/config/seat.c146
-rw-r--r--sway/container.c1016
-rw-r--r--sway/criteria.c156
-rw-r--r--sway/debug-tree.c119
-rw-r--r--sway/debug_log.c103
-rw-r--r--sway/desktop/desktop.c14
-rw-r--r--sway/desktop/layer_shell.c347
-rw-r--r--sway/desktop/output.c579
-rw-r--r--sway/desktop/wl_shell.c131
-rw-r--r--sway/desktop/xdg_shell_v6.c249
-rw-r--r--sway/desktop/xwayland.c310
-rw-r--r--sway/extensions.c407
-rw-r--r--sway/focus.c277
-rw-r--r--sway/handlers.c1143
-rw-r--r--sway/input.c69
-rw-r--r--sway/input/cursor.c384
-rw-r--r--sway/input/input-manager.c445
-rw-r--r--sway/input/keyboard.c504
-rw-r--r--sway/input/seat.c675
-rw-r--r--sway/input_state.c490
-rw-r--r--sway/ipc-json.c655
-rw-r--r--sway/ipc-server.c983
-rw-r--r--sway/layout.c1770
-rw-r--r--sway/main.c254
-rw-r--r--sway/meson.build131
-rw-r--r--sway/output.c277
-rw-r--r--sway/security.c212
-rw-r--r--sway/server.c141
-rw-r--r--sway/sway-input.5.txt74
-rw-r--r--sway/sway.1.txt23
-rw-r--r--sway/sway.5.txt75
-rw-r--r--sway/tree/container.c534
-rw-r--r--sway/tree/layout.c1030
-rw-r--r--sway/tree/output.c73
-rw-r--r--sway/tree/view.c329
-rw-r--r--sway/tree/workspace.c401
-rw-r--r--sway/workspace.c374
-rw-r--r--swaybar/CMakeLists.txt51
-rw-r--r--swaybar/bar.c644
-rw-r--r--swaybar/config.c53
-rw-r--r--swaybar/event_loop.c10
-rw-r--r--swaybar/i3bar.c215
-rw-r--r--swaybar/ipc.c445
-rw-r--r--swaybar/main.c33
-rw-r--r--swaybar/meson.build28
-rw-r--r--swaybar/render.c638
-rw-r--r--swaybar/status_line.c594
-rw-r--r--swaybar/tray/dbus.c197
-rw-r--r--swaybar/tray/icon.c400
-rw-r--r--swaybar/tray/sni.c481
-rw-r--r--swaybar/tray/sni_watcher.c497
-rw-r--r--swaybar/tray/tray.c398
-rw-r--r--swaybg/CMakeLists.txt37
-rw-r--r--swaybg/main.c377
-rw-r--r--swaybg/meson.build18
-rw-r--r--swaygrab/CMakeLists.txt28
-rw-r--r--swaylock/CMakeLists.txt51
-rw-r--r--swaylock/main.c918
-rw-r--r--swaylock/meson.build23
-rw-r--r--swaylock/password.c126
-rw-r--r--swaylock/render.c150
-rw-r--r--swaylock/seat.c190
-rw-r--r--swaymsg/CMakeLists.txt23
-rw-r--r--swaymsg/main.c125
-rw-r--r--swaymsg/meson.build8
-rw-r--r--wayland/CMakeLists.txt32
-rw-r--r--wayland/registry.c293
-rw-r--r--wayland/window.c177
281 files changed, 14674 insertions, 20903 deletions
diff --git a/.build.yml b/.build.yml
index 9c9e8adb..3cfb7483 100644
--- a/.build.yml
+++ b/.build.yml
@@ -1,24 +1,29 @@
# vim: ft=yaml ts=2 sw=2 et :
image: archlinux
packages:
- - cmake
- - wlc-git
+ - meson
- xorg-server-xwayland
- xcb-util-image
- json-c
- pango
- cairo
- wayland
+ - wayland-protocols
- gdk-pixbuf2
+ - libinput
+ - libxkbcommon
sources:
- - https://git.sr.ht/~sircmpwn/sway
+ - https://github.com/swaywm/sway
+ - https://github.com/swaywm/wlroots
tasks:
+ - wlroots: |
+ cd wlroots
+ meson --prefix=/usr build
+ ninja -C build
+ sudo ninja -C build install
- setup: |
cd sway
- mkdir build
- cd build
- cmake ..
+ meson build
- build: |
cd sway
- cd build
- make
+ ninja -C build
diff --git a/.gitignore b/.gitignore
index 48f8a0cb..1352970b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,3 @@
-CMakeCache.txt
-CMakeFiles
-Makefile
-cmake_install.cmake
install_manifest.txt
*.swp
*.o
@@ -12,3 +8,5 @@ build/
.lvimrc
config-debug
wayland-*-protocol.*
+/subprojects/wlroots
+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 @@
-sudo: required
-
-language: c
-
-compiler:
- - gcc
- - clang
-
-env:
- - BUILD_TYPE=Release
- - BUILD_TYPE=Debug
- - BUILD_TYPE=ASAN
-
-arch:
- packages:
- - cmake
- - xorg-server-xwayland
- - json-c
- - wayland
- - xcb-util-image
- - pango
- - cairo
- - gdk-pixbuf2
- - wlc-git
- - libcap
- script:
- - "cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ."
- - "make"
-
-script:
- - "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 @@
-#
-# (c)2015 KiCad Developers
-# (c)2015 Brian Sidebotham <brian.sidebotham@gmail.com>
-#
-# CMake module to find a2x (part of the asciidoc toolchain).
-#
-# Variables generated:
-#
-# A2X_FOUND true when A2X_COMMAND is valid
-# A2X_COMMAND The command to run a2x (may be a list including an interpreter)
-# A2X_VERSION The a2x version that has been found
-#
-
-# Have a go at finding a a2x executable
-find_program( A2X_PROGRAM a2x )
-
-# Found something, attempt to try and use it...
-if( A2X_PROGRAM )
- execute_process(
- COMMAND ${A2X_PROGRAM} --version
- OUTPUT_VARIABLE _OUT
- ERROR_VARIABLE _ERR
- RESULT_VARIABLE _RES
- OUTPUT_STRIP_TRAILING_WHITESPACE )
-
- # If it worked, set the A2X_COMMAND
- if( _RES MATCHES 0 )
- set( A2X_COMMAND "${A2X_PROGRAM}" )
- endif()
-endif()
-
-# If nothing could be found, test to see if we can just find the script file,
-# that we'll then run with the python interpreter
-if( NOT A2X_COMMAND )
- find_file( A2X_SCRIPT a2x.py )
-
- if( A2X_SCRIPT )
- # Find the python interpreter quietly
- if( NOT PYTHONINTERP_FOUND )
- find_package( PYTHONINTERP QUIET )
- endif()
-
- if( NOT PYTHONINTERP_FOUND )
- # Python's not available so can't find a2x...
- set( A2X_COMMAND "" )
- else()
- # Build the python based command
- set( A2X_COMMAND "${PYTHON_EXECUTABLE}" "${A2X_SCRIPT}" )
-
- execute_process(
- COMMAND ${A2X_COMMAND} --version
- OUTPUT_VARIABLE _OUT
- ERROR_VARIABLE _ERR
- RESULT_VARIABLE _RES
- OUTPUT_STRIP_TRAILING_WHITESPACE )
-
- # If it still can't be run, then give up
- if( NOT _RES MATCHES 0 )
- set( A2X_COMMAND "" )
- endif()
- endif()
- endif()
-endif()
-
-# If we've found a command that works, check the version
-if( A2X_COMMAND )
- string(REGEX REPLACE ".*a2x[^0-9.]*\([0-9.]+\).*" "\\1" A2X_VERSION "${_OUT}")
-endif()
-
-# Generate the *_FOUND as necessary, etc.
-include( FindPackageHandleStandardArgs )
-find_package_handle_standard_args(
- A2X
- REQUIRED_VARS A2X_COMMAND
- 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 @@
-# - Try to find the cairo library
-# Once done this will define
-#
-# CAIRO_FOUND - system has cairo
-# CAIRO_INCLUDE_DIRS - the cairo include directory
-# CAIRO_LIBRARIES - Link these to use cairo
-#
-# Define CAIRO_MIN_VERSION for which version desired.
-#
-
-find_package(PkgConfig)
-
-if(Cairo_FIND_REQUIRED)
- set(_pkgconfig_REQUIRED "REQUIRED")
-else(Cairo_FIND_REQUIRED)
- set(_pkgconfig_REQUIRED "")
-endif(Cairo_FIND_REQUIRED)
-
-if(CAIRO_MIN_VERSION)
- pkg_check_modules(CAIRO ${_pkgconfig_REQUIRED} cairo>=${CAIRO_MIN_VERSION})
-else(CAIRO_MIN_VERSION)
- pkg_check_modules(CAIRO ${_pkgconfig_REQUIRED} cairo)
-endif(CAIRO_MIN_VERSION)
-
-if(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND)
- find_path(CAIRO_INCLUDE_DIRS cairo.h)
- find_library(CAIRO_LIBRARIES cairo)
-else(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND)
- # Make paths absolute https://stackoverflow.com/a/35476270
- # Important on FreeBSD because /usr/local/lib is not on /usr/bin/ld's default path
- set(CAIRO_LIBS_ABSOLUTE)
- foreach(lib ${CAIRO_LIBRARIES})
- set(var_name CAIRO_${lib}_ABS)
- find_library(${var_name} ${lib} ${CAIRO_LIBRARY_DIRS})
- list(APPEND CAIRO_LIBS_ABSOLUTE ${${var_name}})
- endforeach()
- set(CAIRO_LIBRARIES ${CAIRO_LIBS_ABSOLUTE})
-endif(NOT CAIRO_FOUND AND NOT PKG_CONFIG_FOUND)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(CAIRO DEFAULT_MSG CAIRO_LIBRARIES CAIRO_INCLUDE_DIRS)
-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 @@
-# - Try to find DBus
-# Once done, this will define
-#
-# DBUS_FOUND - system has DBus
-# DBUS_INCLUDE_DIRS - the DBus include directories
-# DBUS_LIBRARIES - link these to use DBus
-#
-# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
-# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-FIND_PACKAGE(PkgConfig)
-PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1)
-
-FIND_LIBRARY(DBUS_LIBRARIES
- NAMES dbus-1
- HINTS ${PC_DBUS_LIBDIR}
- ${PC_DBUS_LIBRARY_DIRS}
-)
-
-FIND_PATH(DBUS_INCLUDE_DIR
- NAMES dbus/dbus.h
- HINTS ${PC_DBUS_INCLUDEDIR}
- ${PC_DBUS_INCLUDE_DIRS}
-)
-
-GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH)
-FIND_PATH(DBUS_ARCH_INCLUDE_DIR
- NAMES dbus/dbus-arch-deps.h
- HINTS ${PC_DBUS_INCLUDEDIR}
- ${PC_DBUS_INCLUDE_DIRS}
- ${_DBUS_LIBRARY_DIR}
- ${DBUS_INCLUDE_DIR}
- PATH_SUFFIXES include
-)
-
-SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
-
-INCLUDE(FindPackageHandleStandardArgs)
-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 @@
-# - Find EpollShim
-# Once done, this will define
-#
-# EPOLLSHIM_FOUND - System has EpollShim
-# EPOLLSHIM_INCLUDE_DIRS - The EpollShim include directories
-# EPOLLSHIM_LIBRARIES - The libraries needed to use EpollShim
-
-find_path(EPOLLSHIM_INCLUDE_DIRS NAMES sys/epoll.h sys/timerfd.h HINTS /usr/local/include/libepoll-shim)
-find_library(EPOLLSHIM_LIBRARIES NAMES epoll-shim libepoll-shim HINTS /usr/local/lib)
-
-if (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES)
- set(EPOLLSHIM_FOUND TRUE)
-endif (EPOLLSHIM_INCLUDE_DIRS AND EPOLLSHIM_LIBRARIES)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(EPOLLSHIM DEFAULT_MSG EPOLLSHIM_LIBRARIES EPOLLSHIM_INCLUDE_DIRS)
-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 @@
-# - Try to find the gdk-pixbuf-2.0 library
-# Once done this will define
-#
-# GDK_PIXBUF_FOUND - system has gdk-pixbuf-2.0
-# GDK_PIXBUF_INCLUDE_DIRS - the gdk-pixbuf-2.0 include directory
-# GDK_PIXBUF_LIBRARIES - Link these to use gdk-pixbuf-2.0
-#
-# Define GDK_PIXBUF_MIN_VERSION for which version desired.
-#
-
-find_package(PkgConfig)
-
-if(GdkPixbuf_FIND_REQUIRED)
- set(_pkgconfig_REQUIRED "REQUIRED")
-else(GdkPixbuf_FIND_REQUIRED)
- set(_pkgconfig_REQUIRED "")
-endif(GdkPixbuf_FIND_REQUIRED)
-
-if(GDK_PIXBUF_MIN_VERSION)
- pkg_check_modules(GDK_PIXBUF ${_pkgconfig_REQUIRED} "gdk-pixbuf-2.0>=${GDK_PIXBUF_MIN_VERSION}")
-else(GDK_PIXBUF_MIN_VERSION)
- pkg_check_modules(GDK_PIXBUF ${_pkgconfig_REQUIRED} "gdk-pixbuf-2.0")
-endif(GDK_PIXBUF_MIN_VERSION)
-
-if(NOT GDK_PIXBUF_FOUND AND NOT PKG_CONFIG_FOUND)
- find_path(GDK_PIXBUF_INCLUDE_DIRS gdk-pixbuf/gdk-pixbuf.h)
- find_library(GDK_PIXBUF_LIBRARIES gdk_pixbuf-2.0)
-else(NOT GDK_PIXBUF_FOUND AND NOT PKG_CONFIG_FOUND)
- SET(GdkPixbuf_FOUND 1)
- # Make paths absolute https://stackoverflow.com/a/35476270
- # Important on FreeBSD because /usr/local/lib is not on /usr/bin/ld's default path
- set(GDK_PIXBUF_LIBS_ABSOLUTE)
- foreach(lib ${GDK_PIXBUF_LIBRARIES})
- set(var_name GDK_PIXBUF_${lib}_ABS)
- find_library(${var_name} ${lib} ${GDK_PIXBUF_LIBRARY_DIRS})
- list(APPEND GDK_PIXBUF_LIBS_ABSOLUTE ${${var_name}})
- endforeach()
- set(GDK_PIXBUF_LIBRARIES ${GDK_PIXBUF_LIBS_ABSOLUTE})
-endif(NOT GDK_PIXBUF_FOUND AND NOT PKG_CONFIG_FOUND)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(GDK_PIXBUF DEFAULT_MSG GDK_PIXBUF_LIBRARIES GDK_PIXBUF_INCLUDE_DIRS)
-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 @@
-# - Find json-c
-# Find the json-c libraries
-#
-# This module defines the following variables:
-# JSONC_FOUND - True if JSONC is found
-# JSONC_LIBRARIES - JSONC libraries
-# JSONC_INCLUDE_DIRS - JSONC include directories
-#
-
-find_package(PkgConfig)
-
-if (JsonC_FIND_REQUIRED)
- set(_pkgconfig_REQUIRED "REQUIRED")
-else()
- set(_pkgconfig_REQUIRED "")
-endif()
-
-if(JsonC_FIND_VERSION)
- pkg_check_modules(PC_JSONC ${_pkgconfig_REQUIRED} json-c=${JsonC_FIND_VERSION})
-else()
- pkg_check_modules(PC_JSONC ${_pkgconfig_REQUIRED} json-c)
-endif()
-
-find_path(JSONC_INCLUDE_DIRS NAMES json-c/json.h HINTS ${PC_JSONC_INCLUDE_DIRS})
-find_library(JSONC_LIBRARIES NAMES json-c HINTS ${PC_JSONC_LIBRARY_DIRS})
-include(FindPackageHandleStandardArgs)
-
-find_package_handle_standard_args(JSONC DEFAULT_MSG JSONC_LIBRARIES JSONC_INCLUDE_DIRS)
-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 @@
-#.rst:
-# FindLibInput
-# -------
-#
-# Find LibInput library
-#
-# Try to find LibInpu library. The following values are defined
-#
-# ::
-#
-# LIBINPUT_FOUND - True if libinput is available
-# LIBINPUT_INCLUDE_DIRS - Include directories for libinput
-# LIBINPUT_LIBRARIES - List of libraries for libinput
-# LIBINPUT_DEFINITIONS - List of definitions for libinput
-#
-# and also the following more fine grained variables
-#
-# ::
-#
-# LIBINPUT_VERSION
-# LIBINPUT_VERSION_MAJOR
-# LIBINPUT_VERSION_MINOR
-# LIBINPUT_VERSION_MICRO
-#
-#=============================================================================
-# Copyright (c) 2015 Jari Vetoniemi
-#
-# Distributed under the OSI-approved BSD License (the "License");
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-
-include(FeatureSummary)
-set_package_properties(LibInput PROPERTIES
- URL "http://freedesktop.org/wiki/Software/libinput/"
- DESCRIPTION "Library to handle input devices")
-
-find_package(PkgConfig)
-pkg_check_modules(PC_INPUT QUIET libinput)
-find_library(LIBINPUT_LIBRARIES NAMES input HINTS ${PC_INPUT_LIBRARY_DIRS})
-find_path(LIBINPUT_INCLUDE_DIRS libinput.h HINTS ${PC_INPUT_INCLUDE_DIRS})
-
-set(LIBINPUT_VERSION ${PC_INPUT_VERSION})
-string(REPLACE "." ";" VERSION_LIST "${PC_INPUT_VERSION}")
-
-LIST(LENGTH VERSION_LIST n)
-if (n EQUAL 3)
- list(GET VERSION_LIST 0 LIBINPUT_VERSION_MAJOR)
- list(GET VERSION_LIST 1 LIBINPUT_VERSION_MINOR)
- list(GET VERSION_LIST 2 LIBINPUT_VERSION_MICRO)
-endif ()
-
-# This is compatible with libinput-version.h that exists in upstream
-# but isn't in distribution (probably forgotten)
-set(LIBINPUT_DEFINITIONS ${PC_INPUT_CFLAGS_OTHER}
- -DLIBINPUT_VERSION=\"${LIBINPUT_VERSION}\"
- -DLIBINPUT_VERSION_MAJOR=${LIBINPUT_VERSION_MAJOR}
- -DLIBINPUT_VERSION_MINOR=${LIBINPUT_VERSION_MINOR}
- -DLIBINPUT_VERSION_MICRO=${LIBINPUT_VERSION_MICRO})
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(LIBINPUT DEFAULT_MSG LIBINPUT_INCLUDE_DIRS LIBINPUT_LIBRARIES)
-mark_as_advanced(LIBINPUT_INCLUDE_DIRS LIBINPUT_LIBRARIES LIBINPUT_DEFINITIONS
- 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 @@
-#.rst:
-# FindLibcap
-# -------
-#
-# Find Libcap library
-#
-# Try to find Libcap library. The following values are defined
-#
-# ::
-#
-# Libcap_FOUND - True if Libcap is available
-# Libcap_INCLUDE_DIRS - Include directories for Libcap
-# Libcap_LIBRARIES - List of libraries for Libcap
-# Libcap_DEFINITIONS - List of definitions for Libcap
-#
-# and also the following more fine grained variables
-#
-# ::
-#
-# Libcap_VERSION
-# Libcap_VERSION_MAJOR
-# Libcap_VERSION_MINOR
-#
-#=============================================================================
-# Copyright (c) 2017 Jerzi Kaminsky
-#
-# Distributed under the OSI-approved BSD License (the "License");
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-
-include(FeatureSummary)
-set_package_properties(Libcap PROPERTIES
- URL "https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2"
- DESCRIPTION "Library for getting and setting POSIX.1e capabilities")
-
-find_package(PkgConfig)
-pkg_check_modules(PC_CAP QUIET Libcap)
-find_library(Libcap_LIBRARIES NAMES cap HINTS ${PC_CAP_LIBRARY_DIRS})
-find_path(Libcap_INCLUDE_DIRS sys/capability.h HINTS ${PC_CAP_INCLUDE_DIRS})
-
-set(Libcap_VERSION ${PC_CAP_VERSION})
-string(REPLACE "." ";" VERSION_LIST "${PC_CAP_VERSION}")
-
-LIST(LENGTH VERSION_LIST n)
-if (n EQUAL 2)
- list(GET VERSION_LIST 0 Libcap_VERSION_MAJOR)
- list(GET VERSION_LIST 1 Libcap_VERSION_MINOR)
-endif ()
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(Libcap DEFAULT_MSG Libcap_INCLUDE_DIRS Libcap_LIBRARIES)
-mark_as_advanced(Libcap_INCLUDE_DIRS Libcap_LIBRARIES Libcap_DEFINITIONS
- 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 @@
-# - Try to find the PAM libraries
-# Once done this will define
-#
-# PAM_FOUND - system has pam
-# PAM_INCLUDE_DIR - the pam include directory
-# PAM_LIBRARIES - libpam library
-
-if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
- set(PAM_FIND_QUIETLY TRUE)
-endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
-
-find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h)
-find_library(PAM_LIBRARY pam)
-find_library(DL_LIBRARY dl)
-
-if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
- set(PAM_FOUND TRUE)
- if (DL_LIBRARY)
- set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY})
- else (DL_LIBRARY)
- set(PAM_LIBRARIES ${PAM_LIBRARY})
- endif (DL_LIBRARY)
-
- if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
- set(HAVE_PAM_PAM_APPL_H 1)
- endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
-endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
-
-if (PAM_FOUND)
- if (NOT PAM_FIND_QUIETLY)
- message(STATUS "Found PAM: ${PAM_LIBRARIES}")
- endif (NOT PAM_FIND_QUIETLY)
-else (PAM_FOUND)
- if (PAM_FIND_REQUIRED)
- message(FATAL_ERROR "PAM was not found")
- endif(PAM_FIND_REQUIRED)
-endif (PAM_FOUND)
-
-mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY)
-
-# This file taken from https://github.com/FreeRDP/FreeRDP
-#
-#
-#
-# Apache License
-# Version 2.0, January 2004
-# http://www.apache.org/licenses/
-#
-# TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-#
-# 1. Definitions.
-#
-# "License" shall mean the terms and conditions for use, reproduction,
-# and distribution as defined by Sections 1 through 9 of this document.
-#
-# "Licensor" shall mean the copyright owner or entity authorized by
-# the copyright owner that is granting the License.
-#
-# "Legal Entity" shall mean the union of the acting entity and all
-# other entities that control, are controlled by, or are under common
-# control with that entity. For the purposes of this definition,
-# "control" means (i) the power, direct or indirect, to cause the
-# direction or management of such entity, whether by contract or
-# otherwise, or (ii) ownership of fifty percent (50%) or more of the
-# outstanding shares, or (iii) beneficial ownership of such entity.
-#
-# "You" (or "Your") shall mean an individual or Legal Entity
-# exercising permissions granted by this License.
-#
-# "Source" form shall mean the preferred form for making modifications,
-# including but not limited to software source code, documentation
-# source, and configuration files.
-#
-# "Object" form shall mean any form resulting from mechanical
-# transformation or translation of a Source form, including but
-# not limited to compiled object code, generated documentation,
-# and conversions to other media types.
-#
-# "Work" shall mean the work of authorship, whether in Source or
-# Object form, made available under the License, as indicated by a
-# copyright notice that is included in or attached to the work
-# (an example is provided in the Appendix below).
-#
-# "Derivative Works" shall mean any work, whether in Source or Object
-# form, that is based on (or derived from) the Work and for which the
-# editorial revisions, annotations, elaborations, or other modifications
-# represent, as a whole, an original work of authorship. For the purposes
-# of this License, Derivative Works shall not include works that remain
-# separable from, or merely link (or bind by name) to the interfaces of,
-# the Work and Derivative Works thereof.
-#
-# "Contribution" shall mean any work of authorship, including
-# the original version of the Work and any modifications or additions
-# to that Work or Derivative Works thereof, that is intentionally
-# submitted to Licensor for inclusion in the Work by the copyright owner
-# or by an individual or Legal Entity authorized to submit on behalf of
-# the copyright owner. For the purposes of this definition, "submitted"
-# means any form of electronic, verbal, or written communication sent
-# to the Licensor or its representatives, including but not limited to
-# communication on electronic mailing lists, source code control systems,
-# and issue tracking systems that are managed by, or on behalf of, the
-# Licensor for the purpose of discussing and improving the Work, but
-# excluding communication that is conspicuously marked or otherwise
-# designated in writing by the copyright owner as "Not a Contribution."
-#
-# "Contributor" shall mean Licensor and any individual or Legal Entity
-# on behalf of whom a Contribution has been received by Licensor and
-# subsequently incorporated within the Work.
-#
-# 2. Grant of Copyright License. Subject to the terms and conditions of
-# this License, each Contributor hereby grants to You a perpetual,
-# worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-# copyright license to reproduce, prepare Derivative Works of,
-# publicly display, publicly perform, sublicense, and distribute the
-# Work and such Derivative Works in Source or Object form.
-#
-# 3. Grant of Patent License. Subject to the terms and conditions of
-# this License, each Contributor hereby grants to You a perpetual,
-# worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-# (except as stated in this section) patent license to make, have made,
-# use, offer to sell, sell, import, and otherwise transfer the Work,
-# where such license applies only to those patent claims licensable
-# by such Contributor that are necessarily infringed by their
-# Contribution(s) alone or by combination of their Contribution(s)
-# with the Work to which such Contribution(s) was submitted. If You
-# institute patent litigation against any entity (including a
-# cross-claim or counterclaim in a lawsuit) alleging that the Work
-# or a Contribution incorporated within the Work constitutes direct
-# or contributory patent infringement, then any patent licenses
-# granted to You under this License for that Work shall terminate
-# as of the date such litigation is filed.
-#
-# 4. Redistribution. You may reproduce and distribute copies of the
-# Work or Derivative Works thereof in any medium, with or without
-# modifications, and in Source or Object form, provided that You
-# meet the following conditions:
-#
-# (a) You must give any other recipients of the Work or
-# Derivative Works a copy of this License; and
-#
-# (b) You must cause any modified files to carry prominent notices
-# stating that You changed the files; and
-#
-# (c) You must retain, in the Source form of any Derivative Works
-# that You distribute, all copyright, patent, trademark, and
-# attribution notices from the Source form of the Work,
-# excluding those notices that do not pertain to any part of
-# the Derivative Works; and
-#
-# (d) If the Work includes a "NOTICE" text file as part of its
-# distribution, then any Derivative Works that You distribute must
-# include a readable copy of the attribution notices contained
-# within such NOTICE file, excluding those notices that do not
-# pertain to any part of the Derivative Works, in at least one
-# of the following places: within a NOTICE text file distributed
-# as part of the Derivative Works; within the Source form or
-# documentation, if provided along with the Derivative Works; or,
-# within a display generated by the Derivative Works, if and
-# wherever such third-party notices normally appear. The contents
-# of the NOTICE file are for informational purposes only and
-# do not modify the License. You may add Your own attribution
-# notices within Derivative Works that You distribute, alongside
-# or as an addendum to the NOTICE text from the Work, provided
-# that such additional attribution notices cannot be construed
-# as modifying the License.
-#
-# You may add Your own copyright statement to Your modifications and
-# may provide additional or different license terms and conditions
-# for use, reproduction, or distribution of Your modifications, or
-# for any such Derivative Works as a whole, provided Your use,
-# reproduction, and distribution of the Work otherwise complies with
-# the conditions stated in this License.
-#
-# 5. Submission of Contributions. Unless You explicitly state otherwise,
-# any Contribution intentionally submitted for inclusion in the Work
-# by You to the Licensor shall be under the terms and conditions of
-# this License, without any additional terms or conditions.
-# Notwithstanding the above, nothing herein shall supersede or modify
-# the terms of any separate license agreement you may have executed
-# with Licensor regarding such Contributions.
-#
-# 6. Trademarks. This License does not grant permission to use the trade
-# names, trademarks, service marks, or product names of the Licensor,
-# except as required for reasonable and customary use in describing the
-# origin of the Work and reproducing the content of the NOTICE file.
-#
-# 7. Disclaimer of Warranty. Unless required by applicable law or
-# agreed to in writing, Licensor provides the Work (and each
-# Contributor provides its Contributions) on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied, including, without limitation, any warranties or conditions
-# of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-# PARTICULAR PURPOSE. You are solely responsible for determining the
-# appropriateness of using or redistributing the Work and assume any
-# risks associated with Your exercise of permissions under this License.
-#
-# 8. Limitation of Liability. In no event and under no legal theory,
-# whether in tort (including negligence), contract, or otherwise,
-# unless required by applicable law (such as deliberate and grossly
-# negligent acts) or agreed to in writing, shall any Contributor be
-# liable to You for damages, including any direct, indirect, special,
-# incidental, or consequential damages of any character arising as a
-# result of this License or out of the use or inability to use the
-# Work (including but not limited to damages for loss of goodwill,
-# work stoppage, computer failure or malfunction, or any and all
-# other commercial damages or losses), even if such Contributor
-# has been advised of the possibility of such damages.
-#
-# 9. Accepting Warranty or Additional Liability. While redistributing
-# the Work or Derivative Works thereof, You may choose to offer,
-# and charge a fee for, acceptance of support, warranty, indemnity,
-# or other liability obligations and/or rights consistent with this
-# License. However, in accepting such obligations, You may act only
-# on Your own behalf and on Your sole responsibility, not on behalf
-# of any other Contributor, and only if You agree to indemnify,
-# defend, and hold each Contributor harmless for any liability
-# incurred by, or claims asserted against, such Contributor by reason
-# of your accepting any such warranty or additional liability.
-#
-# END OF TERMS AND CONDITIONS
-#
-# APPENDIX: How to apply the Apache License to your work.
-#
-# To apply the Apache License to your work, attach the following
-# boilerplate notice, with the fields enclosed by brackets "[]"
-# replaced with your own identifying information. (Don't include
-# the brackets!) The text should be enclosed in the appropriate
-# comment syntax for the file format. We also recommend that a
-# file or class name and description of purpose be included on the
-# same "printed page" as the copyright notice for easier
-# identification within third-party archives.
-#
-# Copyright [yyyy] [name of copyright owner]
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# 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 @@
-# Copyright (C) 2007-2009 LuaDist.
-# Created by Peter Kapec <kapecp@gmail.com>
-# Redistribution and use of this file is allowed according to the terms of the MIT license.
-# For details see the COPYRIGHT file distributed with LuaDist.
-# Note:
-# Searching headers and libraries is very simple and is NOT as powerful as scripts
-# distributed with CMake, because LuaDist defines directories to search for.
-# Everyone is encouraged to contact the author with improvements. Maybe this file
-# becomes part of CMake distribution sometimes.
-
-# - Find pcre
-# Find the native PCRE headers and libraries.
-#
-# PCRE_INCLUDE_DIRS - where to find pcre.h, etc.
-# PCRE_LIBRARIES - List of libraries when using pcre.
-# PCRE_FOUND - True if pcre found.
-
-# Look for the header file.
-FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h)
-
-# Look for the library.
-FIND_LIBRARY(PCRE_LIBRARY NAMES pcre)
-
-# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
-
-# Copy the results to the output variables.
-IF(PCRE_FOUND)
- SET(PCRE_LIBRARIES ${PCRE_LIBRARY})
- SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
-ELSE(PCRE_FOUND)
- SET(PCRE_LIBRARIES)
- SET(PCRE_INCLUDE_DIRS)
-ENDIF(PCRE_FOUND)
-
-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 @@
-# - Try to find the pango library
-# Once done this will define
-#
-# PANGO_FOUND - system has pango
-# PANGO_INCLUDE_DIRS - the pango include directory
-# PANGO_LIBRARIES - Link these to use pango
-#
-# Define PANGO_MIN_VERSION for which version desired.
-#
-
-find_package(PkgConfig)
-
-if(Pango_FIND_REQUIRED)
- set(_pkgconfig_REQUIRED "REQUIRED")
-else(Pango_FIND_REQUIRED)
- set(_pkgconfig_REQUIRED "")
-endif(Pango_FIND_REQUIRED)
-
-if(PANGO_MIN_VERSION)
- pkg_check_modules(PANGO ${_pkgconfig_REQUIRED} "pango>=${PANGO_MIN_VERSION}" "pangocairo>=${PANGO_MIN_VERSION}")
-else(PANGO_MIN_VERSION)
- pkg_check_modules(PANGO ${_pkgconfig_REQUIRED} pango pangocairo)
-endif(PANGO_MIN_VERSION)
-
-if(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND)
- find_path(PANGO_INCLUDE_DIRS pango.h)
- find_library(PANGO_LIBRARIES pango pangocairo)
-else(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND)
- # Make paths absolute https://stackoverflow.com/a/35476270
- # Important on FreeBSD because /usr/local/lib is not on /usr/bin/ld's default path
- set(PANGO_LIBS_ABSOLUTE)
- foreach(lib ${PANGO_LIBRARIES})
- set(var_name PANGO_${lib}_ABS)
- find_library(${var_name} ${lib} ${PANGO_LIBRARY_DIRS})
- list(APPEND PANGO_LIBS_ABSOLUTE ${${var_name}})
- endforeach()
- set(PANGO_LIBRARIES ${PANGO_LIBS_ABSOLUTE})
-endif(NOT PANGO_FOUND AND NOT PKG_CONFIG_FOUND)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(PANGO DEFAULT_MSG PANGO_LIBRARIES PANGO_INCLUDE_DIRS)
-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 @@
-# - Find wlc
-# Find the wlc libraries
-#
-# This module defines the following variables:
-# WLC_FOUND - True if wlc is found
-# WLC_LIBRARIES - wlc libraries
-# WLC_INCLUDE_DIRS - wlc include directories
-# WLC_DEFINITIONS - Compiler switches required for using wlc
-#
-
-find_package(PkgConfig)
-pkg_check_modules(PC_WLC QUIET wlc)
-find_path(WLC_INCLUDE_DIRS NAMES wlc/wlc.h HINTS ${PC_WLC_INCLUDE_DIRS})
-find_library(WLC_LIBRARIES NAMES wlc HINTS ${PC_WLC_LIBRARY_DIRS})
-
-set(WLC_DEFINITIONS ${PC_WLC_CFLAGS_OTHER})
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(wlc DEFAULT_MSG WLC_LIBRARIES WLC_INCLUDE_DIRS)
-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 @@
-# Try to find Wayland on a Unix system
-#
-# This will define:
-#
-# WAYLAND_FOUND - True if Wayland is found
-# WAYLAND_LIBRARIES - Link these to use Wayland
-# WAYLAND_INCLUDE_DIR - Include directory for Wayland
-# WAYLAND_DEFINITIONS - Compiler flags for using Wayland
-#
-# In addition the following more fine grained variables will be defined:
-#
-# WAYLAND_CLIENT_FOUND WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
-# WAYLAND_SERVER_FOUND WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
-# WAYLAND_EGL_FOUND WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
-# WAYLAND_CURSOR_FOUND WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES
-#
-# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
-#
-# Redistribution and use is allowed according to the terms of the BSD license.
-# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
-
-IF (NOT WIN32)
- IF (WAYLAND_INCLUDE_DIR AND WAYLAND_LIBRARIES)
- # In the cache already
- SET(WAYLAND_FIND_QUIETLY TRUE)
- ENDIF ()
-
- # Use pkg-config to get the directories and then use these values
- # in the FIND_PATH() and FIND_LIBRARY() calls
- FIND_PACKAGE(PkgConfig)
- PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor)
-
- SET(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS})
-
- FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
- FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
- FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
- FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
-
- FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
- FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
- FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
- FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
-
- set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR} ${WAYLAND_CURSOR_INCLUDE_DIR})
-
- set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES})
-
- list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIR)
-
- include(FindPackageHandleStandardArgs)
-
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT DEFAULT_MSG WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR)
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER DEFAULT_MSG WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR)
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL DEFAULT_MSG WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR)
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR DEFAULT_MSG WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR)
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIR)
-
- MARK_AS_ADVANCED(
- WAYLAND_INCLUDE_DIR WAYLAND_LIBRARIES
- WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
- WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
- WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
- WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES
- )
-
-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 @@
-# - Find XKBCommon
-# Once done, this will define
-#
-# XKBCOMMON_FOUND - System has XKBCommon
-# XKBCOMMON_INCLUDE_DIRS - The XKBCommon include directories
-# XKBCOMMON_LIBRARIES - The libraries needed to use XKBCommon
-# XKBCOMMON_DEFINITIONS - Compiler switches required for using XKBCommon
-
-find_package(PkgConfig)
-pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon)
-find_path(XKBCOMMON_INCLUDE_DIRS NAMES xkbcommon/xkbcommon.h HINTS ${PC_XKBCOMMON_INCLUDE_DIRS})
-find_library(XKBCOMMON_LIBRARIES NAMES xkbcommon HINTS ${PC_XKBCOMMON_LIBRARY_DIRS})
-
-set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER})
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(XKBCOMMON DEFAULT_MSG XKBCOMMON_LIBRARIES XKBCOMMON_INCLUDE_DIRS)
-mark_as_advanced(XKBCOMMON_LIBRARIES XKBCOMMON_INCLUDE_DIRS)
-
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 @@
-find_package(A2X)
-
-if (A2X_FOUND)
- add_custom_target(man ALL)
-
- function(add_manpage name section)
- add_custom_command(
- OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}.${section}
- COMMAND ${A2X_COMMAND}
- --no-xmllint
- --doctype manpage
- --format manpage
- -D ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
- ${CMAKE_CURRENT_SOURCE_DIR}/${name}.${section}.txt
- DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.${section}.txt
- COMMENT Generating manpage for ${name}.${section}
- )
-
- add_custom_target(man-${name}.${section}
- DEPENDS
- ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}.${section}
- )
- add_dependencies(man
- man-${name}.${section}
- )
-
- install(
- FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}.${section}
- DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man${section}
- COMPONENT documentation
- )
- endfunction()
-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 @@
-#=============================================================================
-# Copyright (C) 2012-2013 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# * Neither the name of Pier Luigi Fiorini nor the names of his
-# contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#=============================================================================
-
-find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
-
-# wayland_add_protocol_client(outfiles inputfile basename)
-function(WAYLAND_ADD_PROTOCOL_CLIENT _sources _protocol _basename)
- if(NOT WAYLAND_SCANNER_EXECUTABLE)
- message(FATAL "The wayland-scanner executable has not been found on your system. You must install it.")
- endif()
-
- get_filename_component(_infile ${_protocol} ABSOLUTE)
- set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.h")
- set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-client-protocol.c")
-
- add_custom_command(OUTPUT "${_client_header}"
- COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header < ${_infile} > ${_client_header}
- DEPENDS ${_infile} VERBATIM)
-
- add_custom_command(OUTPUT "${_code}"
- COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code}
- DEPENDS ${_infile} VERBATIM)
-
- list(APPEND ${_sources} "${_client_header}" "${_code}")
- set(${_sources} ${${_sources}} PARENT_SCOPE)
-endfunction()
-
-# wayland_add_protocol_server(outfiles inputfile basename)
-function(WAYLAND_ADD_PROTOCOL_SERVER _sources _protocol _basename)
- if(NOT WAYLAND_SCANNER_EXECUTABLE)
- message(FATAL "The wayland-scanner executable has not been found on your system. You must install it.")
- endif()
-
- get_filename_component(_infile ${_protocol} ABSOLUTE)
- set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-server-protocol.h")
- set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${_basename}-server-protocol.c")
-
- add_custom_command(OUTPUT "${_server_header}"
- COMMAND ${WAYLAND_SCANNER_EXECUTABLE} server-header < ${_infile} > ${_server_header}
- DEPENDS ${_infile} VERBATIM)
-
- add_custom_command(OUTPUT "${_code}"
- COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code < ${_infile} > ${_code}
- DEPENDS ${_infile} VERBATIM)
-
- list(APPEND ${_sources} "${_server_header}" "${_code}")
- set(${_sources} ${${_sources}} PARENT_SCOPE)
-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 @@
-cmake_minimum_required(VERSION 3.1.0)
-
-project(sway C)
-
-add_compile_options(-g)
-set(CMAKE_C_STANDARD 99)
-set(CMAKE_C_EXTENSIONS OFF)
-set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
-add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-result -Werror)
-
-# Add Address Sanitiezed build type
-set(CMAKE_C_FLAGS_ASAN
- "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer"
- CACHE STRING "Flags used by the C compiler during address sanitizer builds."
- FORCE )
-mark_as_advanced(
- CMAKE_C_FLAGS_ASAN
- CMAKE_EXE_LINKER_FLAGS_DEBUG
- CMAKE_SHARED_LINKER_FLAGS_DEBUG
- )
-
-list(INSERT CMAKE_MODULE_PATH 0
- ${CMAKE_CURRENT_SOURCE_DIR}/CMake
- )
-
-if (VERSION)
- add_definitions(-DSWAY_VERSION=\"${VERSION}\")
-else()
- execute_process(
- COMMAND git describe --always --tags
- OUTPUT_VARIABLE GIT_COMMIT_HASH
- OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- )
- execute_process(
- COMMAND git rev-parse --abbrev-ref HEAD
- OUTPUT_VARIABLE GIT_BRANCH
- OUTPUT_STRIP_TRAILING_WHITESPACE
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- )
-
- string(TIMESTAMP CURRENT_DATE "%Y-%m-%d" UTC)
- add_definitions("-DSWAY_VERSION=\"${GIT_COMMIT_HASH} (${CURRENT_DATE}, branch \\\"${GIT_BRANCH}\\\")\"")
-endif()
-
-option(enable-swaylock "Enables the swaylock utility" YES)
-option(enable-swaybg "Enables the wallpaper utility" YES)
-option(enable-swaybar "Enables the swaybar utility" YES)
-option(enable-swaygrab "Enables the swaygrab utility" YES)
-option(enable-swaymsg "Enables the swaymsg utility" YES)
-option(enable-gdk-pixbuf "Use Pixbuf to support more image formats" YES)
-option(enable-tray "Enables the swaybar tray" YES)
-option(zsh-completions "Zsh shell completions" NO)
-option(default-wallpaper "Installs the default wallpaper" YES)
-option(LD_LIBRARY_PATH "Configure sway's default LD_LIBRARY_PATH")
-
-if (LD_LIBRARY_PATH)
- add_definitions(-D_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}")
-endif()
-
-find_package(JsonC 0.12.1 REQUIRED)
-find_package(PCRE REQUIRED)
-find_package(WLC REQUIRED)
-find_package(Wayland REQUIRED)
-find_package(XKBCommon REQUIRED)
-find_package(Cairo REQUIRED)
-find_package(Pango REQUIRED)
-find_package(GdkPixbuf)
-find_package(PAM)
-find_package(DBus 1.10)
-
-find_package(LibInput REQUIRED)
-
-if (CMAKE_SYSTEM_NAME STREQUAL Linux)
- find_package(Libcap REQUIRED)
-endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
-
-if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
- find_package(EpollShim REQUIRED)
-endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
-
-include(FeatureSummary)
-include(Manpage)
-include(GNUInstallDirs)
-
-if (enable-gdk-pixbuf)
- if (GDK_PIXBUF_FOUND)
- set(WITH_GDK_PIXBUF YES)
- add_definitions(-DWITH_GDK_PIXBUF)
- else()
- message(WARNING "gdk-pixbuf required but not found, only png images supported.")
- endif()
-else()
- message(STATUS "Building without gdk-pixbuf, only png images supported.")
-endif()
-
-if (enable-tray)
- if (DBUS_FOUND)
- set(ENABLE_TRAY TRUE)
- add_definitions(-DENABLE_TRAY)
- else()
- message(WARNING "Tray required but DBus was not found. Tray will not be included")
- endif()
-else()
- message(STATUS "Building without the tray.")
-endif()
-
-include_directories(include)
-
-add_subdirectory(protocols)
-add_subdirectory(common)
-add_subdirectory(wayland)
-
-add_subdirectory(sway)
-if(enable-swaybg)
- if(CAIRO_FOUND AND PANGO_FOUND)
- add_subdirectory(swaybg)
- else()
- message(WARNING "Not building swaybg - cairo, and pango are required.")
- endif()
-endif()
-if(enable-swaymsg)
- add_subdirectory(swaymsg)
-endif()
-if(enable-swaygrab)
- add_subdirectory(swaygrab)
-endif()
-if(enable-swaybar)
- if(CAIRO_FOUND AND PANGO_FOUND)
- add_subdirectory(swaybar)
- else()
- message(WARNING "Not building swaybar - cairo, and pango are required.")
- endif()
-endif()
-if(enable-swaylock)
- if(CAIRO_FOUND AND PANGO_FOUND AND PAM_FOUND)
- add_subdirectory(swaylock)
- else()
- message(WARNING "Not building swaylock - cairo, pango, and PAM are required.")
- endif()
-endif()
-if(zsh-completions)
- add_subdirectory(completions/zsh)
-endif()
-install(
- FILES ${CMAKE_CURRENT_SOURCE_DIR}/sway.desktop
- DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/wayland-sessions
- COMPONENT data
- )
-
-if(default-wallpaper)
- install(
- DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/assets/
- DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/backgrounds/sway
- COMPONENT data
- FILES_MATCHING PATTERN "*Wallpaper*"
- )
-endif()
-
-feature_summary(WHAT ALL)
diff --git a/HACKING.md b/HACKING.md
index cf18dbb0..3600db24 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -22,14 +22,3 @@ assertion fails.
Outside of the compositor (swaymsg, swaybar, etc), using `assert.h` is
permitted.
-
-## Building against a local wlc
-
-1. Build wlc as described [here](https://github.com/Cloudef/wlc#building)
-2. Inside your sway source folder, tell `cmake` to use your local version of wlc:
-
-```bash
-cmake \
- -DWLC_LIBRARIES=path/to/wlc/target/src/libwlc.so \
- -DWLC_INCLUDE_DIRS=path/to/wlc/include .
-```
diff --git a/README.de.md b/README.de.md
index 2e497dd0..d63d8318 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,4 +1,4 @@
-# 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)
+# sway
Der Fortschritt dieser Ãœbersetzung kann [hier](https://github.com/swaywm/sway/issues/1318)
eingesehen werden.
@@ -52,7 +52,7 @@ schaue im IRC-Channel vorbei oder schreibe eine E‑Mail an sir@cmpwn.com (nur e
Abhängigkeiten:
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -60,7 +60,7 @@ Abhängigkeiten:
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -74,11 +74,9 @@ _\*\*Nur erforderlich für swaylock_
Führe diese Befehle aus:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
In Systemen mit logind musst du `sway` einige Capabilities geben:
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 @@
-# 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)
+# sway
"Ο Sway (**S**irCmpwn's **Way**land) είναι ένας **υπό ανάπτυξη** [Wayland](http://wayland.freedesktop.org/) διαχειÏιστής παÏαθÏÏων συμβατός με τον αντίστοιχο διαχειÏιστή παÏαθÏÏων i3 για τον X11.
Διαβάστε τις [Συνήθεις ΕÏωτήσεις](https://github.com/swaywm/sway/wiki). Συνδεθείτε στο [κανάλι μας στο IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway στο
@@ -45,7 +45,7 @@ To username μου στο Freenode είναι kon14 και θα με βÏείτÎ
Εγκατάσταση εξαÏτήσεων:
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -53,7 +53,7 @@ To username μου στο Freenode είναι kon14 και θα με βÏείτÎ
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -67,11 +67,9 @@ _\*\*Απαιτείται μόνο για swaylock_
Εκτελέστε αυτές τις εντολές:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
Σε συστήματα με logind, χÏειάζεται να οÏίσετε μεÏικά δικαιώματα caps στο εκτελέσιμο αÏχείο:
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 @@
-# 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)
+# sway
"**S**irCmpwn's **Way**land compositor" est un compositeur [Wayland](http://wayland.freedesktop.org/)
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
Installez les dépendances :
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -55,7 +55,7 @@ Installez les dépendances :
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -69,11 +69,9 @@ _\*\*Uniquement requis pour swaylock_
Exécutez ces commandes :
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
Sur les systèmes avec logind, vous devez définir quelques caps sur le binaire :
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 @@
-# 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)
+# sway
"**S**irCmpwn's **Way**land compositor" è un compositor
[Wayland](http://wayland.freedesktop.org/) **in via di sviluppo**
@@ -48,7 +48,7 @@ a sir@cmpwn.com.
Installa queste dipendenze:
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -56,7 +56,7 @@ Installa queste dipendenze:
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -70,11 +70,9 @@ _\*\*Richiesto solo per swaylock_
Esegui questi comandi:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
Per i sistemi con logind, devi impostare un paio di caps sull'eseguibile:
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 @@
-# 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)
+# sway
"**S**irCmpwn's **Way**land compositor"ã¯**開発中**ã®
i3互æ›ãª[Wayland](http://wayland.freedesktop.org/)コンãƒã‚¸ã‚¿ã§ã™ã€‚
@@ -38,7 +38,7 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚"
次ã®ä¾å­˜ãƒ‘ッケージをインストールã—ã¦ãã ã•ã„:
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -46,7 +46,7 @@ Swayã¯æ²¢å±±ã®ãƒ‡ã‚£ã‚¹ãƒˆãƒªãƒ“ューションã§æä¾›ã•ã‚Œã¦ã„ã¾ã™ã€‚"
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -60,11 +60,9 @@ _\*\*swaylockã§ã®ã¿å¿…è¦ã§ã™_
次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã—ã¦ãã ã•ã„:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
logindを使用ã—ã¦ã„るシステムã§ã¯ã€ãƒã‚¤ãƒŠãƒªã«ã„ãã¤ã‹ã®ã‚±ãƒ¼ãƒ‘ビリティを設定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™:
diff --git a/README.md b/README.md
index 0e92f78e..30911765 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# 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)
+# sway
[**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--) -
[РуÑÑкий](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--)
@@ -9,21 +9,13 @@ Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the
[IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on
irc.freenode.net).
-**Notice**: Sway is currently *frozen* and no new features are being shipped
-until we finish integrating Sway and wlroots. Bug fixes are still going out.
-for more information, [see this
-article](https://drewdevault.com/2017/10/09/Future-of-sway.html). To follow
-wlroots integration status, [see this
-ticket](https://github.com/swaywm/sway/issues/1390).
+**Notice**: You are viewing the **unstable** and **unsupported** master branch
+of sway, where work is ongoing to port it to
+[wlroots](https://github.com/swaywm/wlroots). The supported branch is the 0.15
+branch, and end users are encouraged to use the stable releases cut from it.
-[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
-
-If you'd like to support Sway development, you can contribute to [SirCmpwn's
-Patreon page](https://patreon.com/sircmpwn) or you can contribute to
-[bounties](https://github.com/swaywm/sway/issues/986) for specific features.
-Anyone is welcome to claim a bounty and you can make a bounty for any feature
-you wish, and Patreon is more useful for supporting the overall health and
-maintenance of Sway.
+If you'd like to support sway development, please contribute to [SirCmpwn's
+Patreon page](https://patreon.com/sircmpwn).
## Release Signatures
@@ -46,22 +38,22 @@ Sway is available in many distributions. Try installing the "sway" package for
yours. If it's not available, check out [this wiki page](https://github.com/swaywm/sway/wiki/Unsupported-packages)
for information on installation for your distributions.
-If you're interested in packaging Sway for your distribution, stop by the IRC
+If you're interested in packaging sway for your distribution, stop by the IRC
channel or shoot an email to sir@cmpwn.com for advice.
### Compiling from Source
Install dependencies:
-* cmake
-* [wlc](https://github.com/Cloudef/wlc)
+* meson
+* [wlroots](https://github.com/swaywm/wlroots)
* wayland
* xwayland
* libinput >= 1.6.0
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -78,11 +70,9 @@ _\*\*\*Only required for tray support_
Run these commands:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
On systems with logind, you need to set a few caps on the binary:
@@ -102,4 +92,4 @@ Run `man 5 sway` for information on the configuration.
## Running
Run `sway` from a TTY. Some display managers may work but are not supported by
-Sway (gdm is known to work fairly well).
+sway (gdm is known to work fairly well).
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 @@
-# 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)
+# sway
"**S**irCmpwn's **Way**land compositor" é um compositor [Wayland](http://wayland.freedesktop.org/)
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*).
Antes de iniciar a compilação, instale as dependências:
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -62,7 +62,7 @@ Antes de iniciar a compilação, instale as dependências:
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -76,11 +76,9 @@ _\*\*Dependência apenas de swaylock_
Para compilar, execute estes comandos:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
Em sistemas com logind, configure as seguintes capacidades para o arquivo binário:
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 @@
-# 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)
+# sway
"**S**irCmpwn's **Way**land compositor" на данный момент **(в разработке)**
i3-ÑовмеÑтимый [Wayland](http://wayland.freedesktop.org/) композитор.
@@ -49,7 +49,7 @@ Sway доÑтупен во многих диÑтрибутивах и наход
УÑтановите Ñледующие пакеты:
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -57,7 +57,7 @@ Sway доÑтупен во многих диÑтрибутивах и наход
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -74,11 +74,9 @@ _\*\*\*ТребуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ tray_
Выполните Ñледующие команды:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
ЕÑли у Ð²Ð°Ñ logind:
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 @@
-# 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)
+# sway
**Sway** ("**S**irCmpwn's **Way**land compositor") це ÑуміÑний з i3 композитор
[Wayland](http://wayland.freedesktop.org/) (**у Ñтані розробки**).
@@ -54,7 +54,7 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а
Ð’Ñтановіть залежноÑÑ‚Ñ–:
-* cmake
+* meson
* [wlc](https://github.com/Cloudef/wlc)
* wayland
* xwayland
@@ -62,7 +62,7 @@ Sway доÑтупний у багатьох диÑтрибутивах Linux (а
* libcap
* asciidoc
* pcre
-* json-c <= 0.12.1
+* json-c >= 0.13
* pango
* cairo
* gdk-pixbuf2 *
@@ -76,11 +76,9 @@ _\*\*Лише Ð´Ð»Ñ swaylock_
Виконайте ці команди:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_SYSCONFDIR=/etc ..
- make
- sudo make install
+ meson build
+ ninja -C build
+ sudo ninja -C build install
Ðа ÑиÑтемах **з** logind, варто вÑтановити декілька можливоÑтей (caps)
на виконуваний файл 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 @@
+lib_sway_client = static_library(
+ 'sway-client',
+ files(
+ 'pool-buffer.c',
+ ),
+ dependencies: [
+ cairo,
+ gdk_pixbuf,
+ pango,
+ pangocairo,
+ wlroots,
+ wayland_client,
+ ],
+ link_with: [lib_sway_common],
+ include_directories: sway_inc
+)
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 @@
#define _XOPEN_SOURCE 500
-#include <wayland-client.h>
+#include <assert.h>
#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
#include <sys/mman.h>
-#include "client/buffer.h"
-#include "list.h"
-#include "log.h"
+#include <pango/pangocairo.h>
+#include <unistd.h>
+#include <wayland-client.h>
+#include "config.h"
+#include "pool-buffer.h"
static int create_pool_file(size_t size, char **name) {
static const char template[] = "sway-client-XXXXXX";
const char *path = getenv("XDG_RUNTIME_DIR");
- if (!path) {
+ if (!path) {
return -1;
}
@@ -42,7 +41,7 @@ static int create_pool_file(size_t size, char **name) {
}
static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
- struct buffer *buffer = data;
+ struct pool_buffer *buffer = data;
buffer->busy = false;
}
@@ -50,22 +49,17 @@ static const struct wl_buffer_listener buffer_listener = {
.release = buffer_release
};
-static struct buffer *create_buffer(struct window *window, struct buffer *buf,
- int32_t width, int32_t height, int32_t scale, uint32_t format) {
-
- width *= scale;
- height *= scale;
+static struct pool_buffer *create_buffer(struct wl_shm *shm,
+ struct pool_buffer *buf, int32_t width, int32_t height,
+ uint32_t format) {
uint32_t stride = width * 4;
uint32_t size = stride * height;
char *name;
int fd = create_pool_file(size, &name);
- if (fd == -1) {
- sway_abort("Unable to allocate buffer");
- return NULL; // never reached
- }
+ assert(fd != -1);
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- struct wl_shm_pool *pool = wl_shm_create_pool(window->registry->shm, fd, size);
+ struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
buf->buffer = wl_shm_pool_create_buffer(pool, 0,
width, height, stride, format);
wl_shm_pool_destroy(pool);
@@ -85,7 +79,7 @@ static struct buffer *create_buffer(struct window *window, struct buffer *buf,
return buf;
}
-static void destroy_buffer(struct buffer *buffer) {
+void destroy_buffer(struct pool_buffer *buffer) {
if (buffer->buffer) {
wl_buffer_destroy(buffer->buffer);
}
@@ -98,37 +92,33 @@ static void destroy_buffer(struct buffer *buffer) {
if (buffer->pango) {
g_object_unref(buffer->pango);
}
- memset(buffer, 0, sizeof(struct buffer));
+ memset(buffer, 0, sizeof(struct pool_buffer));
}
-struct buffer *get_next_buffer(struct window *window) {
- struct buffer *buffer = NULL;
+struct pool_buffer *get_next_buffer(struct wl_shm *shm,
+ struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
+ struct pool_buffer *buffer = NULL;
- int i;
- for (i = 0; i < 2; ++i) {
- if (window->buffers[i].busy) {
+ for (size_t i = 0; i < 2; ++i) {
+ if (pool[i].busy) {
continue;
}
- buffer = &window->buffers[i];
+ buffer = &pool[i];
}
if (!buffer) {
return NULL;
}
- if (buffer->width != window->width || buffer->height != window->height) {
+ if (buffer->width != width || buffer->height != height) {
destroy_buffer(buffer);
}
if (!buffer->buffer) {
- if (!create_buffer(window, buffer,
- window->width, window->height, window->scale,
+ if (!create_buffer(shm, buffer, width, height,
WL_SHM_FORMAT_ARGB8888)) {
return NULL;
}
}
-
- window->cairo = buffer->cairo;
- window->buffer = buffer;
return buffer;
}
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 @@
-include_directories(
- ${WLC_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
-)
-
-add_library(sway-common STATIC
- ipc-client.c
- list.c
- log.c
- util.c
- readline.c
- stringop.c
-)
-
-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 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <wlr/util/log.h>
+#include "background-image.h"
+#include "cairo.h"
+
+enum background_mode parse_background_mode(const char *mode) {
+ if (strcmp(mode, "stretch") == 0) {
+ return BACKGROUND_MODE_STRETCH;
+ } else if (strcmp(mode, "fill") == 0) {
+ return BACKGROUND_MODE_FILL;
+ } else if (strcmp(mode, "fit") == 0) {
+ return BACKGROUND_MODE_FIT;
+ } else if (strcmp(mode, "center") == 0) {
+ return BACKGROUND_MODE_CENTER;
+ } else if (strcmp(mode, "tile") == 0) {
+ return BACKGROUND_MODE_TILE;
+ } else if (strcmp(mode, "solid_color") == 0) {
+ return BACKGROUND_MODE_SOLID_COLOR;
+ }
+ wlr_log(L_ERROR, "Unsupported background mode: %s", mode);
+ return BACKGROUND_MODE_INVALID;
+}
+
+cairo_surface_t *load_background_image(const char *path) {
+ cairo_surface_t *image;
+#ifdef HAVE_GDK_PIXBUF
+ GError *err = NULL;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
+ if (!pixbuf) {
+ wlr_log(L_ERROR, "Failed to load background image (%s).",
+ err->message);
+ return false;
+ }
+ image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
+ g_object_unref(pixbuf);
+#else
+ image = cairo_image_surface_create_from_png(path);
+#endif //HAVE_GDK_PIXBUF
+ if (!image) {
+ wlr_log(L_ERROR, "Failed to read background image.");
+ return NULL;
+ }
+ if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
+ wlr_log(L_ERROR, "Failed to read background image: %s."
+#ifndef HAVE_GDK_PIXBUF
+ "\nSway was compiled without gdk_pixbuf support, so only"
+ "\nPNG images can be loaded. This is the likely cause."
+#endif //HAVE_GDK_PIXBUF
+ , cairo_status_to_string(cairo_surface_status(image)));
+ return NULL;
+ }
+ return image;
+}
+
+void render_background_image(cairo_t *cairo, cairo_surface_t *image,
+ enum background_mode mode, int buffer_width, int buffer_height) {
+ double width = cairo_image_surface_get_width(image);
+ double height = cairo_image_surface_get_height(image);
+
+ switch (mode) {
+ case BACKGROUND_MODE_STRETCH:
+ cairo_scale(cairo,
+ (double)buffer_width / width,
+ (double)buffer_height / height);
+ cairo_set_source_surface(cairo, image, 0, 0);
+ break;
+ case BACKGROUND_MODE_FILL: {
+ double window_ratio = (double)buffer_width / buffer_height;
+ double bg_ratio = width / height;
+
+ if (window_ratio > bg_ratio) {
+ double scale = (double)buffer_width / width;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ 0, (double)buffer_height / 2 / scale - height / 2);
+ } else {
+ double scale = (double)buffer_height / height;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ (double)buffer_width / 2 / scale - width / 2, 0);
+ }
+ break;
+ }
+ case BACKGROUND_MODE_FIT: {
+ double window_ratio = (double)buffer_width / buffer_height;
+ double bg_ratio = width / height;
+
+ if (window_ratio > bg_ratio) {
+ double scale = (double)buffer_height / height;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ (double)buffer_width / 2 / scale - width / 2, 0);
+ } else {
+ double scale = (double)buffer_width / width;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ 0, (double)buffer_height / 2 / scale - height / 2);
+ }
+ break;
+ }
+ case BACKGROUND_MODE_CENTER:
+ cairo_set_source_surface(cairo, image,
+ (double)buffer_width / 2 - width / 2,
+ (double)buffer_height / 2 - height / 2);
+ break;
+ case BACKGROUND_MODE_TILE: {
+ cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ cairo_set_source(cairo, pattern);
+ break;
+ }
+ case BACKGROUND_MODE_SOLID_COLOR:
+ case BACKGROUND_MODE_INVALID:
+ assert(0);
+ break;
+ }
+ cairo_paint(cairo);
+}
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 @@
-#include "client/cairo.h"
+#include <stdint.h>
+#include <cairo/cairo.h>
+#include "cairo.h"
+#ifdef HAVE_GDK_PIXBUF
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#endif
void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
cairo_set_source_rgba(cairo,
@@ -8,45 +13,31 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
(color >> (0*8) & 0xFF) / 255.0);
}
-cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height) {
+cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
+ int width, int height) {
int image_width = cairo_image_surface_get_width(image);
int image_height = cairo_image_surface_get_height(image);
cairo_surface_t *new =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
-
cairo_t *cairo = cairo_create(new);
-
- cairo_scale(cairo, (double) width / image_width, (double) height / image_height);
-
+ cairo_scale(cairo, (double)width / image_width,
+ (double)height / image_height);
cairo_set_source_surface(cairo, image, 0, 0);
- cairo_paint(cairo);
+ cairo_paint(cairo);
cairo_destroy(cairo);
-
return new;
}
-#ifdef WITH_GDK_PIXBUF
-#include <gdk-pixbuf/gdk-pixbuf.h>
-
-#ifndef GDK_PIXBUF_CHECK_VERSION
-#define GDK_PIXBUF_CHECK_VERSION(major,minor,micro) \
- (GDK_PIXBUF_MAJOR > (major) || \
- (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR > (minor)) || \
- (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR == (minor) && \
- GDK_PIXBUF_MICRO >= (micro)))
-#endif
-
+#ifdef HAVE_GDK_PIXBUF
cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) {
int chan = gdk_pixbuf_get_n_channels(gdkbuf);
- if (chan < 3) return NULL;
+ if (chan < 3) {
+ return NULL;
+ }
-#if GDK_PIXBUF_CHECK_VERSION(2,32,0)
const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);
-#else
- const guint8* gdkpix = gdk_pixbuf_get_pixels(gdkbuf);
-#endif
if (!gdkpix) {
return NULL;
}
@@ -101,7 +92,9 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
* ------
* tested as equal to lround(z/255.0) for uint z in [0..0xfe02]
*/
-#define PREMUL_ALPHA(x,a,b,z) G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } G_STMT_END
+#define PREMUL_ALPHA(x,a,b,z) \
+ G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \
+ G_STMT_END
int i;
for (i = h; i; --i) {
const guint8 *gp = gdkpix;
@@ -131,4 +124,4 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
cairo_surface_mark_dirty(cs);
return cs;
}
-#endif //WITH_GDK_PIXBUF
+#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 @@
-#define _POSIX_C_SOURCE 2
+#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -14,13 +14,31 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
static const size_t ipc_header_size = sizeof(ipc_magic)+8;
char *get_socketpath(void) {
- FILE *fp = popen("sway --get-socketpath", "r");
- if (!fp) {
- return NULL;
+ const char *swaysock = getenv("SWAYSOCK");
+ if (swaysock) {
+ return strdup(swaysock);
}
- char *line = read_line(fp);
- pclose(fp);
- return line;
+ FILE *fp = popen("sway --get-socketpath 2>/dev/null", "r");
+ if (fp) {
+ char *line = read_line(fp);
+ pclose(fp);
+ if (line && *line) {
+ return line;
+ }
+ }
+ const char *i3sock = getenv("I3SOCK");
+ if (i3sock) {
+ return strdup(i3sock);
+ }
+ fp = popen("i3 --get-socketpath 2>/dev/null", "r");
+ if (fp) {
+ char *line = read_line(fp);
+ pclose(fp);
+ if (line && *line) {
+ return line;
+ }
+ }
+ return NULL;
}
int ipc_open_socket(const char *socket_path) {
@@ -79,7 +97,7 @@ struct ipc_response *ipc_recv_response(int socketfd) {
error_2:
free(response);
error_1:
- sway_log(L_ERROR, "Unable to allocate memory for IPC response");
+ wlr_log(L_ERROR, "Unable to allocate memory for IPC response");
return NULL;
}
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 @@
-#define _POSIX_C_SOURCE 199506L
-#include <errno.h>
-#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <time.h>
#include "log.h"
-#include "sway.h"
-#include "readline.h"
-static int colored = 1;
-static log_importance_t loglevel_default = L_ERROR;
-static log_importance_t v = L_SILENT;
+void sway_terminate(int code);
-static const char *verbosity_colors[] = {
- [L_SILENT] = "",
- [L_ERROR ] = "\x1B[1;31m",
- [L_INFO ] = "\x1B[1;34m",
- [L_DEBUG ] = "\x1B[1;30m",
-};
-static const char verbosity_chars[] = {
- [L_SILENT] = '\0',
- [L_ERROR ] = 'E',
- [L_INFO ] = 'I',
- [L_DEBUG ] = 'D',
-};
-
-void init_log(log_importance_t verbosity) {
- if (verbosity != L_DEBUG) {
- // command "debuglog" needs to know the user specified log level when
- // turning off debug logging.
- loglevel_default = verbosity;
- }
- v = verbosity;
-}
-
-void set_log_level(log_importance_t verbosity) {
- v = verbosity;
-}
-
-log_importance_t get_log_level(void) {
- return v;
-}
-
-void reset_log_level(void) {
- v = loglevel_default;
-}
-
-bool toggle_debug_logging(void) {
- v = (v == L_DEBUG) ? loglevel_default : L_DEBUG;
- return (v == L_DEBUG);
-}
-
-void sway_log_colors(int mode) {
- colored = (mode == 1) ? 1 : 0;
-}
-
-void _sway_vlog(const char *filename, int line, log_importance_t verbosity,
- const char *format, va_list args) {
- if (verbosity <= v) {
- // prefix the time to the log message
- static struct tm result;
- static time_t t;
- static struct tm *tm_info;
- char buffer[26];
-
- unsigned int c = verbosity;
- if (c > sizeof(verbosity_colors) / sizeof(char *) - 1) {
- c = sizeof(verbosity_colors) / sizeof(char *) - 1;
- }
-
- // First, if not printing color, show the log level
- if (!(colored && isatty(STDERR_FILENO)) && c != L_SILENT) {
- fprintf(stderr, "%c: ", verbosity_chars[c]);
- }
-
- // get current time
- t = time(NULL);
- // convert time to local time (determined by the locale)
- tm_info = localtime_r(&t, &result);
- // generate time prefix
- strftime(buffer, sizeof(buffer), "%x %X - ", tm_info);
- fprintf(stderr, "%s", buffer);
-
- if (colored && isatty(STDERR_FILENO)) {
- fprintf(stderr, "%s", verbosity_colors[c]);
- }
-
- if (filename && line) {
- const char *file = filename + strlen(filename);
- while (file != filename && *file != '/') {
- --file;
- }
- if (*file == '/') {
- ++file;
- }
- fprintf(stderr, "[%s:%d] ", file, line);
- }
-
- vfprintf(stderr, format, args);
-
- if (colored && isatty(STDERR_FILENO)) {
- fprintf(stderr, "\x1B[0m");
- }
- fprintf(stderr, "\n");
- }
-}
-
-void _sway_log(const char *filename, int line, log_importance_t verbosity, const char* format, ...) {
- va_list args;
- va_start(args, format);
- _sway_vlog(filename, line, verbosity, format, args);
- va_end(args);
-}
-
-
-void _sway_abort(const char *filename, int line, const char* format, ...) {
+void _sway_abort(const char *format, ...) {
va_list args;
va_start(args, format);
- _sway_vlog(filename, line, L_ERROR, format, args);
+ _wlr_vlog(L_ERROR, format, args);
va_end(args);
sway_terminate(EXIT_FAILURE);
}
-void sway_log_errno(log_importance_t verbosity, char* format, ...) {
- if (verbosity <= v) {
- unsigned int c = verbosity;
- if (c > sizeof(verbosity_colors) / sizeof(char *) - 1) {
- c = sizeof(verbosity_colors) / sizeof(char *) - 1;
- }
-
- if (colored && isatty(STDERR_FILENO)) {
- fprintf(stderr, "%s", verbosity_colors[c]);
- } else if (c != L_SILENT) {
- fprintf(stderr, "%c: ", verbosity_chars[c]);
- }
-
- va_list args;
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
-
- fprintf(stderr, ": ");
- fprintf(stderr, "%s", strerror(errno));
-
- if (colored && isatty(STDERR_FILENO)) {
- fprintf(stderr, "\x1B[0m");
- }
- fprintf(stderr, "\n");
- }
-}
-
-bool _sway_assert(bool condition, const char *filename, int line, const char* format, ...) {
+bool _sway_assert(bool condition, const char *format, ...) {
if (condition) {
return true;
}
va_list args;
va_start(args, format);
- _sway_vlog(filename, line, L_ERROR, format, args);
+ _wlr_vlog(L_ERROR, format, args);
va_end(args);
#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 @@
+lib_sway_common = static_library(
+ 'sway-common',
+ files(
+ 'background-image.c',
+ 'cairo.c',
+ 'ipc-client.c',
+ 'log.c',
+ 'list.c',
+ 'pango.c',
+ 'readline.c',
+ 'stringop.c',
+ 'unicode.c',
+ 'util.c'
+ ),
+ dependencies: [
+ cairo,
+ gdk_pixbuf,
+ pango,
+ pangocairo,
+ wlroots
+ ],
+ include_directories: sway_inc
+)
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 @@
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "log.h"
-PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
- int32_t scale, bool markup) {
+PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
+ const char *text, int32_t scale, bool markup) {
PangoLayout *layout = pango_cairo_create_layout(cairo);
PangoAttrList *attrs;
if (markup) {
char *buf;
- pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL);
+ GError *error = NULL;
+ if (!sway_assert(pango_parse_markup(
+ text, -1, 0, &attrs, &buf, NULL, &error),
+ "pango_parse_markup '%s' -> error %s", text,
+ error ? error->message : NULL)) {
+ return NULL;
+ }
pango_layout_set_markup(layout, buf, -1);
free(buf);
} else {
@@ -33,7 +39,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
int32_t scale, bool markup, const char *fmt, ...) {
- char *buf = malloc(2048);
+ static char buf[2048];
va_list args;
va_start(args, fmt);
@@ -44,16 +50,13 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
pango_cairo_update_layout(cairo, layout);
-
pango_layout_get_pixel_size(layout, width, height);
-
g_object_unref(layout);
-
- free(buf);
}
-void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...) {
- char *buf = malloc(2048);
+void pango_printf(cairo_t *cairo, const char *font,
+ int32_t scale, bool markup, const char *fmt, ...) {
+ static char buf[2048];
va_list args;
va_start(args, fmt);
@@ -64,10 +67,6 @@ void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup,
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
pango_cairo_update_layout(cairo, layout);
-
pango_cairo_show_layout(cairo, layout);
-
g_object_unref(layout);
-
- free(buf);
}
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) {
char *string = malloc(size);
char lastChar = '\0';
if (!string) {
- sway_log(L_ERROR, "Unable to allocate memory for read_line");
+ wlr_log(L_ERROR, "Unable to allocate memory for read_line");
return NULL;
}
while (1) {
@@ -29,7 +29,7 @@ char *read_line(FILE *file) {
char *new_string = realloc(string, size *= 2);
if (!new_string) {
free(string);
- sway_log(L_ERROR, "Unable to allocate memory for read_line");
+ wlr_log(L_ERROR, "Unable to allocate memory for read_line");
return NULL;
}
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 @@
+#include <stdint.h>
+#include <stddef.h>
+#include "unicode.h"
+
+size_t utf8_chsize(uint32_t ch) {
+ if (ch < 0x80) {
+ return 1;
+ } else if (ch < 0x800) {
+ return 2;
+ } else if (ch < 0x10000) {
+ return 3;
+ }
+ return 4;
+}
+
+static const uint8_t masks[] = {
+ 0x7F,
+ 0x1F,
+ 0x0F,
+ 0x07,
+ 0x03,
+ 0x01
+};
+
+uint32_t utf8_decode(const char **char_str) {
+ uint8_t **s = (uint8_t **)char_str;
+
+ uint32_t cp = 0;
+ if (**s < 128) {
+ // shortcut
+ cp = **s;
+ ++*s;
+ return cp;
+ }
+ int size = utf8_size((char *)*s);
+ if (size == -1) {
+ ++*s;
+ return UTF8_INVALID;
+ }
+ uint8_t mask = masks[size - 1];
+ cp = **s & mask;
+ ++*s;
+ while (--size) {
+ cp <<= 6;
+ cp |= **s & 0x3f;
+ ++*s;
+ }
+ return cp;
+}
+
+size_t utf8_encode(char *str, uint32_t ch) {
+ size_t len = 0;
+ uint8_t first;
+
+ if (ch < 0x80) {
+ first = 0;
+ len = 1;
+ } else if (ch < 0x800) {
+ first = 0xc0;
+ len = 2;
+ } else if (ch < 0x10000) {
+ first = 0xe0;
+ len = 3;
+ } else {
+ first = 0xf0;
+ len = 4;
+ }
+
+ for (size_t i = len - 1; i > 0; --i) {
+ str[i] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ }
+
+ str[0] = ch | first;
+ return len;
+}
+
+
+static const struct {
+ uint8_t mask;
+ uint8_t result;
+ int octets;
+} sizes[] = {
+ { 0x80, 0x00, 1 },
+ { 0xE0, 0xC0, 2 },
+ { 0xF0, 0xE0, 3 },
+ { 0xF8, 0xF0, 4 },
+ { 0xFC, 0xF8, 5 },
+ { 0xFE, 0xF8, 6 },
+ { 0x80, 0x80, -1 },
+};
+
+int utf8_size(const char *s) {
+ uint8_t c = (uint8_t)*s;
+ for (size_t i = 0; i < sizeof(sizes) / 2; ++i) {
+ if ((c & sizes[i].mask) == sizes[i].result) {
+ return sizes[i].octets;
+ }
+ }
+ return -1;
+}
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 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
-#include <wlc/wlc.h>
#include <xkbcommon/xkbcommon-names.h>
+#include <wlr/types/wlr_keyboard.h>
#include "log.h"
#include "readline.h"
#include "util.h"
@@ -29,16 +29,16 @@ static struct modifier_key {
char *name;
uint32_t mod;
} modifiers[] = {
- { XKB_MOD_NAME_SHIFT, WLC_BIT_MOD_SHIFT },
- { XKB_MOD_NAME_CAPS, WLC_BIT_MOD_CAPS },
- { XKB_MOD_NAME_CTRL, WLC_BIT_MOD_CTRL },
- { "Ctrl", WLC_BIT_MOD_CTRL },
- { XKB_MOD_NAME_ALT, WLC_BIT_MOD_ALT },
- { "Alt", WLC_BIT_MOD_ALT },
- { XKB_MOD_NAME_NUM, WLC_BIT_MOD_MOD2 },
- { "Mod3", WLC_BIT_MOD_MOD3 },
- { XKB_MOD_NAME_LOGO, WLC_BIT_MOD_LOGO },
- { "Mod5", WLC_BIT_MOD_MOD5 },
+ { XKB_MOD_NAME_SHIFT, WLR_MODIFIER_SHIFT },
+ { XKB_MOD_NAME_CAPS, WLR_MODIFIER_CAPS },
+ { XKB_MOD_NAME_CTRL, WLR_MODIFIER_CTRL },
+ { "Ctrl", WLR_MODIFIER_CTRL },
+ { XKB_MOD_NAME_ALT, WLR_MODIFIER_ALT },
+ { "Alt", WLR_MODIFIER_ALT },
+ { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
+ { "Mod3", WLR_MODIFIER_MOD3 },
+ { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
+ { "Mod5", WLR_MODIFIER_MOD5 },
};
uint32_t get_modifier_mask_by_name(const char *name) {
@@ -113,7 +113,7 @@ uint32_t parse_color(const char *color) {
int len = strlen(color);
if (len != 6 && len != 8) {
- sway_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);
+ wlr_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);
return 0xFFFFFFFF;
}
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 @@
-install(
- FILES _sway _swaymsg _swaygrab _swaylock
- DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/zsh/site-functions/
-)
diff --git a/config.in b/config.in
index b39a39aa..54817f2a 100644
--- a/config.in
+++ b/config.in
@@ -21,7 +21,7 @@ set $menu dmenu_run
### Output configuration
#
# Default wallpaper (more resolutions are available in __DATADIR__/backgrounds/sway/)
-output * bg __DATADIR__/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
+output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
#
# Example configuration:
#
@@ -164,16 +164,16 @@ mode "resize" {
# right will grow the containers width
# up will shrink the containers height
# down will grow the containers height
- bindsym $left resize shrink width 10 px or 10 ppt
- bindsym $down resize grow height 10 px or 10 ppt
- bindsym $up resize shrink height 10 px or 10 ppt
- bindsym $right resize grow width 10 px or 10 ppt
+ bindsym $left resize shrink width 10px
+ bindsym $down resize grow height 10px
+ bindsym $up resize shrink height 10px
+ bindsym $right resize grow width 10px
# ditto, with arrow keys
- bindsym Left resize shrink width 10 px or 10 ppt
- bindsym Down resize grow height 10 px or 10 ppt
- bindsym Up resize shrink height 10 px or 10 ppt
- bindsym Right resize grow width 10 px or 10 ppt
+ bindsym Left resize shrink width 10px
+ bindsym Down resize grow height 10px
+ bindsym Up resize shrink height 10px
+ bindsym Right resize grow width 10px
# return to default mode
bindsym Return mode "default"
@@ -194,4 +194,4 @@ bar {
}
}
-include __SYSCONFDIR__/sway/config.d/*
+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 @@
+#ifndef _SWAY_BACKGROUND_IMAGE_H
+#define _SWAY_BACKGROUND_IMAGE_H
+#include "cairo.h"
+
+enum background_mode {
+ BACKGROUND_MODE_STRETCH,
+ BACKGROUND_MODE_FILL,
+ BACKGROUND_MODE_FIT,
+ BACKGROUND_MODE_CENTER,
+ BACKGROUND_MODE_TILE,
+ BACKGROUND_MODE_SOLID_COLOR,
+ BACKGROUND_MODE_INVALID,
+};
+
+enum background_mode parse_background_mode(const char *mode);
+cairo_surface_t *load_background_image(const char *path);
+void render_background_image(cairo_t *cairo, cairo_surface_t *image,
+ enum background_mode mode, int buffer_width, int buffer_height);
+
+#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 @@
#ifndef _SWAY_CAIRO_H
#define _SWAY_CAIRO_H
-
#include <stdint.h>
#include <cairo/cairo.h>
void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
-cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, int width, int height);
+cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
+ int width, int height);
-#ifdef WITH_GDK_PIXBUF
+#include "config.h"
+#ifdef HAVE_GDK_PIXBUF
#include <gdk-pixbuf/gdk-pixbuf.h>
-cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf);
+cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
+ const GdkPixbuf *gdkbuf);
#endif //WITH_GDK_PIXBUF
#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 @@
-#ifndef _BUFFER_H
-#define _BUFFER_H
-
-#include "client/window.h"
-
-struct buffer *get_next_buffer(struct window *state);
-
-#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 @@
-#ifndef _SWAY_CLIENT_PANGO_H
-#define _SWAY_CLIENT_PANGO_H
-
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text,
- int32_t scale, bool markup);
-void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
- int32_t scale, bool markup, const char *fmt, ...);
-void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...);
-
-#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 @@
-#ifndef _SWAY_CLIENT_REGISTRY_H
-#define _SWAY_CLIENT_REGISTRY_H
-
-#include <wayland-client.h>
-#include <xkbcommon/xkbcommon.h>
-#include "wayland-desktop-shell-client-protocol.h"
-#include "wayland-swaylock-client-protocol.h"
-#include "list.h"
-
-enum mod_bit {
- MOD_SHIFT = 1<<0,
- MOD_CAPS = 1<<1,
- MOD_CTRL = 1<<2,
- MOD_ALT = 1<<3,
- MOD_MOD2 = 1<<4,
- MOD_MOD3 = 1<<5,
- MOD_LOGO = 1<<6,
- MOD_MOD5 = 1<<7,
-};
-
-enum mask {
- MASK_SHIFT,
- MASK_CAPS,
- MASK_CTRL,
- MASK_ALT,
- MASK_MOD2,
- MASK_MOD3,
- MASK_LOGO,
- MASK_MOD5,
- MASK_LAST
-};
-
-struct output_state {
- struct wl_output *output;
- uint32_t flags;
- uint32_t width, height;
- uint32_t scale;
-};
-
-struct xkb {
- struct xkb_state *state;
- struct xkb_context *context;
- struct xkb_keymap *keymap;
- xkb_mod_mask_t masks[MASK_LAST];
-};
-
-struct input {
- struct xkb xkb;
-
- xkb_keysym_t sym;
- uint32_t code;
- uint32_t last_code;
- uint32_t modifiers;
-
- void (*notify)(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint);
-};
-
-struct registry {
- struct wl_compositor *compositor;
- struct wl_display *display;
- struct wl_pointer *pointer;
- struct wl_keyboard *keyboard;
- struct wl_seat *seat;
- struct wl_shell *shell;
- struct wl_shm *shm;
- struct desktop_shell *desktop_shell;
- struct lock *swaylock;
- struct input *input;
- list_t *outputs;
-};
-
-struct registry *registry_poll(void);
-void registry_teardown(struct registry *registry);
-
-#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 @@
-#ifndef _CLIENT_H
-#define _CLIENT_H
-
-#include <wayland-client.h>
-#include "wayland-desktop-shell-client-protocol.h"
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdbool.h>
-#include "list.h"
-#include "client/registry.h"
-
-struct window;
-
-struct buffer {
- struct wl_buffer *buffer;
- cairo_surface_t *surface;
- cairo_t *cairo;
- PangoContext *pango;
- uint32_t width, height;
- bool busy;
-};
-
-struct cursor {
- struct wl_surface *surface;
- struct wl_cursor_theme *cursor_theme;
- struct wl_cursor *cursor;
- struct wl_pointer *pointer;
-};
-
-enum scroll_direction {
- SCROLL_UP,
- SCROLL_DOWN,
- SCROLL_LEFT,
- SCROLL_RIGHT,
-};
-
-struct pointer_input {
- int last_x;
- int last_y;
-
- void (*notify_button)(struct window *window, int x, int y, uint32_t button, uint32_t state_w);
- void (*notify_scroll)(struct window *window, enum scroll_direction direction);
-};
-
-struct window {
- struct registry *registry;
- struct buffer buffers[2];
- struct buffer *buffer;
- struct wl_surface *surface;
- struct wl_shell_surface *shell_surface;
- struct wl_callback *frame_cb;
- struct cursor cursor;
- uint32_t width, height;
- int32_t scale;
- char *font;
- cairo_t *cairo;
- struct pointer_input pointer_input;
-};
-
-struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
- int32_t scale, bool shell_surface);
-void window_teardown(struct window *state);
-int window_prerender(struct window *state);
-int window_render(struct window *state);
-void window_make_shell(struct window *window);
-
-#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 {
IPC_EVENT_BINDING = ((1<<31) | 5),
IPC_EVENT_MODIFIER = ((1<<31) | 6),
IPC_EVENT_INPUT = ((1<<31) | 7),
- IPC_SWAY_GET_PIXELS = 0x81
};
#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 @@
#ifndef _SWAY_LOG_H
#define _SWAY_LOG_H
#include <stdbool.h>
+#include <wlr/util/log.h>
-typedef enum {
- L_SILENT = 0,
- L_ERROR = 1,
- L_INFO = 2,
- L_DEBUG = 3,
-} log_importance_t;
-
-void init_log(log_importance_t verbosity);
-void set_log_level(log_importance_t verbosity);
-log_importance_t get_log_level(void);
-void reset_log_level(void);
-// returns whether debug logging is on after switching.
-bool toggle_debug_logging(void);
-void sway_log_colors(int mode);
-void sway_log_errno(log_importance_t verbosity, char* format, ...) __attribute__((format(printf,2,3)));
-
-void _sway_abort(const char *filename, int line, const char* format, ...) __attribute__((format(printf,3,4)));
+void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);
#define sway_abort(FMT, ...) \
- _sway_abort(__FILE__, __LINE__, FMT, ##__VA_ARGS__)
+ _sway_abort("[%s:%d] " FMT, wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__)
-bool _sway_assert(bool condition, const char *filename, int line, const char* format, ...) __attribute__((format(printf,4,5)));
+bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);
#define sway_assert(COND, FMT, ...) \
- _sway_assert(COND, __FILE__, __LINE__, "%s:" FMT, __PRETTY_FUNCTION__, ##__VA_ARGS__)
-
-void _sway_log(const char *filename, int line, log_importance_t verbosity, const char* format, ...) __attribute__((format(printf,4,5)));
-
-#define sway_log(VERBOSITY, FMT, ...) \
- _sway_log(__FILE__, __LINE__, VERBOSITY, FMT, ##__VA_ARGS__)
-
-#define sway_vlog(VERBOSITY, FMT, VA_ARGS) \
- _sway_vlog(__FILE__, __LINE__, VERBOSITY, FMT, VA_ARGS)
+ _sway_assert(COND, "[%s:%d] %s:" FMT, wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
void error_handler(int sig);
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 @@
+#ifndef _SWAY_PANGO_H
+#define _SWAY_PANGO_H
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+
+PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
+ const char *text, int32_t scale, bool markup);
+void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
+ int32_t scale, bool markup, const char *fmt, ...);
+void pango_printf(cairo_t *cairo, const char *font,
+ int32_t scale, bool markup, const char *fmt, ...);
+
+#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 @@
+#ifndef _SWAY_BUFFERS_H
+#define _SWAY_BUFFERS_H
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <wayland-client.h>
+
+struct pool_buffer {
+ struct wl_buffer *buffer;
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+ PangoContext *pango;
+ uint32_t width, height;
+ bool busy;
+};
+
+struct pool_buffer *get_next_buffer(struct wl_shm *shm,
+ struct pool_buffer pool[static 2], uint32_t width, uint32_t height);
+void destroy_buffer(struct pool_buffer *buffer);
+
+#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 @@
-#ifndef _SWAY_SWAY_H
-#define _SWAY_SWAY_H
-
-void sway_terminate(int exit_code);
-
-#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 @@
-#ifndef _SWAY_BORDER_H
-#define _SWAY_BORDER_H
-#include <wlc/wlc.h>
-#include "container.h"
-
-/**
- * Border pixel buffer and corresponding geometry.
- */
-struct border {
- unsigned char *buffer;
- struct wlc_geometry geometry;
-};
-
-/**
- * Clear border buffer.
- */
-void border_clear(struct border *border);
-
-/**
- * Recursively update all of the borders within a container.
- */
-void update_container_border(swayc_t *container);
-
-void render_view_borders(wlc_handle view);
-int get_font_text_height(const char *font);
-bool should_hide_top_border(swayc_t *con, double y);
-
-#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 @@
#ifndef _SWAY_COMMANDS_H
#define _SWAY_COMMANDS_H
-#include <stdbool.h>
-#include <json-c/json.h>
-#include <wlc/wlc.h>
-#include "config.h"
-// Container that a called command should act upon. Only valid in command functions.
-extern swayc_t *current_container;
+#include "config.h"
/**
* Indicates the result of a command's execution.
@@ -22,6 +17,7 @@ enum cmd_status {
CMD_BLOCK_BAR,
CMD_BLOCK_BAR_COLORS,
CMD_BLOCK_INPUT,
+ CMD_BLOCK_SEAT,
CMD_BLOCK_COMMANDS,
CMD_BLOCK_IPC,
CMD_BLOCK_IPC_EVENTS,
@@ -46,18 +42,13 @@ enum expected_args {
EXPECTED_EQUAL_TO
};
-struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val);
-struct cmd_results *add_color(const char*, char*, const char*);
-void input_cmd_apply(struct input_config *input);
-void hide_view_in_scratchpad(swayc_t *sp_view);
-
-swayc_t *sp_view;
-int sp_index;
+struct cmd_results *checkarg(int argc, const char *name,
+ enum expected_args type, int val);
/**
- * Parse and handles a command.
+ * Parse and executes a command.
*/
-struct cmd_results *handle_command(char *command, enum command_context context);
+struct cmd_results *execute_command(char *command, struct sway_seat *seat);
/**
* Parse and handles a command during config file loading.
*
@@ -68,7 +59,6 @@ struct cmd_results *config_command(char *command, enum cmd_status block);
* Parses a command policy rule.
*/
struct cmd_results *config_commands_command(char *exec);
-
/**
* Allocates a cmd_results object.
*/
@@ -84,11 +74,8 @@ void free_cmd_results(struct cmd_results *results);
*/
const char *cmd_results_to_json(struct cmd_results *results);
-void remove_view_from_scratchpad(swayc_t *);
-
-/**
- * Actual command function signatures for individual .c files in commands/ directory.
- */
+struct cmd_results *add_color(const char *name,
+ char *buffer, const char *color);
typedef struct cmd_results *sway_cmd(int argc, char **argv);
@@ -108,6 +95,7 @@ sway_cmd cmd_commands;
sway_cmd cmd_debuglog;
sway_cmd cmd_default_border;
sway_cmd cmd_default_floating_border;
+sway_cmd cmd_default_orientation;
sway_cmd cmd_exec;
sway_cmd cmd_exec_always;
sway_cmd cmd_exit;
@@ -126,6 +114,7 @@ sway_cmd cmd_gaps;
sway_cmd cmd_hide_edge_borders;
sway_cmd cmd_include;
sway_cmd cmd_input;
+sway_cmd cmd_seat;
sway_cmd cmd_ipc;
sway_cmd cmd_kill;
sway_cmd cmd_layout;
@@ -134,10 +123,10 @@ sway_cmd cmd_mark;
sway_cmd cmd_mode;
sway_cmd cmd_mouse_warping;
sway_cmd cmd_move;
+sway_cmd cmd_opacity;
sway_cmd cmd_new_float;
sway_cmd cmd_new_window;
sway_cmd cmd_no_focus;
-sway_cmd cmd_orientation;
sway_cmd cmd_output;
sway_cmd cmd_permit;
sway_cmd cmd_reject;
@@ -153,6 +142,7 @@ sway_cmd cmd_splith;
sway_cmd cmd_splitt;
sway_cmd cmd_splitv;
sway_cmd cmd_sticky;
+sway_cmd cmd_swaybg_command;
sway_cmd cmd_unmark;
sway_cmd cmd_workspace;
sway_cmd cmd_ws_auto_back_and_forth;
@@ -195,17 +185,28 @@ sway_cmd bar_colors_cmd_statusline;
sway_cmd bar_colors_cmd_focused_statusline;
sway_cmd bar_colors_cmd_urgent_workspace;
+sway_cmd input_cmd_seat;
sway_cmd input_cmd_accel_profile;
sway_cmd input_cmd_click_method;
sway_cmd input_cmd_drag_lock;
sway_cmd input_cmd_dwt;
sway_cmd input_cmd_events;
sway_cmd input_cmd_left_handed;
+sway_cmd input_cmd_map_to_output;
sway_cmd input_cmd_middle_emulation;
sway_cmd input_cmd_natural_scroll;
sway_cmd input_cmd_pointer_accel;
sway_cmd input_cmd_scroll_method;
sway_cmd input_cmd_tap;
+sway_cmd input_cmd_xkb_layout;
+sway_cmd input_cmd_xkb_model;
+sway_cmd input_cmd_xkb_options;
+sway_cmd input_cmd_xkb_rules;
+sway_cmd input_cmd_xkb_variant;
+
+sway_cmd seat_cmd_attach;
+sway_cmd seat_cmd_fallback;
+sway_cmd seat_cmd_cursor;
sway_cmd cmd_ipc_cmd;
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 @@
#ifndef _SWAY_CONFIG_H
#define _SWAY_CONFIG_H
-
#define PID_WORKSPACE_TIMEOUT 60
-
#include <libinput.h>
#include <stdint.h>
-#include <wlc/geometry.h>
-#include <wlc/wlc.h>
-#include <xkbcommon/xkbcommon.h>
+#include <string.h>
#include <time.h>
-#include "wayland-desktop-shell-server-protocol.h"
+#include <wlr/types/wlr_box.h>
+#include <xkbcommon/xkbcommon.h>
#include "list.h"
-#include "layout.h"
-#include "container.h"
+#include "tree/layout.h"
+#include "tree/container.h"
+#include "wlr-layer-shell-unstable-v1-protocol.h"
+
+// TODO: Refactor this shit
/**
* Describes a variable created via the `set` command.
@@ -47,11 +47,12 @@ struct sway_mouse_binding {
*/
struct sway_mode {
char *name;
- list_t *bindings;
+ list_t *keysym_bindings;
+ list_t *keycode_bindings;
};
/**
- * libinput options for input devices
+ * options for input devices
*/
struct input_config {
char *identifier;
@@ -68,8 +69,33 @@ struct input_config {
int send_events;
int tap;
+ char *xkb_layout;
+ char *xkb_model;
+ char *xkb_options;
+ char *xkb_rules;
+ char *xkb_variant;
+
+ char *mapped_output;
+
bool capturable;
- struct wlc_geometry region;
+ struct wlr_box region;
+};
+
+/**
+ * Options for misc device configurations that happen in the seat block
+ */
+struct seat_attachment_config {
+ char *identifier;
+ // TODO other things are configured here for some reason
+};
+
+/**
+ * Options for multiseat and other misc device configurations
+ */
+struct seat_config {
+ char *name;
+ int fallback; // -1 means not set
+ list_t *attachments; // list of seat_attachment configs
};
/**
@@ -81,8 +107,11 @@ struct output_config {
char *name;
int enabled;
int width, height;
+ float refresh_rate;
int x, y;
- int scale;
+ float scale;
+ int32_t transform;
+
char *background;
char *background_option;
};
@@ -126,24 +155,13 @@ struct bar_config {
char *id;
uint32_t modifier;
list_t *outputs;
- enum desktop_shell_panel_position position;
+ char *position;
list_t *bindings;
char *status_command;
bool pango_markup;
char *swaybar_command;
char *font;
int height; // -1 not defined
-
-#ifdef ENABLE_TRAY
- // Tray
- char *tray_output;
- char *icon_theme;
- uint32_t tray_padding;
- uint32_t activate_button;
- uint32_t context_button;
- uint32_t secondary_button;
-#endif
-
bool workspace_buttons;
bool wrap_scroll;
char *separator_symbol;
@@ -260,11 +278,13 @@ struct sway_config {
list_t *pid_workspaces;
list_t *output_configs;
list_t *input_configs;
+ list_t *seat_configs;
list_t *criteria;
list_t *no_focus;
list_t *active_bar_modifiers;
struct sway_mode *current_mode;
struct bar_config *current_bar;
+ char *swaybg_command;
uint32_t floating_mod;
uint32_t dragging_key;
uint32_t resizing_key;
@@ -272,8 +292,8 @@ struct sway_config {
char *floating_scroll_down_cmd;
char *floating_scroll_left_cmd;
char *floating_scroll_right_cmd;
- enum swayc_layouts default_orientation;
- enum swayc_layouts default_layout;
+ enum sway_container_layout default_orientation;
+ enum sway_container_layout default_layout;
char *font;
int font_height;
@@ -286,7 +306,6 @@ struct sway_config {
bool reloading;
bool reading;
bool auto_back_and_forth;
- bool seamless_mouse;
bool show_marks;
bool edge_gaps;
@@ -297,8 +316,8 @@ struct sway_config {
list_t *config_chain;
const char *current_config;
- enum swayc_border_types border;
- enum swayc_border_types floating_border;
+ enum sway_container_border border;
+ enum sway_container_border floating_border;
int border_thickness;
int floating_border_thickness;
enum edge_border_types hide_edge_borders;
@@ -323,6 +342,14 @@ struct sway_config {
list_t *command_policies;
list_t *feature_policies;
list_t *ipc_policies;
+
+ // Context for command handlers
+ struct {
+ struct input_config *input_config;
+ struct seat_config *seat_config;
+ struct sway_seat *seat;
+ struct sway_container *current_container;
+ } handler_context;
};
void pid_workspace_add(struct pid_workspace *pw);
@@ -348,6 +375,11 @@ bool read_config(FILE *file, struct sway_config *config);
* Free config struct
*/
void free_config(struct sway_config *config);
+
+void config_clear_handler_context(struct sway_config *config);
+
+void free_sway_variable(struct sway_variable *var);
+
/**
* Does variable replacement for a string based on the config's currently loaded variables.
*/
@@ -356,51 +388,74 @@ char *do_var_replacement(char *str);
struct cmd_results *check_security_config();
int input_identifier_cmp(const void *item, const void *data);
+
+struct input_config *new_input_config(const char* identifier);
+
void merge_input_config(struct input_config *dst, struct input_config *src);
-void apply_input_config(struct input_config *ic, struct libinput_device *dev);
+
+struct input_config *copy_input_config(struct input_config *ic);
+
void free_input_config(struct input_config *ic);
+void apply_input_config(struct input_config *input);
+
+int seat_name_cmp(const void *item, const void *data);
+
+struct seat_config *new_seat_config(const char* name);
+
+void merge_seat_config(struct seat_config *dst, struct seat_config *src);
+
+struct seat_config *copy_seat_config(struct seat_config *seat);
+
+void free_seat_config(struct seat_config *ic);
+
+struct seat_attachment_config *seat_attachment_config_new();
+
+struct seat_attachment_config *seat_config_get_attachment(
+ struct seat_config *seat_config, char *identifier);
+
+void apply_seat_config(struct seat_config *seat);
+
int output_name_cmp(const void *item, const void *data);
+
+void output_get_identifier(char *identifier, size_t len,
+ struct sway_output *output);
+
+struct output_config *new_output_config(const char *name);
+
void merge_output_config(struct output_config *dst, struct output_config *src);
-/** Sets up a WLC output handle based on a given output_config.
- */
-void apply_output_config(struct output_config *oc, swayc_t *output);
-void free_output_config(struct output_config *oc);
-/**
- * Updates the list of active bar modifiers
- */
-void update_active_bar_modifiers(void);
+void apply_output_config(struct output_config *oc,
+ struct sway_container *output);
+
+void free_output_config(struct output_config *oc);
int workspace_output_cmp_workspace(const void *a, const void *b);
int sway_binding_cmp(const void *a, const void *b);
+
int sway_binding_cmp_qsort(const void *a, const void *b);
+
int sway_binding_cmp_keys(const void *a, const void *b);
+
void free_sway_binding(struct sway_binding *sb);
-struct sway_binding *sway_binding_dup(struct sway_binding *sb);
-int sway_mouse_binding_cmp(const void *a, const void *b);
-int sway_mouse_binding_cmp_qsort(const void *a, const void *b);
-int sway_mouse_binding_cmp_buttons(const void *a, const void *b);
-void free_sway_mouse_binding(struct sway_mouse_binding *smb);
+struct sway_binding *sway_binding_dup(struct sway_binding *sb);
void load_swaybars();
+
+void invoke_swaybar(struct bar_config *bar);
+
void terminate_swaybg(pid_t pid);
-/**
- * Allocate and initialize default bar configuration.
- */
struct bar_config *default_bar_config(void);
-/**
- * Global config singleton.
- */
+void free_bar_config(struct bar_config *bar);
+
+/* Global config singleton. */
extern struct sway_config *config;
-/**
- * Config file currently being read.
- */
+/* Config file currently being read */
extern const char *current_config_path;
#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 @@
-#ifndef _SWAY_CONTAINER_H
-#define _SWAY_CONTAINER_H
-#include <sys/types.h>
-#include <wlc/wlc.h>
-#include <stdint.h>
-
-#include "list.h"
-
-typedef struct sway_container swayc_t;
-
-extern swayc_t root_container;
-extern swayc_t *current_focus;
-
-/**
- * Different kinds of containers.
- *
- * This enum is in order. A container will never be inside of a container below
- * it on this list.
- */
-enum swayc_types {
- C_ROOT, /**< The root container. Only one of these ever exists. */
- C_OUTPUT, /**< An output (aka monitor, head, etc). */
- C_WORKSPACE, /**< A workspace. */
- C_CONTAINER, /**< A manually created container. */
- C_VIEW, /**< A view (aka window). */
- // Keep last
- C_TYPES,
-};
-
-/**
- * Different ways to arrange a container.
- */
-enum swayc_layouts {
- L_NONE, /**< Used for containers that have no layout (views, root) */
- L_HORIZ,
- L_VERT,
- L_STACKED,
- L_TABBED,
- L_FLOATING, /**< A psuedo-container, removed from the tree, to hold floating windows */
-
- /* Awesome/Monad style auto layouts */
- L_AUTO_LEFT,
- L_AUTO_RIGHT,
- L_AUTO_TOP,
- L_AUTO_BOTTOM,
-
- L_AUTO_FIRST = L_AUTO_LEFT,
- L_AUTO_LAST = L_AUTO_BOTTOM,
-
- // Keep last
- L_LAYOUTS,
-};
-
-enum swayc_border_types {
- B_NONE, /**< No border */
- B_PIXEL, /**< 1px border */
- B_NORMAL /**< Normal border with title bar */
-};
-
-/**
- * Stores information about a container.
- *
- * The tree is made of these. Views are containers that cannot have children.
- */
-struct sway_container {
- /**
- * If this container maps to a WLC object, this is set to that object's
- * handle. Otherwise, NULL.
- */
- wlc_handle handle;
-
- /**
- * A unique ID to identify this container. Primarily used in the
- * get_tree JSON output.
- */
- size_t id;
-
- enum swayc_types type;
- enum swayc_layouts layout;
- enum swayc_layouts prev_layout;
- enum swayc_layouts workspace_layout;
-
- /**
- * Width and height of this container, without borders or gaps.
- */
- double width, height;
-
- /**
- * Views may request geometry, which is stored in this and ignored until
- * the views are floated.
- */
- int desired_width, desired_height;
-
- /**
- * The coordinates that this view appear at, relative to the output they
- * are located on (output containers have absolute coordinates).
- */
- double x, y;
-
- /**
- * Cached geometry used to store view/container geometry when switching
- * between tabbed/stacked and horizontal/vertical layouts.
- */
- struct wlc_geometry cached_geometry;
-
- /**
- * False if this view is invisible. It could be in the scratchpad or on a
- * workspace that is not shown.
- */
- bool visible;
- bool is_floating;
- bool is_focused;
- bool sticky; // floating view always visible on its output
-
- // Attributes that mostly views have.
- char *name;
- char *class;
- char *instance;
- char *app_id;
-
- // Used by output containers to keep track of swaybg child processes.
- pid_t bg_pid;
-
- int gaps;
-
- list_t *children;
- /**
- * Children of this container that are floated.
- */
- list_t *floating;
- /**
- * Unmanaged view handles in this container.
- */
- list_t *unmanaged;
-
- /**
- * The parent of this container. NULL for the root container.
- */
- struct sway_container *parent;
- /**
- * Which of this container's children has focus.
- */
- struct sway_container *focused;
- /**
- * If this container's children include a fullscreen view, this is that view.
- */
- struct sway_container *fullscreen;
- /**
- * If this container is a view, this may be set to the window's decoration
- * buffer (or NULL).
- */
- struct border *border;
- enum swayc_border_types border_type;
- struct wlc_geometry border_geometry;
- struct wlc_geometry title_bar_geometry;
- struct wlc_geometry actual_geometry;
- int border_thickness;
-
- /**
- * Number of master views in auto layouts.
- */
- size_t nb_master;
-
- /**
- * Number of slave groups (e.g. columns) in auto layouts.
- */
- size_t nb_slave_groups;
-
- /**
- * Marks applied to the container, list_t of char*.
- */
- list_t *marks;
-};
-
-enum visibility_mask {
- VISIBLE = true
-} visible;
-
-/**
- * Allocates a new output container.
- */
-swayc_t *new_output(wlc_handle handle);
-/**
- * Allocates a new workspace container.
- */
-swayc_t *new_workspace(swayc_t *output, const char *name);
-/**
- * Allocates a new container and places a child into it.
- *
- * This is used from the split command, which creates a new container with the
- * requested layout and replaces the focused container in the tree with the new
- * one. Then the removed container is added as a child of the new container.
- */
-swayc_t *new_container(swayc_t *child, enum swayc_layouts layout);
-/**
- * Allocates a new view container.
- *
- * Pass in a sibling view, or a workspace to become this container's parent.
- */
-swayc_t *new_view(swayc_t *sibling, wlc_handle handle);
-/**
- * Allocates a new floating view in the active workspace.
- */
-swayc_t *new_floating_view(wlc_handle handle);
-
-void floating_view_sane_size(swayc_t *view);
-
-/**
- * Frees an output's container.
- */
-swayc_t *destroy_output(swayc_t *output);
-/**
- * Destroys a workspace container and returns the parent pointer, or NULL.
- */
-swayc_t *destroy_workspace(swayc_t *workspace);
-/**
- * Destroys a container and all empty parents. Returns the topmost non-empty
- * parent container, or NULL.
- */
-swayc_t *destroy_container(swayc_t *container);
-/**
- * Destroys a view container and all empty parents. Returns the topmost
- * non-empty parent container, or NULL.
- */
-swayc_t *destroy_view(swayc_t *view);
-
-/**
- * Finds a container based on test criteria. Returns the first container that
- * passes the test.
- */
-swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data);
-/**
- * Finds a parent container with the given swayc_type.
- */
-swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types);
-/**
- * Finds a parent with the given swayc_layout.
- */
-swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts);
-/**
- * Finds the bottom-most focused container of a type.
- */
-swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types);
-/**
- * Finds the bottom-most focused container of a layout.
- */
-swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts);
-
-/**
- * Gets the swayc_t associated with a wlc_handle.
- */
-swayc_t *swayc_by_handle(wlc_handle handle);
-/**
- * Gets the named swayc_t.
- */
-swayc_t *swayc_by_name(const char *name);
-/**
- * Gets the active output's container.
- */
-swayc_t *swayc_active_output(void);
-/**
- * Gets the active workspace's container.
- */
-swayc_t *swayc_active_workspace(void);
-/**
- * Gets the workspace for the given view container.
- */
-swayc_t *swayc_active_workspace_for(swayc_t *view);
-/**
- * Finds the container currently underneath the pointer.
- */
-swayc_t *container_under_pointer(void);
-/**
- * Finds the first container following a callback.
- */
-swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data);
-
-/**
- * Returns true if a container is fullscreen.
- */
-bool swayc_is_fullscreen(swayc_t *view);
-/**
- * Returns true if this view is focused.
- */
-bool swayc_is_active(swayc_t *view);
-/**
- * Returns true if the parent is an ancestor of the child.
- */
-bool swayc_is_parent_of(swayc_t *parent, swayc_t *child);
-/**
- * Returns true if the child is a desecendant of the parent.
- */
-bool swayc_is_child_of(swayc_t *child, swayc_t *parent);
-
-/**
- * Returns true if this container is an empty workspace.
- */
-bool swayc_is_empty_workspace(swayc_t *container);
-
-/**
- * Returns the top most tabbed or stacked parent container. Returns NULL if
- * view is not in a tabbed/stacked layout.
- */
-swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view);
-
-/**
- * Returns the immediate tabbed or stacked parent container. Returns NULL if
- * view is not directly in a tabbed/stacked layout.
- */
-swayc_t *swayc_tabbed_stacked_parent(swayc_t *view);
-
-/**
- * Returns the gap (padding) of the container.
- *
- * This returns the inner gaps for a view, the outer gaps for a workspace, and
- * 0 otherwise.
- */
-int swayc_gap(swayc_t *container);
-
-/**
- * Maps a container's children over a function.
- */
-void container_map(swayc_t *, void (*f)(swayc_t *, void *), void *);
-
-/**
- * Set a view as visible or invisible.
- *
- * This will perform the required wlc calls as well; it is not sufficient to
- * simply toggle the boolean in swayc_t.
- */
-void set_view_visibility(swayc_t *view, void *data);
-/**
- * Set the gaps value for a view.
- */
-void set_gaps(swayc_t *view, void *amount);
-/**
- * Add to the gaps value for a view.
- */
-void add_gaps(swayc_t *view, void *amount);
-
-/**
- * Issue wlc calls to make the visibility of a container consistent.
- */
-void update_visibility(swayc_t *container);
-
-/**
- * Close all child views of container
- */
-void close_views(swayc_t *container);
-
-/**
- * Assign layout to a container. Needed due to workspace container specifics.
- * Workspace should always have either L_VERT or L_HORIZ layout.
- */
-swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout);
-
-#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 @@
#ifndef _SWAY_CRITERIA_H
#define _SWAY_CRITERIA_H
-#include "container.h"
+#include "tree/container.h"
#include "list.h"
/**
@@ -31,12 +31,12 @@ char *extract_crit_tokens(list_t *tokens, const char *criteria);
// Returns list of criteria that match given container. These criteria have
// been set with `for_window` commands and have an associated cmdlist.
-list_t *criteria_for(swayc_t *cont);
+list_t *criteria_for(struct sway_container *cont);
// Returns a list of all containers that match the given list of tokens.
-list_t *container_for(list_t *tokens);
+list_t *container_for_crit_tokens(list_t *tokens);
// Returns true if any criteria in the given list matches this container
-bool criteria_any(swayc_t *cont, list_t *criteria);
+bool criteria_any(struct sway_container *cont, list_t *criteria);
#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 @@
+#ifndef SWAY_DEBUG_H
+#define SWAY_DEBUG_H
+
+extern bool enable_debug_tree;
+void update_debug_tree();
+
+#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 @@
+#include <wlr/types/wlr_surface.h>
+
+void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
+ 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 @@
-#ifndef _SWAY_EXTENSIONS_H
-#define _SWAY_EXTENSIONS_H
-
-#include <wayland-server.h>
-#include <wlc/wlc-wayland.h>
-#include "wayland-desktop-shell-server-protocol.h"
-#include "list.h"
-
-struct background_config {
- wlc_handle output;
- wlc_resource surface;
- // we need the wl_resource of the surface in the destructor
- struct wl_resource *wl_surface_res;
- struct wl_client *client;
- wlc_handle handle;
-};
-
-struct panel_config {
- // wayland resource used in callbacks, is used to track this panel
- struct wl_resource *wl_resource;
- wlc_handle output;
- wlc_resource surface;
- // we need the wl_resource of the surface in the destructor
- struct wl_resource *wl_surface_res;
- enum desktop_shell_panel_position panel_position;
- // used to determine if client is a panel
- struct wl_client *client;
- // wlc handle for this panel's surface, not set until panel is created
- wlc_handle handle;
-};
-
-struct desktop_shell_state {
- list_t *backgrounds;
- list_t *panels;
- list_t *lock_surfaces;
- bool is_locked;
-};
-
-struct swaylock_state {
- bool active;
- wlc_handle output;
- wlc_resource surface;
-};
-
-struct decoration_state {
- list_t *csd_resources;
-};
-
-extern struct desktop_shell_state desktop_shell;
-extern struct decoration_state decoration_state;
-
-void register_extensions(void);
-
-void server_decoration_enable_csd(wlc_handle handle);
-
-#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 @@
-#ifndef _SWAY_FOCUS_H
-#define _SWAY_FOCUS_H
-enum movement_direction {
- MOVE_LEFT,
- MOVE_RIGHT,
- MOVE_UP,
- MOVE_DOWN,
- MOVE_PARENT,
- MOVE_CHILD,
- MOVE_NEXT,
- MOVE_PREV,
- MOVE_FIRST
-};
-
-#include "container.h"
-
-// focused_container - the container found by following the `focused` pointer
-// from a given container to a container with `is_focused` boolean set
-// ---
-// focused_view - the container found by following the `focused` pointer from a
-// given container to a view.
-// ---
-
-swayc_t *get_focused_container(swayc_t *parent);
-swayc_t *get_focused_view(swayc_t *parent);
-swayc_t *get_focused_float(swayc_t *ws);
-
-// a special-case function to get the focused view, regardless
-// of whether it's tiled or floating
-swayc_t *get_focused_view_include_floating(swayc_t *parent);
-
-bool set_focused_container(swayc_t *container);
-bool set_focused_container_for(swayc_t *ancestor, swayc_t *container);
-
-// lock focused container/view. locked by windows with OVERRIDE attribute
-// and unlocked when they are destroyed
-
-extern bool locked_container_focus;
-
-// Prevents wss from being destroyed on focus switch
-extern bool suspend_workspace_cleanup;
-
-bool move_focus(enum movement_direction direction);
-
-#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 @@
-#ifndef _SWAY_HANDLERS_H
-#define _SWAY_HANDLERS_H
-#include "container.h"
-#include <stdbool.h>
-#include <wlc/wlc.h>
-
-void register_wlc_handlers();
-
-extern uint32_t keys_pressed[32];
-
-#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 @@
-#ifndef _SWAY_INPUT_H
-#define _SWAY_INPUT_H
-
-#include <libinput.h>
-#include "config.h"
-#include "list.h"
-
-struct input_config *new_input_config(const char* identifier);
-
-char* libinput_dev_unique_id(struct libinput_device *dev);
-
-/**
- * Global input device list.
- */
-extern list_t *input_devices;
-
-/**
- * Pointer used when reading input blocked.
- * Shared so that it can be cleared from commands.c when closing the block
- */
-extern struct input_config *current_input_config;
-
-#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 @@
+#ifndef _SWAY_INPUT_CURSOR_H
+#define _SWAY_INPUT_CURSOR_H
+#include <stdint.h>
+#include "sway/input/seat.h"
+
+struct sway_cursor {
+ struct sway_seat *seat;
+ struct wlr_cursor *cursor;
+ struct wlr_xcursor_manager *xcursor_manager;
+
+ struct wl_client *image_client;
+
+ struct wl_listener motion;
+ struct wl_listener motion_absolute;
+ struct wl_listener button;
+ struct wl_listener axis;
+
+ struct wl_listener touch_down;
+ struct wl_listener touch_up;
+ struct wl_listener touch_motion;
+
+ struct wl_listener tool_axis;
+ struct wl_listener tool_tip;
+ struct wl_listener tool_button;
+ uint32_t tool_buttons;
+
+ struct wl_listener request_set_cursor;
+};
+
+void sway_cursor_destroy(struct sway_cursor *cursor);
+struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
+void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time);
+void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec,
+ uint32_t button, enum wlr_button_state state);
+
+#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 @@
+#ifndef _SWAY_INPUT_INPUT_MANAGER_H
+#define _SWAY_INPUT_INPUT_MANAGER_H
+#include <libinput.h>
+#include <wlr/types/wlr_input_inhibitor.h>
+#include "sway/server.h"
+#include "sway/config.h"
+#include "list.h"
+
+/**
+ * The global singleton input manager
+ * TODO: make me not a global
+ */
+extern struct sway_input_manager *input_manager;
+
+struct sway_input_device {
+ char *identifier;
+ struct wlr_input_device *wlr_device;
+ struct wl_list link;
+ struct wl_listener device_destroy;
+};
+
+struct sway_input_manager {
+ struct sway_server *server;
+ struct wl_list devices;
+ struct wl_list seats;
+
+ struct wlr_input_inhibit_manager *inhibit;
+
+ struct wl_listener new_input;
+ struct wl_listener inhibit_activate;
+ struct wl_listener inhibit_deactivate;
+};
+
+struct sway_input_manager *input_manager_create(struct sway_server *server);
+
+bool input_manager_has_focus(struct sway_input_manager *input,
+ struct sway_container *container);
+
+void input_manager_set_focus(struct sway_input_manager *input,
+ struct sway_container *container);
+
+void input_manager_configure_xcursor(struct sway_input_manager *input);
+
+void input_manager_apply_input_config(struct sway_input_manager *input,
+ struct input_config *input_config);
+
+void input_manager_apply_seat_config(struct sway_input_manager *input,
+ struct seat_config *seat_config);
+
+struct sway_seat *input_manager_get_default_seat(
+ struct sway_input_manager *input);
+
+struct sway_seat *input_manager_get_seat(struct sway_input_manager *input,
+ const char *seat_name);
+
+/**
+ * Gets the last seat the user interacted with
+ */
+struct sway_seat *input_manager_current_seat(struct sway_input_manager *input);
+
+struct input_config *input_device_get_config(struct sway_input_device *device);
+
+
+#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 @@
+#ifndef _SWAY_INPUT_KEYBOARD_H
+#define _SWAY_INPUT_KEYBOARD_H
+
+#include "sway/input/seat.h"
+
+#define SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP 32
+
+struct sway_keyboard {
+ struct sway_seat_device *seat_device;
+
+ struct xkb_keymap *keymap;
+
+ struct wl_listener keyboard_key;
+ struct wl_listener keyboard_modifiers;
+
+ xkb_keysym_t pressed_keysyms_translated[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP];
+ uint32_t modifiers_translated;
+
+ xkb_keysym_t pressed_keysyms_raw[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP];
+ uint32_t modifiers_raw;
+};
+
+struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
+ struct sway_seat_device *device);
+
+void sway_keyboard_configure(struct sway_keyboard *keyboard);
+
+void sway_keyboard_destroy(struct sway_keyboard *keyboard);
+
+#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 @@
+#ifndef _SWAY_INPUT_SEAT_H
+#define _SWAY_INPUT_SEAT_H
+
+#include <wlr/types/wlr_layer_shell.h>
+#include <wlr/types/wlr_seat.h>
+#include "sway/input/input-manager.h"
+
+struct sway_seat_device {
+ struct sway_seat *sway_seat;
+ struct sway_input_device *input_device;
+ struct sway_keyboard *keyboard;
+ struct wl_list link; // sway_seat::devices
+};
+
+struct sway_seat_container {
+ struct sway_seat *seat;
+ struct sway_container *container;
+
+ struct wl_list link; // sway_seat::focus_stack
+
+ struct wl_listener destroy;
+};
+
+struct sway_seat {
+ struct wlr_seat *wlr_seat;
+ struct sway_cursor *cursor;
+ struct sway_input_manager *input;
+
+ bool has_focus;
+ struct wl_list focus_stack; // list of containers in focus order
+
+ // If the focused layer is set, views cannot receive keyboard focus
+ struct wlr_layer_surface *focused_layer;
+
+ // If exclusive_client is set, no other clients will receive input events
+ struct wl_client *exclusive_client;
+
+ struct wl_listener focus_destroy;
+ struct wl_listener new_container;
+
+ struct wl_list devices; // sway_seat_device::link
+
+ struct wl_list link; // input_manager::seats
+};
+
+struct sway_seat *seat_create(struct sway_input_manager *input,
+ const char *seat_name);
+
+void seat_destroy(struct sway_seat *seat);
+
+void seat_add_device(struct sway_seat *seat,
+ struct sway_input_device *device);
+
+void seat_configure_device(struct sway_seat *seat,
+ struct sway_input_device *device);
+
+void seat_remove_device(struct sway_seat *seat,
+ struct sway_input_device *device);
+
+void seat_configure_xcursor(struct sway_seat *seat);
+
+void seat_set_focus(struct sway_seat *seat, struct sway_container *container);
+
+void seat_set_focus_warp(struct sway_seat *seat,
+ struct sway_container *container, bool warp);
+
+void seat_set_focus_surface(struct sway_seat *seat,
+ struct wlr_surface *surface);
+
+void seat_set_focus_layer(struct sway_seat *seat,
+ struct wlr_layer_surface *layer);
+
+void seat_set_exclusive_client(struct sway_seat *seat,
+ struct wl_client *client);
+
+struct sway_container *seat_get_focus(struct sway_seat *seat);
+
+/**
+ * Return the last container to be focused for the seat (or the most recently
+ * opened if no container has received focused) that is a child of the given
+ * container. The focus-inactive container of the root window is the focused
+ * container for the seat (if the seat does have focus). This function can be
+ * used to determine what container gets focused next if the focused container
+ * is destroyed, or focus moves to a container with children and we need to
+ * descend into the next leaf in focus order.
+ */
+struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
+ struct sway_container *container);
+
+/**
+ * Descend into the focus stack to find the focus-inactive view. Useful for
+ * container placement when they change position in the tree.
+ */
+struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
+ struct sway_container *container);
+
+/**
+ * Iterate over the focus-inactive children of the container calling the
+ * function on each.
+ */
+void seat_focus_inactive_children_for_each(struct sway_seat *seat,
+ struct sway_container *container,
+ void (*f)(struct sway_container *container, void *data), void *data);
+
+void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);
+
+struct seat_config *seat_get_config(struct sway_seat *seat);
+
+bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
+
+#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 @@
-#ifndef _SWAY_KEY_STATE_H
-#define _SWAY_KEY_STATE_H
-#include <stdbool.h>
-#include <stdint.h>
-#include "container.h"
-
-/* Keyboard state */
-
-// returns true if key has been pressed, otherwise false
-bool check_key(uint32_t key_sym, uint32_t key_code);
-
-// returns true if key_sym matches latest released key.
-bool check_released_key(uint32_t key_sym);
-
-// sets a key as pressed
-void press_key(uint32_t key_sym, uint32_t key_code);
-
-// unsets a key as pressed
-void release_key(uint32_t key_sym, uint32_t key_code);
-
-
-/* Pointer state */
-
-enum pointer_values {
- M_LEFT_CLICK = 272,
- M_RIGHT_CLICK = 273,
- M_SCROLL_CLICK = 274,
- M_SCROLL_UP = 275,
- M_SCROLL_DOWN = 276,
-};
-
-enum pointer_mode {
- // Target
- M_FLOATING = 1,
- M_TILING = 2,
- // Action
- M_DRAGGING = 4,
- M_RESIZING = 8,
-};
-
-struct pointer_button_state {
- bool held;
- // state at the point it was pressed
- int x, y;
- swayc_t *view;
-};
-
-extern struct pointer_state {
- // mouse clicks
- struct pointer_button_state left;
- struct pointer_button_state right;
- struct pointer_button_state scroll;
-
- // change in pointer position
- struct {
- int x, y;
- } delta;
-
- // view pointer is currently over
- swayc_t *view;
-
- // Pointer mode
- int mode;
-} pointer_state;
-
-enum modifier_state {
- MOD_STATE_UNCHANGED = 0,
- MOD_STATE_PRESSED = 1,
- MOD_STATE_RELEASED = 2
-};
-
-void pointer_position_set(double new_x, double new_y, bool force_focus);
-void center_pointer_on(swayc_t *view);
-
-// on button release unset mode depending on the button.
-// on button press set mode conditionally depending on the button
-void pointer_mode_set(uint32_t button, bool condition);
-
-// Update mode in mouse motion
-void pointer_mode_update(void);
-
-// Reset mode on any keypress;
-void pointer_mode_reset(void);
-
-void input_init(void);
-
-/**
- * Check if state of mod changed from current state to new_state.
- *
- * Returns MOD_STATE_UNCHANGED if the state didn't change, MOD_STATE_PRESSED if
- * the state changed to pressed and MOD_STATE_RELEASED if the state changed to
- * released.
- */
-uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod);
-
-/**
- * Update the current modifiers state to new_state.
- */
-void modifiers_state_update(uint32_t new_state);
-
-#endif
-
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 @@
#ifndef _SWAY_IPC_JSON_H
#define _SWAY_IPC_JSON_H
-
#include <json-c/json.h>
-#include "config.h"
-#include "container.h"
+#include "sway/tree/container.h"
+#include "sway/input/input-manager.h"
json_object *ipc_json_get_version();
+
+json_object *ipc_json_describe_container(struct sway_container *c);
+json_object *ipc_json_describe_container_recursive(struct sway_container *c);
+json_object *ipc_json_describe_input(struct sway_input_device *device);
json_object *ipc_json_describe_bar_config(struct bar_config *bar);
-json_object *ipc_json_describe_container(swayc_t *c);
-json_object *ipc_json_describe_container_recursive(swayc_t *c);
-json_object *ipc_json_describe_window(swayc_t *c);
-json_object *ipc_json_describe_input(struct libinput_device *device);
#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 @@
#ifndef _SWAY_IPC_SERVER_H
#define _SWAY_IPC_SERVER_H
+#include <sys/socket.h>
+#include "sway/tree/container.h"
+#include "ipc.h"
-#include <wlc/wlc.h>
+struct sway_server;
-#include "container.h"
-#include "config.h"
-#include "ipc.h"
+void ipc_init(struct sway_server *server);
-void ipc_init(void);
void ipc_terminate(void);
+
struct sockaddr_un *ipc_user_sockaddr(void);
-void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change);
+void ipc_event_workspace(struct sway_container *old,
+ struct sway_container *new, const char *change);
+void ipc_event_window(struct sway_container *window, const char *change);
void ipc_event_barconfig_update(struct bar_config *bar);
-/**
- * Send IPC mode event to all listening clients
- */
void ipc_event_mode(const char *mode);
-/**
- * Send IPC window change event
- */
-void ipc_event_window(swayc_t *window, const char *change);
-/**
- * Sends an IPC modifier event to all listening clients. The modifier event
- * includes a key 'change' with the value of state and a key 'modifier' with
- * the name of that modifier.
- */
-void ipc_event_modifier(uint32_t modifier, const char *state);
-/**
- * Send IPC keyboard binding event.
- */
-void ipc_event_binding_keyboard(struct sway_binding *sb);
-const char *swayc_type_string(enum swayc_types type);
-
-/**
- * Send pixel data to registered clients.
- */
-void ipc_get_pixels(wlc_handle output);
#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 @@
+#ifndef _SWAY_LAYERS_H
+#define _SWAY_LAYERS_H
+#include <stdbool.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/types/wlr_layer_shell.h>
+
+struct sway_layer_surface {
+ struct wlr_layer_surface *layer_surface;
+ struct wl_list link;
+
+ struct wl_listener destroy;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener surface_commit;
+ struct wl_listener output_destroy;
+
+ bool configured;
+ struct wlr_box geo;
+};
+
+struct sway_output;
+void arrange_layers(struct sway_output *output);
+
+#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 @@
-#ifndef _SWAY_LAYOUT_H
-#define _SWAY_LAYOUT_H
-
-#include <wlc/wlc.h>
-#include "log.h"
-#include "list.h"
-#include "container.h"
-#include "focus.h"
-
-extern list_t *scratchpad;
-
-extern int min_sane_w;
-extern int min_sane_h;
-
-// Set initial values for root_container
-void init_layout(void);
-
-// Returns the index of child for its parent
-int index_child(const swayc_t *child);
-
-// Adds child to parent, if parent has no focus, it is set to child
-// parent must be of type C_WORKSPACE or C_CONTAINER
-void add_child(swayc_t *parent, swayc_t *child);
-
-// Adds child to parent at index, if parent has no focus, it is set to child
-// parent must be of type C_WORKSPACE or C_CONTAINER
-void insert_child(swayc_t *parent, swayc_t *child, int index);
-
-// Adds child as floating window to ws, if there is no focus it is set to child.
-// ws must be of type C_WORKSPACE
-void add_floating(swayc_t *ws, swayc_t *child);
-
-// insert child after sibling in parents children.
-swayc_t *add_sibling(swayc_t *sibling, swayc_t *child);
-
-// Replace child with new_child in parents children
-// new_child will inherit childs geometry, childs geometry will be reset
-// if parents focus is on child, it will be changed to new_child
-swayc_t *replace_child(swayc_t *child, swayc_t *new_child);
-
-// Remove child from its parent, if focus is on child, focus will be changed to
-// a sibling, or to a floating window, or NULL
-swayc_t *remove_child(swayc_t *child);
-
-// 2 containers are swapped, they inherit eachothers focus
-void swap_container(swayc_t *a, swayc_t *b);
-
-// 2 Containers geometry are swapped, used with `swap_container`
-void swap_geometry(swayc_t *a, swayc_t *b);
-
-void move_container(swayc_t* container, enum movement_direction direction, int move_amt);
-void move_container_to(swayc_t* container, swayc_t* destination);
-void move_workspace_to(swayc_t* workspace, swayc_t* destination);
-
-// Layout
-/**
- * Update child container geometries when switching between layouts.
- */
-void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout);
-void update_geometry(swayc_t *view);
-void arrange_windows(swayc_t *container, double width, double height);
-void arrange_backgrounds(void);
-
-swayc_t *get_focused_container(swayc_t *parent);
-swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir);
-swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit);
-
-void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge);
-
-void layout_log(const swayc_t *c, int depth);
-void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) __attribute__((format(printf,3,4)));
-
-/**
- * Get default layout.
- */
-enum swayc_layouts default_layout(swayc_t *output);
-
-bool is_auto_layout(enum swayc_layouts layout);
-int auto_group_start_index(const swayc_t *container, int index);
-int auto_group_end_index(const swayc_t *container, int index);
-size_t auto_group_count(const swayc_t *container);
-size_t auto_group_index(const swayc_t *container, int index);
-bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end);
-
-#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 @@
#ifndef _SWAY_OUTPUT_H
#define _SWAY_OUTPUT_H
+#include <time.h>
+#include <unistd.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_output.h>
+#include "sway/tree/view.h"
-#include "container.h"
-#include "focus.h"
+struct sway_server;
+struct sway_container;
-// Position is absolute coordinates on the edge where the adjacent output
-// should be searched for.
-swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos);
-swayc_t *swayc_opposite_output(enum movement_direction dir, const struct wlc_point *abs_pos);
-swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir, const struct wlc_point *abs_pos, bool pick_closest);
+struct sway_output {
+ struct wlr_output *wlr_output;
+ struct sway_container *swayc;
+ struct sway_server *server;
-// Place absolute coordinates for given container into given wlc_point.
-void get_absolute_position(swayc_t *container, struct wlc_point *point);
+ struct wl_list layers[4]; // sway_layer_surface::link
+ struct wlr_box usable_area;
-// Place absolute coordinates for the center point of given container into
-// given wlc_point.
-void get_absolute_center_position(swayc_t *container, struct wlc_point *point);
+ struct timespec last_frame;
+ struct wlr_output_damage *damage;
-// stable sort workspaces on this output
-void sort_workspaces(swayc_t *output);
+ struct wl_listener destroy;
+ struct wl_listener mode;
+ struct wl_listener transform;
+ struct wl_listener scale;
-void output_get_scaled_size(wlc_handle handle, struct wlc_size *size);
+ struct wl_listener damage_destroy;
+ struct wl_listener damage_frame;
+
+ pid_t bg_pid;
+};
+
+void output_damage_whole(struct sway_output *output);
+
+void output_damage_surface(struct sway_output *output, double ox, double oy,
+ struct wlr_surface *surface, bool whole);
+
+void output_damage_view(struct sway_output *output, struct sway_view *view,
+ bool whole);
+
+void output_damage_whole_container(struct sway_output *output,
+ struct sway_container *con);
+
+struct sway_container *output_by_name(const char *name);
#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 @@
+#ifndef _SWAY_SERVER_H
+#define _SWAY_SERVER_H
+#include <stdbool.h>
+#include <wayland-server.h>
+#include <wlr/backend.h>
+#include <wlr/backend/session.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_data_device.h>
+#include <wlr/types/wlr_layer_shell.h>
+#include <wlr/types/wlr_xdg_shell_v6.h>
+#include <wlr/render/wlr_renderer.h>
+// TODO WLR: make Xwayland optional
+#include <wlr/xwayland.h>
+
+struct sway_server {
+ struct wl_display *wl_display;
+ struct wl_event_loop *wl_event_loop;
+ const char *socket;
+
+ struct wlr_backend *backend;
+
+ struct wlr_compositor *compositor;
+ struct wlr_data_device_manager *data_device_manager;
+
+ struct sway_input_manager *input;
+
+ struct wl_listener new_output;
+
+ struct wlr_layer_shell *layer_shell;
+ struct wl_listener layer_shell_surface;
+
+ struct wlr_xdg_shell_v6 *xdg_shell_v6;
+ struct wl_listener xdg_shell_v6_surface;
+
+ struct wlr_xwayland *xwayland;
+ struct wlr_xcursor_manager *xcursor_manager;
+ struct wl_listener xwayland_surface;
+ struct wl_listener xwayland_ready;
+
+ struct wlr_wl_shell *wl_shell;
+ struct wl_listener wl_shell_surface;
+};
+
+struct sway_server server;
+
+bool server_init(struct sway_server *server);
+void server_fini(struct sway_server *server);
+void server_run(struct sway_server *server);
+
+void handle_new_output(struct wl_listener *listener, void *data);
+
+void handle_layer_shell_surface(struct wl_listener *listener, void *data);
+void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
+void handle_xwayland_surface(struct wl_listener *listener, void *data);
+void handle_wl_shell_surface(struct wl_listener *listener, void *data);
+
+#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 @@
+#ifndef _SWAY_CONTAINER_H
+#define _SWAY_CONTAINER_H
+#include <stdint.h>
+#include <sys/types.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_surface.h>
+#include "list.h"
+
+extern struct sway_container root_container;
+
+struct sway_view;
+struct sway_seat;
+
+/**
+ * Different kinds of containers.
+ *
+ * This enum is in order. A container will never be inside of a container below
+ * it on this list.
+ */
+enum sway_container_type {
+ C_ROOT,
+ C_OUTPUT,
+ C_WORKSPACE,
+ C_CONTAINER,
+ C_VIEW,
+
+ // Keep last
+ C_TYPES,
+};
+
+enum sway_container_layout {
+ L_NONE,
+ L_HORIZ,
+ L_VERT,
+ L_STACKED,
+ L_TABBED,
+ L_FLOATING,
+};
+
+enum sway_container_border {
+ B_NONE,
+ B_PIXEL,
+ B_NORMAL,
+};
+
+struct sway_root;
+struct sway_output;
+struct sway_view;
+
+struct sway_container {
+ union {
+ // TODO: Encapsulate state for other node types as well like C_CONTAINER
+ struct sway_root *sway_root;
+ struct sway_output *sway_output;
+ struct sway_view *sway_view;
+ };
+
+ /**
+ * A unique ID to identify this container. Primarily used in the
+ * get_tree JSON output.
+ */
+ size_t id;
+
+ char *name;
+
+ enum sway_container_type type;
+ enum sway_container_layout layout;
+ enum sway_container_layout prev_layout;
+ enum sway_container_layout workspace_layout;
+
+ // For C_ROOT, this has no meaning
+ // For C_OUTPUT, this is the output position in layout coordinates
+ // For other types, this is the position in output-local coordinates
+ double x, y;
+ // does not include borders or gaps.
+ double width, height;
+
+ list_t *children;
+
+ struct sway_container *parent;
+
+ list_t *marks; // list of char*
+
+ float alpha;
+
+ struct {
+ struct wl_signal destroy;
+ // Raised after the tree updates, but before arrange_windows
+ // Passed the previous parent
+ struct wl_signal reparent;
+ } events;
+};
+
+struct sway_container *container_create(enum sway_container_type type);
+
+const char *container_type_to_str(enum sway_container_type type);
+
+struct sway_container *output_create(struct sway_output *sway_output);
+
+/**
+ * Create a new container container. A container container can be a a child of
+ * a workspace container or another container container.
+ */
+struct sway_container *container_container_create();
+
+/**
+ * Create a new output. Outputs are children of the root container and have no
+ * order in the tree structure.
+ */
+struct sway_container *output_create(struct sway_output *sway_output);
+
+/**
+ * Create a new workspace container. Workspaces are children of an output
+ * container and are ordered alphabetically by name.
+ */
+struct sway_container *workspace_create(struct sway_container *output,
+ const char *name);
+
+/*
+ * Create a new view container. A view can be a child of a workspace container
+ * or a container container and are rendered in the order and structure of
+ * how they are attached to the tree.
+ */
+struct sway_container *container_view_create(
+ struct sway_container *sibling, struct sway_view *sway_view);
+
+struct sway_container *container_destroy(struct sway_container *container);
+
+struct sway_container *container_close(struct sway_container *container);
+
+void container_descendants(struct sway_container *root,
+ enum sway_container_type type,
+ void (*func)(struct sway_container *item, void *data), void *data);
+
+/**
+ * Search a container's descendants a container based on test criteria. Returns
+ * the first container that passes the test.
+ */
+struct sway_container *container_find(struct sway_container *container,
+ bool (*test)(struct sway_container *view, void *data), void *data);
+
+/**
+ * Finds a parent container with the given struct sway_containerype.
+ */
+struct sway_container *container_parent(struct sway_container *container,
+ enum sway_container_type type);
+
+/**
+ * Find a container at the given coordinates. Returns the the surface and
+ * surface-local coordinates of the given layout coordinates if the container
+ * is a view and the view contains a surface at those coordinates.
+ */
+struct sway_container *container_at(struct sway_container *container,
+ double lx, double ly, struct wlr_surface **surface,
+ double *sx, double *sy);
+
+/**
+ * Apply the function for each descendant of the container breadth first.
+ */
+void container_for_each_descendant_bfs(struct sway_container *container,
+ void (*f)(struct sway_container *container, void *data), void *data);
+
+/**
+ * Apply the function for each child of the container depth first.
+ */
+void container_for_each_descendant_dfs(struct sway_container *container,
+ void (*f)(struct sway_container *container, void *data), void *data);
+
+/**
+ * Returns true if the given container is an ancestor of this container.
+ */
+bool container_has_anscestor(struct sway_container *container,
+ struct sway_container *anscestor);
+
+/**
+ * Returns true if the given container is a child descendant of this container.
+ */
+bool container_has_child(struct sway_container *con,
+ struct sway_container *child);
+
+void container_create_notify(struct sway_container *container);
+
+void container_damage_whole(struct sway_container *container);
+
+bool container_reap_empty(struct sway_container *con);
+
+struct sway_container *container_reap_empty_recursive(
+ struct sway_container *con);
+
+struct sway_container *container_flatten(struct sway_container *container);
+
+#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 @@
+#ifndef _SWAY_LAYOUT_H
+#define _SWAY_LAYOUT_H
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/render/wlr_texture.h>
+#include "sway/tree/container.h"
+
+enum movement_direction {
+ MOVE_LEFT,
+ MOVE_RIGHT,
+ MOVE_UP,
+ MOVE_DOWN,
+ MOVE_PARENT,
+ MOVE_CHILD,
+};
+
+enum resize_edge {
+ RESIZE_EDGE_LEFT,
+ RESIZE_EDGE_RIGHT,
+ RESIZE_EDGE_TOP,
+ RESIZE_EDGE_BOTTOM,
+};
+
+struct sway_container;
+
+struct sway_root {
+ struct wlr_output_layout *output_layout;
+
+ struct wl_listener output_layout_change;
+
+ struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link
+
+ struct wlr_texture *debug_tree;
+
+ struct {
+ struct wl_signal new_container;
+ } events;
+};
+
+void layout_init(void);
+
+void container_add_child(struct sway_container *parent,
+ struct sway_container *child);
+
+struct sway_container *container_add_sibling(struct sway_container *parent,
+ struct sway_container *child);
+
+struct sway_container *container_remove_child(struct sway_container *child);
+
+struct sway_container *container_replace_child(struct sway_container *child,
+ struct sway_container *new_child);
+
+struct sway_container *container_set_layout(struct sway_container *container,
+ enum sway_container_layout layout);
+
+void container_move_to(struct sway_container* container,
+ struct sway_container* destination);
+
+void container_move(struct sway_container *container,
+ enum movement_direction dir, int move_amt);
+
+enum sway_container_layout container_get_default_layout(
+ struct sway_container *con);
+
+void container_sort_workspaces(struct sway_container *output);
+
+void arrange_windows(struct sway_container *container,
+ double width, double height);
+
+struct sway_container *container_get_in_direction(struct sway_container
+ *container, struct sway_seat *seat, enum movement_direction dir);
+
+struct sway_container *container_split(struct sway_container *child,
+ enum sway_container_layout layout);
+
+void container_recursive_resize(struct sway_container *container,
+ double amount, enum resize_edge edge);
+
+#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 @@
+#ifndef _SWAY_VIEW_H
+#define _SWAY_VIEW_H
+#include <wayland-server.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/types/wlr_xdg_shell_v6.h>
+#include <wlr/xwayland.h>
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+
+struct sway_container;
+
+enum sway_view_type {
+ SWAY_VIEW_WL_SHELL,
+ SWAY_VIEW_XDG_SHELL_V6,
+ SWAY_VIEW_XWAYLAND,
+};
+
+enum sway_view_prop {
+ VIEW_PROP_TITLE,
+ VIEW_PROP_APP_ID,
+ VIEW_PROP_CLASS,
+ VIEW_PROP_INSTANCE,
+};
+
+struct sway_view_impl {
+ const char *(*get_prop)(struct sway_view *view,
+ enum sway_view_prop prop);
+ void (*configure)(struct sway_view *view, double ox, double oy, int width,
+ int height);
+ void (*set_activated)(struct sway_view *view, bool activated);
+ void (*for_each_surface)(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data);
+ void (*close)(struct sway_view *view);
+ void (*destroy)(struct sway_view *view);
+};
+
+struct sway_view {
+ enum sway_view_type type;
+ const struct sway_view_impl *impl;
+
+ struct sway_container *swayc; // NULL for unmanaged views
+ struct wlr_surface *surface; // NULL for unmapped views
+ int width, height;
+
+ union {
+ struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
+ struct wlr_xwayland_surface *wlr_xwayland_surface;
+ struct wlr_wl_shell_surface *wlr_wl_shell_surface;
+ };
+
+ struct {
+ struct wl_signal unmap;
+ } events;
+
+ struct wl_listener surface_new_subsurface;
+ struct wl_listener container_reparent;
+};
+
+struct sway_xdg_shell_v6_view {
+ struct sway_view view;
+
+ struct wl_listener commit;
+ struct wl_listener request_move;
+ struct wl_listener request_resize;
+ struct wl_listener request_maximize;
+ struct wl_listener new_popup;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+
+ int pending_width, pending_height;
+};
+
+struct sway_xwayland_view {
+ struct sway_view view;
+
+ struct wl_listener commit;
+ struct wl_listener request_move;
+ struct wl_listener request_resize;
+ struct wl_listener request_maximize;
+ struct wl_listener request_configure;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+
+ int pending_width, pending_height;
+};
+
+struct sway_xwayland_unmanaged {
+ struct wlr_xwayland_surface *wlr_xwayland_surface;
+ struct wl_list link;
+
+ int lx, ly;
+
+ struct wl_listener request_configure;
+ struct wl_listener commit;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+};
+
+struct sway_wl_shell_view {
+ struct sway_view view;
+
+ struct wl_listener commit;
+ struct wl_listener request_move;
+ struct wl_listener request_resize;
+ struct wl_listener request_maximize;
+ struct wl_listener destroy;
+
+ int pending_width, pending_height;
+};
+
+struct sway_view_child;
+
+struct sway_view_child_impl {
+ void (*destroy)(struct sway_view_child *child);
+};
+
+/**
+ * A view child is a surface in the view tree, such as a subsurface or a popup.
+ */
+struct sway_view_child {
+ const struct sway_view_child_impl *impl;
+
+ struct sway_view *view;
+ struct wlr_surface *surface;
+
+ struct wl_listener surface_commit;
+ struct wl_listener surface_new_subsurface;
+ struct wl_listener surface_destroy;
+ struct wl_listener view_unmap;
+};
+
+struct sway_xdg_popup_v6 {
+ struct sway_view_child child;
+
+ struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
+
+ struct wl_listener new_popup;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+};
+
+const char *view_get_title(struct sway_view *view);
+
+const char *view_get_app_id(struct sway_view *view);
+
+const char *view_get_class(struct sway_view *view);
+
+const char *view_get_instance(struct sway_view *view);
+
+void view_configure(struct sway_view *view, double ox, double oy, int width,
+ int height);
+
+void view_set_activated(struct sway_view *view, bool activated);
+
+void view_close(struct sway_view *view);
+
+void view_damage(struct sway_view *view, bool whole);
+
+void view_for_each_surface(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data);
+
+// view implementation
+
+void view_init(struct sway_view *view, enum sway_view_type type,
+ const struct sway_view_impl *impl);
+
+void view_destroy(struct sway_view *view);
+
+void view_map(struct sway_view *view, struct wlr_surface *wlr_surface);
+
+void view_unmap(struct sway_view *view);
+
+void view_update_position(struct sway_view *view, double ox, double oy);
+
+void view_update_size(struct sway_view *view, int width, int height);
+
+void view_child_init(struct sway_view_child *child,
+ const struct sway_view_child_impl *impl, struct sway_view *view,
+ struct wlr_surface *surface);
+
+void view_child_destroy(struct sway_view_child *child);
+
+#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 @@
+#ifndef _SWAY_WORKSPACE_H
+#define _SWAY_WORKSPACE_H
+
+#include "sway/tree/container.h"
+
+extern char *prev_workspace_name;
+
+char *workspace_next_name(const char *output_name);
+
+bool workspace_switch(struct sway_container *workspace);
+
+struct sway_container *workspace_by_number(const char* name);
+
+struct sway_container *workspace_by_name(const char*);
+
+struct sway_container *workspace_output_next(struct sway_container *current);
+
+struct sway_container *workspace_next(struct sway_container *current);
+
+struct sway_container *workspace_output_prev(struct sway_container *current);
+
+struct sway_container *workspace_prev(struct sway_container *current);
+
+bool workspace_is_visible(struct sway_container *ws);
+
+#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 @@
-#ifndef _SWAY_WORKSPACE_H
-#define _SWAY_WORKSPACE_H
-
-#include <wlc/wlc.h>
-#include <unistd.h>
-#include "list.h"
-#include "layout.h"
-
-extern char *prev_workspace_name;
-
-char *workspace_next_name(const char *output_name);
-swayc_t *workspace_create(const char*);
-swayc_t *workspace_by_name(const char*);
-swayc_t *workspace_by_number(const char*);
-bool workspace_switch(swayc_t*);
-swayc_t *workspace_output_next();
-swayc_t *workspace_next();
-swayc_t *workspace_output_prev();
-swayc_t *workspace_prev();
-swayc_t *workspace_for_pid(pid_t pid);
-
-#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 @@
#ifndef _SWAYBAR_BAR_H
#define _SWAYBAR_BAR_H
+#include <wayland-client.h>
+#include "pool-buffer.h"
-#include "client/registry.h"
-#include "client/window.h"
-#include "list.h"
+struct swaybar_config;
+struct swaybar_output;
+struct swaybar_workspace;
-struct bar {
- struct config *config;
+struct swaybar_pointer {
+ struct wl_pointer *pointer;
+ struct wl_cursor_theme *cursor_theme;
+ struct wl_cursor_image *cursor_image;
+ struct wl_surface *cursor_surface;
+ struct swaybar_output *current;
+ int x, y;
+};
+
+struct swaybar_hotspot {
+ struct wl_list link;
+ int x, y, width, height;
+ void (*callback)(struct swaybar_output *output,
+ int x, int y, uint32_t button, void *data);
+ void (*destroy)(void *data);
+ void *data;
+};
+
+struct swaybar {
+ struct wl_display *display;
+ struct wl_compositor *compositor;
+ struct zwlr_layer_shell_v1 *layer_shell;
+ struct wl_shm *shm;
+ struct wl_seat *seat;
+
+ struct swaybar_config *config;
+ struct swaybar_output *focused_output;
+ struct swaybar_pointer pointer;
struct status_line *status;
- list_t *outputs;
- struct output *focused_output;
int ipc_event_socketfd;
int ipc_socketfd;
- int status_read_fd;
- int status_write_fd;
- pid_t status_command_pid;
+
+ struct wl_list outputs;
};
-struct output {
- struct window *window;
- struct registry *registry;
- list_t *workspaces;
-#ifdef ENABLE_TRAY
- list_t *items;
-#endif
+struct swaybar_output {
+ struct wl_list link;
+ struct swaybar *bar;
+ struct wl_output *output;
+ struct wl_surface *surface;
+ struct zwlr_layer_surface_v1 *layer_surface;
+ uint32_t wl_name;
+
+ struct wl_list workspaces;
+ struct wl_list hotspots;
+
char *name;
- int idx;
+ size_t index;
bool focused;
+
+ uint32_t width, height;
+ int32_t scale;
+ struct pool_buffer buffers[2];
+ struct pool_buffer *current_buffer;
};
-struct workspace {
+struct swaybar_workspace {
+ struct wl_list link;
int num;
char *name;
bool focused;
@@ -38,35 +73,10 @@ struct workspace {
bool urgent;
};
-/** Global bar state */
-extern struct bar swaybar;
-
-/** True if sway needs to render */
-extern bool dirty;
-
-/**
- * Setup bar.
- */
-void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id);
-
-/**
- * Create new output struct from name.
- */
-struct output *new_output(const char *name);
+void bar_setup(struct swaybar *bar,
+ const char *socket_path,
+ const char *bar_id);
+void bar_run(struct swaybar *bar);
+void bar_teardown(struct swaybar *bar);
-/**
- * Bar mainloop.
- */
-void bar_run(struct bar *bar);
-
-/**
- * free workspace list.
- */
-void free_workspaces(list_t *workspaces);
-
-/**
- * Teardown bar.
- */
-void bar_teardown(struct bar *bar);
-
-#endif /* _SWAYBAR_BAR_H */
+#endif
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 @@
#ifndef _SWAYBAR_CONFIG_H
#define _SWAYBAR_CONFIG_H
-
-#include <stdint.h>
#include <stdbool.h>
-
-#include "list.h"
+#include <stdint.h>
+#include <wayland-client.h>
#include "util.h"
-/**
- * Colors for a box with background, border and text colors.
- */
struct box_colors {
uint32_t border;
uint32_t background;
uint32_t text;
};
-/**
- * Swaybar config.
- */
-struct config {
+struct config_output {
+ struct wl_list link;
+ char *name;
+ size_t index;
+};
+
+struct swaybar_config {
char *status_command;
bool pango_markup;
- uint32_t position;
+ uint32_t position; // zwlr_layer_surface_v1_anchor
char *font;
char *sep_symbol;
char *mode;
+ bool mode_pango_markup;
bool strip_workspace_numbers;
bool binding_mode_indicator;
bool wrap_scroll;
bool workspace_buttons;
+ struct wl_list outputs;
bool all_outputs;
- list_t *outputs;
-
-#ifdef ENABLE_TRAY
- // Tray
- char *tray_output;
- char *icon_theme;
-
- uint32_t tray_padding;
- uint32_t activate_button;
- uint32_t context_button;
- uint32_t secondary_button;
-#endif
-
int height;
struct {
@@ -63,24 +50,8 @@ struct config {
} colors;
};
-/**
- * Parse position top|bottom|left|right.
- */
+struct swaybar_config *init_config();
+void free_config(struct swaybar_config *config);
uint32_t parse_position(const char *position);
-/**
- * Parse font.
- */
-char *parse_font(const char *font);
-
-/**
- * Initialize default sway config.
- */
-struct config *init_config();
-
-/**
- * Free config struct.
- */
-void free_config(struct config *config);
-
-#endif /* _SWAYBAR_CONFIG_H */
+#endif
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 @@
#ifndef _SWAYBAR_EVENT_LOOP_H
#define _SWAYBAR_EVENT_LOOP_H
-
#include <stdbool.h>
#include <time.h>
@@ -23,4 +22,5 @@ bool remove_timer(timer_t timer);
void event_loop_poll();
void init_event_loop();
-#endif /*_SWAYBAR_EVENT_LOOP_H */
+
+#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 @@
#ifndef _SWAYBAR_IPC_H
#define _SWAYBAR_IPC_H
+#include <stdbool.h>
+#include "swaybar/bar.h"
-#include "bar.h"
-
-/**
- * Initialize ipc connection to sway and get sway state, outputs, bar_config.
- */
-void ipc_bar_init(struct bar *bar, const char *bar_id);
-
-/**
- * Handle ipc event from sway.
- */
-bool handle_ipc_event(struct bar *bar);
-
-
-/**
- * Send workspace command to sway
- */
-void ipc_send_workspace_command(const char *workspace_name);
-
-#endif /* _SWAYBAR_IPC_H */
+void ipc_initialize(struct swaybar *bar, const char *bar_id);
+bool handle_ipc_readable(struct swaybar *bar);
+void ipc_get_workspaces(struct swaybar *bar);
+void ipc_send_workspace_command(struct swaybar *bar, const char *ws);
+#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 @@
#ifndef _SWAYBAR_RENDER_H
#define _SWAYBAR_RENDER_H
-#include "config.h"
-#include "bar.h"
+struct swaybar;
+struct swaybar_output;
+struct swaybar_config;
-/**
- * Render swaybar.
- */
-void render(struct output *output, struct config *config, struct status_line *line);
+void render_frame(struct swaybar *bar, struct swaybar_output *output);
-/**
- * Set window height and modify internal spacing accordingly.
- */
-void set_window_height(struct window *window, int height);
-
-/**
- * Compute the size of a workspace name
- */
-void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height);
-
-#endif /* _SWAYBAR_RENDER_H */
+#endif
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 @@
#ifndef _SWAYBAR_STATUS_LINE_H
#define _SWAYBAR_STATUS_LINE_H
-
#include <stdint.h>
+#include <stdio.h>
#include <stdbool.h>
-
-#include "list.h"
#include "bar.h"
-typedef enum {UNDEF, TEXT, I3BAR} command_protocol;
+enum status_protocol {
+ PROTOCOL_UNDEF,
+ PROTOCOL_ERROR,
+ PROTOCOL_TEXT,
+ PROTOCOL_I3BAR,
+};
+
+struct text_protocol_state {
+ char *buffer;
+ size_t buffer_size;
+};
-struct status_line {
- list_t *block_line;
- const char *text_line;
- command_protocol protocol;
+enum json_node_type {
+ JSON_NODE_UNKNOWN,
+ JSON_NODE_ARRAY,
+ JSON_NODE_STRING,
+};
+
+struct i3bar_protocol_state {
bool click_events;
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_index;
+ const char *current_node;
+ bool escape;
+ size_t depth;
+ enum json_node_type nodes[16];
};
-struct status_block {
+struct i3bar_block {
+ struct wl_list link;
char *full_text, *short_text, *align;
bool urgent;
- uint32_t color;
+ uint32_t *color;
int min_width;
char *name, *instance;
bool separator;
@@ -32,30 +51,27 @@ struct status_block {
int border_bottom;
int border_left;
int border_right;
-
- // Set during rendering
- int x;
- int width;
};
-/**
- * Initialize status line struct.
- */
-struct status_line *init_status_line();
+struct status_line {
+ pid_t pid;
+ int read_fd, write_fd;
+ FILE *read, *write;
-/**
- * handle status line activity.
- */
-bool handle_status_line(struct bar *bar);
+ enum status_protocol protocol;
+ const char *text;
+ struct wl_list blocks; // i3bar_block::link
-/**
- * Handle mouse clicks.
- */
-bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button);
+ struct text_protocol_state text_state;
+ struct i3bar_protocol_state i3bar_state;
+};
-/**
- * Free status line struct.
- */
-void free_status_line(struct status_line *line);
+struct status_line *status_line_init(char *cmd);
+void status_error(struct status_line *status, const char *text);
+bool status_handle_readable(struct status_line *status);
+void status_line_free(struct status_line *status);
+bool i3bar_handle_readable(struct status_line *status);
+void i3bar_block_send_click(struct status_line *status,
+ struct i3bar_block *block, int x, int y, uint32_t button);
-#endif /* _SWAYBAR_STATUS_LINE_H */
+#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 @@
+#ifndef _SWAYLOCK_SEAT_H
+#define _SWAYLOCK_SEAT_H
+#include <xkbcommon/xkbcommon.h>
+
+enum mod_bit {
+ MOD_SHIFT = 1<<0,
+ MOD_CAPS = 1<<1,
+ MOD_CTRL = 1<<2,
+ MOD_ALT = 1<<3,
+ MOD_MOD2 = 1<<4,
+ MOD_MOD3 = 1<<5,
+ MOD_LOGO = 1<<6,
+ MOD_MOD5 = 1<<7,
+};
+
+enum mask {
+ MASK_SHIFT,
+ MASK_CAPS,
+ MASK_CTRL,
+ MASK_ALT,
+ MASK_MOD2,
+ MASK_MOD3,
+ MASK_LOGO,
+ MASK_MOD5,
+ MASK_LAST
+};
+
+struct swaylock_xkb {
+ uint32_t modifiers;
+ struct xkb_state *state;
+ struct xkb_context *context;
+ struct xkb_keymap *keymap;
+ xkb_mod_mask_t masks[MASK_LAST];
+};
+
+extern const struct wl_seat_listener seat_listener;
+
+#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 @@
#ifndef _SWAYLOCK_H
#define _SWAYLOCK_H
-
-#include "client/cairo.h"
-
-enum scaling_mode {
- SCALING_MODE_STRETCH,
- SCALING_MODE_FILL,
- SCALING_MODE_FIT,
- SCALING_MODE_CENTER,
- SCALING_MODE_TILE,
-};
+#include <stdbool.h>
+#include <stdint.h>
+#include <wayland-client.h>
+#include "background-image.h"
+#include "cairo.h"
+#include "pool-buffer.h"
+#include "swaylock/seat.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
enum auth_state {
- AUTH_STATE_IDLE,
- AUTH_STATE_INPUT,
- AUTH_STATE_BACKSPACE,
- AUTH_STATE_VALIDATING,
- AUTH_STATE_INVALID,
+ AUTH_STATE_IDLE,
+ AUTH_STATE_INPUT,
+ AUTH_STATE_BACKSPACE,
+ AUTH_STATE_VALIDATING,
+ AUTH_STATE_INVALID,
};
-enum line_source {
- LINE_SOURCE_DEFAULT,
- LINE_SOURCE_RING,
- LINE_SOURCE_INSIDE,
-};
-
-struct render_data {
- list_t *surfaces;
- // Output specific images
- cairo_surface_t **images;
- // OR one image for all outputs:
- cairo_surface_t *image;
- int num_images;
- int color_set;
+struct swaylock_args {
uint32_t color;
- enum scaling_mode scaling_mode;
- enum auth_state auth_state;
+ enum background_mode mode;
+ bool show_indicator;
};
-struct lock_colors {
- uint32_t inner_ring;
- uint32_t outer_ring;
+struct swaylock_password {
+ size_t size;
+ size_t len;
+ char *buffer;
};
-struct lock_config {
- char *font;
-
- struct {
- uint32_t text;
- uint32_t line;
- uint32_t separator;
- uint32_t input_cursor;
- uint32_t backspace_cursor;
- struct lock_colors normal;
- struct lock_colors validating;
- struct lock_colors invalid;
- } colors;
+struct swaylock_state {
+ struct wl_display *display;
+ struct wl_compositor *compositor;
+ struct zwlr_layer_shell_v1 *layer_shell;
+ struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager;
+ struct wl_shm *shm;
+ struct wl_list surfaces;
+ struct swaylock_args args;
+ struct swaylock_password password;
+ struct swaylock_xkb xkb;
+ enum auth_state auth_state;
+ bool run_display;
+};
- int radius;
- int thickness;
+struct swaylock_surface {
+ cairo_surface_t *image;
+ struct swaylock_state *state;
+ struct wl_output *output;
+ struct wl_surface *surface;
+ struct zwlr_layer_surface_v1 *layer_surface;
+ struct pool_buffer buffers[2];
+ struct pool_buffer *current_buffer;
+ uint32_t width, height;
+ int32_t scale;
+ struct wl_list link;
};
-void render(struct render_data* render_data, struct lock_config *config);
+void swaylock_handle_key(struct swaylock_state *state,
+ xkb_keysym_t keysym, uint32_t codepoint);
+void render_frame(struct swaylock_surface *surface);
+void render_frames(struct swaylock_state *state);
#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 @@
+#ifndef _SWAY_UNICODE_H
+#define _SWAY_UNICODE_H
+#include <stddef.h>
+#include <stdint.h>
+
+// Technically UTF-8 supports up to 6 byte codepoints, but Unicode itself
+// doesn't really bother with more than 4.
+#define UTF8_MAX_SIZE 4
+
+#define UTF8_INVALID 0x80
+
+/**
+ * Grabs the next UTF-8 character and advances the string pointer
+ */
+uint32_t utf8_decode(const char **str);
+
+/**
+ * Encodes a character as UTF-8 and returns the length of that character.
+ */
+size_t utf8_encode(char *str, uint32_t ch);
+
+/**
+ * Returns the size of the next UTF-8 character
+ */
+int utf8_size(const char *str);
+
+/**
+ * Returns the size of a UTF-8 character
+ */
+size_t utf8_chsize(uint32_t ch);
+
+#endif
+
diff --git a/meson.build b/meson.build
new file mode 100644
index 00000000..bf266e5f
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,171 @@
+project(
+ 'sway',
+ 'c',
+ license: 'MIT',
+ default_options: [
+ 'c_std=c11',
+ 'warning_level=2',
+ 'werror=true',
+ ],
+)
+
+add_project_arguments('-Wno-unused-parameter', language: 'c')
+add_project_arguments('-Wno-unused-function', language: 'c')
+add_project_arguments('-Wno-unused-result', language: 'c')
+
+cc = meson.get_compiler('c')
+
+is_freebsd = host_machine.system().startswith('freebsd')
+datadir = get_option('datadir')
+sysconfdir = get_option('sysconfdir')
+prefix = get_option('prefix')
+
+jsonc = dependency('json-c', version: '>=0.13')
+pcre = dependency('libpcre')
+wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
+wayland_server = dependency('wayland-server')
+wayland_client = dependency('wayland-client')
+wayland_cursor = dependency('wayland-cursor')
+wayland_egl = dependency('wayland-egl')
+wayland_protos = dependency('wayland-protocols')
+xkbcommon = dependency('xkbcommon')
+cairo = dependency('cairo')
+pango = dependency('pango')
+pangocairo = dependency('pangocairo')
+gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
+pixman = dependency('pixman-1')
+libcap = dependency('libcap')
+libinput = dependency('libinput', version: '>=1.6.0')
+libpam = cc.find_library('libpam')
+math = cc.find_library('m')
+rt = cc.find_library('rt')
+git = find_program('git', required: false)
+a2x = find_program('a2x', required: false)
+
+conf_data = configuration_data()
+
+if gdk_pixbuf.found()
+ conf_data.set('HAVE_GDK_PIXBUF', true)
+endif
+
+if a2x.found()
+ mandir = get_option('mandir')
+ man_files = [
+ 'sway/sway.1.txt',
+ 'sway/sway.5.txt',
+ 'sway/sway-bar.5.txt',
+ 'sway/sway-input.5.txt',
+ 'sway/sway-security.7.txt',
+ 'swaymsg/swaymsg.1.txt',
+ ]
+ foreach filename : man_files
+ topic = filename.split('.')[-3].split('/')[-1]
+ section = filename.split('.')[-2]
+
+ custom_target(
+ 'man-@0@-@1@'.format(topic, section),
+ input: filename,
+ output: '@BASENAME@',
+ command: [
+ a2x,
+ '--no-xmllint',
+ '--doctype', 'manpage',
+ '--format', 'manpage',
+ '--destination-dir', meson.current_build_dir(),
+ '@INPUT@'
+ ],
+ install: true,
+ install_dir: '@0@/man@1@'.format(mandir, section)
+ )
+ endforeach
+endif
+
+add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c')
+
+version = get_option('sway_version')
+if version != ''
+ version = '"@0@"'.format(version)
+else
+ if not git.found()
+ error('git is required to make the version string')
+ endif
+
+ git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip()
+ git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip()
+ version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch)
+endif
+add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
+
+sway_inc = include_directories('include')
+
+subdir('include')
+subdir('protocols')
+subdir('common')
+subdir('sway')
+subdir('swaymsg')
+
+subdir('client')
+subdir('swaybg')
+subdir('swaybar')
+subdir('swaylock')
+
+config = configuration_data()
+config.set('sysconfdir', join_paths(prefix, sysconfdir))
+config.set('datadir', join_paths(prefix, datadir))
+config.set('prefix', prefix)
+
+configure_file(
+ configuration: config,
+ input: 'config.in',
+ output: '@BASENAME@',
+ install_dir: sysconfdir + '/sway'
+)
+
+if is_freebsd
+ configure_file(
+ configuration: config,
+ input: 'security.d/10-freebsd.in',
+ output: '@BASENAME@',
+ install_dir: sysconfdir + '/sway/security.d'
+ )
+else
+ configure_file(
+ configuration: config,
+ input: 'security.d/00-defaults.in',
+ output: '@BASENAME@',
+ install_dir: sysconfdir + '/sway/security.d'
+ )
+endif
+
+install_data(
+ 'sway.desktop',
+ install_dir: datadir + '/wayland-sessions'
+)
+
+if (get_option('default_wallpaper'))
+ wallpaper_files = files(
+ 'assets/Sway_Wallpaper_Blue_768x1024.png',
+ 'assets/Sway_Wallpaper_Blue_768x1024_Portrait.png',
+ 'assets/Sway_Wallpaper_Blue_1136x640.png',
+ 'assets/Sway_Wallpaper_Blue_1136x640_Portrait.png',
+ 'assets/Sway_Wallpaper_Blue_1366x768.png',
+ 'assets/Sway_Wallpaper_Blue_1920x1080.png',
+ 'assets/Sway_Wallpaper_Blue_2048x1536.png',
+ 'assets/Sway_Wallpaper_Blue_2048x1536_Portrait.png',
+ )
+ wallpaper_install_dir = datadir + '/backgrounds/sway'
+
+ install_data(wallpaper_files, install_dir: wallpaper_install_dir)
+endif
+
+if (get_option('zsh_completions'))
+ zsh_files = files(
+ 'completions/zsh/_sway',
+ 'completions/zsh/_swaygrab',
+ 'completions/zsh/_swaylock',
+ 'completions/zsh/_swaymsg',
+ )
+ zsh_install_dir = datadir + '/zsh/site-functions'
+
+ install_data(zsh_files, install_dir: zsh_install_dir)
+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 @@
+option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.')
+option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.')
+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 @@
-include(Wayland)
-
-WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-xdg-shell
- xdg-shell.xml
- xdg-shell
-)
-WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-desktop-shell
- desktop-shell.xml
- desktop-shell
-)
-WAYLAND_ADD_PROTOCOL_SERVER(proto-server-desktop-shell
- desktop-shell.xml
- desktop-shell
-)
-
-WAYLAND_ADD_PROTOCOL_CLIENT(proto-client-swaylock
- swaylock.xml
- swaylock
-)
-WAYLAND_ADD_PROTOCOL_SERVER(proto-server-swaylock
- swaylock.xml
- swaylock
-)
-
-WAYLAND_ADD_PROTOCOL_SERVER(proto-server-gamma-control
- gamma-control.xml
- gamma-control
-)
-
-WAYLAND_ADD_PROTOCOL_SERVER(proto-server-server-decoration
- server-decoration.xml
- server-decoration
-)
-
-add_library(sway-protocols STATIC
- ${proto-client-xdg-shell}
- ${proto-client-desktop-shell}
- ${proto-server-desktop-shell}
- ${proto-client-swaylock}
- ${proto-server-swaylock}
- ${proto-server-gamma-control}
- ${proto-server-server-decoration}
- )
-
-set(PROTOCOLS_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/protocols PARENT_SCOPE)
-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 @@
-<protocol name="desktop">
-
- <interface name="desktop_shell" version="3">
- <description summary="create desktop widgets and helpers">
- Traditional user interfaces can rely on this interface to define the
- foundations of typical desktops. Currently it's possible to set up
- background, panels and locking surfaces.
- </description>
-
- <request name="set_background">
- <arg name="output" type="object" interface="wl_output"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- </request>
-
- <request name="set_panel">
- <arg name="output" type="object" interface="wl_output"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- </request>
-
- <request name="set_lock_surface">
- <arg name="surface" type="object" interface="wl_surface"/>
- </request>
-
- <request name="unlock"/>
-
- <request name="set_grab_surface">
- <description summary="set grab surface">
- The surface set by this request will receive a fake
- pointer.enter event during grabs at position 0, 0 and is
- expected to set an appropriate cursor image as described by
- the grab_cursor event sent just before the enter event.
- </description>
- <arg name="surface" type="object" interface="wl_surface"/>
- </request>
-
- <!-- We'll fold most of wl_shell into this interface and then
- they'll share the configure event. -->
- <event name="configure">
- <arg name="edges" type="uint"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
- </event>
-
- <event name="prepare_lock_surface">
- <description summary="tell the client to create, set the lock surface">
- Tell the client we want it to create and set the lock surface, which is
- a GUI asking the user to unlock the screen. The lock surface is
- announced with 'set_lock_surface'. Whether or not the client actually
- implements locking, it MUST send 'unlock' request to let the normal
- desktop resume.
- </description>
- </event>
-
- <event name="grab_cursor">
- <description summary="tell client what cursor to show during a grab">
- This event will be sent immediately before a fake enter event on the
- grab surface.
- </description>
- <arg name="cursor" type="uint"/>
- </event>
-
- <enum name="cursor">
- <entry name="none" value="0"/>
-
- <entry name="resize_top" value="1"/>
- <entry name="resize_bottom" value="2"/>
-
- <entry name="arrow" value="3"/>
-
- <entry name="resize_left" value="4"/>
- <entry name="resize_top_left" value="5"/>
- <entry name="resize_bottom_left" value="6"/>
-
- <entry name="move" value="7"/>
-
- <entry name="resize_right" value="8"/>
- <entry name="resize_top_right" value="9"/>
- <entry name="resize_bottom_right" value="10"/>
-
- <entry name="busy" value="11"/>
- </enum>
-
- <!-- Version 2 additions -->
-
- <request name="desktop_ready" since="2">
- <description summary="desktop is ready to be shown">
- Tell the server, that enough desktop elements have been drawn
- to make the desktop look ready for use. During start-up, the
- server can wait for this request with a black screen before
- starting to fade in the desktop, for instance. If the client
- parts of a desktop take a long time to initialize, we avoid
- showing temporary garbage.
- </description>
- </request>
-
- <!-- Version 3 additions -->
-
- <enum name="panel_position">
- <entry name="top" value="0"/>
- <entry name="bottom" value="1"/>
- <entry name="left" value="2"/>
- <entry name="right" value="3"/>
- </enum>
-
- <enum name="error">
- <entry name="invalid_argument" value="0"
- summary="an invalid argument was provided in a request"/>
- </enum>
-
- <request name="set_panel_position" since="3">
- <description summary="set panel position">
- Tell the shell which side of the screen the panel is
- located. This is so that new windows do not overlap the panel
- and maximized windows maximize properly.
- </description>
- <arg name="position" type="uint"/>
- </request>
-
- </interface>
-
- <interface name="screensaver" version="1">
- <description summary="interface for implementing screensavers">
- Only one client can bind this interface at a time.
- </description>
-
- <request name="set_surface">
- <description summary="set the surface type as a screensaver">
- A screensaver surface is normally hidden, and only visible after an
- idle timeout.
- </description>
-
- <arg name="surface" type="object" interface="wl_surface"/>
- <arg name="output" type="object" interface="wl_output"/>
- </request>
-
- </interface>
-</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 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="gamma_control">
-
- <copyright>
- Copyright © 2015 Giulio camuffo
-
- Permission to use, copy, modify, distribute, and sell this
- software and its documentation for any purpose is hereby granted
- without fee, provided that the above copyright notice appear in
- all copies and that both that copyright notice and this permission
- notice appear in supporting documentation, and that the name of
- the copyright holders not be used in advertising or publicity
- pertaining to distribution of the software without specific,
- written prior permission. The copyright holders make no
- representations about the suitability of this software for any
- purpose. It is provided "as is" without express or implied
- warranty.
-
- THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- THIS SOFTWARE.
- </copyright>
-
- <interface name="gamma_control_manager" version="1">
- <request name="destroy" type="destructor"/>
-
- <request name="get_gamma_control">
- <arg name="id" type="new_id" interface="gamma_control"/>
- <arg name="output" type="object" interface="wl_output"/>
- </request>
- </interface>
-
- <interface name="gamma_control" version="1">
- <enum name="error">
- <entry name="invalid_gamma" value="0"/>
- </enum>
-
- <request name="destroy" type="destructor"/>
-
- <request name="set_gamma">
- <arg name="red" type="array"/>
- <arg name="green" type="array"/>
- <arg name="blue" type="array"/>
- </request>
-
- <request name="reset_gamma"/>
-
- <event name="gamma_size">
- <arg name="size" type="uint"/>
- </event>
- </interface>
-</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 @@
+wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
+
+wayland_scanner = find_program('wayland-scanner')
+
+wayland_scanner_code = generator(
+ wayland_scanner,
+ output: '@BASENAME@-protocol.c',
+ arguments: ['code', '@INPUT@', '@OUTPUT@'],
+)
+
+wayland_scanner_client = generator(
+ wayland_scanner,
+ output: '@BASENAME@-client-protocol.h',
+ arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
+)
+
+wayland_scanner_server = generator(
+ wayland_scanner,
+ output: '@BASENAME@-protocol.h',
+ arguments: ['server-header', '@INPUT@', '@OUTPUT@'],
+)
+
+client_protocols = [
+ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
+ ['wlr-layer-shell-unstable-v1.xml'],
+ ['wlr-input-inhibitor-unstable-v1.xml']
+]
+
+server_protocols = [
+ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
+ ['wlr-layer-shell-unstable-v1.xml'],
+ ['wlr-input-inhibitor-unstable-v1.xml']
+]
+
+client_protos_src = []
+client_protos_headers = []
+
+server_protos_src = []
+server_protos_headers = []
+
+foreach p : client_protocols
+ xml = join_paths(p)
+ client_protos_src += wayland_scanner_code.process(xml)
+ client_protos_headers += wayland_scanner_client.process(xml)
+endforeach
+
+foreach p : server_protocols
+ xml = join_paths(p)
+ server_protos_src += wayland_scanner_code.process(xml)
+ server_protos_headers += wayland_scanner_server.process(xml)
+endforeach
+
+lib_client_protos = static_library(
+ 'client_protos',
+ client_protos_src + client_protos_headers,
+ dependencies: [wayland_client]
+) # for the include directory
+
+client_protos = declare_dependency(
+ link_with: lib_client_protos,
+ sources: client_protos_headers,
+)
+
+lib_server_protos = static_library(
+ 'server_protos',
+ server_protos_src + server_protos_headers,
+ dependencies: [wayland_client]
+) # for the include directory
+
+server_protos = declare_dependency(
+ link_with: lib_server_protos,
+ sources: server_protos_headers,
+)
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 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="server_decoration">
- <copyright><![CDATA[
- Copyright (C) 2015 Martin Gräßlin
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation, either version 2.1 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- ]]></copyright>
- <interface name="org_kde_kwin_server_decoration_manager" version="1">
- <description summary="Server side window decoration manager">
- This interface allows to coordinate whether the server should create
- a server-side window decoration around a wl_surface representing a
- shell surface (wl_shell_surface or similar). By announcing support
- for this interface the server indicates that it supports server
- side decorations.
- </description>
- <request name="create">
- <description summary="Create a server-side decoration object for a given surface">
- When a client creates a server-side decoration object it indicates
- that it supports the protocol. The client is supposed to tell the
- server whether it wants server-side decorations or will provide
- client-side decorations.
-
- If the client does not create a server-side decoration object for
- a surface the server interprets this as lack of support for this
- protocol and considers it as client-side decorated. Nevertheless a
- client-side decorated surface should use this protocol to indicate
- to the server that it does not want a server-side deco.
- </description>
- <arg name="id" type="new_id" interface="org_kde_kwin_server_decoration"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- </request>
- <enum name="mode">
- <description summary="Possible values to use in request_mode and the event mode."/>
- <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."/>
- <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
- <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
- </enum>
- <event name="default_mode">
- <description summary="The default mode used on the server">
- This event is emitted directly after binding the interface. It contains
- the default mode for the decoration. When a new server decoration object
- is created this new object will be in the default mode until the first
- request_mode is requested.
-
- The server may change the default mode at any time.
- </description>
- <arg name="mode" type="uint" summary="The default decoration mode applied to newly created server decorations."/>
- </event>
- </interface>
- <interface name="org_kde_kwin_server_decoration" version="1">
- <request name="release" type="destructor">
- <description summary="release the server decoration object"/>
- </request>
- <enum name="mode">
- <description summary="Possible values to use in request_mode and the event mode."/>
- <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."/>
- <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
- <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
- </enum>
- <request name="request_mode">
- <description summary="The decoration mode the surface wants to use."/>
- <arg name="mode" type="uint" summary="The mode this surface wants to use."/>
- </request>
- <event name="mode">
- <description summary="The new decoration mode applied by the server">
- This event is emitted directly after the decoration is created and
- represents the base decoration policy by the server. E.g. a server
- which wants all surfaces to be client-side decorated will send Client,
- a server which wants server-side decoration will send Server.
-
- The client can request a different mode through the decoration request.
- The server will acknowledge this by another event with the same mode. So
- even if a server prefers server-side decoration it's possible to force a
- client-side decoration.
-
- The server may emit this event at any time. In this case the client can
- again request a different mode. It's the responsibility of the server to
- prevent a feedback loop.
- </description>
- <arg name="mode" type="uint" summary="The decoration mode applied to the surface by the server."/>
- </event>
- </interface>
-</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 @@
-<protocol name="lock">
-
- <interface name="lock" version="1">
- <description summary="create lock screen UIs">
- The Weston desktop-shell protocol's locking functionality depends more
- on the behavior of the compositor than of a screen locking client, so
- another protocol is necessary.
- </description>
-
- <request name="set_lock_surface">
- <arg name="output" type="object" interface="wl_output"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- </request>
-
- <request name="unlock"/>
-
- </interface>
-</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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_input_inhibit_unstable_v1">
+ <copyright>
+ Copyright © 2018 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <interface name="zwlr_input_inhibit_manager_v1" version="1">
+ <description summary="inhibits input events to other clients">
+ Clients can use this interface to prevent input events from being sent to
+ any surfaces but its own, which is useful for example in lock screen
+ software. It is assumed that access to this interface will be locked down
+ to whitelisted clients by the compositor.
+ </description>
+
+ <request name="get_inhibitor">
+ <description summary="inhibit input to other clients">
+ Activates the input inhibitor. As long as the inhibitor is active, the
+ compositor will not send input events to other clients.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
+ </request>
+
+ <enum name="error">
+ <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
+ </enum>
+ </interface>
+
+ <interface name="zwlr_input_inhibitor_v1" version="1">
+ <description summary="inhibits input to other clients">
+ While this resource exists, input to clients other than the owner of the
+ inhibitor resource will not receive input events. The client that owns
+ this resource will receive all input events normally. The compositor will
+ also disable all of its own input processing (such as keyboard shortcuts)
+ while the inhibitor is active.
+
+ The compositor may continue to send input events to selected clients,
+ such as an on-screen keyboard (via the input-method protocol).
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the input inhibitor object">
+ Destroy the inhibitor and allow other clients to receive input.
+ </description>
+ </request>
+ </interface>
+</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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_layer_shell_unstable_v1">
+ <copyright>
+ Copyright © 2017 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <interface name="zwlr_layer_shell_v1" version="1">
+ <description summary="create surfaces that are layers of the desktop">
+ Clients can use this interface to assign the surface_layer role to
+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+ rendered with a defined z-depth respective to each other. They may also be
+ anchored to the edges and corners of a screen and specify input handling
+ semantics. This interface should be suitable for the implementation of
+ many desktop shell components, and a broad number of other applications
+ that interact with the desktop.
+ </description>
+
+ <request name="get_layer_surface">
+ <description summary="create a layer_surface from a surface">
+ Create a layer surface for an existing surface. This assigns the role of
+ layer_surface, or raises a protocol error if another role is already
+ assigned.
+
+ Creating a layer surface from a wl_surface which has a buffer attached
+ or committed is a client error, and any attempts by a client to attach
+ or manipulate a buffer prior to the first layer_surface.configure call
+ must also be treated as errors.
+
+ Clients can specify a namespace that defines the purpose of the layer
+ surface.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="output" type="object" interface="wl_output"/>
+ <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
+ <arg name="namespace" type="string" summary="namespace for the layer surface"/>
+ </request>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="wl_surface has another role"/>
+ <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
+ <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
+ </enum>
+
+ <enum name="layer">
+ <description summary="available layers for surfaces">
+ These values indicate which layers a surface can be rendered in. They
+ are ordered by z depth, bottom-most first. Traditional shell surfaces
+ will typically be rendered between the bottom and top layers.
+ Fullscreen shell surfaces are typically rendered at the top layer.
+ Multiple surfaces can share a single layer, and ordering within a
+ single layer is undefined.
+ </description>
+
+ <entry name="background" value="0"/>
+ <entry name="bottom" value="1"/>
+ <entry name="top" value="2"/>
+ <entry name="overlay" value="3"/>
+ </enum>
+ </interface>
+
+ <interface name="zwlr_layer_surface_v1" version="1">
+ <description summary="layer metadata interface">
+ An interface that may be implemented by a wl_surface, for surfaces that
+ are designed to be rendered as a layer of a stacked desktop-like
+ environment.
+
+ Layer surface state (size, anchor, exclusive zone, margin, interactivity)
+ is double-buffered, and will be applied at the time wl_surface.commit of
+ the corresponding wl_surface is called.
+ </description>
+
+ <request name="set_size">
+ <description summary="sets the size of the surface">
+ Sets the size of the surface in surface-local coordinates. The
+ compositor will display the surface centered with respect to its
+ anchors.
+
+ If you pass 0 for either value, the compositor will assign it and
+ inform you of the assignment in the configure event. You must set your
+ anchor to opposite edges in the dimensions you omit; not doing so is a
+ protocol error. Both values are 0 by default.
+
+ Size is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </request>
+
+ <request name="set_anchor">
+ <description summary="configures the anchor point of the surface">
+ Requests that the compositor anchor the surface to the specified edges
+ and corners. If two orthoginal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left corner of the output); otherwise the anchor point
+ will be centered on that edge, or in the center if none is specified.
+
+ Anchor is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"/>
+ </request>
+
+ <request name="set_exclusive_zone">
+ <description summary="configures the exclusive geometry of this surface">
+ Requests that the compositor avoids occluding an area of the surface
+ with other surfaces. The compositor's use of this information is
+ implementation-dependent - do not assume that this region will not
+ actually be occluded.
+
+ A positive value is only meaningful if the surface is anchored to an
+ edge, rather than a corner. The zone is the number of surface-local
+ coordinates from the edge that are considered exclusive.
+
+ Surfaces that do not wish to have an exclusive zone may instead specify
+ how they should interact with surfaces that do. If set to zero, the
+ surface indicates that it would like to be moved to avoid occluding
+ surfaces with a positive excluzive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accomodate for other
+ surfaces, and the compositor should extend it all the way to the edges
+ it is anchored to.
+
+ For example, a panel might set its exclusive zone to 10, so that
+ maximized shell surfaces are not shown on top of it. A notification
+ might set its exclusive zone to 0, so that it is moved to avoid
+ occluding the panel, but shell surfaces are shown underneath it. A
+ wallpaper or lock screen might set their exclusive zone to -1, so that
+ they stretch below or over the panel.
+
+ The default value is 0.
+
+ Exclusive zone is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="zone" type="int"/>
+ </request>
+
+ <request name="set_margin">
+ <description summary="sets a margin from the anchor point">
+ Requests that the surface be placed some distance away from the anchor
+ point on the output, in surface-local coordinates. Setting this value
+ for edges you are not anchored to has no effect.
+
+ The exclusive zone includes the margin.
+
+ Margin is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="top" type="int"/>
+ <arg name="right" type="int"/>
+ <arg name="bottom" type="int"/>
+ <arg name="left" type="int"/>
+ </request>
+
+ <request name="set_keyboard_interactivity">
+ <description summary="requests keyboard events">
+ Set to 1 to request that the seat send keyboard events to this layer
+ surface. For layers below the shell surface layer, the seat will use
+ normal focus semantics. For layers above the shell surface layers, the
+ seat will always give exclusive keyboard focus to the top-most layer
+ which has keyboard interactivity set to true.
+
+ Layer surfaces receive pointer, touch, and tablet events normally. If
+ you do not want to receive them, set the input region on your surface
+ to an empty region.
+
+ Events is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="keyboard_interactivity" type="uint"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign this layer_surface as an xdg_popup parent">
+ This assigns an xdg_popup's parent to this layer_surface. This popup
+ should have been created via xdg_surface::get_popup with the parent set
+ to NULL, and this request must be invoked before committing the popup's
+ initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="popup" type="object" interface="xdg_popup"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the layer_surface">
+ This request destroys the layer surface.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event asks the client to resize its surface.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ The client is free to dismiss all but the last configure event it
+ received.
+
+ The width and height arguments specify the size of the window in
+ surface-local coordinates.
+
+ The size is a hint, in the sense that the client is free to ignore it if
+ it doesn't resize, pick a smaller size (to satisfy aspect ratio or
+ resize in steps of NxM pixels). If the client picks a smaller size and
+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+ surface will be centered on this axis.
+
+ If the width or height arguments are zero, it means the client should
+ decide its own window dimension.
+ </description>
+ <arg name="serial" type="uint"/>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </event>
+
+ <event name="closed">
+ <description summary="surface should be closed">
+ The closed event is sent by the compositor when the surface will no
+ longer be shown. The output may have been destroyed or the user may
+ have asked for it to be removed. Further changes to the surface will be
+ ignored. The client should destroy the resource after receiving this
+ event, and create a new surface if they so choose.
+ </description>
+ </event>
+
+ <enum name="error">
+ <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
+ <entry name="invalid_size" value="1" summary="size is invalid"/>
+ <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
+ </enum>
+
+ <enum name="anchor" bitfield="true">
+ <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
+ <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
+ <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
+ <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
+ </enum>
+ </interface>
+</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 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="xdg_shell">
-
- <copyright>
- Copyright © 2008-2013 Kristian Høgsberg
- Copyright © 2013 Rafael Antognolli
- Copyright © 2013 Jasper St. Pierre
- Copyright © 2010-2013 Intel Corporation
-
- Permission to use, copy, modify, distribute, and sell this
- software and its documentation for any purpose is hereby granted
- without fee, provided that the above copyright notice appear in
- all copies and that both that copyright notice and this permission
- notice appear in supporting documentation, and that the name of
- the copyright holders not be used in advertising or publicity
- pertaining to distribution of the software without specific,
- written prior permission. The copyright holders make no
- representations about the suitability of this software for any
- purpose. It is provided "as is" without express or implied
- warranty.
-
- THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- THIS SOFTWARE.
- </copyright>
-
- <interface name="xdg_shell" version="1">
- <description summary="create desktop-style surfaces">
- This interface is implemented by servers that provide
- desktop-style user interfaces.
-
- It allows clients to associate a xdg_surface with
- a basic surface.
- </description>
-
- <enum name="version">
- <description summary="latest protocol version">
- The 'current' member of this enum gives the version of the
- protocol. Implementations can compare this to the version
- they implement using static_assert to ensure the protocol and
- implementation versions match.
- </description>
- <entry name="current" value="4" summary="Always the latest version"/>
- </enum>
-
- <enum name="error">
- <entry name="role" value="0" summary="given wl_surface has another role"/>
- </enum>
-
- <request name="use_unstable_version">
- <description summary="enable use of this unstable version">
- Negotiate the unstable version of the interface. This
- mechanism is in place to ensure client and server agree on the
- unstable versions of the protocol that they speak or exit
- cleanly if they don't agree. This request will go away once
- the xdg-shell protocol is stable.
- </description>
- <arg name="version" type="int"/>
- </request>
-
- <request name="get_xdg_surface">
- <description summary="create a shell surface from a surface">
- Create a shell surface for an existing surface.
-
- This request gives the surface the role of xdg_surface. If the
- surface already has another role, it raises a protocol error.
-
- Only one shell or popup surface can be associated with a given
- surface.
- </description>
- <arg name="id" type="new_id" interface="xdg_surface"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- </request>
-
- <request name="get_xdg_popup">
- <description summary="create a shell surface from a surface">
- Create a popup surface for an existing surface.
-
- This request gives the surface the role of xdg_popup. If the
- surface already has another role, it raises a protocol error.
-
- Only one shell or popup surface can be associated with a given
- surface.
- </description>
- <arg name="id" type="new_id" interface="xdg_popup"/>
- <arg name="surface" type="object" interface="wl_surface"/>
- <arg name="parent" type="object" interface="wl_surface"/>
- <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
- <arg name="flags" type="uint"/>
- </request>
-
- <event name="ping">
- <description summary="check if the client is alive">
- The ping event asks the client if it's still alive. Pass the
- serial specified in the event back to the compositor by sending
- a "pong" request back with the specified serial.
-
- Compositors can use this to determine if the client is still
- alive. It's unspecified what will happen if the client doesn't
- respond to the ping request, or in what timeframe. Clients should
- try to respond in a reasonable amount of time.
- </description>
- <arg name="serial" type="uint" summary="pass this to the callback"/>
- </event>
-
- <request name="pong">
- <description summary="respond to a ping event">
- A client must respond to a ping event with a pong request or
- the client may be deemed unresponsive.
- </description>
- <arg name="serial" type="uint" summary="serial of the ping event"/>
- </request>
- </interface>
-
- <interface name="xdg_surface" version="1">
-
- <description summary="desktop-style metadata interface">
- An interface that may be implemented by a wl_surface, for
- implementations that provide a desktop-style user interface.
-
- It provides requests to treat surfaces like windows, allowing to set
- properties like maximized, fullscreen, minimized, and to move and resize
- them, and associate metadata like title and app id.
-
- On the server side the object is automatically destroyed when
- the related wl_surface is destroyed. On client side,
- xdg_surface.destroy() must be called before destroying
- the wl_surface object.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="remove xdg_surface interface">
- The xdg_surface interface is removed from the wl_surface object
- that was turned into a xdg_surface with
- xdg_shell.get_xdg_surface request. The xdg_surface properties,
- like maximized and fullscreen, are lost. The wl_surface loses
- its role as a xdg_surface. The wl_surface is unmapped.
- </description>
- </request>
-
- <request name="set_parent">
- <description summary="surface is a child of another surface">
- Child surfaces are stacked above their parents, and will be
- unmapped if the parent is unmapped too. They should not appear
- on task bars and alt+tab.
- </description>
- <arg name="parent" type="object" interface="wl_surface" allow-null="true"/>
- </request>
-
- <request name="set_title">
- <description summary="set surface title">
- Set a short title for the surface.
-
- This string may be used to identify the surface in a task bar,
- window list, or other user interface elements provided by the
- compositor.
-
- The string must be encoded in UTF-8.
- </description>
- <arg name="title" type="string"/>
- </request>
-
- <request name="set_app_id">
- <description summary="set surface class">
- Set an id for the surface.
-
- The app id identifies the general class of applications to which
- the surface belongs.
-
- It should be the ID that appears in the new desktop entry
- specification, the interface name.
- </description>
- <arg name="app_id" type="string"/>
- </request>
-
- <request name="show_window_menu">
- <description summary="show the window menu">
- Clients implementing client-side decorations might want to show
- a context menu when right-clicking on the decorations, giving the
- user a menu that they can use to maximize or minimize the window.
-
- This request asks the compositor to pop up such a window menu at
- the given position, relative to the parent surface. There are
- no guarantees as to what the window menu contains.
-
- Your surface must have focus on the seat passed in to pop up the
- window menu.
- </description>
-
- <arg name="seat" type="object" interface="wl_seat" summary="the seat to pop the window up on"/>
- <arg name="serial" type="uint" summary="serial of the event to pop up the window for"/>
- <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
- <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
- </request>
-
- <request name="move">
- <description summary="start an interactive move">
- Start a pointer-driven move of the surface.
-
- This request must be used in response to a button press event.
- The server may ignore move requests depending on the state of
- the surface (e.g. fullscreen or maximized).
- </description>
- <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
- </request>
-
- <enum name="resize_edge">
- <description summary="edge values for resizing">
- These values are used to indicate which edge of a surface
- is being dragged in a resize operation. The server may
- use this information to adapt its behavior, e.g. choose
- an appropriate cursor image.
- </description>
- <entry name="none" value="0"/>
- <entry name="top" value="1"/>
- <entry name="bottom" value="2"/>
- <entry name="left" value="4"/>
- <entry name="top_left" value="5"/>
- <entry name="bottom_left" value="6"/>
- <entry name="right" value="8"/>
- <entry name="top_right" value="9"/>
- <entry name="bottom_right" value="10"/>
- </enum>
-
- <request name="resize">
- <description summary="start an interactive resize">
- Start a pointer-driven resizing of the surface.
-
- This request must be used in response to a button press event.
- The server may ignore resize requests depending on the state of
- the surface (e.g. fullscreen or maximized).
- </description>
- <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
- <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
- </request>
-
- <enum name="state">
- <description summary="types of state on the surface">
- The different state values used on the surface. This is designed for
- state values like maximized, fullscreen. It is paired with the
- configure event to ensure that both the client and the compositor
- setting the state can be synchronized.
-
- States set in this way are double-buffered. They will get applied on
- the next commit.
-
- Desktop environments may extend this enum by taking up a range of
- values and documenting the range they chose in this description.
- They are not required to document the values for the range that they
- chose. Ideally, any good extensions from a desktop environment should
- make its way into standardization into this enum.
-
- The current reserved ranges are:
-
- 0x0000 - 0x0FFF: xdg-shell core values, documented below.
- 0x1000 - 0x1FFF: GNOME
- </description>
- <entry name="maximized" value="1">
- <description summary="the surface is maximized">
- The surface is maximized. The window geometry specified in the configure
- event must be obeyed by the client.
- </description>
- </entry>
- <entry name="fullscreen" value="2">
- <description summary="the surface is fullscreen">
- The surface is fullscreen. The window geometry specified in the configure
- event must be obeyed by the client.
- </description>
- </entry>
- <entry name="resizing" value="3">
- <description summary="the surface is being resized">
- The surface is being resized. The window geometry specified in the
- configure event is a maximum; the client cannot resize beyond it.
- Clients that have aspect ratio or cell sizing configuration can use
- a smaller size, however.
- </description>
- </entry>
- <entry name="activated" value="4">
- <description summary="the client window is active">
- Client window decorations should be painted as if the window is
- active. Do not assume this means that the window actually has
- keyboard or pointer focus.
- </description>
- </entry>
- </enum>
-
- <event name="configure">
- <description summary="suggest a surface change">
- The configure event asks the client to resize its surface.
-
- The width and height arguments specify a hint to the window
- about how its surface should be resized in window geometry
- coordinates. The states listed in the event specify how the
- width/height arguments should be interpreted.
-
- A client should arrange a new surface, and then send a
- ack_configure request with the serial sent in this configure
- event before attaching a new surface.
-
- If the client receives multiple configure events before it
- can respond to one, it is free to discard all but the last
- event it received.
- </description>
-
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
- <arg name="states" type="array"/>
- <arg name="serial" type="uint"/>
- </event>
-
- <request name="ack_configure">
- <description summary="ack a configure event">
- When a configure event is received, a client should then ack it
- using the ack_configure request to ensure that the compositor
- knows the client has seen the event.
-
- By this point, the state is confirmed, and the next attach should
- contain the buffer drawn for the configure event you are acking.
- </description>
- <arg name="serial" type="uint" summary="a serial to configure for"/>
- </request>
-
- <request name="set_window_geometry">
- <description summary="set the new window geometry">
- The window geometry of a window is its "visible bounds" from the
- user's perspective. Client-side decorations often have invisible
- portions like drop-shadows which should be ignored for the
- purposes of aligning, placing and constraining windows.
-
- The default value is the full bounds of the surface, including any
- subsurfaces. Once the window geometry of the surface is set once,
- it is not possible to unset it, and it will remain the same until
- set_window_geometry is called again, even if a new subsurface or
- buffer is attached.
-
- If responding to a configure event, the window geometry in here
- must respect the sizing negotiations specified by the states in
- the configure event.
- </description>
- <arg name="x" type="int"/>
- <arg name="y" type="int"/>
- <arg name="width" type="int"/>
- <arg name="height" type="int"/>
- </request>
-
- <request name="set_maximized" />
- <request name="unset_maximized" />
-
- <request name="set_fullscreen">
- <description summary="set the window as fullscreen on a monitor">
- Make the surface fullscreen.
-
- You can specify an output that you would prefer to be fullscreen.
- If this value is NULL, it's up to the compositor to choose which
- display will be used to map this surface.
- </description>
- <arg name="output" type="object" interface="wl_output" allow-null="true"/>
- </request>
- <request name="unset_fullscreen" />
-
- <request name="set_minimized" />
-
- <event name="close">
- <description summary="surface wants to be closed">
- The close event is sent by the compositor when the user
- wants the surface to be closed. This should be equivalent to
- the user clicking the close button in client-side decorations,
- if your application has any...
-
- This is only a request that the user intends to close your
- window. The client may choose to ignore this request, or show
- a dialog to ask the user to save their data...
- </description>
- </event>
- </interface>
-
- <interface name="xdg_popup" version="1">
- <description summary="desktop-style metadata interface">
- An interface that may be implemented by a wl_surface, for
- implementations that provide a desktop-style popups/menus. A popup
- surface is a transient surface with an added pointer grab.
-
- An existing implicit grab will be changed to owner-events mode,
- and the popup grab will continue after the implicit grab ends
- (i.e. releasing the mouse button does not cause the popup to be
- unmapped).
-
- The popup grab continues until the window is destroyed or a mouse
- button is pressed in any other clients window. A click in any of
- the clients surfaces is reported as normal, however, clicks in
- other clients surfaces will be discarded and trigger the callback.
-
- The x and y arguments specify the locations of the upper left
- corner of the surface relative to the upper left corner of the
- parent surface, in surface local coordinates.
-
- xdg_popup surfaces are always transient for another surface.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="remove xdg_surface interface">
- The xdg_surface interface is removed from the wl_surface object
- that was turned into a xdg_surface with
- xdg_shell.get_xdg_surface request. The xdg_surface properties,
- like maximized and fullscreen, are lost. The wl_surface loses
- its role as a xdg_surface. The wl_surface is unmapped.
- </description>
- </request>
-
- <event name="popup_done">
- <description summary="popup interaction is done">
- The popup_done event is sent out when a popup grab is broken,
- that is, when the users clicks a surface that doesn't belong
- to the client owning the popup surface.
- </description>
- <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
- </event>
-
- </interface>
-</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 @@
# installation.
#
# DO NOT CHANGE THIS FILE. Override these defaults by writing new files in
-# __SYSCONFDIR__/sway/security.d/*
+# @sysconfdir@/sway/security.d/*
# Configures enabled compositor features for specific programs
permit * fullscreen keyboard mouse
-permit __PREFIX__/bin/swaylock lock
-permit __PREFIX__/bin/swaybg background
-permit __PREFIX__/bin/swaygrab screenshot
-permit __PREFIX__/bin/swaybar panel
+permit @prefix@/bin/swaylock lock
+permit @prefix@/bin/swaybg background
+permit @prefix@/bin/swaygrab screenshot
+permit @prefix@/bin/swaybar panel
# Configures enabled IPC features for specific programs
-ipc __PREFIX__/bin/swaymsg {
+ipc @prefix@/bin/swaymsg {
* enabled
events {
@@ -24,7 +24,7 @@ ipc __PREFIX__/bin/swaymsg {
}
}
-ipc __PREFIX__/bin/swaybar {
+ipc @prefix@/bin/swaybar {
bar-config enabled
outputs enabled
workspaces enabled
@@ -36,12 +36,12 @@ ipc __PREFIX__/bin/swaybar {
}
}
-ipc __PREFIX__/bin/swaygrab {
+ipc @prefix@/bin/swaygrab {
outputs enabled
tree enabled
}
-ipc __PREFIX__/bin/swaylock {
+ipc @prefix@/bin/swaylock {
outputs enabled
}
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 @@
-include_directories(
- ${PROTOCOLS_INCLUDE_DIRS}
- ${WLC_INCLUDE_DIRS}
- ${PCRE_INCLUDE_DIRS}
- ${JSONC_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
- ${LIBINPUT_INCLUDE_DIRS}
- ${CAIRO_INCLUDE_DIRS}
- ${PANGO_INCLUDE_DIRS}
- ${WAYLAND_INCLUDE_DIR}
-)
-
-file(GLOB cmds
- "commands/*.c"
- "commands/bar/*.c"
- "commands/input/*.c"
-)
-
-add_executable(sway
- commands.c
- ${cmds}
- base64.c
- config.c
- container.c
- criteria.c
- debug_log.c
- extensions.c
- focus.c
- handlers.c
- input.c
- input_state.c
- ipc-json.c
- ipc-server.c
- layout.c
- main.c
- output.c
- workspace.c
- border.c
- security.c
-)
-
-add_definitions(
- -DSYSCONFDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}"
-)
-
-target_link_libraries(sway
- sway-common
- sway-protocols
- sway-wayland
- ${WLC_LIBRARIES}
- ${XKBCOMMON_LIBRARIES}
- ${PCRE_LIBRARIES}
- ${JSONC_LIBRARIES}
- ${WAYLAND_SERVER_LIBRARIES}
- ${LIBINPUT_LIBRARIES}
- ${PANGO_LIBRARIES}
- ${JSONC_LIBRARIES}
- m
-)
-
-if (CMAKE_SYSTEM_NAME STREQUAL Linux)
- target_link_libraries(sway cap)
-endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
-
-install(
- TARGETS sway
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
-)
-
-add_custom_target(configs ALL)
-
-function(add_config name source destination)
- add_custom_command(
- OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
- COMMAND sed -r
- 's?__PREFIX__?${CMAKE_INSTALL_PREFIX}?g\; s?__SYSCONFDIR__?${CMAKE_INSTALL_FULL_SYSCONFDIR}?g\; s?__DATADIR__?${CMAKE_INSTALL_FULL_DATADIR}?g'
- ${PROJECT_SOURCE_DIR}/${source}.in > ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
- DEPENDS ${PROJECT_SOURCE_DIR}/${source}.in
- COMMENT "Generating config file ${source}"
- )
-
- install(
- FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}
- DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/${destination}
- COMPONENT configuration
- )
-
- add_custom_target(config-${name} DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name})
- add_dependencies(configs config-${name})
-endfunction()
-
-add_config(config config sway)
-add_config(00-defaults security.d/00-defaults sway/security.d)
-
-if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
- add_config(10-freebsd security.d/10-freebsd sway/security.d)
-endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
-
-if (A2X_FOUND)
- add_manpage(sway 1)
- add_manpage(sway 5)
- add_manpage(sway-input 5)
- add_manpage(sway-bar 5)
- add_manpage(sway-security 7)
-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 @@
-#define _XOPEN_SOURCE 700
-#include <wlc/wlc-render.h>
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <strings.h>
-#include <arpa/inet.h>
-#include "sway/border.h"
-#include "sway/container.h"
-#include "sway/config.h"
-#include "client/pango.h"
-
-void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
- color = htonl(color);
-
- cairo_set_source_rgba(cairo,
- (color >> (2*8) & 0xFF) / 255.0,
- (color >> (1*8) & 0xFF) / 255.0,
- (color >> (0*8) & 0xFF) / 255.0,
- (color >> (3*8) & 0xFF) / 255.0);
-}
-
-void border_clear(struct border *border) {
- if (border && border->buffer) {
- free(border->buffer);
- border->buffer = NULL;
- }
-}
-
-static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry g, cairo_surface_t **surface) {
- if (view->border == NULL) {
- view->border = malloc(sizeof(struct border));
- if (!view->border) {
- sway_log(L_ERROR, "Unable to allocate window border information");
- return NULL;
- }
- }
- cairo_t *cr;
- int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, g.size.w);
- view->border->buffer = calloc(stride * g.size.h, sizeof(unsigned char));
- view->border->geometry = g;
- if (!view->border->buffer) {
- sway_log(L_ERROR, "Unable to allocate window border buffer");
- return NULL;
- }
- *surface = cairo_image_surface_create_for_data(view->border->buffer,
- CAIRO_FORMAT_ARGB32, g.size.w, g.size.h, stride);
- if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) {
- border_clear(view->border);
- sway_log(L_ERROR, "Unable to allocate window border surface");
- return NULL;
- }
- cr = cairo_create(*surface);
- cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
- if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
- cairo_surface_destroy(*surface);
- border_clear(view->border);
- sway_log(L_ERROR, "Unable to create cairo context");
- return NULL;
- }
- return cr;
-}
-
-// TODO: move to client/cairo.h when local set_source_u32 is fixed.
-/**
- * Renders a sharp line of any width and height.
- *
- * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
- * if the line has a width/height of one pixel, respectively.
- */
-static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
- cairo_set_source_u32(cairo, color);
-
- if (width > 1 && height > 1) {
- cairo_rectangle(cairo, x, y, width, height);
- cairo_fill(cairo);
- } else {
- if (width == 1) {
- x += 0.5;
- height += y;
- width = x;
- }
-
- if (height == 1) {
- y += 0.5;
- width += x;
- height = y;
- }
-
- cairo_move_to(cairo, x, y);
- cairo_set_line_width(cairo, 1.0);
- cairo_line_to(cairo, width, height);
- cairo_stroke(cairo);
- }
-}
-
-int get_font_text_height(const char *font) {
- cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
- cairo_t *cr = cairo_create(surface);
- int width, height;
- get_text_size(cr, font, &width, &height, 1, false, "Gg");
- cairo_surface_destroy(surface);
- cairo_destroy(cr);
- return height;
-}
-
-static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors, bool top) {
- struct wlc_geometry *g = &view->border->geometry;
- struct wlc_geometry *b = &view->border_geometry;
- struct wlc_geometry *v = &view->actual_geometry;
- enum swayc_layouts layout = view->parent->layout;
- uint32_t color;
-
- int x = b->origin.x - g->origin.x;
- int y = b->origin.y - g->origin.y;
-
- // draw vertical/horizontal indicator if container is the only child of its parent container
- bool is_only_child = view->parent && view->parent->children && view->parent->children->length == 1;
-
- // left border
- int left_border = v->origin.x - b->origin.x;
- if (left_border > 0) {
- render_sharp_line(cr,
- colors->child_border,
- x, y,
- left_border,
- b->size.h);
- }
-
- // right border
- int right_border = b->size.w - v->size.w - left_border;
- if (right_border > 0) {
- if (is_only_child && layout == L_HORIZ && !view->is_floating) {
- color = colors->indicator;
- } else {
- color = colors->child_border;
- }
- render_sharp_line(cr,
- color,
- x + b->size.w - right_border,
- y,
- right_border,
- b->size.h);
- }
-
- // top border
- int top_border = v->origin.y - b->origin.y;
- if (top && top_border > 0) {
- render_sharp_line(cr,
- colors->child_border,
- x, y,
- b->size.w,
- top_border);
- }
-
- // bottom border
- int bottom_border = b->size.h - (top_border + v->size.h);
- if (bottom_border > 0) {
- if (is_only_child && layout == L_VERT && !view->is_floating) {
- color = colors->indicator;
- } else {
- color = colors->child_border;
- }
- render_sharp_line(cr,
- color,
- x,
- y + b->size.h - bottom_border,
- b->size.w,
- bottom_border);
- }
-}
-
-static void render_title_bar(swayc_t *view, cairo_t *cr, struct wlc_geometry *b, struct border_colors *colors) {
- struct wlc_geometry *tb = &view->title_bar_geometry;
- int x = MIN(tb->origin.x, tb->origin.x - b->origin.x);
- int y = MIN(tb->origin.y, tb->origin.y - b->origin.y);
-
- // title bar background
- cairo_set_source_u32(cr, colors->background);
- cairo_rectangle(cr, x, y, tb->size.w, tb->size.h);
- cairo_fill(cr);
-
- // header top line
- render_sharp_line(cr, colors->border, x, y, tb->size.w, 1);
-
- // text
- if (view->name) {
- int width, height;
- get_text_size(cr, config->font, &width, &height, 1, false, "%s", view->name);
- cairo_move_to(cr, x + 2, y + 2);
- cairo_set_source_u32(cr, colors->text);
- pango_printf(cr, config->font, 1, false, "%s", view->name);
- }
- // Marks
- if (config->show_marks && view->marks) {
- int total_len = 0;
-
- for(int i = view->marks->length - 1; i >= 0; --i) {
- char *mark = (char *)view->marks->items[i];
- if (*mark != '_') {
- int width, height;
- get_text_size(cr, config->font, &width, &height, 1, false, "[%s]", mark);
- total_len += width;
- if ((int)tb->size.w + x - (total_len + 2) < x + 2) {
- break;
- } else {
- cairo_move_to(cr, (int)tb->size.w + x - (total_len + 2), y + 2);
- cairo_set_source_u32(cr, colors->text);
- pango_printf(cr, config->font, 1, false, "[%s]", mark);
- }
- }
- }
- }
-
- // titlebars has a border all around for tabbed layouts
- if (view->parent->layout == L_TABBED) {
- // header bottom line
- render_sharp_line(cr, colors->border, x, y + tb->size.h - 1,
- tb->size.w, 1);
-
- // left border
- render_sharp_line(cr, colors->border, x, y, 1, tb->size.h);
-
- // right border
- render_sharp_line(cr, colors->border, x + tb->size.w - 1, y,
- 1, tb->size.h);
-
- return;
- }
-
- if ((uint32_t)(view->actual_geometry.origin.y - tb->origin.y) == tb->size.h) {
- // header bottom line
- render_sharp_line(cr, colors->border,
- x + view->actual_geometry.origin.x - tb->origin.x,
- y + tb->size.h - 1,
- view->actual_geometry.size.w, 1);
- } else {
- // header bottom line
- render_sharp_line(cr, colors->border, x,
- y + tb->size.h - 1,
- tb->size.w, 1);
- }
-}
-
-/**
- * Generate nested container title for tabbed/stacked layouts
- */
-static char *generate_container_title(swayc_t *container) {
- char layout = 'H';
- char *name, *prev_name = NULL;
- switch (container->layout) {
- case L_TABBED:
- layout = 'T';
- break;
- case L_STACKED:
- layout = 'S';
- break;
- case L_VERT:
- layout = 'V';
- break;
- default:
- layout = 'H';
- }
- int len = 9;
- name = malloc(len * sizeof(char));
- if (!name) {
- sway_log(L_ERROR, "Unable to allocate container title");
- return NULL;
- }
- snprintf(name, len, "sway: %c[", layout);
-
- int i;
- for (i = 0; i < container->children->length; ++i) {
- prev_name = name;
- swayc_t* child = container->children->items[i];
- const char *title = NULL;
- if (child->type == C_VIEW) {
- title = child->app_id ? child->app_id :
- (child->instance ? child->instance :
- (child->class ? child->class :"(null)"));
- } else { //child->type == C_CONTAINER
- title = generate_container_title(child);
- }
-
- len = strlen(name) + strlen(title) + 1;
- if (i < container->children->length-1) {
- len++;
- }
-
- name = malloc(len * sizeof(char));
- if (!name) {
- free(prev_name);
- sway_log(L_ERROR, "Unable to allocate container title");
- return NULL;
- }
- if (i < container->children->length-1) {
- snprintf(name, len, "%s%s ", prev_name, title);
- } else {
- snprintf(name, len, "%s%s", prev_name, title);
- }
- free(prev_name);
- }
-
- prev_name = name;
- len = strlen(name) + 2;
- name = malloc(len * sizeof(char));
- if (!name) {
- free(prev_name);
- sway_log(L_ERROR, "Unable to allocate container title");
- return NULL;
- }
- snprintf(name, len, "%s]", prev_name);
- free(prev_name);
- free(container->name);
- container->name = name;
- return container->name + 6; // don't include "sway: "
-}
-
-void update_tabbed_stacked_titlebars(swayc_t *c, cairo_t *cr, struct wlc_geometry *g, swayc_t *focused, swayc_t *focused_inactive) {
- if (c->type == C_CONTAINER) {
- if (c->parent->focused == c) {
- render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
- } else {
- render_title_bar(c, cr, g, &config->border_colors.unfocused);
- }
-
- if (!c->visible) {
- return;
- }
-
- int i;
- for (i = 0; i < c->children->length; ++i) {
- swayc_t *child = c->children->items[i];
- update_tabbed_stacked_titlebars(child, cr, g, focused, focused_inactive);
- }
- } else {
- bool is_child_of_focused = swayc_is_child_of(c, get_focused_container(&root_container));
-
- if (focused == c || is_child_of_focused) {
- render_title_bar(c, cr, g, &config->border_colors.focused);
- } else if (focused_inactive == c) {
- render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
- } else {
- render_title_bar(c, cr, g, &config->border_colors.unfocused);
- }
- }
-}
-
-static void update_view_border(swayc_t *view) {
- if (!view->visible) {
- return;
- }
-
- cairo_t *cr = NULL;
- cairo_surface_t *surface = NULL;
-
- // clear previous border buffer.
- border_clear(view->border);
-
- // get focused and focused_inactive views
- swayc_t *focused = get_focused_view(&root_container);
- swayc_t *container = swayc_parent_by_type(view, C_CONTAINER);
- swayc_t *focused_inactive = NULL;
-
- bool is_child_of_focused = swayc_is_parent_of(get_focused_container(&root_container), view);
-
- if (container) {
- focused_inactive = swayc_focus_by_type(container, C_VIEW);
- } else {
- container = swayc_parent_by_type(view, C_WORKSPACE);
- if (container) {
- focused_inactive = swayc_focus_by_type(container, C_VIEW);
- }
- }
-
- // for tabbed/stacked layouts the focused view has to draw all the
- // titlebars of the hidden views.
- swayc_t *p = NULL;
- if (view->parent->focused == view && (p = swayc_tabbed_stacked_ancestor(view))) {
- struct wlc_geometry g = {
- .origin = {
- .x = p->x,
- .y = p->y
- },
- .size = {
- .w = p->width,
- .h = p->height
- }
- };
- cr = create_border_buffer(view, g, &surface);
- if (!cr) {
- goto cleanup;
- }
-
- bool render_top = !should_hide_top_border(view, view->y);
- if (view == focused || is_child_of_focused) {
- render_borders(view, cr, &config->border_colors.focused, render_top);
- } else {
- render_borders(view, cr, &config->border_colors.focused_inactive, render_top);
- }
-
- // generate container titles
- int i;
- for (i = 0; i < p->children->length; ++i) {
- swayc_t *child = p->children->items[i];
- if (child->type == C_CONTAINER) {
- generate_container_title(child);
- }
- }
-
- update_tabbed_stacked_titlebars(p, cr, &g, focused, focused_inactive);
- } else {
- switch (view->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- cr = create_border_buffer(view, view->border_geometry, &surface);
- if (!cr) {
- break;
- }
-
- if (focused == view || is_child_of_focused) {
- render_borders(view, cr, &config->border_colors.focused, true);
- } else if (focused_inactive == view) {
- render_borders(view, cr, &config->border_colors.focused_inactive, true);
- } else {
- render_borders(view, cr, &config->border_colors.unfocused, true);
- }
-
- break;
- case B_NORMAL:
- cr = create_border_buffer(view, view->border_geometry, &surface);
- if (!cr) {
- break;
- }
-
- if (focused == view || is_child_of_focused) {
- render_borders(view, cr, &config->border_colors.focused, false);
- render_title_bar(view, cr, &view->border_geometry,
- &config->border_colors.focused);
- } else if (focused_inactive == view) {
- render_borders(view, cr, &config->border_colors.focused_inactive, false);
- render_title_bar(view, cr, &view->border_geometry,
- &config->border_colors.focused_inactive);
- } else {
- render_borders(view, cr, &config->border_colors.unfocused, false);
- render_title_bar(view, cr, &view->border_geometry,
- &config->border_colors.unfocused);
- }
-
- break;
- }
- }
-
-cleanup:
-
- if (surface) {
- cairo_surface_flush(surface);
- cairo_surface_destroy(surface);
- }
-
- if (cr) {
- cairo_destroy(cr);
- }
-}
-
-void update_container_border(swayc_t *container) {
- if (container->type == C_VIEW) {
- update_view_border(container);
- return;
- } else {
- for (int i = 0; i < container->children->length; ++i) {
- update_container_border(container->children->items[i]);
- }
- }
-}
-
-void render_view_borders(wlc_handle view) {
- swayc_t *c = swayc_by_handle(view);
-
-
- // emulate i3 behavior for drawing borders for tabbed and stacked layouts:
- // if we are not the only child in the container, always draw borders,
- // regardless of the border setting on the individual view
- if (!c || (c->border_type == B_NONE
- && !((c->parent->layout == L_TABBED || c->parent->layout == L_STACKED)
- && c->parent->children->length > 1))) {
- return;
- }
-
- if (c->border && c->border->buffer) {
- wlc_pixels_write(WLC_RGBA8888, &c->border->geometry, c->border->buffer);
- }
-}
-
-bool should_hide_top_border(swayc_t *con, double y) {
- // returns true if container is child of tabbed/stacked layout and is
- // sharing top border with tabbed titlebar
- swayc_t *par = con->parent;
- while (par->type != C_WORKSPACE) {
- if (par->layout == L_TABBED || par->layout == L_STACKED) {
- return con->y == y;
- }
- con = par;
- par = par->parent;
- }
- return false;
-}
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 @@
-#define _XOPEN_SOURCE 700
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-names.h>
-#include <wlc/wlc.h>
-#include <wlc/wlc-render.h>
-#include <stdio.h>
+#define _XOPEN_SOURCE 500
+#include <ctype.h>
+#include <stdarg.h>
#include <stdlib.h>
-#include <errno.h>
#include <string.h>
#include <strings.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <wordexp.h>
-#include <libgen.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <limits.h>
-#include <float.h>
-#include <libinput.h>
-#include "sway/layout.h"
-#include "sway/focus.h"
-#include "sway/workspace.h"
+#include <stdio.h>
+#include <json-c/json.h>
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/output.h"
-#include "sway/handlers.h"
-#include "sway/input_state.h"
+#include "sway/config.h"
#include "sway/criteria.h"
-#include "sway/ipc-server.h"
#include "sway/security.h"
-#include "sway/input.h"
-#include "sway/border.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "stringop.h"
-#include "sway.h"
-#include "util.h"
-#include "list.h"
#include "log.h"
struct cmd_handler {
@@ -41,10 +20,6 @@ struct cmd_handler {
sway_cmd *handle;
};
-int sp_index = 0;
-
-swayc_t *current_container = NULL;
-
// Returns error object, or NULL if check succeeds.
struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
struct cmd_results *error = NULL;
@@ -84,24 +59,7 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
return error;
}
-void hide_view_in_scratchpad(swayc_t *sp_view) {
- if (sp_view == NULL) {
- return;
- }
-
- wlc_view_set_mask(sp_view->handle, 0);
- sp_view->visible = false;
- swayc_t *ws = sp_view->parent;
- remove_child(sp_view);
- if (swayc_active_workspace() != ws && ws->floating->length == 0 && ws->children->length == 0) {
- destroy_workspace(ws);
- } else {
- arrange_windows(ws, -1, -1);
- }
- set_focused_container(container_under_pointer());
-}
-
-void input_cmd_apply(struct input_config *input) {
+void apply_input_config(struct input_config *input) {
int i;
i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
if (i >= 0) {
@@ -114,111 +72,41 @@ void input_cmd_apply(struct input_config *input) {
list_add(config->input_configs, input);
}
- current_input_config = input;
-
- if (input->identifier) {
- // Try to find the input device and apply configuration now. If
- // this is during startup then there will be no container and config
- // will be applied during normal "new input" event from wlc.
- struct libinput_device *device = NULL;
- for (int i = 0; i < input_devices->length; ++i) {
- device = input_devices->items[i];
- char* dev_identifier = libinput_dev_unique_id(device);
- if (!dev_identifier) {
- break;
- }
- int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0;
- free(dev_identifier);
- if (match) {
- apply_input_config(input, device);
- break;
- }
- }
- }
+ input_manager_apply_input_config(input_manager, input);
}
-void remove_view_from_scratchpad(swayc_t *view) {
+void apply_seat_config(struct seat_config *seat_config) {
int i;
- for (i = 0; i < scratchpad->length; i++) {
- if (scratchpad->items[i] == view) {
- if (sp_index == 0) {
- sp_index = scratchpad->length - 1;
- } else {
- sp_index--;
- }
- list_del(scratchpad, sp_index);
- sp_view = NULL;
- }
+ i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
+ if (i >= 0) {
+ // merge existing config
+ struct seat_config *sc = config->seat_configs->items[i];
+ merge_seat_config(sc, seat_config);
+ free_seat_config(seat_config);
+ seat_config = sc;
+ } else {
+ list_add(config->seat_configs, seat_config);
}
+
+ input_manager_apply_seat_config(input_manager, seat_config);
}
/* Keep alphabetized */
static struct cmd_handler handlers[] = {
- { "assign", cmd_assign },
{ "bar", cmd_bar },
{ "bindcode", cmd_bindcode },
{ "bindsym", cmd_bindsym },
- { "border", cmd_border },
- { "client.background", cmd_client_background },
- { "client.focused", cmd_client_focused },
- { "client.focused_inactive", cmd_client_focused_inactive },
- { "client.placeholder", cmd_client_placeholder },
- { "client.unfocused", cmd_client_unfocused },
- { "client.urgent", cmd_client_urgent },
- { "clipboard", cmd_clipboard },
- { "commands", cmd_commands },
- { "debuglog", cmd_debuglog },
- { "default_border", cmd_default_border },
- { "default_floating_border", cmd_default_floating_border },
- { "default_orientation", cmd_orientation },
{ "exec", cmd_exec },
{ "exec_always", cmd_exec_always },
- { "exit", cmd_exit },
- { "floating", cmd_floating },
- { "floating_maximum_size", cmd_floating_maximum_size },
- { "floating_minimum_size", cmd_floating_minimum_size },
- { "floating_modifier", cmd_floating_mod },
- { "floating_scroll", cmd_floating_scroll },
- { "focus", cmd_focus },
{ "focus_follows_mouse", cmd_focus_follows_mouse },
- { "font", cmd_font },
- { "for_window", cmd_for_window },
- { "force_focus_wrapping", cmd_force_focus_wrapping },
- { "fullscreen", cmd_fullscreen },
- { "gaps", cmd_gaps },
- { "hide_edge_borders", cmd_hide_edge_borders },
{ "include", cmd_include },
{ "input", cmd_input },
- { "ipc", cmd_ipc },
- { "kill", cmd_kill },
- { "layout", cmd_layout },
- { "log_colors", cmd_log_colors },
- { "mark", cmd_mark },
{ "mode", cmd_mode },
{ "mouse_warping", cmd_mouse_warping },
- { "move", cmd_move },
- { "new_float", cmd_new_float },
- { "new_window", cmd_new_window },
- { "no_focus", cmd_no_focus },
{ "output", cmd_output },
- { "permit", cmd_permit },
- { "reject", cmd_reject },
- { "reload", cmd_reload },
- { "resize", cmd_resize },
- { "scratchpad", cmd_scratchpad },
- { "seamless_mouse", cmd_seamless_mouse },
- { "set", cmd_set },
- { "show_marks", cmd_show_marks },
- { "smart_gaps", cmd_smart_gaps },
- { "split", cmd_split },
- { "splith", cmd_splith },
- { "splitt", cmd_splitt },
- { "splitv", cmd_splitv },
- { "sticky", cmd_sticky },
- { "unmark", cmd_unmark },
+ { "seat", cmd_seat },
{ "workspace", cmd_workspace },
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
- { "workspace_layout", cmd_workspace_layout },
};
static struct cmd_handler bar_handlers[] = {
@@ -248,54 +136,6 @@ static struct cmd_handler bar_handlers[] = {
{ "wrap_scroll", bar_cmd_wrap_scroll },
};
-/**
- * Check and add color to buffer.
- *
- * return error object, or NULL if color is valid.
- */
-struct cmd_results *add_color(const char *name, char *buffer, const char *color) {
- int len = strlen(color);
- if (len != 7 && len != 9) {
- return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
- }
-
- if (color[0] != '#') {
- return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
- }
-
- int i;
- for (i = 1; i < len; ++i) {
- if (!isxdigit(color[i])) {
- return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
- }
- }
-
- // copy color to buffer
- strncpy(buffer, color, len);
- // add default alpha channel if color was defined without it
- if (len == 7) {
- buffer[7] = 'f';
- buffer[8] = 'f';
- }
- buffer[9] = '\0';
-
- return NULL;
-}
-
-static struct cmd_handler input_handlers[] = {
- { "accel_profile", input_cmd_accel_profile },
- { "click_method", input_cmd_click_method },
- { "drag_lock", input_cmd_drag_lock },
- { "dwt", input_cmd_dwt },
- { "events", input_cmd_events },
- { "left_handed", input_cmd_left_handed },
- { "middle_emulation", input_cmd_middle_emulation },
- { "natural_scroll", input_cmd_natural_scroll },
- { "pointer_accel", input_cmd_pointer_accel },
- { "scroll_method", input_cmd_scroll_method },
- { "tap", input_cmd_tap },
-};
-
static struct cmd_handler bar_colors_handlers[] = {
{ "active_workspace", bar_colors_cmd_active_workspace },
{ "background", bar_colors_cmd_background },
@@ -310,26 +150,27 @@ static struct cmd_handler bar_colors_handlers[] = {
{ "urgent_workspace", bar_colors_cmd_urgent_workspace },
};
-static struct cmd_handler ipc_handlers[] = {
- { "*", cmd_ipc_cmd },
- { "bar-config", cmd_ipc_cmd },
- { "command", cmd_ipc_cmd },
- { "events", cmd_ipc_events },
- { "inputs", cmd_ipc_cmd },
- { "marks", cmd_ipc_cmd },
- { "outputs", cmd_ipc_cmd },
- { "tree", cmd_ipc_cmd },
- { "workspaces", cmd_ipc_cmd },
+/* Config-time only commands. Keep alphabetized */
+static struct cmd_handler config_handlers[] = {
+ { "default_orientation", cmd_default_orientation },
+ { "set", cmd_set },
+ { "swaybg_command", cmd_swaybg_command },
};
-static struct cmd_handler ipc_event_handlers[] = {
- { "*", cmd_ipc_event_cmd },
- { "binding", cmd_ipc_event_cmd },
- { "input", cmd_ipc_event_cmd },
- { "mode", cmd_ipc_event_cmd },
- { "output", cmd_ipc_event_cmd },
- { "window", cmd_ipc_event_cmd },
- { "workspace", cmd_ipc_event_cmd },
+/* Runtime-only commands. Keep alphabetized */
+static struct cmd_handler command_handlers[] = {
+ { "exit", cmd_exit },
+ { "focus", cmd_focus },
+ { "kill", cmd_kill },
+ { "layout", cmd_layout },
+ { "move", cmd_move },
+ { "opacity", cmd_opacity },
+ { "reload", cmd_reload },
+ { "resize", cmd_resize },
+ { "split", cmd_split },
+ { "splith", cmd_splith },
+ { "splitt", cmd_splitt },
+ { "splitv", cmd_splitv },
};
static int handler_compare(const void *_a, const void *_b) {
@@ -338,42 +179,90 @@ static int handler_compare(const void *_a, const void *_b) {
return strcasecmp(a->command, b->command);
}
+// must be in order for the bsearch
+static struct cmd_handler input_handlers[] = {
+ { "accel_profile", input_cmd_accel_profile },
+ { "click_method", input_cmd_click_method },
+ { "drag_lock", input_cmd_drag_lock },
+ { "dwt", input_cmd_dwt },
+ { "events", input_cmd_events },
+ { "left_handed", input_cmd_left_handed },
+ { "map_to_output", input_cmd_map_to_output },
+ { "middle_emulation", input_cmd_middle_emulation },
+ { "natural_scroll", input_cmd_natural_scroll },
+ { "pointer_accel", input_cmd_pointer_accel },
+ { "scroll_method", input_cmd_scroll_method },
+ { "tap", input_cmd_tap },
+ { "xkb_layout", input_cmd_xkb_layout },
+ { "xkb_model", input_cmd_xkb_model },
+ { "xkb_options", input_cmd_xkb_options },
+ { "xkb_rules", input_cmd_xkb_rules },
+ { "xkb_variant", input_cmd_xkb_variant },
+};
+
+// must be in order for the bsearch
+static struct cmd_handler seat_handlers[] = {
+ { "attach", seat_cmd_attach },
+ { "cursor", seat_cmd_cursor },
+ { "fallback", seat_cmd_fallback },
+};
+
static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
struct cmd_handler d = { .command=line };
struct cmd_handler *res = NULL;
- sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT);
+ wlr_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT);
+
+ bool config_loading = config->reading || !config->active;
+
if (block == CMD_BLOCK_BAR) {
- res = bsearch(&d, bar_handlers,
- sizeof(bar_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else if (block == CMD_BLOCK_BAR_COLORS){
- res = bsearch(&d, bar_colors_handlers,
- sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
+ return bsearch(&d, bar_handlers,
+ sizeof(bar_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+ } else if (block == CMD_BLOCK_BAR_COLORS) {
+ return bsearch(&d, bar_colors_handlers,
+ sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
} else if (block == CMD_BLOCK_INPUT) {
- res = bsearch(&d, input_handlers,
- sizeof(input_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else if (block == CMD_BLOCK_IPC) {
- res = bsearch(&d, ipc_handlers,
- sizeof(ipc_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else if (block == CMD_BLOCK_IPC_EVENTS) {
- res = bsearch(&d, ipc_event_handlers,
- sizeof(ipc_event_handlers) / sizeof(struct cmd_handler),
- sizeof(struct cmd_handler), handler_compare);
- } else {
- res = bsearch(&d, handlers,
+ return bsearch(&d, input_handlers,
+ sizeof(input_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+ } else if (block == CMD_BLOCK_SEAT) {
+ return bsearch(&d, seat_handlers,
+ sizeof(seat_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+ }
+
+ if (!config_loading) {
+ res = bsearch(&d, command_handlers,
+ sizeof(command_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+
+ if (res) {
+ return res;
+ }
+ }
+
+ if (config->reading) {
+ res = bsearch(&d, config_handlers,
+ sizeof(config_handlers) / sizeof(struct cmd_handler),
+ sizeof(struct cmd_handler), handler_compare);
+
+ if (res) {
+ return res;
+ }
+ }
+
+ res = bsearch(&d, handlers,
sizeof(handlers) / sizeof(struct cmd_handler),
sizeof(struct cmd_handler), handler_compare);
- }
+
return res;
}
-struct cmd_results *handle_command(char *_exec, enum command_context context) {
+struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
// Even though this function will process multiple commands we will only
// return the last error, if any (for now). (Since we have access to an
- // error string we could e.g. concatonate all errors there.)
+ // error string we could e.g. concatenate all errors there.)
struct cmd_results *results = NULL;
char *exec = strdup(_exec);
char *head = exec;
@@ -381,10 +270,22 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
char *cmd;
list_t *containers = NULL;
+ if (seat == NULL) {
+ // passing a NULL seat means we just pick the default seat
+ seat = input_manager_get_default_seat(input_manager);
+ if (!sway_assert(seat, "could not find a seat to run the command on")) {
+ return NULL;
+ }
+ }
+
+ config->handler_context.seat = seat;
+
head = exec;
do {
// Extract criteria (valid for this command list only).
+ bool has_criteria = false;
if (*head == '[') {
+ has_criteria = true;
++head;
char *criteria_string = argsep(&head, "]");
if (head) {
@@ -393,13 +294,14 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
char *error;
if ((error = extract_crit_tokens(tokens, criteria_string))) {
+ wlr_log(L_DEBUG, "criteria string parse error: %s", error);
results = cmd_results_new(CMD_INVALID, criteria_string,
"Can't parse criteria string: %s", error);
free(error);
free(tokens);
goto cleanup;
}
- containers = container_for(tokens);
+ containers = container_for_crit_tokens(tokens);
free(tokens);
} else {
@@ -419,10 +321,10 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
cmd = argsep(&cmdlist, ",");
cmd += strspn(cmd, whitespace);
if (strcmp(cmd, "") == 0) {
- sway_log(L_INFO, "Ignoring empty command.");
+ wlr_log(L_INFO, "Ignoring empty command.");
continue;
}
- sway_log(L_INFO, "Handling command '%s'", cmd);
+ wlr_log(L_INFO, "Handling command '%s'", cmd);
//TODO better handling of argv
int argc;
char **argv = split_args(cmd, &argc);
@@ -443,31 +345,16 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
free_argv(argc, argv);
goto cleanup;
}
- if (!(get_command_policy_mask(argv[0]) & context)) {
- if (results) {
- free_cmd_results(results);
- }
- results = cmd_results_new(CMD_INVALID, cmd,
- "Permission denied for %s via %s", cmd,
- command_policy_str(context));
- free_argv(argc, argv);
- goto cleanup;
- }
- int i = 0;
- do {
- if (!containers) {
- current_container = get_focused_container(&root_container);
- } else if (containers->length == 0) {
- if (results) {
- free_cmd_results(results);
- }
- results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container");
- goto cleanup;
- } else {
- current_container = (swayc_t *)containers->items[i];
- }
- sway_log(L_INFO, "Running on container '%s'", current_container->name);
+ if (!has_criteria) {
+ // without criteria, the command acts upon the focused
+ // container
+ config->handler_context.current_container =
+ seat_get_focus_inactive(seat, &root_container);
+ if (!sway_assert(config->handler_context.current_container,
+ "could not get focus-inactive for root container")) {
+ return NULL;
+ }
struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);
@@ -478,35 +365,39 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
goto cleanup;
}
free_cmd_results(res);
- ++i;
- } while(containers && i < containers->length);
-
+ } else {
+ for (int i = 0; i < containers->length; ++i) {
+ config->handler_context.current_container = containers->items[i];
+ struct cmd_results *res = handler->handle(argc-1, argv+1);
+ if (res->status != CMD_SUCCESS) {
+ free_argv(argc, argv);
+ if (results) {
+ free_cmd_results(results);
+ }
+ results = res;
+ goto cleanup;
+ }
+ free_cmd_results(res);
+ }
+ }
free_argv(argc, argv);
} while(cmdlist);
-
- if (containers) {
- list_free(containers);
- containers = NULL;
- }
} while(head);
- cleanup:
+cleanup:
free(exec);
- if (containers) {
- free(containers);
- }
if (!results) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
return results;
}
-// this is like handle_command above, except:
+// this is like execute_command above, except:
// 1) it ignores empty commands (empty lines)
// 2) it does variable substitution
// 3) it doesn't split commands (because the multiple commands are supposed to
// be chained together)
-// 4) handle_command handles all state internally while config_command has some
-// state handled outside (notably the block mode, in read_config)
+// 4) execute_command handles all state internally while config_command has
+// some state handled outside (notably the block mode, in read_config)
struct cmd_results *config_command(char *exec, enum cmd_status block) {
struct cmd_results *results = NULL;
int argc;
@@ -516,7 +407,7 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
goto cleanup;
}
- sway_log(L_INFO, "handling config command '%s'", exec);
+ wlr_log(L_INFO, "handling config command '%s'", exec);
// Endblock
if (**argv == '}') {
results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
@@ -530,12 +421,13 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
}
int i;
// Var replacement, for all but first argument of set
+ // TODO commands
for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
argv[i] = do_var_replacement(argv[i]);
unescape_string(argv[i]);
}
- /* Strip quotes for first argument.
- * TODO This part needs to be handled much better */
+ // Strip quotes for first argument.
+ // TODO This part needs to be handled much better
if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
strip_quotes(argv[1]);
}
@@ -619,7 +511,7 @@ struct cmd_results *config_commands_command(char *exec) {
}
policy->context = context;
- sway_log(L_INFO, "Set command policy for %s to %d",
+ wlr_log(L_INFO, "Set command policy for %s to %d",
policy->command, policy->context);
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -629,10 +521,11 @@ cleanup:
return results;
}
-struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
+struct cmd_results *cmd_results_new(enum cmd_status status,
+ const char *input, const char *format, ...) {
struct cmd_results *results = malloc(sizeof(struct cmd_results));
if (!results) {
- sway_log(L_ERROR, "Unable to allocate command results");
+ wlr_log(L_ERROR, "Unable to allocate command results");
return NULL;
}
results->status = status;
@@ -669,12 +562,15 @@ void free_cmd_results(struct cmd_results *results) {
const char *cmd_results_to_json(struct cmd_results *results) {
json_object *result_array = json_object_new_array();
json_object *root = json_object_new_object();
- json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
+ json_object_object_add(root, "success",
+ json_object_new_boolean(results->status == CMD_SUCCESS));
if (results->input) {
- json_object_object_add(root, "input", json_object_new_string(results->input));
+ json_object_object_add(
+ root, "input", json_object_new_string(results->input));
}
if (results->error) {
- json_object_object_add(root, "error", json_object_new_string(results->error));
+ json_object_object_add(
+ root, "error", json_object_new_string(results->error));
}
json_object_array_add(result_array, root);
const char *json = json_object_to_json_string(result_array);
@@ -682,3 +578,35 @@ const char *cmd_results_to_json(struct cmd_results *results) {
free(root);
return json;
}
+
+/**
+ * Check and add color to buffer.
+ *
+ * return error object, or NULL if color is valid.
+ */
+struct cmd_results *add_color(const char *name,
+ char *buffer, const char *color) {
+ int len = strlen(color);
+ if (len != 7 && len != 9) {
+ return cmd_results_new(CMD_INVALID, name,
+ "Invalid color definition %s", color);
+ }
+ if (color[0] != '#') {
+ return cmd_results_new(CMD_INVALID, name,
+ "Invalid color definition %s", color);
+ }
+ for (int i = 1; i < len; ++i) {
+ if (!isxdigit(color[i])) {
+ return cmd_results_new(CMD_INVALID, name,
+ "Invalid color definition %s", color);
+ }
+ }
+ strncpy(buffer, color, len);
+ // add default alpha channel if color was defined without it
+ if (len == 7) {
+ buffer[7] = 'f';
+ buffer[8] = 'f';
+ }
+ buffer[9] = '\0';
+ return NULL;
+}
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 @@
-#define _XOPEN_SOURCE 700
-#include <stdio.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/criteria.h"
-#include "list.h"
-#include "log.h"
-
-struct cmd_results *cmd_assign(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "assign", EXPECTED_AT_LEAST, 2))) {
- return error;
- }
-
- char *criteria = *argv++;
-
- if (strncmp(*argv, "→", strlen("→")) == 0) {
- if (argc < 3) {
- return cmd_results_new(CMD_INVALID, "assign", "Missing workspace");
- }
- argv++;
- }
-
- char *movecmd = "move container to workspace ";
- int arglen = strlen(movecmd) + strlen(*argv) + 1;
- char *cmdlist = calloc(1, arglen);
- if (!cmdlist) {
- return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate command list");
- }
- snprintf(cmdlist, arglen, "%s%s", movecmd, *argv);
-
- struct criteria *crit = malloc(sizeof(struct criteria));
- if (!crit) {
- free(cmdlist);
- return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate criteria");
- }
- crit->crit_raw = strdup(criteria);
- crit->cmdlist = cmdlist;
- crit->tokens = create_list();
- char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
-
- if (err_str) {
- error = cmd_results_new(CMD_INVALID, "assign", err_str);
- free(err_str);
- free_criteria(crit);
- } else if (crit->tokens->length == 0) {
- error = cmd_results_new(CMD_INVALID, "assign", "Found no name/value pairs in criteria");
- free_criteria(crit);
- } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
- sway_log(L_DEBUG, "assign: Duplicate, skipping.");
- free_criteria(crit);
- } else {
- sway_log(L_DEBUG, "assign: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
- list_add(config->criteria, crit);
- }
- return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdio.h>
#include <string.h>
#include <strings.h>
+#include <wlr/util/log.h>
#include "sway/commands.h"
#include "sway/config.h"
-#include "log.h"
#include "util.h"
struct cmd_results *cmd_bar(int argc, char **argv) {
@@ -27,7 +26,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
return bar_cmd_hidden_state(argc-1, argv + 1);
}
}
-
return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file.");
}
@@ -38,15 +36,15 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
}
// set bar id
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
if (bar == config->bars->items[i]) {
const int len = 5 + numlen(i); // "bar-" + i + \0
bar->id = malloc(len * sizeof(char));
if (bar->id) {
snprintf(bar->id, len, "bar-%d", i);
} else {
- return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar ID");
+ return cmd_results_new(CMD_FAILURE,
+ "bar", "Unable to allocate bar ID");
}
break;
}
@@ -54,6 +52,6 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
// Set current bar
config->current_bar = bar;
- sway_log(L_DEBUG, "Configuring bar %s", bar->id);
+ wlr_log(L_DEBUG, "Configuring bar %s", bar->id);
return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL);
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_activate_button(int argc, char **argv) {
- const char *cmd_name = "activate_button";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- // User should be able to prefix with 0x or whatever they want
- config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "activate_button", "TODO TRAY");
}
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 @@
struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
+ if ((error = checkarg(argc,
+ "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "binding_mode_indicator", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->binding_mode_indicator = true;
- sway_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->binding_mode_indicator = false;
- sway_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id);
- } else {
- error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]);
- return error;
+ wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s",
+ config->current_bar->id);
}
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
+ "Invalid value %s", argv[0]);
}
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 @@
#include "stringop.h"
struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
- return error;
- } else if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "bindsym", "No bar defined.");
- }
-
- if (strlen(argv[1]) != 7) {
- return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
- }
- uint32_t numbutton = (uint32_t)atoi(argv[1] + 6);
- if (numbutton < 1 || numbutton > 5 || strncmp(argv[1], "button", 6) != 0) {
- return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
- }
- struct sway_mouse_binding *binding = malloc(sizeof(struct sway_mouse_binding));
- if (!binding) {
- return cmd_results_new(CMD_FAILURE, "bindsym", "Unable to allocate binding");
- }
- binding->button = numbutton;
- binding->command = join_args(argv + 1, argc - 1);
-
- struct bar_config *bar = config->current_bar;
- int i = list_seq_find(bar->bindings, sway_mouse_binding_cmp_buttons, binding);
- if (i > -1) {
- sway_log(L_DEBUG, "bindsym - '%s' for swaybar already exists, overwriting", argv[0]);
- struct sway_mouse_binding *dup = bar->bindings->items[i];
- free_sway_mouse_binding(dup);
- list_del(bar->bindings, i);
- }
- list_add(bar->bindings, binding);
- list_qsort(bar->bindings, sway_mouse_binding_cmp_qsort);
-
- sway_log(L_DEBUG, "bindsym - Bound %s to command %s when clicking swaybar", argv[0], binding->command);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(CMD_FAILURE, "bindsym", "TODO"); // TODO
}
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 @@
#include <string.h>
#include "sway/commands.h"
-static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) {
+static struct cmd_results *parse_single_color(char **color,
+ const char *cmd_name, int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
- if (!*color) {
- *color = malloc(10);
- if (!*color) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
- }
+ if (!*color && !(*color = malloc(10))) {
+ return NULL;
}
-
error = add_color(cmd_name, *color, argv[0]);
if (error) {
return error;
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
-static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) {
+static struct cmd_results *parse_three_colors(char ***colors,
+ const char *cmd_name, int argc, char **argv) {
struct cmd_results *error = NULL;
if (argc != 3) {
- return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly three color values");
+ return cmd_results_new(CMD_INVALID,
+ cmd_name, "Requires exactly three color values");
}
-
- int i;
- for (i = 0; i < 3; i++) {
- if (!*colors[i]) {
- *(colors[i]) = malloc(10);
- if (!*(colors[i])) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color");
- }
+ for (size_t i = 0; i < 3; i++) {
+ if (!*colors[i] && !(*(colors[i]) = malloc(10))) {
+ return NULL;
}
error = add_color(cmd_name, *(colors[i]), argv[i]);
if (error) {
return error;
}
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -50,12 +41,10 @@ struct cmd_results *bar_cmd_colors(int argc, char **argv) {
if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (strcmp("{", argv[0]) != 0) {
return cmd_results_new(CMD_INVALID, "colors",
"Expected '{' at the start of colors config definition.");
}
-
return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL);
}
@@ -69,11 +58,13 @@ struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {
}
struct cmd_results *bar_colors_cmd_background(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.background), "background", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.background),
+ "background", argc, argv);
}
struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.focused_background), "focused_background", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.focused_background),
+ "focused_background", argc, argv);
}
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) {
}
struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.separator), "separator", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.separator),
+ "separator", argc, argv);
}
struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.focused_separator),
+ "focused_separator", argc, argv);
}
struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.statusline), "statusline", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.statusline),
+ "statusline", argc, argv);
}
struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {
- return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv);
+ return parse_single_color(&(config->current_bar->colors.focused_separator),
+ "focused_separator", argc, argv);
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_context_button(int argc, char **argv) {
- const char *cmd_name = "context_button";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- // User should be able to prefix with 0x or whatever they want
- config->current_bar->context_button = strtoul(argv[0], NULL, 0);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "context_button", "TODO TRAY");
}
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 @@
+#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include "sway/commands.h"
#include "log.h"
@@ -8,19 +9,13 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "font", "No bar defined.");
}
-
char *font = join_args(argv, argc);
free(config->current_bar->font);
- if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
- config->current_bar->font = font;
- } else {
- config->current_bar->font = font;
- }
-
- sway_log(L_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id);
+ config->current_bar->font = strdup(font);
+ wlr_log(L_DEBUG, "Settings font '%s' for bar: %s",
+ config->current_bar->font, config->current_bar->id);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
int height = atoi(argv[0]);
if (height < 0) {
return cmd_results_new(CMD_INVALID, "height",
"Invalid height value: %s", argv[0]);
}
-
config->current_bar->height = height;
- sway_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id);
+ wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s",
+ height, config->current_bar->id);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include "sway/ipc-server.h"
#include "log.h"
-static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const char *hidden_state) {
+static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,
+ const char *hidden_state) {
char *old_state = bar->hidden_state;
if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) {
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
} else if (strcasecmp("show", hidden_state) == 0) {
bar->hidden_state = strdup("show");
} else {
- return cmd_results_new(CMD_INVALID, "hidden_state", "Invalid value %s", hidden_state);
+ return cmd_results_new(CMD_INVALID, "hidden_state",
+ "Invalid value %s", hidden_state);
}
-
if (strcmp(old_state, bar->hidden_state) != 0) {
if (!config->reading) {
ipc_event_barconfig_update(bar);
}
- sway_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id);
+ wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s",
+ bar->hidden_state, bar->id);
}
-
// free old mode
free(old_state);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -42,13 +43,12 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) {
return error;
}
-
if (config->reading && argc > 1) {
- return cmd_results_new(CMD_INVALID, "hidden_state", "Unexpected value %s in config mode", argv[1]);
+ return cmd_results_new(CMD_INVALID, "hidden_state",
+ "Unexpected value %s in config mode", argv[1]);
}
const char *state = argv[0];
-
if (config->reading) {
return bar_set_hidden_state(config->current_bar, state);
}
@@ -57,10 +57,8 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
if (argc == 2) {
id = argv[1];
}
-
- int i;
struct bar_config *bar;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
bar = config->bars->items[i];
if (id && strcmp(id, bar->id) == 0) {
return bar_set_hidden_state(bar, state);
@@ -71,9 +69,5 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
return error;
}
}
-
- // active bar modifiers might have changed.
- update_active_bar_modifiers();
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include "sway/commands.h"
struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {
- const char *cmd_name = "tray_output";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- config->current_bar->icon_theme = strdup(argv[0]);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "icon_theme", "TODO TRAY");
}
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) {
const char *name = argv[0];
const char *oldname = config->current_bar->id;
-
// check if id is used by a previously defined bar
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *find = config->bars->items[i];
if (strcmp(name, find->id) == 0 && config->current_bar != find) {
return cmd_results_new(CMD_FAILURE, "id",
@@ -23,11 +21,10 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {
}
}
- sway_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
+ wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
// free old bar id
free(config->current_bar->id);
-
config->current_bar->id = strdup(name);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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
if (strcmp(old_mode, bar->mode) != 0) {
if (!config->reading) {
ipc_event_barconfig_update(bar);
-
- // active bar modifiers might have changed.
- update_active_bar_modifiers();
}
- sway_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
+ wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
}
// free old mode
@@ -47,13 +44,12 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) {
return error;
}
-
if (config->reading && argc > 1) {
- return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]);
+ return cmd_results_new(CMD_INVALID,
+ "mode", "Unexpected value %s in config mode", argv[1]);
}
const char *mode = argv[0];
-
if (config->reading) {
return bar_set_mode(config->current_bar, mode);
}
@@ -63,19 +59,16 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
id = argv[1];
}
- int i;
struct bar_config *bar;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
bar = config->bars->items[i];
if (id && strcmp(id, bar->id) == 0) {
return bar_set_mode(bar, mode);
}
-
error = bar_set_mode(bar, mode);
if (error) {
return error;
}
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
}
uint32_t mod = 0;
-
list_t *split = split_string(argv[0], "+");
for (int i = 0; i < split->length; ++i) {
uint32_t tmp_mod;
@@ -24,12 +23,13 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
continue;
} else {
free_flat_list(split);
- return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]);
+ return cmd_results_new(CMD_INVALID, "modifier",
+ "Unknown modifier '%s'", split->items[i]);
}
}
free_flat_list(split);
-
config->current_bar->modifier = mod;
- sway_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
+ wlr_log(L_DEBUG,
+ "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#define _XOPEN_SOURCE 500
+#include <stdbool.h>
#include <string.h>
#include "sway/commands.h"
#include "list.h"
@@ -9,7 +10,6 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "output", "No bar defined.");
}
@@ -21,21 +21,20 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
config->current_bar->outputs = outputs;
}
- int i;
- int add_output = 1;
+ bool add_output = true;
if (strcmp("*", output) == 0) {
// remove all previous defined outputs and replace with '*'
- for (i = 0; i < outputs->length; ++i) {
+ for (int i = 0; i < outputs->length; ++i) {
free(outputs->items[i]);
list_del(outputs, i);
}
} else {
// only add output if not already defined with either the same
// name or as '*'
- for (i = 0; i < outputs->length; ++i) {
+ for (int i = 0; i < outputs->length; ++i) {
const char *find = outputs->items[i];
if (strcmp("*", find) == 0 || strcmp(output, find) == 0) {
- add_output = 0;
+ add_output = false;
break;
}
}
@@ -43,8 +42,8 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
if (add_output) {
list_add(outputs, strdup(output));
- sway_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output);
+ wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'",
+ config->current_bar->id, output);
}
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined.");
}
-
if (strcasecmp("enabled", argv[0]) == 0) {
config->current_bar->pango_markup = true;
- sway_log(L_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling pango markup for bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("disabled", argv[0]) == 0) {
config->current_bar->pango_markup = false;
- sway_log(L_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Disabling pango markup for bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "pango_markup", "Invalid value %s", argv[0]);
+ error = cmd_results_new(CMD_INVALID, "pango_markup",
+ "Invalid value %s", argv[0]);
return error;
}
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 @@
+#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
@@ -8,26 +9,18 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) {
if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "position", "No bar defined.");
}
-
- if (strcasecmp("top", argv[0]) == 0) {
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_TOP;
- } else if (strcasecmp("bottom", argv[0]) == 0) {
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
- } else if (strcasecmp("left", argv[0]) == 0) {
- sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV");
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_LEFT;
- } else if (strcasecmp("right", argv[0]) == 0) {
- sway_log(L_INFO, "Warning: swaybar currently only supports top and bottom positioning. YMMV");
- config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_RIGHT;
- } else {
- error = cmd_results_new(CMD_INVALID, "position", "Invalid value %s", argv[0]);
- return error;
+ char *valid[] = { "top", "bottom", "left", "right" };
+ for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) {
+ if (strcasecmp(valid[i], argv[0]) == 0) {
+ wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s",
+ argv[0], config->current_bar->id);
+ config->current_bar->position = strdup(argv[0]);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ }
}
-
- sway_log(L_DEBUG, "Setting bar position '%s' for bar: %s", argv[0], config->current_bar->id);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(CMD_INVALID,
+ "position", "Invalid value %s", argv[0]);
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) {
- const char *cmd_name = "secondary_button";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- // User should be able to prefix with 0x or whatever they want
- config->current_bar->secondary_button = strtoul(argv[0], NULL, 0);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "secondary_button", "TODO TRAY");
}
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) {
if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "separator_symbol", "No bar defined.");
}
-
free(config->current_bar->separator_symbol);
config->current_bar->separator_symbol = strdup(argv[0]);
- sway_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id);
-
+ wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s",
+ config->current_bar->separator_symbol, config->current_bar->id);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "status_command", "No bar defined.");
}
-
free(config->current_bar->status_command);
config->current_bar->status_command = join_args(argv, argc);
- sway_log(L_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command);
-
+ wlr_log(L_DEBUG, "Feeding bar with status command: %s",
+ config->current_bar->status_command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
+ if ((error = checkarg(argc,
+ "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "strip_workspace_numbers", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->strip_workspace_numbers = true;
- sway_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->strip_workspace_numbers = false;
- sway_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]);
- return error;
+ return cmd_results_new(CMD_INVALID,
+ "strip_workspace_numbers", "Invalid value %s", argv[0]);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "swaybar_command", "No bar defined.");
}
-
free(config->current_bar->swaybar_command);
config->current_bar->swaybar_command = join_args(argv, argc);
- sway_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command);
-
+ wlr_log(L_DEBUG, "Using custom swaybar command: %s",
+ config->current_bar->swaybar_command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include "sway/commands.h"
struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
- const char *cmd_name = "tray_output";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command "
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- if (strcmp(argv[0], "all") == 0) {
- // Default behaviour
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- config->current_bar->tray_output = strdup(argv[0]);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "tray_output", "TODO TRAY");
}
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 @@
#include "log.h"
struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
- const char *cmd_name = "tray_padding";
-#ifndef ENABLE_TRAY
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command"
- "%s called, but sway was compiled without tray support",
- cmd_name, cmd_name);
-#else
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, cmd_name, EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, cmd_name, "No bar defined.");
- }
-
- if (argc == 1 || (argc == 2 && strcasecmp("px", argv[1]) == 0)) {
- char *inv;
- uint32_t padding = strtoul(argv[0], &inv, 10);
- if (*inv == '\0' || strcasecmp(inv, "px") == 0) {
- config->current_bar->tray_padding = padding;
- sway_log(L_DEBUG, "Enabling tray padding of %d px on bar: %s", padding, config->current_bar->id);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- }
- return cmd_results_new(CMD_FAILURE, cmd_name,
- "Expected 'tray_padding <padding>[px]'");
-#endif
+ // TODO TRAY
+ return cmd_results_new(CMD_INVALID, "tray_padding", "TODO TRAY");
}
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) {
if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
- return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "workspace_buttons", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->workspace_buttons = true;
- sway_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->workspace_buttons = false;
- sway_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]);
- return error;
+ return cmd_results_new(CMD_INVALID, "workspace_buttons",
+ "Invalid value %s", argv[0]);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
if (!config->current_bar) {
return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined.");
}
-
if (strcasecmp("yes", argv[0]) == 0) {
config->current_bar->wrap_scroll = true;
- sway_log(L_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s",
+ config->current_bar->id);
} else if (strcasecmp("no", argv[0]) == 0) {
config->current_bar->wrap_scroll = false;
- sway_log(L_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id);
+ wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s",
+ config->current_bar->id);
} else {
- error = cmd_results_new(CMD_INVALID, "wrap_scroll", "Invalid value %s", argv[0]);
- return error;
+ return cmd_results_new(CMD_INVALID,
+ "wrap_scroll", "Invalid value %s", argv[0]);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-names.h>
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/input_state.h"
#include "list.h"
#include "log.h"
#include "stringop.h"
@@ -11,13 +15,67 @@
int binding_order = 0;
+void free_sway_binding(struct sway_binding *binding) {
+ if (!binding) {
+ return;
+ }
+
+ if (binding->keys) {
+ free_flat_list(binding->keys);
+ }
+ free(binding->command);
+ free(binding);
+}
+
+/**
+ * Returns true if the bindings have the same key and modifier combinations.
+ * Note that keyboard layout is not considered, so the bindings might actually
+ * not be equivalent on some layouts.
+ */
+bool binding_key_compare(struct sway_binding *binding_a,
+ struct sway_binding *binding_b) {
+ if (binding_a->release != binding_b->release) {
+ return false;
+ }
+
+ if (binding_a->bindcode != binding_b->bindcode) {
+ return false;
+ }
+
+ if (binding_a->modifiers ^ binding_b->modifiers) {
+ return false;
+ }
+
+ if (binding_a->keys->length != binding_b->keys->length) {
+ return false;
+ }
+
+ int keys_len = binding_a->keys->length;
+ for (int i = 0; i < keys_len; ++i) {
+ uint32_t key_a = *(uint32_t*)binding_a->keys->items[i];
+ bool found = false;
+ for (int j = 0; j < keys_len; ++j) {
+ uint32_t key_b = *(uint32_t*)binding_b->keys->items[j];
+ if (key_b == key_a) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
struct cmd_results *cmd_bindsym(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
return error;
}
- struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+ struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
if (!binding) {
return cmd_results_new(CMD_FAILURE, "bindsym",
"Unable to allocate binding");
@@ -58,7 +116,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
// Check for mouse binding
if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
strlen(split->items[i]) == strlen("button0")) {
- sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK;
+ sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT;
}
if (!sym) {
struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
@@ -67,7 +125,7 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
free_flat_list(split);
return ret;
}
- xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
+ xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t));
if (!key) {
free_sway_binding(binding);
free_flat_list(split);
@@ -78,20 +136,29 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
list_add(binding->keys, key);
}
free_flat_list(split);
+ binding->order = binding_order++;
- struct sway_mode *mode = config->current_mode;
- int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding);
- if (i > -1) {
- sway_log(L_DEBUG, "bindsym - '%s' already exists, overwriting", argv[0]);
- struct sway_binding *dup = mode->bindings->items[i];
- free_sway_binding(dup);
- list_del(mode->bindings, i);
+ list_t *mode_bindings = config->current_mode->keysym_bindings;
+
+ // overwrite the binding if it already exists
+ bool overwritten = false;
+ for (int i = 0; i < mode_bindings->length; ++i) {
+ struct sway_binding *config_binding = mode_bindings->items[i];
+ if (binding_key_compare(binding, config_binding)) {
+ wlr_log(L_DEBUG, "overwriting old binding with command '%s'",
+ config_binding->command);
+ free_sway_binding(config_binding);
+ mode_bindings->items[i] = binding;
+ overwritten = true;
+ }
}
- binding->order = binding_order++;
- list_add(mode->bindings, binding);
- list_qsort(mode->bindings, sway_binding_cmp_qsort);
- sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command);
+ if (!overwritten) {
+ list_add(mode_bindings, binding);
+ }
+
+ wlr_log(L_DEBUG, "bindsym - Bound %s to command %s",
+ argv[0], binding->command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -101,7 +168,7 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
return error;
}
- struct sway_binding *binding = malloc(sizeof(struct sway_binding));
+ struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
if (!binding) {
return cmd_results_new(CMD_FAILURE, "bindsym",
"Unable to allocate binding");
@@ -138,33 +205,41 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
// parse keycode
xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10);
if (!xkb_keycode_is_legal_ext(keycode)) {
- error = cmd_results_new(CMD_INVALID, "bindcode", "Invalid keycode '%s'", (char *)split->items[i]);
+ error =
+ cmd_results_new(CMD_INVALID, "bindcode",
+ "Invalid keycode '%s'", (char *)split->items[i]);
free_sway_binding(binding);
list_free(split);
return error;
}
- xkb_keycode_t *key = malloc(sizeof(xkb_keycode_t));
+ xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t));
*key = keycode - 8;
list_add(binding->keys, key);
}
free_flat_list(split);
- struct sway_mode *mode = config->current_mode;
- int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding);
- if (i > -1) {
- struct sway_binding *dup = mode->bindings->items[i];
- if (dup->bindcode) {
- sway_log(L_DEBUG, "bindcode - '%s' already exists, overwriting", argv[0]);
- } else {
- sway_log(L_DEBUG, "bindcode - '%s' already exists as bindsym, overwriting", argv[0]);
+ binding->order = binding_order++;
+
+ list_t *mode_bindings = config->current_mode->keycode_bindings;
+
+ // overwrite the binding if it already exists
+ bool overwritten = false;
+ for (int i = 0; i < mode_bindings->length; ++i) {
+ struct sway_binding *config_binding = mode_bindings->items[i];
+ if (binding_key_compare(binding, config_binding)) {
+ wlr_log(L_DEBUG, "overwriting old binding with command '%s'",
+ config_binding->command);
+ free_sway_binding(config_binding);
+ mode_bindings->items[i] = binding;
+ overwritten = true;
}
- free_sway_binding(dup);
- list_del(mode->bindings, i);
}
- binding->order = binding_order++;
- list_add(mode->bindings, binding);
- list_qsort(mode->bindings, sway_binding_cmp_qsort);
- sway_log(L_DEBUG, "bindcode - Bound %s to command %s", argv[0], binding->command);
+ if (!overwritten) {
+ list_add(mode_bindings, binding);
+ }
+
+ wlr_log(L_DEBUG, "bindcode - Bound %s to command %s",
+ argv[0], binding->command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-
-struct cmd_results *cmd_border(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (!config->active) {
- return cmd_results_new(CMD_FAILURE, "border", "Can only be used when sway is running.");
- }
- if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (argc > 2) {
- return cmd_results_new(CMD_INVALID, "border",
- "Expected 'border <normal|pixel|none|toggle> [<n>]");
- }
-
- swayc_t *view = current_container;
- enum swayc_border_types border = view->border_type;
- int thickness = view->border_thickness;
-
- if (strcasecmp(argv[0], "none") == 0) {
- border = B_NONE;
- } else if (strcasecmp(argv[0], "normal") == 0) {
- border = B_NORMAL;
- } else if (strcasecmp(argv[0], "pixel") == 0) {
- border = B_PIXEL;
- } else if (strcasecmp(argv[0], "toggle") == 0) {
- switch (border) {
- case B_NONE:
- border = B_PIXEL;
- break;
- case B_NORMAL:
- border = B_NONE;
- break;
- case B_PIXEL:
- border = B_NORMAL;
- break;
- }
- } else {
- return cmd_results_new(CMD_INVALID, "border",
- "Expected 'border <normal|pixel|none|toggle>");
- }
-
- if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
- thickness = (int)strtol(argv[1], NULL, 10);
- if (errno == ERANGE || thickness < 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "border", "Number is out of range.");
- }
- }
-
- if (view) {
- view->border_type = border;
- view->border_thickness = thickness;
- update_geometry(view);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdlib.h>
-#include <string.h>
-#include "sway/commands.h"
-
-static struct cmd_results *parse_border_color(struct border_colors *border_colors, const char *cmd_name, int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (argc < 3 || argc > 5) {
- return cmd_results_new(CMD_INVALID, cmd_name, "Requires between three and five color values");
- }
-
- uint32_t *colors[5] = {
- &border_colors->border,
- &border_colors->background,
- &border_colors->text,
- &border_colors->indicator,
- &border_colors->child_border
- };
- int i;
- for (i = 0; i < argc; i++) {
- char buffer[10];
- error = add_color(cmd_name, buffer, argv[i]);
- if (error) {
- return error;
- }
- *colors[i] = strtoul(buffer + 1, NULL, 16);
- }
-
- if (argc < 5) {
- border_colors->child_border = border_colors->background;
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
-struct cmd_results *cmd_client_focused(int argc, char **argv) {
- return parse_border_color(&config->border_colors.focused, "client.focused", argc, argv);
-}
-
-struct cmd_results *cmd_client_focused_inactive(int argc, char **argv) {
- return parse_border_color(&config->border_colors.focused_inactive, "client.focused_inactive", argc, argv);
-}
-
-struct cmd_results *cmd_client_unfocused(int argc, char **argv) {
- return parse_border_color(&config->border_colors.unfocused, "client.unfocused", argc, argv);
-}
-
-struct cmd_results *cmd_client_urgent(int argc, char **argv) {
- return parse_border_color(&config->border_colors.urgent, "client.urgent", argc, argv);
-}
-
-struct cmd_results *cmd_client_placeholder(int argc, char **argv) {
- return parse_border_color(&config->border_colors.placeholder, "client.placeholder", argc, argv);
-}
-
-struct cmd_results *cmd_client_background(int argc, char **argv) {
- char buffer[10];
- struct cmd_results *error = NULL;
- uint32_t background;
-
- if (argc != 1) {
- return cmd_results_new(CMD_INVALID, "client.background", "Requires exactly one color value");
- }
-
- error = add_color("client.background", buffer, argv[0]);
- if (error) {
- return error;
- }
-
- background = strtoul(buffer+1, NULL, 16);
- config->border_colors.background = background;
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <wlc/wlc.h>
-#include <unistd.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "stringop.h"
-
-static void send_clipboard(void *data, const char *type, int fd) {
- if (strcmp(type, "text/plain") != 0
- && strcmp(type, "text/plain;charset=utf-8") != 0) {
- close(fd);
- return;
- }
-
- const char *str = data;
- write(fd, str, strlen(str));
- close(fd);
-}
-
-struct cmd_results *cmd_clipboard(int argc, char **argv) {
- static char *current_data = NULL;
-
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "clipboard", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- static const char *types[2] = {
- "text/plain",
- "text/plain;charset=utf-8"
- };
-
- char *str = join_args(argv, argc);
- wlc_set_selection(str, types, 2, &send_clipboard);
-
- free(current_data);
- current_data = str;
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdbool.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/config.h"
-#include "list.h"
-#include "log.h"
-
-struct cmd_results *cmd_commands(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "commands", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- if (strcmp(argv[0], "{") != 0) {
- return cmd_results_new(CMD_FAILURE, "commands", "Expected block declaration");
- }
-
- if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "commands", "Can only be used in config file.");
- }
-
- return cmd_results_new(CMD_BLOCK_COMMANDS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_debuglog(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "debuglog", EXPECTED_EQUAL_TO, 1))) {
- return error;
- } else if (strcasecmp(argv[0], "toggle") == 0) {
- if (config->reading) {
- return cmd_results_new(CMD_FAILURE, "debuglog toggle", "Can't be used in config file.");
- }
- if (toggle_debug_logging()) {
- sway_log(L_DEBUG, "Debuglog turned on.");
- }
- } else if (strcasecmp(argv[0], "on") == 0) {
- set_log_level(L_DEBUG);
- sway_log(L_DEBUG, "Debuglog turned on.");
- } else if (strcasecmp(argv[0], "off") == 0) {
- reset_log_level();
- } else {
- return cmd_results_new(CMD_FAILURE, "debuglog", "Expected 'debuglog on|off|toggle'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
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 @@
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-
-struct cmd_results *cmd_default_border(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "default_border", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (argc > 2) {
- return cmd_results_new(CMD_INVALID, "default_border",
- "Expected 'default_border <normal|none|pixel> [<n>]");
- }
-
- enum swayc_border_types border = config->border;
- int thickness = config->border_thickness;
-
- if (strcasecmp(argv[0], "none") == 0) {
- border = B_NONE;
- } else if (strcasecmp(argv[0], "normal") == 0) {
- border = B_NORMAL;
- } else if (strcasecmp(argv[0], "pixel") == 0) {
- border = B_PIXEL;
- } else {
- return cmd_results_new(CMD_INVALID, "default_border",
- "Expected 'default_border <normal|none|pixel> [<n>]");
- }
-
- if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
- thickness = (int)strtol(argv[1], NULL, 10);
- if (errno == ERANGE || thickness < 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "default_border", "Number is out out of range.");
- }
- }
-
- config->border = border;
- config->border_thickness = thickness;
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-
-struct cmd_results *cmd_default_floating_border(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "default_floating_border", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (argc > 2) {
- return cmd_results_new(CMD_INVALID, "default_floating_border",
- "Expected 'default_floating_border <normal|none|pixel> [<n>]");
- }
-
- enum swayc_border_types border = config->floating_border;
- int thickness = config->floating_border_thickness;
-
- if (strcasecmp(argv[0], "none") == 0) {
- border = B_NONE;
- } else if (strcasecmp(argv[0], "normal") == 0) {
- border = B_NORMAL;
- } else if (strcasecmp(argv[0], "pixel") == 0) {
- border = B_PIXEL;
- } else {
- return cmd_results_new(CMD_INVALID, "default_floating_border",
- "Expected 'default_floating_border <normal|none|pixel> [<n>]");
- }
-
- if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
- thickness = (int)strtol(argv[1], NULL, 10);
- if (errno == ERANGE || thickness < 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "default_floating_border",
- "Number is out out of range.");
- }
- }
-
- config->floating_border = border;
- config->floating_border_thickness = thickness;
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <strings.h>
#include "sway/commands.h"
-struct cmd_results *cmd_orientation(int argc, char **argv) {
+struct cmd_results *cmd_default_orientation(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file.");
- if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
+ if ((error = checkarg(argc, "default_orientation", EXPECTED_EQUAL_TO, 1))) {
return error;
}
if (strcasecmp(argv[0], "horizontal") == 0) {
@@ -15,7 +14,8 @@ struct cmd_results *cmd_orientation(int argc, char **argv) {
} else if (strcasecmp(argv[0], "auto") == 0) {
// Do nothing
} else {
- return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'");
+ return cmd_results_new(CMD_INVALID, "default_orientation",
+ "Expected 'orientation <horizontal|vertical|auto>'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include "sway/commands.h"
+#include "sway/config.h"
#include "log.h"
#include "stringop.h"
@@ -7,10 +8,9 @@ struct cmd_results *cmd_exec(int argc, char **argv) {
if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
if (config->reloading) {
char *args = join_args(argv, argc);
- sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
+ wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
free(args);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
return cmd_exec_always(argc, argv);
}
-
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 @@
#define _XOPEN_SOURCE 500
+#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "sway/tree/container.h"
+#include "sway/tree/workspace.h"
#include "log.h"
#include "stringop.h"
@@ -16,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
char *tmp = NULL;
if (strcmp((char*)*argv, "--no-startup-id") == 0) {
- sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
+ wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
return error;
}
@@ -31,11 +35,11 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
strncpy(cmd, tmp, sizeof(cmd));
cmd[sizeof(cmd) - 1] = 0;
free(tmp);
- sway_log(L_DEBUG, "Executing %s", cmd);
+ wlr_log(L_DEBUG, "Executing %s", cmd);
int fd[2];
if (pipe(fd) != 0) {
- sway_log(L_ERROR, "Unable to create pipe for fork");
+ wlr_log(L_ERROR, "Unable to create pipe for fork");
}
pid_t pid;
@@ -49,7 +53,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
setsid();
if ((*child = fork()) == 0) {
execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
- /* Not reached */
+ // Not reached
}
close(fd[0]);
ssize_t s = 0;
@@ -70,13 +74,9 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
close(fd[0]);
// cleanup child process
wait(0);
- swayc_t *ws = swayc_active_workspace();
- if (*child > 0 && ws) {
- sway_log(L_DEBUG, "Child process created with pid %d for workspace %s", *child, ws->name);
- struct pid_workspace *pw = malloc(sizeof(struct pid_workspace));
- pw->pid = child;
- pw->workspace = strdup(ws->name);
- pid_workspace_add(pw);
+ if (*child > 0) {
+ wlr_log(L_DEBUG, "Child process created with pid %d", *child);
+ // TODO: add PID to active workspace
} else {
free(child);
}
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 @@
+#include <stddef.h>
#include "sway/commands.h"
-#include "sway/container.h"
+#include "sway/config.h"
void sway_terminate(int exit_code);
struct cmd_results *cmd_exit(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
return error;
}
- // Close all views
- close_views(&root_container);
- sway_terminate(EXIT_SUCCESS);
+ sway_terminate(0);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
-
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/ipc-server.h"
-#include "sway/layout.h"
-#include "list.h"
-#include "log.h"
-
-struct cmd_results *cmd_floating(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "floating", "Can't be used in config file.");
- if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- swayc_t *view = current_container;
- bool wants_floating;
- if (strcasecmp(argv[0], "enable") == 0) {
- wants_floating = true;
- } else if (strcasecmp(argv[0], "disable") == 0) {
- wants_floating = false;
- } else if (strcasecmp(argv[0], "toggle") == 0) {
- wants_floating = !view->is_floating;
- } else {
- return cmd_results_new(CMD_FAILURE, "floating",
- "Expected 'floating <enable|disable|toggle>");
- }
-
- // Prevent running floating commands on things like workspaces
- if (view->type != C_VIEW) {
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
-
- // Change from nonfloating to floating
- if (!view->is_floating && wants_floating) {
- // Remove view from its current location
- destroy_container(remove_child(view));
-
- // and move it into workspace floating
- add_floating(swayc_active_workspace(), view);
- view->x = (swayc_active_workspace()->width - view->width)/2;
- view->y = (swayc_active_workspace()->height - view->height)/2;
- if (view->desired_width != -1) {
- view->width = view->desired_width;
- }
- if (view->desired_height != -1) {
- view->height = view->desired_height;
- }
- arrange_windows(swayc_active_workspace(), -1, -1);
-
- } else if (view->is_floating && !wants_floating) {
- // Delete the view from the floating list and unset its is_floating flag
- remove_child(view);
- view->is_floating = false;
- // Get the properly focused container, and add in the view there
- swayc_t *focused = container_under_pointer();
- // If focused is null, it's because the currently focused container is a workspace
- if (focused == NULL || focused->is_floating) {
- focused = swayc_active_workspace();
- }
- set_focused_container(focused);
-
- sway_log(L_DEBUG, "Non-floating focused container is %p", focused);
-
- // Case of focused workspace, just create as child of it
- if (focused->type == C_WORKSPACE) {
- add_child(focused, view);
- }
- // Regular case, create as sibling of current container
- else {
- add_sibling(focused, view);
- }
- // Refocus on the view once its been put back into the layout
- view->width = view->height = 0;
- arrange_windows(swayc_active_workspace(), -1, -1);
- remove_view_from_scratchpad(view);
- ipc_event_window(view, "floating");
- }
- set_focused_container(view);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdlib.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) {
- struct cmd_results *error = NULL;
- int32_t width;
- int32_t height;
- char *ptr;
-
- if ((error = checkarg(argc, "floating_maximum_size", EXPECTED_EQUAL_TO, 3))) {
- return error;
- }
- width = strtol(argv[0], &ptr, 10);
- height = strtol(argv[2], &ptr, 10);
-
- if (width < -1) {
- sway_log(L_DEBUG, "floating_maximum_size invalid width value: '%s'", argv[0]);
-
- } else {
- config->floating_maximum_width = width;
-
- }
-
- if (height < -1) {
- sway_log(L_DEBUG, "floating_maximum_size invalid height value: '%s'", argv[2]);
- }
- else {
- config->floating_maximum_height = height;
-
- }
-
- sway_log(L_DEBUG, "New floating_maximum_size: '%d' x '%d'", config->floating_maximum_width,
- config->floating_maximum_height);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdlib.h>
-#include <string.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) {
- struct cmd_results *error = NULL;
- int32_t width;
- int32_t height;
- char *ptr;
-
- if ((error = checkarg(argc, "floating_minimum_size", EXPECTED_EQUAL_TO, 3))) {
- return error;
- }
- width = strtol(argv[0], &ptr, 10);
- height = strtol(argv[2], &ptr, 10);
-
- if (width <= 0) {
- sway_log(L_DEBUG, "floating_minimum_size invalid width value: '%s'", argv[0]);
-
- } else {
- config->floating_minimum_width = width;
-
- }
-
- if (height <= 0) {
- sway_log(L_DEBUG, "floating_minimum_size invalid height value: '%s'", argv[2]);
- }
- else {
- config->floating_minimum_height = height;
-
- }
-
- sway_log(L_DEBUG, "New floating_minimum_size: '%d' x '%d'", config->floating_minimum_width,
- config->floating_minimum_height);
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/input_state.h"
-#include "list.h"
-#include "log.h"
-#include "stringop.h"
-#include "util.h"
-
-struct cmd_results *cmd_floating_mod(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
- int i;
- list_t *split = split_string(argv[0], "+");
- config->floating_mod = 0;
-
- // set modifier keys
- for (i = 0; i < split->length; ++i) {
- config->floating_mod |= get_modifier_mask_by_name(split->items[i]);
- }
- free_flat_list(split);
- if (!config->floating_mod) {
- error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
- return error;
- }
-
- if (argc >= 2) {
- if (strcasecmp("inverse", argv[1]) == 0) {
- config->dragging_key = M_RIGHT_CLICK;
- config->resizing_key = M_LEFT_CLICK;
- } else if (strcasecmp("normal", argv[1]) == 0) {
- config->dragging_key = M_LEFT_CLICK;
- config->resizing_key = M_RIGHT_CLICK;
- } else {
- error = cmd_results_new(CMD_INVALID, "floating_modifier", "Invalid definition %s", argv[1]);
- return error;
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_floating_scroll(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "floating_scroll", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
- if (!strcasecmp("up", argv[0])) {
- free(config->floating_scroll_up_cmd);
- if (argc < 2) {
- config->floating_scroll_up_cmd = strdup("");
- } else {
- config->floating_scroll_up_cmd = join_args(argv + 1, argc - 1);
- }
- } else if (!strcasecmp("down", argv[0])) {
- free(config->floating_scroll_down_cmd);
- if (argc < 2) {
- config->floating_scroll_down_cmd = strdup("");
- } else {
- config->floating_scroll_down_cmd = join_args(argv + 1, argc - 1);
- }
- } else if (!strcasecmp("left", argv[0])) {
- free(config->floating_scroll_left_cmd);
- if (argc < 2) {
- config->floating_scroll_left_cmd = strdup("");
- } else {
- config->floating_scroll_left_cmd = join_args(argv + 1, argc - 1);
- }
- } else if (!strcasecmp("right", argv[0])) {
- free(config->floating_scroll_right_cmd);
- if (argc < 2) {
- config->floating_scroll_right_cmd = strdup("");
- } else {
- config->floating_scroll_right_cmd = join_args(argv + 1, argc - 1);
- }
- } else {
- error = cmd_results_new(CMD_INVALID, "floating_scroll", "Unknown command: '%s'", argv[0]);
- return error;
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
#include <strings.h>
-#include <wlc/wlc.h>
+#include <wlr/util/log.h>
+#include "log.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/tree/view.h"
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/input_state.h"
-#include "sway/output.h"
-#include "sway/workspace.h"
+
+static bool parse_movement_direction(const char *name,
+ enum movement_direction *out) {
+ if (strcasecmp(name, "left") == 0) {
+ *out = MOVE_LEFT;
+ } else if (strcasecmp(name, "right") == 0) {
+ *out = MOVE_RIGHT;
+ } else if (strcasecmp(name, "up") == 0) {
+ *out = MOVE_UP;
+ } else if (strcasecmp(name, "down") == 0) {
+ *out = MOVE_DOWN;
+ } else if (strcasecmp(name, "parent") == 0) {
+ *out = MOVE_PARENT;
+ } else if (strcasecmp(name, "child") == 0) {
+ *out = MOVE_CHILD;
+ } else {
+ return false;
+ }
+
+ return true;
+}
struct cmd_results *cmd_focus(int argc, char **argv) {
- if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file.");
- struct cmd_results *error = NULL;
- if (argc > 0 && strcasecmp(argv[0], "output") == 0) {
- swayc_t *output = NULL;
- struct wlc_point abs_pos;
- get_absolute_center_position(get_focused_container(&root_container), &abs_pos);
- if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) {
- return error;
- } else if (!(output = output_by_name(argv[1], &abs_pos))) {
- return cmd_results_new(CMD_FAILURE, "focus output",
- "Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y);
- } else if (!workspace_switch(swayc_active_workspace_for(output))) {
- return cmd_results_new(CMD_FAILURE, "focus output",
- "Switching to workspace on output '%s' was blocked", argv[1]);
- } else if (config->mouse_warping) {
- swayc_t *focused = get_focused_view(output);
- if (focused && focused->type == C_VIEW) {
- center_pointer_on(focused);
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- } else if (argc == 0) {
- set_focused_container(current_container);
+ struct sway_container *con = config->handler_context.current_container;
+ struct sway_seat *seat = config->handler_context.seat;
+ if (con->type < C_WORKSPACE) {
+ return cmd_results_new(CMD_FAILURE, "focus",
+ "Command 'focus' cannot be used above the workspace level");
+ }
+
+ if (argc == 0) {
+ seat_set_focus(seat, con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- } else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
- return error;
}
- static int floating_toggled_index = 0;
- static int tiled_toggled_index = 0;
- if (strcasecmp(argv[0], "left") == 0) {
- move_focus(MOVE_LEFT);
- } else if (strcasecmp(argv[0], "right") == 0) {
- move_focus(MOVE_RIGHT);
- } else if (strcasecmp(argv[0], "up") == 0) {
- move_focus(MOVE_UP);
- } else if (strcasecmp(argv[0], "down") == 0) {
- move_focus(MOVE_DOWN);
- } else if (strcasecmp(argv[0], "parent") == 0) {
- move_focus(MOVE_PARENT);
- } else if (strcasecmp(argv[0], "child") == 0) {
- move_focus(MOVE_CHILD);
- } else if (strcasecmp(argv[0], "next") == 0) {
- move_focus(MOVE_NEXT);
- } else if (strcasecmp(argv[0], "prev") == 0) {
- move_focus(MOVE_PREV);
- } else if (strcasecmp(argv[0], "mode_toggle") == 0) {
- int i;
- swayc_t *workspace = swayc_active_workspace();
- swayc_t *focused = get_focused_view(workspace);
- if (focused->is_floating) {
- if (workspace->children->length > 0) {
- for (i = 0;i < workspace->floating->length; i++) {
- if (workspace->floating->items[i] == focused) {
- floating_toggled_index = i;
- break;
- }
- }
- if (workspace->children->length > tiled_toggled_index) {
- set_focused_container(get_focused_view(workspace->children->items[tiled_toggled_index]));
- } else {
- set_focused_container(get_focused_view(workspace->children->items[0]));
- tiled_toggled_index = 0;
- }
- }
- } else {
- if (workspace->floating->length > 0) {
- for (i = 0;i < workspace->children->length; i++) {
- if (workspace->children->items[i] == focused) {
- tiled_toggled_index = i;
- break;
- }
- }
- if (workspace->floating->length > floating_toggled_index) {
- swayc_t *floating = workspace->floating->items[floating_toggled_index];
- set_focused_container(get_focused_view(floating));
- } else {
- swayc_t *floating = workspace->floating->items[workspace->floating->length - 1];
- set_focused_container(get_focused_view(floating));
- tiled_toggled_index = workspace->floating->length - 1;
- }
- }
- }
- } else {
+
+ // TODO mode_toggle
+ enum movement_direction direction = 0;
+ if (!parse_movement_direction(argv[0], &direction)) {
return cmd_results_new(CMD_INVALID, "focus",
"Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
}
+
+ struct sway_container *next_focus = container_get_in_direction(
+ con, seat, direction);
+ if (next_focus) {
+ seat_set_focus(seat, next_focus);
+ }
+
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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) {
if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
return error;
}
-
config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/border.h"
-#include "sway/commands.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_font(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- char *font = join_args(argv, argc);
- free(config->font);
- if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
- config->font = strdup(font + 6);
- free(font);
- } else {
- config->font = font;
- }
-
- config->font_height = get_font_text_height(config->font);
-
- sway_log(L_DEBUG, "Settings font %s", config->font);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/criteria.h"
-#include "list.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_for_window(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) {
- return error;
- }
- // add command to a criteria/command pair that is run against views when they appear.
- char *criteria = argv[0], *cmdlist = join_args(argv + 1, argc - 1);
-
- struct criteria *crit = malloc(sizeof(struct criteria));
- if (!crit) {
- return cmd_results_new(CMD_FAILURE, "for_window", "Unable to allocate criteria");
- }
- crit->crit_raw = strdup(criteria);
- crit->cmdlist = cmdlist;
- crit->tokens = create_list();
- char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
-
- if (err_str) {
- error = cmd_results_new(CMD_INVALID, "for_window", err_str);
- free(err_str);
- free_criteria(crit);
- } else if (crit->tokens->length == 0) {
- error = cmd_results_new(CMD_INVALID, "for_window", "Found no name/value pairs in criteria");
- free_criteria(crit);
- } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
- sway_log(L_DEBUG, "for_window: Duplicate, skipping.");
- free_criteria(crit);
- } else {
- sway_log(L_DEBUG, "for_window: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
- list_add(config->criteria, crit);
- }
- return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "force_focus_wrapping", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- config->force_focus_wrapping = !strcasecmp(argv[0], "yes");
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <stdbool.h>
-#include <string.h>
-#include <wlc/wlc.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/ipc-server.h"
-#include "sway/layout.h"
-
-struct cmd_results *cmd_fullscreen(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
- if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
- return error;
- }
- swayc_t *container = current_container;
- if(container->type != C_VIEW){
- return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen");
- }
- swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
- bool current = swayc_is_fullscreen(container);
- wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
-
- if (container->is_floating) {
- if (current) {
- // set dimensions back to what they were before we fullscreened this
- container->x = container->cached_geometry.origin.x;
- container->y = container->cached_geometry.origin.y;
- container->width = container->cached_geometry.size.w;
- container->height = container->cached_geometry.size.h;
- } else {
- // cache dimensions so we can reset them after we "unfullscreen" this
- struct wlc_geometry geo = {
- .origin = {
- .x = container->x,
- .y = container->y
- },
- .size = {
- .w = container->width,
- .h = container->height
- }
- };
- container->cached_geometry = geo;
- }
- }
-
- // Resize workspace if going from fullscreen -> notfullscreen
- // otherwise just resize container
- if (!current) {
- arrange_windows(workspace, -1, -1);
- workspace->fullscreen = container;
- } else {
- arrange_windows(container, -1, -1);
- workspace->fullscreen = NULL;
- }
- ipc_event_window(container, "fullscreen_mode");
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/layout.h"
-
-struct cmd_results *cmd_gaps(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
- const char *expected_syntax =
- "Expected 'gaps edge_gaps <on|off|toggle>' or "
- "'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'";
- const char *amount_str = argv[0];
- // gaps amount
- if (argc >= 1 && isdigit(*amount_str)) {
- int amount = (int)strtol(amount_str, NULL, 10);
- if (errno == ERANGE) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
- }
- config->gaps_inner = config->gaps_outer = amount;
- arrange_windows(&root_container, -1, -1);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- // gaps inner|outer n
- else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
- int amount = (int)strtol(amount_str, NULL, 10);
- if (errno == ERANGE) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
- }
- const char *target_str = argv[0];
- if (strcasecmp(target_str, "inner") == 0) {
- config->gaps_inner = amount;
- } else if (strcasecmp(target_str, "outer") == 0) {
- config->gaps_outer = amount;
- }
- arrange_windows(&root_container, -1, -1);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- } else if (argc == 2 && strcasecmp(argv[0], "edge_gaps") == 0) {
- // gaps edge_gaps <on|off|toggle>
- if (strcasecmp(argv[1], "toggle") == 0) {
- if (config->reading) {
- return cmd_results_new(CMD_FAILURE, "gaps edge_gaps toggle",
- "Can't be used in config file.");
- }
- config->edge_gaps = !config->edge_gaps;
- } else {
- config->edge_gaps =
- (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "on") == 0);
- }
- arrange_windows(&root_container, -1, -1);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- // gaps inner|outer current|all set|plus|minus n
- if (argc < 4 || config->reading) {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
- // gaps inner|outer ...
- const char *inout_str = argv[0];
- enum {INNER, OUTER} inout;
- if (strcasecmp(inout_str, "inner") == 0) {
- inout = INNER;
- } else if (strcasecmp(inout_str, "outer") == 0) {
- inout = OUTER;
- } else {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
-
- // gaps ... current|all ...
- const char *target_str = argv[1];
- enum {CURRENT, WORKSPACE, ALL} target;
- if (strcasecmp(target_str, "current") == 0) {
- target = CURRENT;
- } else if (strcasecmp(target_str, "all") == 0) {
- target = ALL;
- } else if (strcasecmp(target_str, "workspace") == 0) {
- if (inout == OUTER) {
- target = CURRENT;
- } else {
- // Set gap for views in workspace
- target = WORKSPACE;
- }
- } else {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
-
- // gaps ... n
- amount_str = argv[3];
- int amount = (int)strtol(amount_str, NULL, 10);
- if (errno == ERANGE) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
- }
-
- // gaps ... set|plus|minus ...
- const char *method_str = argv[2];
- enum {SET, ADD} method;
- if (strcasecmp(method_str, "set") == 0) {
- method = SET;
- } else if (strcasecmp(method_str, "plus") == 0) {
- method = ADD;
- } else if (strcasecmp(method_str, "minus") == 0) {
- method = ADD;
- amount *= -1;
- } else {
- return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
- }
-
- if (target == CURRENT) {
- swayc_t *cont;
- if (inout == OUTER) {
- if ((cont = swayc_active_workspace()) == NULL) {
- return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
- }
- } else {
- if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
- }
- }
- cont->gaps = swayc_gap(cont);
- if (method == SET) {
- cont->gaps = amount;
- } else if ((cont->gaps += amount) < 0) {
- cont->gaps = 0;
- }
- arrange_windows(cont->parent, -1, -1);
- } else if (inout == OUTER) {
- //resize all workspace.
- int i,j;
- for (i = 0; i < root_container.children->length; ++i) {
- swayc_t *op = root_container.children->items[i];
- for (j = 0; j < op->children->length; ++j) {
- swayc_t *ws = op->children->items[j];
- if (method == SET) {
- ws->gaps = amount;
- } else if ((ws->gaps += amount) < 0) {
- ws->gaps = 0;
- }
- }
- }
- arrange_windows(&root_container, -1, -1);
- } else {
- // Resize gaps for all views in workspace
- swayc_t *top;
- if (target == WORKSPACE) {
- if ((top = swayc_active_workspace()) == NULL) {
- return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
- }
- } else {
- top = &root_container;
- }
- int top_gap = top->gaps;
- container_map(top, method == SET ? set_gaps : add_gaps, &amount);
- top->gaps = top_gap;
- arrange_windows(top, -1, -1);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "none") == 0) {
- config->hide_edge_borders = E_NONE;
- } else if (strcasecmp(argv[0], "vertical") == 0) {
- config->hide_edge_borders = E_VERTICAL;
- } else if (strcasecmp(argv[0], "horizontal") == 0) {
- config->hide_edge_borders = E_HORIZONTAL;
- } else if (strcasecmp(argv[0], "both") == 0) {
- config->hide_edge_borders = E_BOTH;
- } else if (strcasecmp(argv[0], "smart") == 0) {
- config->hide_edge_borders = E_SMART;
- } else {
- return cmd_results_new(CMD_INVALID, "hide_edge_borders",
- "Expected 'hide_edge_borders <none|vertical|horizontal|both>'");
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *cmd_input(int argc, char **argv) {
@@ -11,45 +11,73 @@ struct cmd_results *cmd_input(int argc, char **argv) {
}
if (config->reading && strcmp("{", argv[1]) == 0) {
- current_input_config = new_input_config(argv[0]);
- sway_log(L_DEBUG, "entering input block: %s", current_input_config->identifier);
+ free_input_config(config->handler_context.input_config);
+ config->handler_context.input_config = new_input_config(argv[0]);
+ if (!config->handler_context.input_config) {
+ return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
+ }
+ wlr_log(L_DEBUG, "entering input block: %s", argv[0]);
return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
}
- if (argc > 2) {
- int argc_new = argc-2;
- char **argv_new = argv+2;
-
- struct cmd_results *res;
- current_input_config = new_input_config(argv[0]);
- if (strcasecmp("accel_profile", argv[1]) == 0) {
- res = input_cmd_accel_profile(argc_new, argv_new);
- } else if (strcasecmp("click_method", argv[1]) == 0) {
- res = input_cmd_click_method(argc_new, argv_new);
- } else if (strcasecmp("drag_lock", argv[1]) == 0) {
- res = input_cmd_drag_lock(argc_new, argv_new);
- } else if (strcasecmp("dwt", argv[1]) == 0) {
- res = input_cmd_dwt(argc_new, argv_new);
- } else if (strcasecmp("events", argv[1]) == 0) {
- res = input_cmd_events(argc_new, argv_new);
- } else if (strcasecmp("left_handed", argv[1]) == 0) {
- res = input_cmd_left_handed(argc_new, argv_new);
- } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
- res = input_cmd_middle_emulation(argc_new, argv_new);
- } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
- res = input_cmd_natural_scroll(argc_new, argv_new);
- } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
- res = input_cmd_pointer_accel(argc_new, argv_new);
- } else if (strcasecmp("scroll_method", argv[1]) == 0) {
- res = input_cmd_scroll_method(argc_new, argv_new);
- } else if (strcasecmp("tap", argv[1]) == 0) {
- res = input_cmd_tap(argc_new, argv_new);
- } else {
- res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
+ if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 3))) {
+ return error;
+ }
+
+ bool has_context = (config->handler_context.input_config != NULL);
+ if (!has_context) {
+ // caller did not give a context so create one just for this command
+ config->handler_context.input_config = new_input_config(argv[0]);
+ if (!config->handler_context.input_config) {
+ return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config");
}
- current_input_config = NULL;
- return res;
}
- return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
+ int argc_new = argc-2;
+ char **argv_new = argv+2;
+
+ struct cmd_results *res;
+ if (strcasecmp("accel_profile", argv[1]) == 0) {
+ res = input_cmd_accel_profile(argc_new, argv_new);
+ } else if (strcasecmp("click_method", argv[1]) == 0) {
+ res = input_cmd_click_method(argc_new, argv_new);
+ } else if (strcasecmp("drag_lock", argv[1]) == 0) {
+ res = input_cmd_drag_lock(argc_new, argv_new);
+ } else if (strcasecmp("dwt", argv[1]) == 0) {
+ res = input_cmd_dwt(argc_new, argv_new);
+ } else if (strcasecmp("events", argv[1]) == 0) {
+ res = input_cmd_events(argc_new, argv_new);
+ } else if (strcasecmp("left_handed", argv[1]) == 0) {
+ res = input_cmd_left_handed(argc_new, argv_new);
+ } else if (strcasecmp("middle_emulation", argv[1]) == 0) {
+ res = input_cmd_middle_emulation(argc_new, argv_new);
+ } else if (strcasecmp("natural_scroll", argv[1]) == 0) {
+ res = input_cmd_natural_scroll(argc_new, argv_new);
+ } else if (strcasecmp("pointer_accel", argv[1]) == 0) {
+ res = input_cmd_pointer_accel(argc_new, argv_new);
+ } else if (strcasecmp("scroll_method", argv[1]) == 0) {
+ res = input_cmd_scroll_method(argc_new, argv_new);
+ } else if (strcasecmp("tap", argv[1]) == 0) {
+ res = input_cmd_tap(argc_new, argv_new);
+ } else if (strcasecmp("xkb_layout", argv[1]) == 0) {
+ res = input_cmd_xkb_layout(argc_new, argv_new);
+ } else if (strcasecmp("xkb_model", argv[1]) == 0) {
+ res = input_cmd_xkb_model(argc_new, argv_new);
+ } else if (strcasecmp("xkb_options", argv[1]) == 0) {
+ res = input_cmd_xkb_options(argc_new, argv_new);
+ } else if (strcasecmp("xkb_rules", argv[1]) == 0) {
+ res = input_cmd_xkb_rules(argc_new, argv_new);
+ } else if (strcasecmp("xkb_variant", argv[1]) == 0) {
+ res = input_cmd_xkb_variant(argc_new, argv_new);
+ } else {
+ res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
+ }
+
+ if (!has_context) {
+ // clean up the context we created earlier
+ free_input_config(config->handler_context.input_config);
+ config->handler_context.input_config = NULL;
+ }
+
+ return res;
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "accel_profile",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "adaptive") == 0) {
new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
@@ -22,6 +27,6 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
"Expected 'accel_profile <adaptive|flat>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *input_cmd_click_method(int argc, char **argv) {
- sway_log(L_DEBUG, "click_method for device: %d %s", current_input_config==NULL, current_input_config->identifier);
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "click_method",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "none") == 0) {
new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
@@ -22,9 +26,10 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) {
} else if (strcasecmp(argv[0], "clickfinger") == 0) {
new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
} else {
- return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'");
+ return cmd_results_new(CMD_INVALID, "click_method",
+ "Expected 'click_method <none|button_areas|clickfinger'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "drag_lock", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "drag_lock",
+ "Expected 'drag_lock <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_dwt(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "dwt",
+ "Expected 'dwt <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *input_cmd_events(int argc, char **argv) {
- sway_log(L_DEBUG, "events for device: %s", current_input_config->identifier);
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "events", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "events",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ wlr_log(L_DEBUG, "events for device: %s",
+ current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
} else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
- new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+ new_config->send_events =
+ LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
} else {
- return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
+ return cmd_results_new(CMD_INVALID, "events",
+ "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "left_handed",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->left_handed = 1;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->left_handed = 0;
} else {
- return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "left_handed",
+ "Expected 'left_handed <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <string.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_map_to_output(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "map_to_output",
+ "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->mapped_output = strdup(argv[0]);
+ apply_input_config(new_config);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index 33cdd7d6..a551fd51 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -1,26 +1,33 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "middle_emulation",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
- new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+ new_config->middle_emulation =
+ LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "middle_emulation",
+ "Expected 'middle_emulation <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "natural_scoll",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->natural_scroll = 1;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->natural_scroll = 0;
} else {
- return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "natural_scroll",
+ "Expected 'natural_scroll <enabled|disabled>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <stdlib.h>
#include <string.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE,
+ "pointer_accel", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
float pointer_accel = atof(argv[0]);
if (pointer_accel < -1 || pointer_accel > 1) {
- return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]");
+ return cmd_results_new(CMD_INVALID, "pointer_accel",
+ "Input out of range [-1, 1]");
}
new_config->pointer_accel = pointer_accel;
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
- return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined.");
+ return cmd_results_new(CMD_FAILURE, "scroll_method",
+ "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "none") == 0) {
new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
@@ -22,9 +27,10 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
} else if (strcasecmp(argv[0], "on_button_down") == 0) {
new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
} else {
- return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
+ return cmd_results_new(CMD_INVALID, "scroll_method",
+ "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
}
- input_cmd_apply(new_config);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
+#include "sway/config.h"
#include "sway/commands.h"
-#include "sway/input.h"
+#include "sway/input/input-manager.h"
#include "log.h"
struct cmd_results *input_cmd_tap(int argc, char **argv) {
- sway_log(L_DEBUG, "tap for device: %s", current_input_config->identifier);
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) {
return error;
}
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
if (!current_input_config) {
return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
}
- struct input_config *new_config = new_input_config(current_input_config->identifier);
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
if (strcasecmp(argv[0], "enabled") == 0) {
new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
} else if (strcasecmp(argv[0], "disabled") == 0) {
new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
} else {
- return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap <enabled|disabled>'");
+ return cmd_results_new(CMD_INVALID, "tap",
+ "Expected 'tap <enabled|disabled>'");
}
- sway_log(L_DEBUG, "apply-tap for device: %s", current_input_config->identifier);
- input_cmd_apply(new_config);
+ wlr_log(L_DEBUG, "apply-tap for device: %s",
+ current_input_config->identifier);
+ apply_input_config(new_config);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_layout", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_layout = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s",
+ current_input_config->identifier, new_config->xkb_layout);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_model", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_model = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s",
+ current_input_config->identifier, new_config->xkb_model);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_options", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_options = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s",
+ current_input_config->identifier, new_config->xkb_options);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_rules", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_rules = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s",
+ current_input_config->identifier, new_config->xkb_rules);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ struct input_config *current_input_config =
+ config->handler_context.input_config;
+ if (!current_input_config) {
+ return cmd_results_new(CMD_FAILURE, "xkb_variant", "No input device defined.");
+ }
+ struct input_config *new_config =
+ new_input_config(current_input_config->identifier);
+
+ new_config->xkb_variant = strdup(argv[0]);
+
+ wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s",
+ current_input_config->identifier, new_config->xkb_variant);
+ apply_input_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
-#define _XOPEN_SOURCE 500
-#include <stdio.h>
-#include <string.h>
-#include "sway/security.h"
-#include "sway/commands.h"
-#include "sway/config.h"
-#include "ipc.h"
-#include "log.h"
-#include "util.h"
-
-static struct ipc_policy *current_policy = NULL;
-
-struct cmd_results *cmd_ipc(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 2))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- char *program = NULL;
-
- if (!strcmp(argv[0], "*")) {
- program = strdup(argv[0]);
- } else if (!(program = resolve_path(argv[0]))) {
- return cmd_results_new(
- CMD_INVALID, "ipc", "Unable to resolve IPC Policy target.");
- }
- if (config->reading && strcmp("{", argv[1]) != 0) {
- return cmd_results_new(CMD_INVALID, "ipc",
- "Expected '{' at start of IPC config definition.");
- }
-
- if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "ipc", "Can only be used in config file.");
- }
-
- current_policy = alloc_ipc_policy(program);
- list_add(config->ipc_policies, current_policy);
-
- free(program);
- return cmd_results_new(CMD_BLOCK_IPC, NULL, NULL);
-}
-
-struct cmd_results *cmd_ipc_events(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "events", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (config->reading && strcmp("{", argv[0]) != 0) {
- return cmd_results_new(CMD_INVALID, "events",
- "Expected '{' at start of IPC event config definition.");
- }
-
- if (!config->reading) {
- return cmd_results_new(CMD_FAILURE, "events", "Can only be used in config file.");
- }
-
- return cmd_results_new(CMD_BLOCK_IPC_EVENTS, NULL, NULL);
-}
-
-struct cmd_results *cmd_ipc_cmd(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- bool enabled;
- if (strcmp(argv[0], "enabled") == 0) {
- enabled = true;
- } else if (strcmp(argv[0], "disabled") == 0) {
- enabled = false;
- } else {
- return cmd_results_new(CMD_INVALID, argv[-1],
- "Argument must be one of 'enabled' or 'disabled'");
- }
-
- struct {
- char *name;
- enum ipc_feature type;
- } types[] = {
- { "*", IPC_FEATURE_ALL_COMMANDS },
- { "command", IPC_FEATURE_COMMAND },
- { "workspaces", IPC_FEATURE_GET_WORKSPACES },
- { "outputs", IPC_FEATURE_GET_OUTPUTS },
- { "tree", IPC_FEATURE_GET_TREE },
- { "marks", IPC_FEATURE_GET_MARKS },
- { "bar-config", IPC_FEATURE_GET_BAR_CONFIG },
- { "inputs", IPC_FEATURE_GET_INPUTS },
- { "clipboard", IPC_FEATURE_GET_CLIPBOARD },
- };
-
- uint32_t type = 0;
-
- for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
- if (strcmp(types[i].name, argv[-1]) == 0) {
- type = types[i].type;
- break;
- }
- }
-
- if (enabled) {
- current_policy->features |= type;
- sway_log(L_DEBUG, "Enabled IPC %s feature", argv[-1]);
- } else {
- current_policy->features &= ~type;
- sway_log(L_DEBUG, "Disabled IPC %s feature", argv[-1]);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
-struct cmd_results *cmd_ipc_event_cmd(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "ipc", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- bool enabled;
- if (strcmp(argv[0], "enabled") == 0) {
- enabled = true;
- } else if (strcmp(argv[0], "disabled") == 0) {
- enabled = false;
- } else {
- return cmd_results_new(CMD_INVALID, argv[-1],
- "Argument must be one of 'enabled' or 'disabled'");
- }
-
- struct {
- char *name;
- enum ipc_feature type;
- } types[] = {
- { "*", IPC_FEATURE_ALL_EVENTS },
- { "workspace", IPC_FEATURE_EVENT_WORKSPACE },
- { "output", IPC_FEATURE_EVENT_OUTPUT },
- { "mode", IPC_FEATURE_EVENT_MODE },
- { "window", IPC_FEATURE_EVENT_WINDOW },
- { "binding", IPC_FEATURE_EVENT_BINDING },
- { "input", IPC_FEATURE_EVENT_INPUT },
- };
-
- uint32_t type = 0;
-
- for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
- if (strcmp(types[i].name, argv[-1]) == 0) {
- type = types[i].type;
- break;
- }
- }
-
- if (enabled) {
- current_policy->features |= type;
- sway_log(L_DEBUG, "Enabled IPC %s event", argv[-1]);
- } else {
- current_policy->features &= ~type;
- sway_log(L_DEBUG, "Disabled IPC %s event", argv[-1]);
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <wlr/util/log.h>
+#include "log.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/tree/view.h"
+#include "sway/tree/container.h"
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
struct cmd_results *cmd_kill(int argc, char **argv) {
- if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
+ struct sway_container *con =
+ config->handler_context.current_container;
+
+ container_close(con);
- swayc_t *container = current_container;
- close_views(container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <string.h>
#include <strings.h>
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/layout.h"
-
-/**
- * handle "layout auto" command group
- */
-static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv);
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "log.h"
struct cmd_results *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error;
}
- swayc_t *parent = current_container;
+ struct sway_container *parent = config->handler_context.current_container;
+
+ // TODO: floating
+ /*
if (parent->is_floating) {
return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
}
+ */
while (parent->type == C_VIEW) {
parent = parent->parent;
}
- enum swayc_layouts old_layout = parent->layout;
+ // TODO: stacks and tabs
if (strcasecmp(argv[0], "default") == 0) {
- swayc_change_layout(parent, parent->prev_layout);
+ container_set_layout(parent, parent->prev_layout);
if (parent->layout == L_NONE) {
- swayc_t *output = swayc_parent_by_type(parent, C_OUTPUT);
- swayc_change_layout(parent, default_layout(output));
+ container_set_layout(parent, container_get_default_layout(parent));
}
} else {
if (parent->layout != L_TABBED && parent->layout != L_STACKED) {
parent->prev_layout = parent->layout;
}
- if (strcasecmp(argv[0], "tabbed") == 0) {
- if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)){
- parent = new_container(parent, L_TABBED);
- }
-
- swayc_change_layout(parent, L_TABBED);
- } else if (strcasecmp(argv[0], "stacking") == 0) {
- if (parent->type != C_CONTAINER && !swayc_is_empty_workspace(parent)) {
- parent = new_container(parent, L_STACKED);
- }
-
- swayc_change_layout(parent, L_STACKED);
- } else if (strcasecmp(argv[0], "splith") == 0) {
- swayc_change_layout(parent, L_HORIZ);
+ if (strcasecmp(argv[0], "splith") == 0) {
+ container_set_layout(parent, L_HORIZ);
} else if (strcasecmp(argv[0], "splitv") == 0) {
- swayc_change_layout(parent, L_VERT);
+ container_set_layout(parent, L_VERT);
} else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
|| parent->workspace_layout == L_HORIZ)) {
- swayc_change_layout(parent, L_VERT);
+ container_set_layout(parent, L_VERT);
} else {
- swayc_change_layout(parent, L_HORIZ);
+ container_set_layout(parent, L_HORIZ);
}
- } else if (strcasecmp(argv[0], "auto") == 0) {
- return cmd_layout_auto(parent, argc, argv);
}
}
- update_layout_geometry(parent, old_layout);
- update_geometry(parent);
-
arrange_windows(parent, parent->width, parent->height);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
-
-static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv) {
- // called after checking that argv[0] is auto, so just continue parsing from there
- struct cmd_results *error = NULL;
- const char *cmd_name = "layout auto";
- const char *set_inc_cmd_name = "layout auto [master|ncol] [set|inc]";
- const char *err_msg = "Allowed arguments are <right|left|top|bottom|next|prev|master|ncol>";
-
- bool need_layout_update = false;
- enum swayc_layouts old_layout = container->layout;
- enum swayc_layouts layout = old_layout;
-
- if ((error = checkarg(argc, "layout auto", EXPECTED_MORE_THAN, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[1], "left") == 0) {
- layout = L_AUTO_LEFT;
- } else if (strcasecmp(argv[1], "right") == 0) {
- layout = L_AUTO_RIGHT;
- } else if (strcasecmp(argv[1], "top") == 0) {
- layout = L_AUTO_TOP;
- } else if (strcasecmp(argv[1], "bottom") == 0) {
- layout = L_AUTO_BOTTOM;
- } else if (strcasecmp(argv[1], "next") == 0) {
- if (is_auto_layout(container->layout) && container->layout < L_AUTO_LAST) {
- layout = container->layout + 1;
- } else {
- layout = L_AUTO_FIRST;
- }
- } else if (strcasecmp(argv[1], "prev") == 0) {
- if (is_auto_layout(container->layout) && container->layout > L_AUTO_FIRST) {
- layout = container->layout - 1;
- } else {
- layout = L_AUTO_LAST;
- }
- } else {
- bool is_nmaster;
- bool is_set;
- if (strcasecmp(argv[1], "master") == 0) {
- is_nmaster = true;
- } else if (strcasecmp(argv[1], "ncol") == 0) {
- is_nmaster = false;
- } else {
- return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command. %s",
- cmd_name, err_msg);
- }
- if ((error = checkarg(argc, "auto <master|ncol>", EXPECTED_EQUAL_TO, 4))) {
- return error;
- }
- if (strcasecmp(argv[2], "set") == 0) {
- is_set = true;
- } else if (strcasecmp(argv[2], "inc") == 0) {
- is_set = false;
- } else {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command. %s, "
- "Argument must be on of <set|inc>",
- set_inc_cmd_name);
- }
- char *end;
- int n = (int)strtol(argv[3], &end, 10);
- if (*end) {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
- "(argument must be an integer)", set_inc_cmd_name);
- }
- if (is_auto_layout(container->layout)) {
- int inc = 0; /* difference between current master/ncol and requested value */
- if (is_nmaster) {
- if (is_set) {
- if (n < 0) {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
- "(master must be >= 0)", set_inc_cmd_name);
- }
- inc = n - (int)container->nb_master;
- } else { /* inc command */
- if ((int)container->nb_master + n >= 0) {
- inc = n;
- }
- }
- if (inc) {
- for (int i = container->nb_master;
- i >= 0 && i < container->children->length
- && i != (int)container->nb_master + inc;) {
- ((swayc_t *)container->children->items[i])->height = -1;
- ((swayc_t *)container->children->items[i])->width = -1;
- i += inc > 0 ? 1 : -1;
- }
- container->nb_master += inc;
- need_layout_update = true;
- }
- } else { /* ncol modification */
- if (is_set) {
- if (n <= 0) {
- return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
- "(ncol must be > 0)", set_inc_cmd_name);
- }
- inc = n - (int)container->nb_slave_groups;
- } else { /* inc command */
- if ((int)container->nb_slave_groups + n > 0) {
- inc = n;
- }
- }
- if (inc) {
- container->nb_slave_groups += inc;
- need_layout_update = true;
- }
- }
- }
- }
-
- if (layout != old_layout) {
- swayc_change_layout(container, layout);
- update_layout_geometry(container, old_layout);
- need_layout_update = true;
- }
- if (need_layout_update) {
- update_geometry(container);
- arrange_windows(container, container->width, container->height);
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "log.h"
-
-struct cmd_results *cmd_log_colors(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
- if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- if (strcasecmp(argv[0], "no") == 0) {
- sway_log_colors(0);
- } else if (strcasecmp(argv[0], "yes") == 0) {
- sway_log_colors(1);
- } else {
- error = cmd_results_new(CMD_FAILURE, "log_colors",
- "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
- return error;
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include <stdbool.h>
-#include "sway/commands.h"
-#include "list.h"
-#include "stringop.h"
-
-static void find_marks_callback(swayc_t *container, void *_mark) {
- char *mark = (char *)_mark;
-
- int index;
- if (container->marks && ((index = list_seq_find(container->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1)) {
- list_del(container->marks, index);
- }
-}
-
-struct cmd_results *cmd_mark(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "mark", "Can't be used in config file.");
- if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- swayc_t *view = current_container;
- bool add = false;
- bool toggle = false;
-
- if (strcmp(argv[0], "--add") == 0) {
- --argc; ++argv;
- add = true;
- } else if (strcmp(argv[0], "--replace") == 0) {
- --argc; ++argv;
- }
-
- if (argc && strcmp(argv[0], "--toggle") == 0) {
- --argc; ++argv;
- toggle = true;
- }
-
- if (argc) {
- char *mark = join_args(argv, argc);
-
- // Remove all existing marks of this type
- container_map(&root_container, find_marks_callback, mark);
-
- if (view->marks) {
- if (add) {
- int index;
- if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
- if (toggle) {
- free(view->marks->items[index]);
- list_del(view->marks, index);
-
- if (0 == view->marks->length) {
- list_free(view->marks);
- view->marks = NULL;
- }
- }
- free(mark);
- } else {
- list_add(view->marks, mark);
- }
- } else {
- if (toggle && list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark) != -1) {
- // Delete the list
- list_foreach(view->marks, free);
- list_free(view->marks);
- view->marks = NULL;
- } else {
- // Delete and replace with a new list
- list_foreach(view->marks, free);
- list_free(view->marks);
-
- view->marks = create_list();
- list_add(view->marks, mark);
- }
- }
- } else {
- view->marks = create_list();
- list_add(view->marks, mark);
- }
- } else {
- return cmd_results_new(CMD_FAILURE, "mark",
- "Expected 'mark [--add|--replace] [--toggle] <mark>'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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) {
}
const char *mode_name = argv[0];
- bool mode_make = (argc == 2 && strcmp(argv[1], "{") == 0);
- if (mode_make) {
- if (!config->reading)
- return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file.");
+ bool new_mode = (argc == 2 && strcmp(argv[1], "{") == 0);
+ if (new_mode && !config->reading) {
+ return cmd_results_new(CMD_FAILURE,
+ "mode", "Can only be used in config file.");
}
struct sway_mode *mode = NULL;
// Find mode
- int i, len = config->modes->length;
- for (i = 0; i < len; ++i) {
- struct sway_mode *find = config->modes->items[i];
- if (strcasecmp(find->name, mode_name) == 0) {
- mode = find;
+ for (int i = 0; i < config->modes->length; ++i) {
+ struct sway_mode *test = config->modes->items[i];
+ if (strcasecmp(test->name, mode_name) == 0) {
+ mode = test;
break;
}
}
// Create mode if it doesn't exist
- if (!mode && mode_make) {
- mode = malloc(sizeof(struct sway_mode));
+ if (!mode && new_mode) {
+ mode = calloc(1, sizeof(struct sway_mode));
if (!mode) {
- return cmd_results_new(CMD_FAILURE, "mode", "Unable to allocate mode");
+ return cmd_results_new(CMD_FAILURE,
+ "mode", "Unable to allocate mode");
}
mode->name = strdup(mode_name);
- mode->bindings = create_list();
+ mode->keysym_bindings = create_list();
+ mode->keycode_bindings = create_list();
list_add(config->modes, mode);
}
if (!mode) {
- error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name);
+ error = cmd_results_new(CMD_INVALID,
+ "mode", "Unknown mode `%s'", mode_name);
return error;
}
- if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
- sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
+ if ((config->reading && new_mode) || (!config->reading && !new_mode)) {
+ wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name);
}
// Set current mode
config->current_mode = mode;
- if (!mode_make) {
+ if (!new_mode) {
// trigger IPC mode event
ipc_event_mode(config->current_mode->name);
}
- return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
+ return cmd_results_new(new_mode ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
}
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) {
} else if (strcasecmp(argv[0], "none") == 0) {
config->mouse_warping = false;
} else {
- return cmd_results_new(CMD_FAILURE, "mouse_warping", "Expected 'mouse_warping output|none'");
+ return cmd_results_new(CMD_FAILURE, "mouse_warping",
+ "Expected 'mouse_warping output|none'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
+
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 @@
+#define _XOPEN_SOURCE 500
#include <string.h>
#include <strings.h>
-#include <wlc/wlc.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/util/log.h>
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/layout.h"
+#include "sway/input/seat.h"
#include "sway/output.h"
-#include "sway/workspace.h"
-#include "list.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/workspace.h"
#include "stringop.h"
+#include "list.h"
+
+static const char* expected_syntax =
+ "Expected 'move <left|right|up|down> <[px] px>' or "
+ "'move <container|window> to workspace <name>' or "
+ "'move <container|window|workspace> to output <name|direction>' or "
+ "'move position mouse'";
+
+static struct sway_container *output_in_direction(const char *direction,
+ struct wlr_output *reference, int ref_ox, int ref_oy) {
+ int ref_lx = ref_ox + reference->lx,
+ ref_ly = ref_oy + reference->ly;
+ struct {
+ char *name;
+ enum wlr_direction direction;
+ } names[] = {
+ { "up", WLR_DIRECTION_UP },
+ { "down", WLR_DIRECTION_DOWN },
+ { "left", WLR_DIRECTION_LEFT },
+ { "right", WLR_DIRECTION_RIGHT },
+ };
+ for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
+ if (strcasecmp(names[i].name, direction) == 0) {
+ struct wlr_output *adjacent = wlr_output_layout_adjacent_output(
+ root_container.sway_root->output_layout,
+ names[i].direction, reference, ref_lx, ref_ly);
+ if (adjacent) {
+ struct sway_output *sway_output = adjacent->data;
+ return sway_output->swayc;
+ }
+ break;
+ }
+ }
+ return output_by_name(direction);
+}
+
+static struct cmd_results *cmd_move_container(struct sway_container *current,
+ int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "move container/window",
+ EXPECTED_AT_LEAST, 4))) {
+ return error;
+ } else if (strcasecmp(argv[1], "to") == 0
+ && strcasecmp(argv[2], "workspace") == 0) {
+ // move container to workspace x
+ if (current->type == C_WORKSPACE) {
+ // TODO: Wrap children in a container and move that
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+ } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
+ return cmd_results_new(CMD_FAILURE, "move",
+ "Can only move containers and views.");
+ }
+ struct sway_container *ws;
+ char *ws_name = NULL;
+ if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
+ // move "container to workspace number x"
+ ws_name = strdup(argv[4]);
+ ws = workspace_by_number(ws_name);
+ } else {
+ ws_name = join_args(argv + 3, argc - 3);
+ ws = workspace_by_name(ws_name);
+ }
+
+ if (config->auto_back_and_forth && prev_workspace_name) {
+ // auto back and forth move
+ struct sway_container *curr_ws = container_parent(current, C_WORKSPACE);
+ if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) {
+ // if target workspace is the current one
+ free(ws_name);
+ ws_name = strdup(prev_workspace_name);
+ ws = workspace_by_name(ws_name);
+ }
+ }
+
+ if (!ws) {
+ ws = workspace_create(NULL, ws_name);
+ }
+ free(ws_name);
+ struct sway_container *old_parent = current->parent;
+ struct sway_container *focus = seat_get_focus_inactive(
+ config->handler_context.seat, ws);
+ container_move_to(current, focus);
+ seat_set_focus(config->handler_context.seat, old_parent);
+ container_reap_empty(old_parent);
+ container_reap_empty(focus->parent);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ } else if (strcasecmp(argv[1], "to") == 0
+ && strcasecmp(argv[2], "output") == 0) {
+ if (current->type == C_WORKSPACE) {
+ // TODO: Wrap children in a container and move that
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
+ } else if (current->type != C_CONTAINER
+ && current->type != C_VIEW) {
+ return cmd_results_new(CMD_FAILURE, "move",
+ "Can only move containers and views.");
+ }
+ struct sway_container *source = container_parent(current, C_OUTPUT);
+ struct sway_container *destination = output_in_direction(argv[3],
+ source->sway_output->wlr_output, current->x, current->y);
+ if (!destination) {
+ return cmd_results_new(CMD_FAILURE, "move workspace",
+ "Can't find output with name/direction '%s'", argv[3]);
+ }
+ struct sway_container *focus = seat_get_focus_inactive(
+ config->handler_context.seat, destination);
+ if (!focus) {
+ // We've never been to this output before
+ focus = destination->children->items[0];
+ }
+ struct sway_container *old_parent = current->parent;
+ container_move_to(current, focus);
+ seat_set_focus(config->handler_context.seat, old_parent);
+ container_reap_empty(old_parent);
+ container_reap_empty(focus->parent);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ }
+ return cmd_results_new(CMD_INVALID, "move", expected_syntax);
+}
+
+static struct cmd_results *cmd_move_workspace(struct sway_container *current,
+ int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
+ return error;
+ } else if (strcasecmp(argv[1], "to") != 0
+ || strcasecmp(argv[2], "output") != 0) {
+ return cmd_results_new(CMD_INVALID, "move", expected_syntax);
+ }
+ struct sway_container *source = container_parent(current, C_OUTPUT);
+ int center_x = current->width / 2 + current->x,
+ center_y = current->height / 2 + current->y;
+ struct sway_container *destination = output_in_direction(argv[3],
+ source->sway_output->wlr_output, center_x, center_y);
+ if (!destination) {
+ return cmd_results_new(CMD_FAILURE, "move workspace",
+ "Can't find output with name/direction '%s'", argv[3]);
+ }
+ if (current->type != C_WORKSPACE) {
+ current = container_parent(current, C_WORKSPACE);
+ }
+ container_move_to(current, destination);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
struct cmd_results *cmd_move(int argc, char **argv) {
struct cmd_results *error = NULL;
int move_amt = 10;
-
- if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
return error;
}
- const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first> <[px] px>' or "
- "'move <container|window> to workspace <name>' or "
- "'move <container|window|workspace> to output <name|direction>' or "
- "'move position mouse'";
- swayc_t *view = current_container;
+ struct sway_container *current = config->handler_context.current_container;
- if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) {
+ if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) {
char *inv;
move_amt = (int)strtol(argv[1], &inv, 10);
if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
- move_amt = 10;
+ return cmd_results_new(CMD_FAILURE, "move",
+ "Invalid distance specified");
}
}
if (strcasecmp(argv[0], "left") == 0) {
- move_container(view, MOVE_LEFT, move_amt);
+ container_move(current, MOVE_LEFT, move_amt);
} else if (strcasecmp(argv[0], "right") == 0) {
- move_container(view, MOVE_RIGHT, move_amt);
+ container_move(current, MOVE_RIGHT, move_amt);
} else if (strcasecmp(argv[0], "up") == 0) {
- move_container(view, MOVE_UP, move_amt);
+ container_move(current, MOVE_UP, move_amt);
} else if (strcasecmp(argv[0], "down") == 0) {
- move_container(view, MOVE_DOWN, move_amt);
- } else if (strcasecmp(argv[0], "next") == 0) {
- move_container(view, MOVE_NEXT, move_amt);
- } else if (strcasecmp(argv[0], "prev") == 0) {
- move_container(view, MOVE_PREV, move_amt);
- } else if (strcasecmp(argv[0], "first") == 0) {
- move_container(view, MOVE_FIRST, move_amt);
- } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
- // "move container ...
- if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
- return error;
- } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "workspace") == 0) {
- // move container to workspace x
- if (view->type == C_WORKSPACE) {
- if (!view->children || view->children->length == 0) {
- return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
- }
- view = new_container(view, view->workspace_layout);
- } if (view->type != C_CONTAINER && view->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
- }
-
- swayc_t *ws;
- const char *num_name = NULL;
- char *ws_name = NULL;
- if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
- // move "container to workspace number x"
- num_name = argv[4];
- ws = workspace_by_number(num_name);
- } else {
- ws_name = join_args(argv + 3, argc - 3);
- ws = workspace_by_name(ws_name);
- }
-
- if (ws == NULL) {
- ws = workspace_create(ws_name ? ws_name : num_name);
- }
- if (ws_name) {
- free(ws_name);
- }
- move_container_to(view, get_focused_container(ws));
- } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
- // move container to output x
- swayc_t *output = NULL;
- struct wlc_point abs_pos;
- get_absolute_center_position(view, &abs_pos);
- if (view->type == C_WORKSPACE) {
- if (!view->children || view->children->length == 0) {
- return cmd_results_new(CMD_FAILURE, "move", "Cannot move an empty workspace");
- }
- view = new_container(view, view->workspace_layout);
- } else if (view->type != C_CONTAINER && view->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
- } else if (!(output = output_by_name(argv[3], &abs_pos))) {
- return cmd_results_new(CMD_FAILURE, "move",
- "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
- }
-
- swayc_t *container = get_focused_container(output);
- if (container->is_floating) {
- move_container_to(view, container->parent);
- } else {
- move_container_to(view, container);
- }
- } else {
- return cmd_results_new(CMD_INVALID, "move", expected_syntax);
- }
+ container_move(current, MOVE_DOWN, move_amt);
+ } else if (strcasecmp(argv[0], "container") == 0
+ || strcasecmp(argv[0], "window") == 0) {
+ return cmd_move_container(current, argc, argv);
} else if (strcasecmp(argv[0], "workspace") == 0) {
- // move workspace (to output x)
- swayc_t *output = NULL;
- struct wlc_point abs_pos;
- get_absolute_center_position(view, &abs_pos);
- if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
- return error;
- } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) {
- return cmd_results_new(CMD_INVALID, "move", expected_syntax);
- } else if (!(output = output_by_name(argv[3], &abs_pos))) {
- return cmd_results_new(CMD_FAILURE, "move workspace",
- "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
- }
- if (view->type == C_WORKSPACE) {
- // This probably means we're moving an empty workspace, but
- // that's fine.
- move_workspace_to(view, output);
- } else {
- swayc_t *workspace = swayc_parent_by_type(view, C_WORKSPACE);
- move_workspace_to(workspace, output);
- }
- } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && strcasecmp(argv[1], "scratchpad") == 0)) {
- // move scratchpad ...
- if (view->type != C_CONTAINER && view->type != C_VIEW) {
- return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
- }
- swayc_t *view = current_container;
- int i;
- for (i = 0; i < scratchpad->length; i++) {
- if (scratchpad->items[i] == view) {
- hide_view_in_scratchpad(view);
- sp_view = NULL;
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- }
- list_add(scratchpad, view);
- if (!view->is_floating) {
- destroy_container(remove_child(view));
- } else {
- remove_child(view);
- }
- wlc_view_set_mask(view->handle, 0);
- arrange_windows(swayc_active_workspace(), -1, -1);
- swayc_t *focused = container_under_pointer();
- if (focused == NULL) {
- focused = swayc_active_workspace();
- }
- set_focused_container(focused);
+ return cmd_move_workspace(current, argc, argv);
+ } else if (strcasecmp(argv[0], "scratchpad") == 0
+ || (strcasecmp(argv[0], "to") == 0
+ && strcasecmp(argv[1], "scratchpad") == 0)) {
+ // TODO: scratchpad
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
} else if (strcasecmp(argv[0], "position") == 0) {
- if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 2))) {
- return error;
- }
- if (strcasecmp(argv[1], "mouse")) {
- return cmd_results_new(CMD_INVALID, "move", expected_syntax);
- }
-
- if (view->is_floating) {
- swayc_t *output = swayc_parent_by_type(view, C_OUTPUT);
- struct wlc_geometry g;
- wlc_view_get_visible_geometry(view->handle, &g);
- const struct wlc_size *size = wlc_output_get_resolution(output->handle);
-
- double x_pos, y_pos;
- wlc_pointer_get_position_v2(&x_pos, &y_pos);
-
- int32_t x = x_pos - g.size.w / 2;
- int32_t y = y_pos - g.size.h / 2;
-
- uint32_t w = size->w - g.size.w;
- uint32_t h = size->h - g.size.h;
-
- view->x = g.origin.x = MIN((int32_t)w, MAX(x, 0));
- view->y = g.origin.y = MIN((int32_t)h, MAX(y, 0));
-
- wlc_view_set_geometry(view->handle, 0, &g);
- }
+ // TODO: floating
+ return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
} else {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
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 @@
-#include "log.h"
-#include "sway/commands.h"
-
-struct cmd_results *cmd_new_float(int argc, char **argv) {
- sway_log(L_INFO, "`new_float` is deprecated and will be removed in the future. "
- "Please use `default_floating_border` instead.");
- return cmd_default_floating_border(argc, argv);
-}
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 @@
-#include "log.h"
-#include "sway/commands.h"
-
-struct cmd_results *cmd_new_window(int argc, char **argv) {
- sway_log(L_INFO, "`new_window` is deprecated and will be removed in the future. "
- "Please use `default_border` instead.");
- return cmd_default_border(argc, argv);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/criteria.h"
-#include "list.h"
-#include "log.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_no_focus(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "no_focus", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- // add command to a criteria/command pair that is run against views when they appear.
- char *criteria = argv[0];
-
- struct criteria *crit = malloc(sizeof(struct criteria));
- if (!crit) {
- return cmd_results_new(CMD_FAILURE, "no_focus", "Unable to allocate criteria");
- }
- crit->crit_raw = strdup(criteria);
- crit->tokens = create_list();
- crit->cmdlist = NULL;
- char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
-
- if (err_str) {
- error = cmd_results_new(CMD_INVALID, "no_focus", err_str);
- free(err_str);
- free_criteria(crit);
- } else if (crit->tokens->length == 0) {
- error = cmd_results_new(CMD_INVALID, "no_focus", "Found no name/value pairs in criteria");
- free_criteria(crit);
- } else if (list_seq_find(config->no_focus, criteria_cmp, crit) != -1) {
- sway_log(L_DEBUG, "no_focus: Duplicate, skipping.");
- free_criteria(crit);
- } else {
- sway_log(L_DEBUG, "no_focus: '%s' added", crit->crit_raw);
- list_add(config->no_focus, crit);
- }
- return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <assert.h>
+#include <stdlib.h>
+#include "sway/commands.h"
+#include "sway/tree/view.h"
+#include "log.h"
+
+static bool parse_opacity(const char *opacity, float *val) {
+ char *err;
+ *val = strtof(opacity, &err);
+ if (*val < 0 || *val > 1 || *err) {
+ return false;
+ }
+ return true;
+}
+
+struct cmd_results *cmd_opacity(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "layout", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+
+ struct sway_container *con =
+ config->handler_context.current_container;
+
+ float opacity = 0.0f;
+
+ if (!parse_opacity(argv[0], &opacity)) {
+ return cmd_results_new(CMD_INVALID, "opacity <value>",
+ "Invalid value (expected 0..1): %s", argv[0]);
+ }
+
+ con->alpha = opacity;
+ container_damage_whole(con);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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[] = {
"center",
"fill",
"fit",
- "tile"
+ "tile",
};
-struct cmd_results *cmd_output(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
- return error;
+static struct cmd_results *cmd_output_mode(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output", "Missing mode argument.");
}
- const char *name = argv[0];
- struct output_config *output = calloc(1, sizeof(struct output_config));
- if (!output) {
- return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config");
+ char *end;
+ output->width = strtol(argv[*i], &end, 10);
+ if (*end) {
+ // Format is 1234x4321
+ if (*end != 'x') {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode width.");
+ }
+ ++end;
+ output->height = strtol(end, &end, 10);
+ if (*end) {
+ if (*end != '@') {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode height.");
+ }
+ ++end;
+ output->refresh_rate = strtof(end, &end);
+ if (strcasecmp("Hz", end) != 0) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode refresh rate.");
+ }
+ }
+ } else {
+ // Format is 1234 4321
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing mode argument (height).");
+ }
+ output->height = strtol(argv[*i], &end, 10);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid mode height.");
+ }
}
- output->x = output->y = output->width = output->height = -1;
- output->name = strdup(name);
- output->enabled = -1;
- output->scale = 1;
- // TODO: atoi doesn't handle invalid numbers
+ return NULL;
+}
- int i;
- for (i = 1; i < argc; ++i) {
- const char *command = argv[i];
+static struct cmd_results *cmd_output_position(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing position argument.");
+ }
- if (strcasecmp(command, "disable") == 0) {
- output->enabled = 0;
- } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) {
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument.");
- goto fail;
- }
- char *res = argv[i];
- char *x = strchr(res, 'x');
- int width = -1, height = -1;
- if (x != NULL) {
- // Format is 1234x4321
- *x = '\0';
- width = atoi(res);
- height = atoi(x + 1);
- *x = 'x';
- } else {
- // Format is 1234 4321
- width = atoi(res);
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height).");
- goto fail;
+ char *end;
+ output->x = strtol(argv[*i], &end, 10);
+ if (*end) {
+ // Format is 1234,4321
+ if (*end != ',') {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid position x.");
+ }
+ ++end;
+ output->y = strtol(end, &end, 10);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid position y.");
+ }
+ } else {
+ // Format is 1234 4321 (legacy)
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing position argument (y).");
+ }
+ output->y = strtol(argv[*i], &end, 10);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid position y.");
+ }
+ }
+
+ return NULL;
+}
+
+static struct cmd_results *cmd_output_scale(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing scale argument.");
+ }
+
+ char *end;
+ output->scale = strtof(argv[*i], &end);
+ if (*end) {
+ return cmd_results_new(CMD_INVALID, "output", "Invalid scale.");
+ }
+
+ return NULL;
+}
+
+static struct cmd_results *cmd_output_transform(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing transform argument.");
+ }
+
+ char *value = argv[*i];
+ if (strcmp(value, "normal") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ } else if (strcmp(value, "90") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_90;
+ } else if (strcmp(value, "180") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_180;
+ } else if (strcmp(value, "270") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_270;
+ } else if (strcmp(value, "flipped") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
+ } else if (strcmp(value, "flipped-90") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
+ } else if (strcmp(value, "flipped-180") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
+ } else if (strcmp(value, "flipped-270") == 0) {
+ output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
+ } else {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid output transform.");
+ }
+
+ return NULL;
+}
+
+static struct cmd_results *cmd_output_background(struct output_config *output,
+ int *i, int argc, char **argv) {
+ if (++*i >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing background file or color specification.");
+ }
+ const char *background = argv[*i];
+ if (*i + 1 >= argc) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing background scaling mode or `solid_color`.");
+ }
+ const char *background_option = argv[*i];
+
+ if (strcasecmp(background_option, "solid_color") == 0) {
+ output->background = strdup(background);
+ output->background_option = strdup("solid_color");
+ } else {
+ bool valid = false;
+ char *mode;
+ size_t j;
+ for (j = 0; j < (size_t)(argc - *i); ++j) {
+ mode = argv[*i + j];
+ size_t n = sizeof(bg_options) / sizeof(char *);
+ for (size_t k = 0; k < n; ++k) {
+ if (strcasecmp(mode, bg_options[k]) == 0) {
+ valid = true;
+ break;
}
- res = argv[i];
- height = atoi(res);
}
- output->width = width;
- output->height = height;
- } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
- goto fail;
+ if (valid) {
+ break;
}
- char *res = argv[i];
- char *c = strchr(res, ',');
- int x = -1, y = -1;
- if (c != NULL) {
- // Format is 1234,4321
- *c = '\0';
- x = atoi(res);
- y = atoi(c + 1);
- *c = ',';
- } else {
- // Format is 1234 4321
- x = atoi(res);
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y).");
- goto fail;
+ }
+ if (!valid) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Missing background scaling mode.");
+ }
+
+ wordexp_t p;
+ char *src = join_args(argv + *i, j);
+ if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
+ return cmd_results_new(CMD_INVALID, "output",
+ "Invalid syntax (%s).", src);
+ }
+ free(src);
+ src = p.we_wordv[0];
+ if (config->reading && *src != '/') {
+ char *conf = strdup(config->current_config);
+ if (conf) {
+ char *conf_path = dirname(conf);
+ src = malloc(strlen(conf_path) + strlen(src) + 2);
+ if (src) {
+ sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
+ } else {
+ wlr_log(L_ERROR,
+ "Unable to allocate background source");
}
- res = argv[i];
- y = atoi(res);
- }
- output->x = x;
- output->y = y;
- } else if (strcasecmp(command, "scale") == 0) {
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter.");
- goto fail;
- }
- output->scale = atoi(argv[i]);
- } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
- wordexp_t p;
- if (++i >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
- goto fail;
- }
- if (i + 1 >= argc) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
- goto fail;
- }
- if (strcasecmp(argv[i + 1], "solid_color") == 0) {
- output->background = strdup(argv[argc - 2]);
- output->background_option = strdup("solid_color");
+ free(conf);
} else {
- // argv[i+j]=bg_option
- bool valid = false;
- char *mode;
- size_t j;
- for (j = 0; j < (size_t) (argc - i); ++j) {
- mode = argv[i + j];
- for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) {
- if (strcasecmp(mode, bg_options[k]) == 0) {
- valid = true;
- break;
- }
- }
- if (valid) {
- break;
- }
- }
- if (!valid) {
- error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
- goto fail;
- }
+ wlr_log(L_ERROR, "Unable to allocate background source");
+ }
+ }
+ if (!src || access(src, F_OK) == -1) {
+ wordfree(&p);
+ return cmd_results_new(CMD_INVALID, "output",
+ "Background file unreadable (%s).", src);
+ }
- char *src = join_args(argv + i, j);
- if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
- error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src);
- goto fail;
- }
- free(src);
- src = p.we_wordv[0];
- if (config->reading && *src != '/') {
- char *conf = strdup(config->current_config);
- if (conf) {
- char *conf_path = dirname(conf);
- src = malloc(strlen(conf_path) + strlen(src) + 2);
- if (src) {
- sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
- } else {
- sway_log(L_ERROR, "Unable to allocate background source");
- }
- free(conf);
- } else {
- sway_log(L_ERROR, "Unable to allocate background source");
- }
- }
- if (!src || access(src, F_OK) == -1) {
- error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
- wordfree(&p);
- goto fail;
- }
+ output->background = strdup(src);
+ output->background_option = strdup(mode);
+ if (src != p.we_wordv[0]) {
+ free(src);
+ }
+ wordfree(&p);
- output->background = strdup(src);
- output->background_option = strdup(mode);
- if (src != p.we_wordv[0]) {
- free(src);
- }
- wordfree(&p);
+ *i += j;
+ }
- i += j;
- }
+ return NULL;
+}
+
+struct cmd_results *cmd_output(int argc, char **argv) {
+ struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1);
+ if (error != NULL) {
+ return error;
+ }
+
+ struct output_config *output = new_output_config(argv[0]);
+ if (!output) {
+ wlr_log(L_ERROR, "Failed to allocate output config");
+ return NULL;
+ }
+
+ for (int i = 1; i < argc; ++i) {
+ const char *command = argv[i];
+
+ if (strcasecmp(command, "enable") == 0) {
+ output->enabled = 1;
+ } else if (strcasecmp(command, "disable") == 0) {
+ output->enabled = 0;
+ } else if (strcasecmp(command, "mode") == 0 ||
+ strcasecmp(command, "resolution") == 0 ||
+ strcasecmp(command, "res") == 0) {
+ error = cmd_output_mode(output, &i, argc, argv);
+ } else if (strcasecmp(command, "position") == 0 ||
+ strcasecmp(command, "pos") == 0) {
+ error = cmd_output_position(output, &i, argc, argv);
+ } else if (strcasecmp(command, "scale") == 0) {
+ error = cmd_output_scale(output, &i, argc, argv);
+ } else if (strcasecmp(command, "transform") == 0) {
+ error = cmd_output_transform(output, &i, argc, argv);
+ } else if (strcasecmp(command, "background") == 0 ||
+ strcasecmp(command, "bg") == 0) {
+ error = cmd_output_background(output, &i, argc, argv);
+ } else {
+ error = cmd_results_new(CMD_INVALID, "output",
+ "Invalid output subcommand: %s.", command);
+ }
+
+ if (error != NULL) {
+ goto fail;
}
}
- i = list_seq_find(config->output_configs, output_name_cmp, name);
+ int i = list_seq_find(config->output_configs, output_name_cmp, output->name);
if (i >= 0) {
- // merge existing config
- struct output_config *oc = config->output_configs->items[i];
- merge_output_config(oc, output);
+ // Merge existing config
+ struct output_config *current = config->output_configs->items[i];
+ merge_output_config(current, output);
free_output_config(output);
- output = oc;
+ output = current;
} else {
list_add(config->output_configs, output);
}
- sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ %d, %d scale %d) (bg %s %s)",
- output->name, output->enabled, output->width,
- output->height, output->x, output->y, output->scale,
- output->background, output->background_option);
+ wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
+ "position %d,%d scale %f transform %d) (bg %s %s)",
+ output->name, output->enabled, output->width, output->height,
+ output->refresh_rate, output->x, output->y, output->scale,
+ output->transform, output->background, output->background_option);
- if (output->name) {
- // Try to find the output container and apply configuration now. If
- // this is during startup then there will be no container and config
- // will be applied during normal "new output" event from wlc.
- swayc_t *cont = NULL;
- for (int i = 0; i < root_container.children->length; ++i) {
- cont = root_container.children->items[i];
- if (cont->name && ((strcmp(cont->name, output->name) == 0) || (strcmp(output->name, "*") == 0))) {
- apply_output_config(output, cont);
+ // Try to find the output container and apply configuration now. If
+ // this is during startup then there will be no container and config
+ // will be applied during normal "new output" event from wlroots.
+ char identifier[128];
+ bool all = strcmp(output->name, "*") == 0;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *cont = root_container.children->items[i];
+ if (cont->type != C_OUTPUT) {
+ continue;
+ }
- if (strcmp(output->name, "*") != 0) {
- // stop looking if the output config isn't applicable to all outputs
- break;
- }
+ output_get_identifier(identifier, sizeof(identifier), cont->sway_output);
+ if (all || strcmp(cont->name, output->name) == 0 ||
+ strcmp(identifier, output->name) == 0) {
+ apply_output_config(output, cont);
+
+ if (!all) {
+ // Stop looking if the output config isn't applicable to all
+ // outputs
+ break;
}
}
}
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 @@
-#define _XOPEN_SOURCE 500
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/config.h"
-#include "sway/security.h"
-#include "util.h"
-#include "log.h"
-
-static enum secure_feature get_features(int argc, char **argv,
- struct cmd_results **error) {
- enum secure_feature features = 0;
-
- struct {
- char *name;
- enum secure_feature feature;
- } feature_names[] = {
- { "lock", FEATURE_LOCK },
- { "panel", FEATURE_PANEL },
- { "background", FEATURE_BACKGROUND },
- { "screenshot", FEATURE_SCREENSHOT },
- { "fullscreen", FEATURE_FULLSCREEN },
- { "keyboard", FEATURE_KEYBOARD },
- { "mouse", FEATURE_MOUSE },
- };
-
- for (int i = 1; i < argc; ++i) {
- size_t j;
- for (j = 0; j < sizeof(feature_names) / sizeof(feature_names[0]); ++j) {
- if (strcmp(feature_names[j].name, argv[i]) == 0) {
- break;
- }
- }
- if (j == sizeof(feature_names) / sizeof(feature_names[0])) {
- *error = cmd_results_new(CMD_INVALID,
- "permit", "Invalid feature grant %s", argv[i]);
- return 0;
- }
- features |= feature_names[j].feature;
- }
- return features;
-}
-
-struct cmd_results *cmd_permit(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "permit", EXPECTED_MORE_THAN, 1))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- bool assign_perms = true;
- char *program = NULL;
-
- if (!strcmp(argv[0], "*")) {
- program = strdup(argv[0]);
- } else {
- program = resolve_path(argv[0]);
- }
- if (!program) {
- sway_assert(program, "Unable to resolve IPC permit target '%s'."
- " will issue empty policy", argv[0]);
- assign_perms = false;
- program = strdup(argv[0]);
- }
-
- struct feature_policy *policy = get_feature_policy(program);
- if (policy && assign_perms) {
- policy->features |= get_features(argc, argv, &error);
- sway_log(L_DEBUG, "Permissions granted to %s for features %d",
- policy->program, policy->features);
- }
-
- free(program);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
-
-struct cmd_results *cmd_reject(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "reject", EXPECTED_MORE_THAN, 1))) {
- return error;
- }
- if ((error = check_security_config())) {
- return error;
- }
-
- char *program = NULL;
- if (!strcmp(argv[0], "*")) {
- program = strdup(argv[0]);
- } else {
- program = resolve_path(argv[0]);
- }
- if (!program) {
- // Punt
- sway_log(L_INFO, "Unable to resolve IPC reject target '%s'."
- " Will use provided path", argv[0]);
- program = strdup(argv[0]);
- }
-
- struct feature_policy *policy = get_feature_policy(program);
- policy->features &= ~get_features(argc, argv, &error);
-
- sway_log(L_DEBUG, "Permissions granted to %s for features %d",
- policy->program, policy->features);
-
- free(program);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/layout.h"
+#include "sway/tree/layout.h"
struct cmd_results *cmd_reload(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
return error;
}
@@ -13,7 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
}
load_swaybars();
-
arrange_windows(&root_container, -1, -1);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
-#include <wlc/wlc.h>
+#include <wlr/util/log.h>
#include "sway/commands.h"
-#include "sway/layout.h"
-#include "sway/focus.h"
-#include "sway/input_state.h"
-#include "sway/handlers.h"
+#include "sway/tree/layout.h"
#include "log.h"
-enum resize_dim_types {
- RESIZE_DIM_PX,
- RESIZE_DIM_PPT,
- RESIZE_DIM_DEFAULT,
-};
-
-static bool set_size_floating(int new_dimension, bool use_width) {
- swayc_t *view = current_container;
- if (view) {
- if (use_width) {
- int current_width = view->width;
- view->desired_width = new_dimension;
- floating_view_sane_size(view);
-
- int new_x = view->x + (int)(((view->desired_width - current_width) / 2) * -1);
- view->width = view->desired_width;
- view->x = new_x;
+static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
- update_geometry(view);
- } else {
- int current_height = view->height;
- view->desired_height = new_dimension;
- floating_view_sane_size(view);
-
- int new_y = view->y + (int)(((view->desired_height - current_height) / 2) * -1);
- view->height = view->desired_height;
- view->y = new_y;
+enum resize_unit {
+ RESIZE_UNIT_PX,
+ RESIZE_UNIT_PPT,
+ RESIZE_UNIT_DEFAULT,
+ RESIZE_UNIT_INVALID,
+};
- update_geometry(view);
- }
+enum resize_axis {
+ RESIZE_AXIS_HORIZONTAL,
+ RESIZE_AXIS_VERTICAL,
+ RESIZE_AXIS_INVALID,
+};
- return true;
+static enum resize_unit parse_resize_unit(const char *unit) {
+ if (strcasecmp(unit, "px") == 0) {
+ return RESIZE_UNIT_PX;
}
-
- return false;
+ if (strcasecmp(unit, "ppt") == 0) {
+ return RESIZE_UNIT_PPT;
+ }
+ if (strcasecmp(unit, "default") == 0) {
+ return RESIZE_UNIT_DEFAULT;
+ }
+ return RESIZE_UNIT_INVALID;
}
-static bool resize_floating(int amount, bool use_width) {
- swayc_t *view = current_container;
-
- if (view) {
- if (use_width) {
- return set_size_floating(view->width + amount, true);
- } else {
- return set_size_floating(view->height + amount, false);
- }
+static enum resize_axis parse_resize_axis(const char *axis) {
+ if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) {
+ return RESIZE_AXIS_HORIZONTAL;
+ }
+ if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) {
+ return RESIZE_AXIS_VERTICAL;
}
+ return RESIZE_AXIS_INVALID;
+}
- return false;
+static int parallel_coord(struct sway_container *c, enum resize_axis a) {
+ return a == RESIZE_AXIS_HORIZONTAL ? c->x : c->y;
}
-static bool resize_tiled(int amount, bool use_width) {
- swayc_t *container = current_container;
- swayc_t *parent = container->parent;
- int idx_focused = 0;
- bool use_major = false;
- size_t nb_before = 0;
- size_t nb_after = 0;
-
- // 1. Identify a container ancestor that will allow the focused child to grow in the requested
- // direction.
- while (container->parent) {
- parent = container->parent;
- if ((parent->children && parent->children->length > 1)
- && (is_auto_layout(parent->layout)
- || (use_width ? parent->layout == L_HORIZ : parent->layout == L_VERT))) {
- // check if container has siblings that can provide/absorb the space needed for
- // the resize operation.
- use_major = use_width
- ? parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
- : parent->layout == L_AUTO_TOP || parent->layout == L_AUTO_BOTTOM;
- // Note: use_major will be false for L_HORIZ and L_VERT
-
- idx_focused = index_child(container);
- if (idx_focused < 0) {
- sway_log(L_ERROR, "Something weird is happening, child container not "
- "present in its parent's children list.");
- continue;
- }
- if (use_major) {
- nb_before = auto_group_index(parent, idx_focused);
- nb_after = auto_group_count(parent) - nb_before - 1;
- } else {
- nb_before = idx_focused - auto_group_start_index(parent, idx_focused);
- nb_after = auto_group_end_index(parent, idx_focused) - idx_focused - 1;
- sway_log(L_DEBUG, "+++ focused: %d, start: %d, end: %d, before: %d, after: %d",
- idx_focused,
- (int)auto_group_start_index(parent, idx_focused),
- (int)auto_group_end_index(parent, idx_focused),
- (int)nb_before, (int)nb_after);
+static int parallel_size(struct sway_container *c, enum resize_axis a) {
+ return a == RESIZE_AXIS_HORIZONTAL ? c->width : c->height;
+}
- }
- if (nb_before || nb_after) {
- break;
- }
- }
- container = parent; /* continue up the tree to the next ancestor */
- }
- if (parent == &root_container) {
- return true;
+static void resize_tiled(int amount, enum resize_axis axis) {
+ struct sway_container *parent = config->handler_context.current_container;
+ struct sway_container *focused = parent;
+ if (!parent) {
+ return;
}
- sway_log(L_DEBUG, "Found the proper parent: %p. It has %zu before conts, "
- "and %zu after conts", parent, nb_before, nb_after);
- // 2. Ensure that the resize operation will not make one of the resized containers drop
- // below the "sane" size threshold.
- bool valid = true;
- swayc_t *focused = parent->children->items[idx_focused];
- int start = use_major ? 0 : auto_group_start_index(parent, idx_focused);
- int end = use_major ? parent->children->length : auto_group_end_index(parent, idx_focused);
- sway_log(L_DEBUG, "Check children of container %p [%d,%d[", container, start, end);
- for (int i = start; i < end; ) {
- swayc_t *sibling = parent->children->items[i];
- double pixels = amount;
- bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
- bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
- if (is_before || is_after) {
- pixels = -pixels;
- pixels /= is_before ? nb_before : nb_after;
- if (nb_after != 0 && nb_before != 0) {
- pixels /= 2;
- }
- }
- 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);
- if (use_width ?
- sibling->width + pixels < min_sane_w :
- sibling->height + pixels < min_sane_h) {
- valid = false;
- sway_log(L_DEBUG, "Container size no longer sane");
- break;
- }
- i = use_major ? auto_group_end_index(parent, i) : (i + 1);
- sway_log(L_DEBUG, "+++++ check %i", i);
- }
- // 3. Apply the size change
- if (valid) {
- for (int i = start; i < end; ) {
- int next_i = use_major ? auto_group_end_index(parent, i) : (i + 1);
- swayc_t *sibling = parent->children->items[i];
- double pixels = amount;
- bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
- bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
- if (is_before || is_after) {
- pixels = -pixels;
- pixels /= is_before ? nb_before : nb_after;
- if (nb_after != 0 && nb_before != 0) {
- pixels /= 2;
- }
- sway_log(L_DEBUG, "%p: %s", sibling, is_before ? "before" : "after");
- if (use_major) {
- for (int j = i; j < next_i; ++j) {
- recursive_resize(parent->children->items[j], pixels,
- use_width ?
- (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
- (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
- }
- } else {
- recursive_resize(sibling, pixels,
- use_width ?
- (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
- (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
- }
- } else {
- if (use_major) {
- for (int j = i; j < next_i; ++j) {
- recursive_resize(parent->children->items[j], pixels / 2,
- use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
- recursive_resize(parent->children->items[j], pixels / 2,
- use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
+
+ enum sway_container_layout parallel_layout =
+ axis == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
+ int minor_weight = 0;
+ int major_weight = 0;
+ while (parent->parent) {
+ struct sway_container *next = parent->parent;
+ if (next->layout == parallel_layout) {
+ for (int i = 0; i < next->children->length; i++) {
+ struct sway_container *sibling = next->children->items[i];
+
+ int sibling_pos = parallel_coord(sibling, axis);
+ int focused_pos = parallel_coord(focused, axis);
+ int parent_pos = parallel_coord(parent, axis);
+
+ if (sibling_pos != focused_pos) {
+ if (sibling_pos < parent_pos) {
+ minor_weight++;
+ } else if (sibling_pos > parent_pos) {
+ major_weight++;
}
- } else {
- recursive_resize(sibling, pixels / 2,
- use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
- recursive_resize(sibling, pixels / 2,
- use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
}
}
- i = next_i;
+ if (major_weight || minor_weight) {
+ break;
+ }
}
- // Recursive resize does not handle positions, let arrange_windows
- // take care of that.
- arrange_windows(swayc_active_workspace(), -1, -1);
+ parent = next;
}
- return true;
-}
-
-static bool set_size_tiled(int amount, bool use_width) {
- int desired;
- swayc_t *focused = current_container;
- if (use_width) {
- desired = amount - focused->width;
- } else {
- desired = amount - focused->height;
+ if (parent->type == C_ROOT) {
+ return;
}
- return resize_tiled(desired, use_width);
-}
-
-static bool set_size(int dimension, bool use_width) {
- swayc_t *focused = current_container;
+ wlr_log(L_DEBUG,
+ "Found the proper parent: %p. It has %d l conts, and %d r conts",
+ parent->parent, minor_weight, major_weight);
- if (focused) {
- if (focused->is_floating) {
- return set_size_floating(dimension, use_width);
- } else {
- return set_size_tiled(dimension, use_width);
- }
- }
+ int min_sane = axis == RESIZE_AXIS_HORIZONTAL ? MIN_SANE_W : MIN_SANE_H;
- return false;
-}
+ //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
+ // ^ ?????
-static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) {
- swayc_t *focused = current_container;
+ for (int i = 0; i < parent->parent->children->length; i++) {
+ struct sway_container *sibling = parent->parent->children->items[i];
- // translate "10 ppt" (10%) to appropriate # of pixels in case we need it
- float ppt_dim = (float)dimension / 100;
+ int sibling_pos = parallel_coord(sibling, axis);
+ int focused_pos = parallel_coord(focused, axis);
+ int parent_pos = parallel_coord(parent, axis);
- if (use_width) {
- ppt_dim = focused->width * ppt_dim;
- } else {
- ppt_dim = focused->height * ppt_dim;
- }
+ int sibling_size = parallel_size(sibling, axis);
+ int parent_size = parallel_size(parent, axis);
- if (focused) {
- if (focused->is_floating) {
- // floating view resize dimensions should default to px, so only
- // use ppt if specified
- if (dim_type == RESIZE_DIM_PPT) {
- dimension = (int)ppt_dim;
+ if (sibling_pos != focused_pos) {
+ if (sibling_pos < parent_pos) {
+ double pixels = -amount / minor_weight;
+ if (major_weight && (sibling_size + pixels / 2) < min_sane) {
+ return; // Too small
+ } else if ((sibling_size + pixels) < min_sane) {
+ return; // Too small
+ }
+ } else if (sibling_pos > parent_pos) {
+ double pixels = -amount / major_weight;
+ if (minor_weight && (sibling_size + pixels / 2) < min_sane) {
+ return; // Too small
+ } else if ((sibling_size + pixels) < min_sane) {
+ return; // Too small
+ }
}
-
- return resize_floating(dimension, use_width);
} else {
- // tiled view resize dimensions should default to ppt, so only use
- // px if specified
- if (dim_type != RESIZE_DIM_PX) {
- dimension = (int)ppt_dim;
+ double pixels = amount;
+ if (parent_size + pixels < min_sane) {
+ return; // Too small
}
-
- return resize_tiled(dimension, use_width);
}
}
- return false;
-}
+ enum resize_edge minor_edge = axis == RESIZE_AXIS_HORIZONTAL ?
+ RESIZE_EDGE_LEFT : RESIZE_EDGE_TOP;
+ enum resize_edge major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
+ RESIZE_EDGE_RIGHT : RESIZE_EDGE_BOTTOM;
-static struct cmd_results *cmd_resize_set(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "resize set", EXPECTED_AT_LEAST, 2))) {
- return error;
- }
+ for (int i = 0; i < parent->parent->children->length; i++) {
+ struct sway_container *sibling = parent->parent->children->items[i];
- if (strcasecmp(argv[0], "width") == 0 || strcasecmp(argv[0], "height") == 0) {
- // handle `reset set width 100 px height 100 px` syntax, also allows
- // specifying only one dimension for a `resize set`
- int cmd_num = 0;
- int dim;
-
- while ((cmd_num + 1) < argc) {
- dim = (int)strtol(argv[cmd_num + 1], NULL, 10);
- if (errno == ERANGE || dim == 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
- }
+ int sibling_pos = parallel_coord(sibling, axis);
+ int focused_pos = parallel_coord(focused, axis);
+ int parent_pos = parallel_coord(parent, axis);
- if (strcasecmp(argv[cmd_num], "width") == 0) {
- set_size(dim, true);
- } else if (strcasecmp(argv[cmd_num], "height") == 0) {
- set_size(dim, false);
- } else {
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
+ if (sibling_pos != focused_pos) {
+ if (sibling_pos < parent_pos) {
+ double pixels = -1 * amount;
+ pixels /= minor_weight;
+ if (major_weight) {
+ container_recursive_resize(sibling, pixels / 2, major_edge);
+ } else {
+ container_recursive_resize(sibling, pixels, major_edge);
+ }
+ } else if (sibling_pos > parent_pos) {
+ double pixels = -1 * amount;
+ pixels /= major_weight;
+ if (minor_weight) {
+ container_recursive_resize(sibling, pixels / 2, minor_edge);
+ } else {
+ container_recursive_resize(sibling, pixels, minor_edge);
+ }
}
-
- cmd_num += 2;
-
- if (cmd_num < argc && strcasecmp(argv[cmd_num], "px") == 0) {
- // if this was `resize set width 400 px height 300 px`, disregard the `px` arg
- cmd_num++;
+ } else {
+ if (major_weight != 0 && minor_weight != 0) {
+ double pixels = amount;
+ pixels /= 2;
+ container_recursive_resize(parent, pixels, minor_edge);
+ container_recursive_resize(parent, pixels, major_edge);
+ } else if (major_weight) {
+ container_recursive_resize(parent, amount, major_edge);
+ } else if (minor_weight) {
+ container_recursive_resize(parent, amount, minor_edge);
}
}
- } else {
- // handle `reset set 100 px 100 px` syntax
- int width = (int)strtol(argv[0], NULL, 10);
- if (errno == ERANGE || width == 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width> [px] <height> [px]'");
- }
+ }
- int height_arg = 1;
- if (strcasecmp(argv[1], "px") == 0) {
- height_arg = 2;
- }
+ arrange_windows(parent->parent, -1, -1);
+}
- int height = (int)strtol(argv[height_arg], NULL, 10);
- if (errno == ERANGE || height == 0) {
- errno = 0;
- return cmd_results_new(CMD_INVALID, "resize set",
- "Expected 'resize set <width> [px] <height> [px]'");
- }
+static void resize(int amount, enum resize_axis axis, enum resize_unit unit) {
+ struct sway_container *current = config->handler_context.current_container;
+ if (unit == RESIZE_UNIT_DEFAULT) {
+ // Default for tiling; TODO floating should be px
+ unit = RESIZE_UNIT_PPT;
+ }
- set_size(width, true);
- set_size(height, false);
+ if (unit == RESIZE_UNIT_PPT) {
+ float pct = amount / 100.0f;
+ switch (axis) {
+ case RESIZE_AXIS_HORIZONTAL:
+ amount = (float)current->width * pct;
+ break;
+ case RESIZE_AXIS_VERTICAL:
+ amount = (float)current->height * pct;
+ break;
+ default:
+ sway_assert(0, "invalid resize axis");
+ return;
+ }
}
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+ return resize_tiled(amount, axis);
}
struct cmd_results *cmd_resize(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running.");
-
+ struct sway_container *current = config->handler_context.current_container;
+ if (!current) {
+ return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
+ }
+ if (current->type != C_VIEW && current->type != C_CONTAINER) {
+ return cmd_results_new(CMD_INVALID, "resize",
+ "Can only resize views/containers");
+ }
if (strcasecmp(argv[0], "set") == 0) {
- return cmd_resize_set(argc - 1, &argv[1]);
+ // TODO
+ //return cmd_resize_set(argc - 1, &argv[1]);
+ return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented");
}
-
+ struct cmd_results *error;
if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
return error;
}
- int dim_arg = argc - 1;
+ // TODO: resize grow|shrink left|right|up|down
- enum resize_dim_types dim_type = RESIZE_DIM_DEFAULT;
- if (strcasecmp(argv[dim_arg], "ppt") == 0) {
- dim_type = RESIZE_DIM_PPT;
- dim_arg--;
- } else if (strcasecmp(argv[dim_arg], "px") == 0) {
- dim_type = RESIZE_DIM_PX;
- dim_arg--;
+ const char *usage = "Expected 'resize <shrink|grow> "
+ "<width|height> [<amount>] [px|ppt]'";
+
+ int multiplier = 0;
+ if (strcasecmp(*argv, "grow") == 0) {
+ multiplier = 1;
+ } else if (strcasecmp(*argv, "shrink") == 0) {
+ multiplier = -1;
+ } else {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
}
+ --argc; ++argv;
- int amount = (int)strtol(argv[dim_arg], NULL, 10);
- if (errno == ERANGE || amount == 0) {
- errno = 0;
- amount = 10; // this is the default resize dimension used by i3 for both px and ppt
- sway_log(L_DEBUG, "Tried to get resize dimension out of '%s' but failed; setting dimension to default %d",
- argv[dim_arg], amount);
+ enum resize_axis axis = parse_resize_axis(*argv);
+ if (axis == RESIZE_AXIS_INVALID) {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
+ }
+ --argc; ++argv;
+
+ int amount = 10; // Default amount
+ enum resize_unit unit = RESIZE_UNIT_DEFAULT;
+
+ if (argc) {
+ char *err;
+ amount = (int)strtol(*argv, &err, 10);
+ if (*err) {
+ // e.g. `resize grow width 10px`
+ unit = parse_resize_unit(err);
+ if (unit == RESIZE_UNIT_INVALID) {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
+ }
+ }
+ --argc; ++argv;
}
- bool use_width = false;
- if (strcasecmp(argv[1], "width") == 0) {
- use_width = true;
- } else if (strcasecmp(argv[1], "height") != 0) {
- return cmd_results_new(CMD_INVALID, "resize",
- "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
+ if (argc) {
+ unit = parse_resize_unit(*argv);
+ if (unit == RESIZE_UNIT_INVALID) {
+ return cmd_results_new(CMD_INVALID, "resize", usage);
+ }
+ --argc; ++argv;
}
- if (strcasecmp(argv[0], "shrink") == 0) {
- amount *= -1;
- } else if (strcasecmp(argv[0], "grow") != 0) {
- return cmd_results_new(CMD_INVALID, "resize",
- "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
+ if (argc) {
+ // Provied too many args, the bastard
+ return cmd_results_new(CMD_INVALID, "resize", usage);
}
- resize(amount, use_width, dim_type);
+ resize(amount * multiplier, axis, unit);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
-#include <string.h>
-#include <strings.h>
-#include <wlc/wlc.h>
-#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/layout.h"
-
-static swayc_t *fetch_view_from_scratchpad() {
- sp_index = (sp_index + 1) % scratchpad->length;
- swayc_t *view = scratchpad->items[sp_index];
-
- if (wlc_view_get_output(view->handle) != swayc_active_output()->handle) {
- wlc_view_set_output(view->handle, swayc_active_output()->handle);
- }
- if (!view->is_floating) {
- view->width = swayc_active_workspace()->width * 0.5;
- view->height = swayc_active_workspace()->height * 0.75;
- view->x = (swayc_active_workspace()->width - view->width)/2;
- view->y = (swayc_active_workspace()->height - view->height)/2;
- }
- if (swayc_active_workspace()->width < view->x + 20 || view->x + view->width < 20) {
- view->x = (swayc_active_workspace()->width - view->width)/2;
- }
- if (swayc_active_workspace()->height < view->y + 20 || view->y + view->height < 20) {
- view->y = (swayc_active_workspace()->height - view->height)/2;
- }
-
- add_floating(swayc_active_workspace(), view);
- wlc_view_set_mask(view->handle, VISIBLE);
- view->visible = true;
- arrange_windows(swayc_active_workspace(), -1, -1);
- set_focused_container(view);
- return view;
-}
-
-struct cmd_results *cmd_scratchpad(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
- if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
- if (!sp_view) {
- if (current_container) {
- // Haxor the scratchpad index if criteria'd
- for (int i = 0; i < scratchpad->length; ++i) {
- if (scratchpad->items[i] == current_container) {
- sp_index = (i - 1) % scratchpad->length;
- }
- }
- }
- sp_view = fetch_view_from_scratchpad();
- } else {
- if (swayc_active_workspace() != sp_view->parent) {
- hide_view_in_scratchpad(sp_view);
- if (sp_index == 0) {
- sp_index = scratchpad->length;
- }
- sp_index--;
- sp_view = fetch_view_from_scratchpad();
- } else {
- hide_view_in_scratchpad(sp_view);
- sp_view = NULL;
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *cmd_seat(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 2))) {
+ return error;
+ }
+
+ if (config->reading && strcmp("{", argv[1]) == 0) {
+ free_seat_config(config->handler_context.seat_config);
+ config->handler_context.seat_config = new_seat_config(argv[0]);
+ if (!config->handler_context.seat_config) {
+ return cmd_results_new(CMD_FAILURE, NULL,
+ "Couldn't allocate config");
+ }
+ wlr_log(L_DEBUG, "entering seat block: %s", argv[0]);
+ return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
+ }
+
+ if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 3))) {
+ return error;
+ }
+
+ bool has_context = (config->handler_context.seat_config != NULL);
+ if (!has_context) {
+ config->handler_context.seat_config = new_seat_config(argv[0]);
+ if (!config->handler_context.seat_config) {
+ return cmd_results_new(CMD_FAILURE, NULL,
+ "Couldn't allocate config");
+ }
+ }
+
+ int argc_new = argc-2;
+ char **argv_new = argv+2;
+
+ struct cmd_results *res;
+ if (strcasecmp("attach", argv[1]) == 0) {
+ res = seat_cmd_attach(argc_new, argv_new);
+ } else if (strcasecmp("cursor", argv[1]) == 0) {
+ res = seat_cmd_cursor(argc_new, argv_new);
+ } else if (strcasecmp("fallback", argv[1]) == 0) {
+ res = seat_cmd_fallback(argc_new, argv_new);
+ } else {
+ res =
+ cmd_results_new(CMD_INVALID,
+ "seat <name>", "Unknown command %s",
+ argv[1]);
+ }
+
+ if (!has_context) {
+ // clean up the context we created earlier
+ free_seat_config(config->handler_context.seat_config);
+ config->handler_context.seat_config = NULL;
+ }
+
+ return res;
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <string.h>
+#include <strings.h>
+#include "sway/input/input-manager.h"
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "log.h"
+#include "stringop.h"
+
+struct cmd_results *seat_cmd_attach(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+ struct seat_config *current_seat_config =
+ config->handler_context.seat_config;
+ if (!current_seat_config) {
+ return cmd_results_new(CMD_FAILURE, "attach", "No seat defined");
+ }
+
+ struct seat_config *new_config = new_seat_config(current_seat_config->name);
+ struct seat_attachment_config *new_attachment = seat_attachment_config_new();
+ new_attachment->identifier = strdup(argv[0]);
+ list_add(new_config->attachments, new_attachment);
+
+ apply_seat_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
+
+#include <strings.h>
+#include <wlr/types/wlr_cursor.h>
+#include "sway/commands.h"
+#include "sway/input/cursor.h"
+
+static struct cmd_results *press_or_release(struct sway_cursor *cursor,
+ char *action, char *button_str, uint32_t time);
+
+static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or "
+ "'cursor <set> <x> <y>' or "
+ "'curor <press|release> <left|right|1|2|3...>'";
+
+struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) {
+ return error;
+ }
+ struct sway_seat *seat = config->handler_context.seat;
+ if (!seat) {
+ return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined");
+ }
+
+ struct sway_cursor *cursor = seat->cursor;
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ uint32_t time = now.tv_nsec / 1000;
+
+ if (strcasecmp(argv[0], "move") == 0) {
+ if (argc < 3) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ int delta_x = strtol(argv[1], NULL, 10);
+ int delta_y = strtol(argv[2], NULL, 10);
+ wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y);
+ cursor_send_pointer_motion(cursor, time);
+ } else if (strcasecmp(argv[0], "set") == 0) {
+ if (argc < 3) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ // map absolute coords (0..1,0..1) to root container coords
+ float x = strtof(argv[1], NULL) / root_container.width;
+ float y = strtof(argv[2], NULL) / root_container.height;
+ wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
+ cursor_send_pointer_motion(cursor, time);
+ } else {
+ if (argc < 2) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ if ((error = press_or_release(cursor, argv[0], argv[1], time))) {
+ return error;
+ }
+ }
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
+
+static struct cmd_results *press_or_release(struct sway_cursor *cursor,
+ char *action, char *button_str, uint32_t time) {
+ enum wlr_button_state state;
+ uint32_t button;
+ if (strcasecmp(action, "press") == 0) {
+ state = WLR_BUTTON_PRESSED;
+ } else if (strcasecmp(action, "release") == 0) {
+ state = WLR_BUTTON_RELEASED;
+ } else {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+
+ if (strcasecmp(button_str, "left") == 0) {
+ button = BTN_LEFT;
+ } else if (strcasecmp(button_str, "right") == 0) {
+ button = BTN_RIGHT;
+ } else {
+ button = strtol(button_str, NULL, 10);
+ if (button == 0) {
+ return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
+ }
+ }
+ dispatch_cursor_button(cursor, time, button, state);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "fallback", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+ struct seat_config *current_seat_config =
+ config->handler_context.seat_config;
+ if (!current_seat_config) {
+ return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined");
+ }
+ struct seat_config *new_config =
+ new_seat_config(current_seat_config->name);
+
+ if (strcasecmp(argv[0], "true") == 0) {
+ new_config->fallback = 1;
+ } else if (strcasecmp(argv[0], "false") == 0) {
+ new_config->fallback = 0;
+ } else {
+ free_seat_config(new_config);
+ return cmd_results_new(CMD_INVALID, "fallback",
+ "Expected 'fallback <true|false>'");
+ }
+
+ apply_seat_config(new_config);
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "list.h"
+#include "log.h"
#include "stringop.h"
// sort in order of longest->shortest
@@ -14,16 +15,24 @@ static int compare_set_qsort(const void *_l, const void *_r) {
return strlen(r->name) - strlen(l->name);
}
+void free_sway_variable(struct sway_variable *var) {
+ if (!var) {
+ return;
+ }
+ free(var->name);
+ free(var->value);
+ free(var);
+}
+
struct cmd_results *cmd_set(int argc, char **argv) {
char *tmp;
struct cmd_results *error = NULL;
- if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
return error;
}
if (argv[0][0] != '$') {
- sway_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);
+ wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);
size_t size = snprintf(NULL, 0, "$%s", argv[0]);
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_show_marks(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "show_marks", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- config->show_marks = !strcasecmp(argv[0], "on");
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "smart_gaps", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "on") == 0) {
- config->smart_gaps = true;
- } else if (strcasecmp(argv[0], "off") == 0) {
- config->smart_gaps = false;
- } else {
- return cmd_results_new(CMD_INVALID, "smart_gaps", "Expected 'smart_gaps <on|off>'");
- }
-
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <string.h>
#include <strings.h>
-#include <wlc/wlc-render.h>
-#include "sway/border.h"
#include "sway/commands.h"
-#include "sway/container.h"
-#include "sway/focus.h"
-#include "sway/layout.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "log.h"
-static struct cmd_results *_do_split(int argc, char **argv, int layout) {
- char *name = layout == L_VERT ? "splitv" :
- layout == L_HORIZ ? "splith" : "split";
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
- if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
- return error;
- }
- swayc_t *focused = current_container;
-
- // Case of floating window, don't split
- if (focused->is_floating) {
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
- }
- /* Case that focus is on an workspace with 0/1 children.change its layout */
- if (focused->type == C_WORKSPACE && focused->children->length <= 1) {
- sway_log(L_DEBUG, "changing workspace layout");
- swayc_change_layout(focused, layout);
- } else if (focused->type != C_WORKSPACE && focused->parent->children->length == 1) {
- /* Case of no siblings. change parent layout */
- sway_log(L_DEBUG, "changing container layout");
- swayc_change_layout(focused->parent, layout);
- } else {
- /* regular case where new split container is build around focused container
- * or in case of workspace, container inherits its children */
- sway_log(L_DEBUG, "Adding new container around current focused container");
- sway_log(L_INFO, "FOCUSED SIZE: %.f %.f", focused->width, focused->height);
- swayc_t *parent = new_container(focused, layout);
- set_focused_container(focused);
- arrange_windows(parent, -1, -1);
- }
-
- // update container every time
- // if it is tabbed/stacked then the title must change
- // if the indicator color is different then the border must change
- update_container_border(focused);
- swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
- // schedule render to make changes take effect right away,
- // otherwise we would have to wait for the view to render,
- // which is unpredictable.
- wlc_output_schedule_render(output->handle);
+static struct cmd_results *do_split(int layout) {
+ struct sway_container *con = config->handler_context.current_container;
+ struct sway_container *parent = container_split(con, layout);
+ container_create_notify(parent);
+ arrange_windows(parent, -1, -1);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
struct cmd_results *cmd_split(int argc, char **argv) {
struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
return error;
}
if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
- _do_split(argc - 1, argv + 1, L_VERT);
- } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
- _do_split(argc - 1, argv + 1, L_HORIZ);
- } else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) {
- swayc_t *focused = current_container;
+ do_split(L_VERT);
+ } else if (strcasecmp(argv[0], "h") == 0 ||
+ strcasecmp(argv[0], "horizontal") == 0) {
+ do_split(L_HORIZ);
+ } else if (strcasecmp(argv[0], "t") == 0 ||
+ strcasecmp(argv[0], "toggle") == 0) {
+ struct sway_container *focused =
+ config->handler_context.current_container;
+
if (focused->parent->layout == L_VERT) {
- _do_split(argc - 1, argv + 1, L_HORIZ);
+ do_split(L_HORIZ);
} else {
- _do_split(argc - 1, argv + 1, L_VERT);
+ do_split(L_VERT);
}
} else {
error = cmd_results_new(CMD_FAILURE, "split",
@@ -81,18 +46,32 @@ struct cmd_results *cmd_split(int argc, char **argv) {
}
struct cmd_results *cmd_splitv(int argc, char **argv) {
- return _do_split(argc, argv, L_VERT);
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
+ return error;
+ }
+ return do_split(L_VERT);
}
struct cmd_results *cmd_splith(int argc, char **argv) {
- return _do_split(argc, argv, L_HORIZ);
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
+ return error;
+ }
+ return do_split(L_HORIZ);
}
struct cmd_results *cmd_splitt(int argc, char **argv) {
- swayc_t *focused = current_container;
- if (focused->parent->layout == L_VERT) {
- return _do_split(argc, argv, L_HORIZ);
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
+ return error;
+ }
+
+ struct sway_container *con = config->handler_context.current_container;
+
+ if (con->parent->layout == L_VERT) {
+ return do_split(L_HORIZ);
} else {
- return _do_split(argc, argv, L_VERT);
+ return do_split(L_VERT);
}
}
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 @@
-#include <string.h>
-#include "sway/commands.h"
-#include "sway/focus.h"
-
-struct cmd_results *cmd_sticky(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if (config->reading) return cmd_results_new(CMD_FAILURE, "sticky", "Can't be used in config file.");
- if (!config->active) return cmd_results_new(CMD_FAILURE, "sticky", "Can only be used when sway is running.");
- if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
- return error;
- }
- char *action = argv[0];
- swayc_t *cont = get_focused_view(&root_container);
- if (strcmp(action, "toggle") == 0) {
- cont->sticky = !cont->sticky;
- } else if (strcmp(action, "enable") == 0) {
- cont->sticky = true;
- } else if (strcmp(action, "disable") == 0) {
- cont->sticky = false;
- } else {
- return cmd_results_new(CMD_FAILURE, "sticky",
- "Expected 'sticky enable|disable|toggle'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
+#include <string.h>
+#include "sway/commands.h"
+#include "log.h"
+#include "stringop.h"
+
+struct cmd_results *cmd_swaybg_command(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "swaybg_command", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+
+ if (config->swaybg_command) {
+ free(config->swaybg_command);
+ }
+ config->swaybg_command = join_args(argv, argc);
+ wlr_log(L_DEBUG, "Using custom swaybg command: %s",
+ config->swaybg_command);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "list.h"
-#include "stringop.h"
-
-struct cmd_results *cmd_unmark(int argc, char **argv) {
- swayc_t *view = current_container;
-
- if (view->marks) {
- if (argc) {
- char *mark = join_args(argv, argc);
- int index;
- if ((index = list_seq_find(view->marks, (int (*)(const void *, const void *))strcmp, mark)) != -1) {
- free(view->marks->items[index]);
- list_del(view->marks, index);
-
- if (view->marks->length == 0) {
- list_free(view->marks);
- view->marks = NULL;
- }
- }
- free(mark);
- } else {
- list_foreach(view->marks, free);
- list_free(view->marks);
- view->marks = NULL;
- }
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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 @@
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/input_state.h"
-#include "sway/workspace.h"
+#include "sway/input/seat.h"
+#include "sway/tree/workspace.h"
#include "list.h"
#include "log.h"
#include "stringop.h"
@@ -17,6 +17,17 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
int output_location = -1;
+ struct sway_container *current_container = config->handler_context.current_container;
+ struct sway_container *old_workspace = NULL, *old_output = NULL;
+ if (current_container) {
+ if (current_container->type == C_WORKSPACE) {
+ old_workspace = current_container;
+ } else {
+ old_workspace = container_parent(current_container, C_WORKSPACE);
+ }
+ old_output = container_parent(current_container, C_OUTPUT);
+ }
+
for (int i = 0; i < argc; ++i) {
if (strcasecmp(argv[i], "output") == 0) {
output_location = i;
@@ -40,52 +51,51 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
free(old); // workspaces can only be assigned to a single output
list_del(config->workspace_outputs, i);
}
- sway_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
+ wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
list_add(config->workspace_outputs, wso);
} else {
if (config->reading || !config->active) {
return cmd_results_new(CMD_DEFER, "workspace", NULL);
}
- swayc_t *ws = NULL;
+ struct sway_container *ws = NULL;
if (strcasecmp(argv[0], "number") == 0) {
if (!(ws = workspace_by_number(argv[1]))) {
char *name = join_args(argv + 1, argc - 1);
- ws = workspace_create(name);
+ ws = workspace_create(NULL, name);
free(name);
}
} else if (strcasecmp(argv[0], "next") == 0) {
- ws = workspace_next();
+ ws = workspace_next(old_workspace);
} else if (strcasecmp(argv[0], "prev") == 0) {
- ws = workspace_prev();
+ ws = workspace_prev(old_workspace);
} else if (strcasecmp(argv[0], "next_on_output") == 0) {
- ws = workspace_output_next();
+ ws = workspace_output_next(old_output);
} else if (strcasecmp(argv[0], "prev_on_output") == 0) {
- ws = workspace_output_prev();
+ ws = workspace_output_prev(old_output);
} else if (strcasecmp(argv[0], "back_and_forth") == 0) {
// if auto_back_and_forth is enabled, workspace_switch will swap
// the workspaces. If we created prev_workspace here, workspace_switch
// would put us back on original workspace.
if (config->auto_back_and_forth) {
- ws = swayc_active_workspace();
- } else if (prev_workspace_name && !(ws = workspace_by_name(prev_workspace_name))) {
- ws = workspace_create(prev_workspace_name);
+ ws = old_workspace;
+ } else if (prev_workspace_name
+ && !(ws = workspace_by_name(prev_workspace_name))) {
+ ws = workspace_create(NULL, prev_workspace_name);
}
} else {
char *name = join_args(argv, argc);
if (!(ws = workspace_by_name(name))) {
- ws = workspace_create(name);
+ ws = workspace_create(NULL, name);
}
free(name);
}
- swayc_t *old_output = swayc_active_output();
workspace_switch(ws);
- swayc_t *new_output = swayc_active_output();
+ current_container =
+ seat_get_focus(config->handler_context.seat);
+ struct sway_container *new_output = container_parent(current_container, C_OUTPUT);
if (config->mouse_warping && old_output != new_output) {
- swayc_t *focused = get_focused_view(ws);
- if (focused && focused->type == C_VIEW) {
- center_pointer_on(focused);
- }
+ // TODO: Warp mouse
}
}
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 @@
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-
-struct cmd_results *cmd_workspace_layout(int argc, char **argv) {
- struct cmd_results *error = NULL;
- if ((error = checkarg(argc, "workspace_layout", EXPECTED_AT_LEAST, 1))) {
- return error;
- }
-
- if (strcasecmp(argv[0], "default") == 0) {
- config->default_layout = L_NONE;
- } else if (strcasecmp(argv[0], "stacking") == 0) {
- config->default_layout = L_STACKED;
- } else if (strcasecmp(argv[0], "tabbed") == 0) {
- config->default_layout = L_TABBED;
- } else if (strcasecmp(argv[0], "auto") == 0) {
- if (argc == 1) {
- config->default_layout = L_AUTO_FIRST;
- } else {
- if ((error = checkarg(argc, "workspace_layout auto", EXPECTED_EQUAL_TO, 2))) {
- return error;
- }
- if (strcasecmp(argv[1], "left") == 0) {
- config->default_layout = L_AUTO_LEFT;
- } else if (strcasecmp(argv[1], "right") == 0) {
- config->default_layout = L_AUTO_RIGHT;
- } else if (strcasecmp(argv[1], "top") == 0) {
- config->default_layout = L_AUTO_TOP;
- } else if (strcasecmp(argv[1], "bottom") == 0) {
- config->default_layout = L_AUTO_BOTTOM;
- } else {
- return cmd_results_new(CMD_INVALID, "workspace_layout auto", "Expected 'workspace_layout auto <left|right|top|bottom>'");
- }
- }
- } else {
- return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed|auto|auto left|auto right|auto top|auto bottom>'");
- }
- return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
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) {
if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
return error;
}
- if (strcasecmp(argv[0], "yes") == 0) {
- config->auto_back_and_forth = true;
- } else if (strcasecmp(argv[0], "no") == 0) {
- config->auto_back_and_forth = false;
- } else {
- return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
- }
+ config->auto_back_and_forth = !strcasecmp(argv[0], "yes");
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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 @@
#include <signal.h>
#include <libinput.h>
#include <limits.h>
-#include <float.h>
#include <dirent.h>
#include <strings.h>
-#include "wayland-desktop-shell-server-protocol.h"
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
+#include <wlr/types/wlr_output.h>
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "sway/commands.h"
#include "sway/config.h"
-#include "sway/layout.h"
-#include "sway/input_state.h"
-#include "sway/criteria.h"
-#include "sway/input.h"
-#include "sway/border.h"
+#include "sway/tree/layout.h"
#include "readline.h"
#include "stringop.h"
#include "list.h"
@@ -30,269 +32,102 @@
struct sway_config *config = NULL;
-static void terminate_swaybar(pid_t pid);
-
-static void free_variable(struct sway_variable *var) {
- if (!var) {
- return;
- }
- free(var->name);
- free(var->value);
- free(var);
-}
-
-static void free_binding(struct sway_binding *bind) {
- if (!bind) {
- return;
- }
- free_flat_list(bind->keys);
- free(bind->command);
- free(bind);
-}
-
static void free_mode(struct sway_mode *mode) {
- if (!mode) {
- return;
- }
- free(mode->name);
- int i;
- for (i = 0; mode->bindings && i < mode->bindings->length; ++i) {
- free_binding(mode->bindings->items[i]);
- }
- list_free(mode->bindings);
- free(mode);
-}
-
-static void free_bar(struct bar_config *bar) {
- if (!bar) {
- return;
- }
- free(bar->mode);
- free(bar->hidden_state);
-#ifdef ENABLE_TRAY
- free(bar->tray_output);
- free(bar->icon_theme);
-#endif
- free(bar->status_command);
- free(bar->font);
- free(bar->separator_symbol);
int i;
- for (i = 0; bar->bindings && i < bar->bindings->length; ++i) {
- free_sway_mouse_binding(bar->bindings->items[i]);
- }
- list_free(bar->bindings);
-
- if (bar->outputs) {
- free_flat_list(bar->outputs);
- }
-
- if (bar->pid != 0) {
- terminate_swaybar(bar->pid);
- }
-
- free(bar->colors.background);
- free(bar->colors.statusline);
- free(bar->colors.separator);
- free(bar->colors.focused_background);
- free(bar->colors.focused_statusline);
- free(bar->colors.focused_separator);
- free(bar->colors.focused_workspace_border);
- free(bar->colors.focused_workspace_bg);
- free(bar->colors.focused_workspace_text);
- free(bar->colors.active_workspace_border);
- free(bar->colors.active_workspace_bg);
- free(bar->colors.active_workspace_text);
- free(bar->colors.inactive_workspace_border);
- free(bar->colors.inactive_workspace_bg);
- free(bar->colors.inactive_workspace_text);
- free(bar->colors.urgent_workspace_border);
- free(bar->colors.urgent_workspace_bg);
- free(bar->colors.urgent_workspace_text);
- free(bar->colors.binding_mode_border);
- free(bar->colors.binding_mode_bg);
- free(bar->colors.binding_mode_text);
-
- free(bar);
-}
-void free_input_config(struct input_config *ic) {
- if (!ic) {
- return;
- }
- free(ic->identifier);
- free(ic);
-}
-
-void free_output_config(struct output_config *oc) {
- if (!oc) {
- return;
- }
- free(oc->name);
- free(oc->background);
- free(oc->background_option);
- free(oc);
-}
-
-static void free_workspace_output(struct workspace_output *wo) {
- if (!wo) {
+ if (!mode) {
return;
}
- free(wo->output);
- free(wo->workspace);
- free(wo);
-}
-
-static void pid_workspace_cleanup() {
- struct timespec ts;
- struct pid_workspace *pw = NULL;
-
- clock_gettime(CLOCK_MONOTONIC, &ts);
-
- // work backwards through list and remove any entries
- // older than PID_WORKSPACE_TIMEOUT
- for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
- pw = config->pid_workspaces->items[i];
-
- if (difftime(ts.tv_sec, *pw->time_added) >= PID_WORKSPACE_TIMEOUT) {
- free_pid_workspace(config->pid_workspaces->items[i]);
- list_del(config->pid_workspaces, i);
+ free(mode->name);
+ if (mode->keysym_bindings) {
+ for (i = 0; i < mode->keysym_bindings->length; i++) {
+ free_sway_binding(mode->keysym_bindings->items[i]);
}
+ list_free(mode->keysym_bindings);
}
-}
-
-// de-dupe pid_workspaces to ensure pid uniqueness
-void pid_workspace_add(struct pid_workspace *pw) {
- struct pid_workspace *list_pw = NULL;
- struct timespec ts;
- time_t *now = malloc(sizeof(time_t));
- if (!now) {
- sway_log(L_ERROR, "Allocating time for pid_workspace failed");
- return;
- }
-
- pid_workspace_cleanup();
-
- // add current time to pw
- clock_gettime(CLOCK_MONOTONIC, &ts);
- *now = ts.tv_sec;
-
- pw->time_added = now;
-
- // work backwards through list and delete any entries that
- // have the same pid as that in our new pid_workspace
- for (int i = config->pid_workspaces->length - 1; i > -1; i--) {
- list_pw = config->pid_workspaces->items[i];
-
- if (pw->pid == list_pw->pid) {
- free_pid_workspace(config->pid_workspaces->items[i]);
- list_del(config->pid_workspaces, i);
+ if (mode->keycode_bindings) {
+ for (i = 0; i < mode->keycode_bindings->length; i++) {
+ free_sway_binding(mode->keycode_bindings->items[i]);
}
+ list_free(mode->keycode_bindings);
}
-
- list_add(config->pid_workspaces, pw);
-}
-
-void free_pid_workspace(struct pid_workspace *pw) {
- if (!pw) {
- return;
- }
- free(pw->pid);
- free(pw->workspace);
- free(pw->time_added);
- free(pw);
-}
-
-void free_command_policy(struct command_policy *policy) {
- if (!policy) {
- return;
- }
- free(policy->command);
- free(policy);
-}
-
-void free_feature_policy(struct feature_policy *policy) {
- if (!policy) {
- return;
- }
- free(policy->program);
- free(policy);
+ free(mode);
}
void free_config(struct sway_config *config) {
+ config_clear_handler_context(config);
+
if (!config) {
return;
}
- int i;
- for (i = 0; config->symbols && i < config->symbols->length; ++i) {
- free_variable(config->symbols->items[i]);
- }
- list_free(config->symbols);
- for (i = 0; config->modes && i < config->modes->length; ++i) {
- free_mode(config->modes->items[i]);
+ // TODO: handle all currently unhandled lists as we add implementations
+ if (config->symbols) {
+ for (int i = 0; i < config->symbols->length; ++i) {
+ free_sway_variable(config->symbols->items[i]);
+ }
+ list_free(config->symbols);
}
- list_free(config->modes);
-
- for (i = 0; config->bars && i < config->bars->length; ++i) {
- free_bar(config->bars->items[i]);
+ if (config->modes) {
+ for (int i = 0; i < config->modes->length; ++i) {
+ free_mode(config->modes->items[i]);
+ }
+ list_free(config->modes);
}
- list_free(config->bars);
-
- free_flat_list(config->cmd_queue);
-
- for (i = 0; config->workspace_outputs && i < config->workspace_outputs->length; ++i) {
- free_workspace_output(config->workspace_outputs->items[i]);
+ if (config->bars) {
+ for (int i = 0; i < config->bars->length; ++i) {
+ free_bar_config(config->bars->items[i]);
+ }
+ list_free(config->bars);
}
+ list_free(config->cmd_queue);
list_free(config->workspace_outputs);
-
- for (i = 0; config->pid_workspaces && i < config->pid_workspaces->length; ++i) {
- free_pid_workspace(config->pid_workspaces->items[i]);
- }
list_free(config->pid_workspaces);
-
- for (i = 0; config->criteria && i < config->criteria->length; ++i) {
- free_criteria(config->criteria->items[i]);
+ list_free(config->output_configs);
+ if (config->input_configs) {
+ for (int i = 0; i < config->input_configs->length; i++) {
+ free_input_config(config->input_configs->items[i]);
+ }
+ list_free(config->input_configs);
}
- list_free(config->criteria);
-
- for (i = 0; config->no_focus && i < config->no_focus->length; ++i) {
- free_criteria(config->no_focus->items[i]);
+ if (config->seat_configs) {
+ for (int i = 0; i < config->seat_configs->length; i++) {
+ free_seat_config(config->seat_configs->items[i]);
+ }
+ list_free(config->seat_configs);
}
+ list_free(config->criteria);
list_free(config->no_focus);
-
- for (i = 0; config->input_configs && i < config->input_configs->length; ++i) {
- free_input_config(config->input_configs->items[i]);
- }
- list_free(config->input_configs);
-
- for (i = 0; config->output_configs && i < config->output_configs->length; ++i) {
- free_output_config(config->output_configs->items[i]);
- }
- list_free(config->output_configs);
-
- for (i = 0; config->command_policies && i < config->command_policies->length; ++i) {
- free_command_policy(config->command_policies->items[i]);
- }
+ list_free(config->active_bar_modifiers);
+ list_free(config->config_chain);
list_free(config->command_policies);
-
- for (i = 0; config->feature_policies && i < config->feature_policies->length; ++i) {
- free_feature_policy(config->feature_policies->items[i]);
- }
list_free(config->feature_policies);
-
- list_free(config->active_bar_modifiers);
- free_flat_list(config->config_chain);
- free(config->font);
+ list_free(config->ipc_policies);
+ free(config->current_bar);
free(config->floating_scroll_up_cmd);
free(config->floating_scroll_down_cmd);
free(config->floating_scroll_left_cmd);
free(config->floating_scroll_right_cmd);
+ free(config->font);
+ free((char *)config->current_config);
free(config);
}
-
-static bool file_exists(const char *path) {
- return path && access(path, R_OK) != -1;
+static void destroy_removed_seats(struct sway_config *old_config,
+ struct sway_config *new_config) {
+ struct seat_config *seat_config;
+ struct sway_seat *seat;
+ int i;
+ for (i = 0; i < old_config->seat_configs->length; i++) {
+ seat_config = old_config->seat_configs->items[i];
+ /* Also destroy seats that aren't present in new config */
+ if (new_config && list_seq_find(new_config->seat_configs,
+ seat_name_cmp, seat_config->name) < 0) {
+ seat = input_manager_get_seat(input_manager,
+ seat_config->name);
+ seat_destroy(seat);
+ }
+ }
}
static void config_defaults(struct sway_config *config) {
@@ -304,19 +139,22 @@ static void config_defaults(struct sway_config *config) {
if (!(config->criteria = create_list())) goto cleanup;
if (!(config->no_focus = create_list())) goto cleanup;
if (!(config->input_configs = create_list())) goto cleanup;
+ if (!(config->seat_configs = create_list())) goto cleanup;
if (!(config->output_configs = create_list())) goto cleanup;
if (!(config->cmd_queue = create_list())) goto cleanup;
- if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) goto cleanup;
+ if (!(config->current_mode = malloc(sizeof(struct sway_mode))))
+ goto cleanup;
if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup;
strcpy(config->current_mode->name, "default");
- if (!(config->current_mode->bindings = create_list())) goto cleanup;
+ if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;
+ if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
list_add(config->modes, config->current_mode);
config->floating_mod = 0;
- config->dragging_key = M_LEFT_CLICK;
- config->resizing_key = M_RIGHT_CLICK;
+ config->dragging_key = BTN_LEFT;
+ config->resizing_key = BTN_RIGHT;
if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup;
if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup;
if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup;
@@ -324,7 +162,8 @@ static void config_defaults(struct sway_config *config) {
config->default_layout = L_NONE;
config->default_orientation = L_NONE;
if (!(config->font = strdup("monospace 10"))) goto cleanup;
- config->font_height = get_font_text_height(config->font);
+ // TODO: border
+ //config->font_height = get_font_text_height(config->font);
// floating view
config->floating_maximum_width = 0;
@@ -339,7 +178,6 @@ static void config_defaults(struct sway_config *config) {
config->active = false;
config->failed = false;
config->auto_back_and_forth = false;
- config->seamless_mouse = true;
config->reading = false;
config->show_marks = true;
@@ -403,29 +241,8 @@ cleanup:
sway_abort("Unable to allocate config structures");
}
-static int compare_modifiers(const void *left, const void *right) {
- uint32_t a = *(uint32_t *)left;
- uint32_t b = *(uint32_t *)right;
-
- return a - b;
-}
-
-void update_active_bar_modifiers() {
- if (config->active_bar_modifiers->length > 0) {
- list_free(config->active_bar_modifiers);
- config->active_bar_modifiers = create_list();
- }
-
- struct bar_config *bar;
- int i;
- for (i = 0; i < config->bars->length; ++i) {
- bar = config->bars->items[i];
- if (strcmp(bar->mode, "hide") == 0 && strcmp(bar->hidden_state, "hide") == 0) {
- if (list_seq_find(config->active_bar_modifiers, compare_modifiers, &bar->modifier) < 0) {
- list_add(config->active_bar_modifiers, &bar->modifier);
- }
- }
- }
+static bool file_exists(const char *path) {
+ return path && access(path, R_OK) != -1;
}
static char *get_config_path(void) {
@@ -442,12 +259,12 @@ static char *get_config_path(void) {
char *home = getenv("HOME");
char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
if (!config_home) {
- sway_log(L_ERROR, "Unable to allocate $HOME/.config");
+ wlr_log(L_ERROR, "Unable to allocate $HOME/.config");
} else {
strcpy(config_home, home);
strcat(config_home, "/.config");
setenv("XDG_CONFIG_HOME", config_home, 1);
- sway_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
+ wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
free(config_home);
}
}
@@ -463,6 +280,7 @@ static char *get_config_path(void) {
if (file_exists(path)) {
return path;
}
+ free(path);
}
}
@@ -472,7 +290,7 @@ static char *get_config_path(void) {
const char *current_config_path;
static bool load_config(const char *path, struct sway_config *config) {
- sway_log(L_INFO, "Loading config from %s", path);
+ wlr_log(L_INFO, "Loading config from %s", path);
current_config_path = path;
struct stat sb;
@@ -481,13 +299,13 @@ static bool load_config(const char *path, struct sway_config *config) {
}
if (path == NULL) {
- sway_log(L_ERROR, "Unable to find a config file!");
+ wlr_log(L_ERROR, "Unable to find a config file!");
return false;
}
FILE *f = fopen(path, "r");
if (!f) {
- sway_log(L_ERROR, "Unable to open %s for reading", path);
+ wlr_log(L_ERROR, "Unable to open %s for reading", path);
return false;
}
@@ -495,20 +313,14 @@ static bool load_config(const char *path, struct sway_config *config) {
fclose(f);
if (!config_load_success) {
- sway_log(L_ERROR, "Error(s) loading config!");
+ wlr_log(L_ERROR, "Error(s) loading config!");
}
current_config_path = NULL;
return true;
}
-static int qstrcmp(const void* a, const void* b) {
- return strcmp(*((char**) a), *((char**) b));
-}
-
bool load_main_config(const char *file, bool is_active) {
- input_init();
-
char *path;
if (file != NULL) {
path = strdup(file);
@@ -524,7 +336,7 @@ bool load_main_config(const char *file, bool is_active) {
config_defaults(config);
if (is_active) {
- sway_log(L_DEBUG, "Performing configuration file reload");
+ wlr_log(L_DEBUG, "Performing configuration file reload");
config->reloading = true;
config->active = true;
}
@@ -535,11 +347,14 @@ bool load_main_config(const char *file, bool is_active) {
config->reading = true;
// Read security configs
+ // TODO: Security
bool success = true;
+ /*
DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
if (!dir) {
- sway_log(L_ERROR, "%s does not exist, sway will have no security configuration"
- " and will probably be broken", SYSCONFDIR "/sway/security.d");
+ wlr_log(L_ERROR,
+ "%s does not exist, sway will have no security configuration"
+ " and will probably be broken", SYSCONFDIR "/sway/security.d");
} else {
list_t *secconfigs = create_list();
char *base = SYSCONFDIR "/sway/security.d/";
@@ -563,8 +378,12 @@ bool load_main_config(const char *file, bool is_active) {
list_qsort(secconfigs, qstrcmp);
for (int i = 0; i < secconfigs->length; ++i) {
char *_path = secconfigs->items[i];
- if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) {
- sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644 or 444", _path);
+ if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
+ (((s.st_mode & 0777) != 0644) &&
+ (s.st_mode & 0777) != 0444)) {
+ wlr_log(L_ERROR,
+ "Refusing to load %s - it must be owned by root "
+ "and mode 644 or 444", _path);
success = false;
} else {
success = success && load_config(_path, config);
@@ -573,6 +392,7 @@ bool load_main_config(const char *file, bool is_active) {
free_flat_list(secconfigs);
}
+ */
success = success && load_config(path, config);
@@ -581,18 +401,15 @@ bool load_main_config(const char *file, bool is_active) {
}
if (old_config) {
+ destroy_removed_seats(old_config, config);
free_config(old_config);
}
config->reading = false;
-
- if (success) {
- update_active_bar_modifiers();
- }
-
return success;
}
-static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) {
+static bool load_include_config(const char *path, const char *parent_dir,
+ struct sway_config *config) {
// save parent config
const char *parent_config = config->current_config;
@@ -602,7 +419,8 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
len = len + strlen(parent_dir) + 2;
full_path = malloc(len * sizeof(char));
if (!full_path) {
- sway_log(L_ERROR, "Unable to allocate full path to included config");
+ wlr_log(L_ERROR,
+ "Unable to allocate full path to included config");
return false;
}
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
free(full_path);
if (real_path == NULL) {
- sway_log(L_DEBUG, "%s not found.", path);
+ wlr_log(L_DEBUG, "%s not found.", path);
return false;
}
@@ -621,7 +439,9 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
for (j = 0; j < config->config_chain->length; ++j) {
char *old_path = config->config_chain->items[j];
if (strcmp(real_path, old_path) == 0) {
- sway_log(L_DEBUG, "%s already included once, won't be included again.", real_path);
+ wlr_log(L_DEBUG,
+ "%s already included once, won't be included again.",
+ real_path);
free(real_path);
return false;
}
@@ -673,7 +493,7 @@ bool load_include_configs(const char *path, struct sway_config *config) {
// restore wd
if (chdir(wd) < 0) {
free(wd);
- sway_log(L_ERROR, "failed to restore working directory");
+ wlr_log(L_ERROR, "failed to restore working directory");
return false;
}
@@ -681,13 +501,11 @@ bool load_include_configs(const char *path, struct sway_config *config) {
return true;
}
-struct cmd_results *check_security_config() {
- if (!current_config_path || strncmp(SYSCONFDIR "/sway/security.d/", current_config_path,
- strlen(SYSCONFDIR "/sway/security.d/")) != 0) {
- return cmd_results_new(CMD_INVALID, "permit",
- "This command is only permitted to run from " SYSCONFDIR "/sway/security.d/*");
- }
- return NULL;
+void config_clear_handler_context(struct sway_config *config) {
+ free_input_config(config->handler_context.input_config);
+ free_seat_config(config->handler_context.seat_config);
+
+ memset(&config->handler_context, 0, sizeof(config->handler_context));
}
bool read_config(FILE *file, struct sway_config *config) {
@@ -717,13 +535,13 @@ bool read_config(FILE *file, struct sway_config *config) {
switch(res->status) {
case CMD_FAILURE:
case CMD_INVALID:
- sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, line,
- res->error, config->current_config);
+ wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number,
+ line, res->error, config->current_config);
success = false;
break;
case CMD_DEFER:
- sway_log(L_DEBUG, "Defferring command `%s'", line);
+ wlr_log(L_DEBUG, "Deferring command `%s'", line);
list_add(config->cmd_queue, strdup(line));
break;
@@ -731,7 +549,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_MODE;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -739,7 +557,15 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_INPUT;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
+ }
+ break;
+
+ case CMD_BLOCK_SEAT:
+ if (block == CMD_BLOCK_END) {
+ block = CMD_BLOCK_SEAT;
+ } else {
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -747,7 +573,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_BAR;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -755,7 +581,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_BAR) {
block = CMD_BLOCK_BAR_COLORS;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -763,7 +589,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_COMMANDS;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -771,7 +597,7 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_END) {
block = CMD_BLOCK_IPC;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
@@ -779,56 +605,61 @@ bool read_config(FILE *file, struct sway_config *config) {
if (block == CMD_BLOCK_IPC) {
block = CMD_BLOCK_IPC_EVENTS;
} else {
- sway_log(L_ERROR, "Invalid block '%s'", line);
+ wlr_log(L_ERROR, "Invalid block '%s'", line);
}
break;
case CMD_BLOCK_END:
switch(block) {
case CMD_BLOCK_MODE:
- sway_log(L_DEBUG, "End of mode block");
+ wlr_log(L_DEBUG, "End of mode block");
config->current_mode = config->modes->items[0];
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_INPUT:
- sway_log(L_DEBUG, "End of input block");
- current_input_config = NULL;
+ wlr_log(L_DEBUG, "End of input block");
+ block = CMD_BLOCK_END;
+ break;
+
+ case CMD_BLOCK_SEAT:
+ wlr_log(L_DEBUG, "End of seat block");
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_BAR:
- sway_log(L_DEBUG, "End of bar block");
+ wlr_log(L_DEBUG, "End of bar block");
config->current_bar = NULL;
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_BAR_COLORS:
- sway_log(L_DEBUG, "End of bar colors block");
+ wlr_log(L_DEBUG, "End of bar colors block");
block = CMD_BLOCK_BAR;
break;
case CMD_BLOCK_COMMANDS:
- sway_log(L_DEBUG, "End of commands block");
+ wlr_log(L_DEBUG, "End of commands block");
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_IPC:
- sway_log(L_DEBUG, "End of IPC block");
+ wlr_log(L_DEBUG, "End of IPC block");
block = CMD_BLOCK_END;
break;
case CMD_BLOCK_IPC_EVENTS:
- sway_log(L_DEBUG, "End of IPC events block");
+ wlr_log(L_DEBUG, "End of IPC events block");
block = CMD_BLOCK_IPC;
break;
case CMD_BLOCK_END:
- sway_log(L_ERROR, "Unmatched }");
+ wlr_log(L_ERROR, "Unmatched }");
break;
default:;
}
+ config_clear_handler_context(config);
default:;
}
free(line);
@@ -838,365 +669,6 @@ bool read_config(FILE *file, struct sway_config *config) {
return success;
}
-int input_identifier_cmp(const void *item, const void *data) {
- const struct input_config *ic = item;
- const char *identifier = data;
- return strcmp(ic->identifier, identifier);
-}
-
-int output_name_cmp(const void *item, const void *data) {
- const struct output_config *output = item;
- const char *name = data;
-
- return strcmp(output->name, name);
-}
-
-void merge_input_config(struct input_config *dst, struct input_config *src) {
- if (src->identifier) {
- if (dst->identifier) {
- free(dst->identifier);
- }
- dst->identifier = strdup(src->identifier);
- }
- if (src->accel_profile != INT_MIN) {
- dst->accel_profile = src->accel_profile;
- }
- if (src->click_method != INT_MIN) {
- dst->click_method = src->click_method;
- }
- if (src->drag_lock != INT_MIN) {
- dst->drag_lock = src->drag_lock;
- }
- if (src->dwt != INT_MIN) {
- dst->dwt = src->dwt;
- }
- if (src->middle_emulation != INT_MIN) {
- dst->middle_emulation = src->middle_emulation;
- }
- if (src->natural_scroll != INT_MIN) {
- dst->natural_scroll = src->natural_scroll;
- }
- if (src->pointer_accel != FLT_MIN) {
- dst->pointer_accel = src->pointer_accel;
- }
- if (src->scroll_method != INT_MIN) {
- dst->scroll_method = src->scroll_method;
- }
- if (src->send_events != INT_MIN) {
- dst->send_events = src->send_events;
- }
- if (src->tap != INT_MIN) {
- dst->tap = src->tap;
- }
-}
-
-void merge_output_config(struct output_config *dst, struct output_config *src) {
- if (src->name) {
- if (dst->name) {
- free(dst->name);
- }
- dst->name = strdup(src->name);
- }
- if (src->enabled != -1) {
- dst->enabled = src->enabled;
- }
- if (src->width != -1) {
- dst->width = src->width;
- }
- if (src->height != -1) {
- dst->height = src->height;
- }
- if (src->x != -1) {
- dst->x = src->x;
- }
- if (src->y != -1) {
- dst->y = src->y;
- }
- if (src->scale != -1) {
- dst->scale = src->scale;
- }
- if (src->background) {
- if (dst->background) {
- free(dst->background);
- }
- dst->background = strdup(src->background);
- }
- if (src->background_option) {
- if (dst->background_option) {
- free(dst->background_option);
- }
- dst->background_option = strdup(src->background_option);
- }
-}
-
-static void invoke_swaybar(struct bar_config *bar) {
- // Pipe to communicate errors
- int filedes[2];
- if (pipe(filedes) == -1) {
- sway_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
- return;
- }
-
- bar->pid = fork();
- if (bar->pid == 0) {
- close(filedes[0]);
- if (!bar->swaybar_command) {
- char *const cmd[] = {
- "swaybar",
- "-b",
- bar->id,
- NULL,
- };
-
- close(filedes[1]);
- execvp(cmd[0], cmd);
- _exit(EXIT_SUCCESS);
- } else {
- // run custom swaybar
- int len = strlen(bar->swaybar_command) + strlen(bar->id) + 5;
- char *command = malloc(len * sizeof(char));
- if (!command) {
- const char msg[] = "Unable to allocate swaybar command string";
- int len = sizeof(msg);
- if (write(filedes[1], &len, sizeof(int))) {};
- if (write(filedes[1], msg, len)) {};
- close(filedes[1]);
- _exit(EXIT_FAILURE);
- }
- snprintf(command, len, "%s -b %s", bar->swaybar_command, bar->id);
-
- char *const cmd[] = {
- "sh",
- "-c",
- command,
- NULL,
- };
-
- close(filedes[1]);
- execvp(cmd[0], cmd);
- free(command);
- _exit(EXIT_SUCCESS);
- }
- }
- close(filedes[0]);
- int len;
- if(read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
- char *buf = malloc(len);
- if(!buf) {
- sway_log(L_ERROR, "Cannot allocate error string");
- return;
- }
- if(read(filedes[1], buf, len)) {
- sway_log(L_ERROR, "%s", buf);
- }
- free(buf);
- }
- close(filedes[1]);
-}
-
-static void terminate_swaybar(pid_t pid) {
- int ret = kill(pid, SIGTERM);
- if (ret != 0) {
- sway_log(L_ERROR, "Unable to terminate swaybar [pid: %d]", pid);
- } else {
- int status;
- waitpid(pid, &status, 0);
- }
-}
-
-void terminate_swaybg(pid_t pid) {
- int ret = kill(pid, SIGTERM);
- if (ret != 0) {
- sway_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
- } else {
- int status;
- waitpid(pid, &status, 0);
- }
-}
-
-static bool active_output(const char *name) {
- int i;
- swayc_t *cont = NULL;
- for (i = 0; i < root_container.children->length; ++i) {
- cont = root_container.children->items[i];
- if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
- return true;
- }
- }
-
- return false;
-}
-
-void load_swaybars() {
- // Check for bars
- list_t *bars = create_list();
- struct bar_config *bar = NULL;
- int i;
- for (i = 0; i < config->bars->length; ++i) {
- bar = config->bars->items[i];
- bool apply = false;
- if (bar->outputs) {
- int j;
- for (j = 0; j < bar->outputs->length; ++j) {
- char *o = bar->outputs->items[j];
- if (!strcmp(o, "*") || active_output(o)) {
- apply = true;
- break;
- }
- }
- } else {
- apply = true;
- }
- if (apply) {
- list_add(bars, bar);
- }
- }
-
- for (i = 0; i < bars->length; ++i) {
- bar = bars->items[i];
- if (bar->pid != 0) {
- terminate_swaybar(bar->pid);
- }
- sway_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
- invoke_swaybar(bar);
- }
-
- list_free(bars);
-}
-
-void apply_input_config(struct input_config *ic, struct libinput_device *dev) {
- if (!ic) {
- return;
- }
-
- sway_log(L_DEBUG, "apply_input_config(%s)", ic->identifier);
-
- if (ic->accel_profile != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) accel_set_profile(%d)", ic->identifier, ic->accel_profile);
- libinput_device_config_accel_set_profile(dev, ic->accel_profile);
- }
- if (ic->click_method != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) click_set_method(%d)", ic->identifier, ic->click_method);
- libinput_device_config_click_set_method(dev, ic->click_method);
- }
- if (ic->drag_lock != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) tap_set_drag_lock_enabled(%d)", ic->identifier, ic->click_method);
- libinput_device_config_tap_set_drag_lock_enabled(dev, ic->drag_lock);
- }
- if (ic->dwt != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) dwt_set_enabled(%d)", ic->identifier, ic->dwt);
- libinput_device_config_dwt_set_enabled(dev, ic->dwt);
- }
- if (ic->left_handed != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) left_handed_set_enabled(%d)", ic->identifier, ic->left_handed);
- libinput_device_config_left_handed_set(dev, ic->left_handed);
- }
- if (ic->middle_emulation != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) middle_emulation_set_enabled(%d)", ic->identifier, ic->middle_emulation);
- libinput_device_config_middle_emulation_set_enabled(dev, ic->middle_emulation);
- }
- if (ic->natural_scroll != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) natural_scroll_set_enabled(%d)", ic->identifier, ic->natural_scroll);
- libinput_device_config_scroll_set_natural_scroll_enabled(dev, ic->natural_scroll);
- }
- if (ic->pointer_accel != FLT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) accel_set_speed(%f)", ic->identifier, ic->pointer_accel);
- libinput_device_config_accel_set_speed(dev, ic->pointer_accel);
- }
- if (ic->scroll_method != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) scroll_set_method(%d)", ic->identifier, ic->scroll_method);
- libinput_device_config_scroll_set_method(dev, ic->scroll_method);
- }
- if (ic->send_events != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) send_events_set_mode(%d)", ic->identifier, ic->send_events);
- libinput_device_config_send_events_set_mode(dev, ic->send_events);
- }
- if (ic->tap != INT_MIN) {
- sway_log(L_DEBUG, "apply_input_config(%s) tap_set_enabled(%d)", ic->identifier, ic->tap);
- libinput_device_config_tap_set_enabled(dev, ic->tap);
- }
-}
-
-void apply_output_config(struct output_config *oc, swayc_t *output) {
- if (oc && oc->enabled == 0) {
- destroy_output(output);
- return;
- }
-
- if (oc && oc->width > 0 && oc->height > 0) {
- output->width = oc->width;
- output->height = oc->height;
-
- sway_log(L_DEBUG, "Set %s size to %ix%i (%d)", oc->name, oc->width, oc->height, oc->scale);
- struct wlc_size new_size = { .w = oc->width, .h = oc->height };
- wlc_output_set_resolution(output->handle, &new_size, (uint32_t)oc->scale);
- } else if (oc) {
- const struct wlc_size *new_size = wlc_output_get_resolution(output->handle);
- wlc_output_set_resolution(output->handle, new_size, (uint32_t)oc->scale);
- }
-
- // Find position for it
- if (oc && oc->x != -1 && oc->y != -1) {
- sway_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
- output->x = oc->x;
- output->y = oc->y;
- } else {
- int x = 0;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c->type == C_OUTPUT) {
- if (c->width + c->x > x) {
- x = c->width + c->x;
- }
- }
- }
- output->x = x;
- }
-
- if (!oc || !oc->background) {
- // Look for a * config for background
- int i = list_seq_find(config->output_configs, output_name_cmp, "*");
- if (i >= 0) {
- oc = config->output_configs->items[i];
- } else {
- oc = NULL;
- }
- }
-
- int output_i;
- for (output_i = 0; output_i < root_container.children->length; ++output_i) {
- if (root_container.children->items[output_i] == output) {
- break;
- }
- }
-
- if (oc && oc->background) {
- if (output->bg_pid != 0) {
- terminate_swaybg(output->bg_pid);
- }
-
- sway_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background);
-
- size_t bufsize = 12;
- char output_id[bufsize];
- snprintf(output_id, bufsize, "%d", output_i);
- output_id[bufsize-1] = 0;
-
- char *const cmd[] = {
- "swaybg",
- output_id,
- oc->background,
- oc->background_option,
- NULL,
- };
-
- output->bg_pid = fork();
- if (output->bg_pid == 0) {
- execvp(cmd[0], cmd);
- }
- }
-}
-
char *do_var_replacement(char *str) {
int i;
char *find = str;
@@ -1216,8 +688,9 @@ char *do_var_replacement(char *str) {
int vvlen = strlen(var->value);
char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
if (!newstr) {
- sway_log(L_ERROR,
- "Unable to allocate replacement during variable expansion");
+ wlr_log(L_ERROR,
+ "Unable to allocate replacement "
+ "during variable expansion");
break;
}
char *newptr = newstr;
@@ -1247,197 +720,3 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
const struct workspace_output *wsa = a, *wsb = b;
return lenient_strcmp(wsa->workspace, wsb->workspace);
}
-
-int sway_binding_cmp_keys(const void *a, const void *b) {
- const struct sway_binding *binda = a, *bindb = b;
-
- // Count keys pressed for this binding. important so we check long before
- // short ones. for example mod+a+b before mod+a
- unsigned int moda = 0, modb = 0, i;
-
- // Count how any modifiers are pressed
- for (i = 0; i < 8 * sizeof(binda->modifiers); ++i) {
- moda += (binda->modifiers & 1 << i) != 0;
- modb += (bindb->modifiers & 1 << i) != 0;
- }
- if (bindb->keys->length + modb != binda->keys->length + moda) {
- return (bindb->keys->length + modb) - (binda->keys->length + moda);
- }
-
- // Otherwise compare keys
- if (binda->modifiers > bindb->modifiers) {
- return 1;
- } else if (binda->modifiers < bindb->modifiers) {
- return -1;
- }
- struct wlc_modifiers no_mods = { 0, 0 };
- for (int i = 0; i < binda->keys->length; i++) {
- xkb_keysym_t ka = *(xkb_keysym_t *)binda->keys->items[i],
- kb = *(xkb_keysym_t *)bindb->keys->items[i];
- if (binda->bindcode) {
- uint32_t *keycode = binda->keys->items[i];
- ka = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
- }
-
- if (bindb->bindcode) {
- uint32_t *keycode = bindb->keys->items[i];
- kb = wlc_keyboard_get_keysym_for_key(*keycode, &no_mods);
- }
-
- if (ka > kb) {
- return 1;
- } else if (ka < kb) {
- return -1;
- }
- }
-
- return 0;
-}
-
-int sway_binding_cmp(const void *a, const void *b) {
- int cmp = 0;
- if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
- return cmp;
- }
- const struct sway_binding *binda = a, *bindb = b;
- return lenient_strcmp(binda->command, bindb->command);
-}
-
-int sway_binding_cmp_qsort(const void *a, const void *b) {
- return sway_binding_cmp(*(void **)a, *(void **)b);
-}
-
-void free_sway_binding(struct sway_binding *binding) {
- if (binding->keys) {
- for (int i = 0; i < binding->keys->length; i++) {
- free(binding->keys->items[i]);
- }
- list_free(binding->keys);
- }
- if (binding->command) {
- free(binding->command);
- }
- free(binding);
-}
-
-int sway_mouse_binding_cmp_buttons(const void *a, const void *b) {
- const struct sway_mouse_binding *binda = a, *bindb = b;
- if (binda->button > bindb->button) {
- return 1;
- }
- if (binda->button < bindb->button) {
- return -1;
- }
- return 0;
-}
-
-int sway_mouse_binding_cmp(const void *a, const void *b) {
- int cmp = 0;
- if ((cmp = sway_binding_cmp_keys(a, b)) != 0) {
- return cmp;
- }
- const struct sway_mouse_binding *binda = a, *bindb = b;
- return lenient_strcmp(binda->command, bindb->command);
-}
-
-int sway_mouse_binding_cmp_qsort(const void *a, const void *b) {
- return sway_mouse_binding_cmp(*(void **)a, *(void **)b);
-}
-
-void free_sway_mouse_binding(struct sway_mouse_binding *binding) {
- if (binding->command) {
- free(binding->command);
- }
- free(binding);
-}
-
-struct sway_binding *sway_binding_dup(struct sway_binding *sb) {
- struct sway_binding *new_sb = malloc(sizeof(struct sway_binding));
- if (!new_sb) {
- return NULL;
- }
-
- new_sb->order = sb->order;
- new_sb->modifiers = sb->modifiers;
- new_sb->command = strdup(sb->command);
-
- new_sb->keys = create_list();
- int i;
- for (i = 0; i < sb->keys->length; ++i) {
- xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
- if (!key) {
- free_sway_binding(new_sb);
- return NULL;
- }
- *key = *(xkb_keysym_t *)sb->keys->items[i];
- list_add(new_sb->keys, key);
- }
-
- return new_sb;
-}
-
-struct bar_config *default_bar_config(void) {
- struct bar_config *bar = NULL;
- bar = malloc(sizeof(struct bar_config));
- if (!bar) {
- return NULL;
- }
- if (!(bar->mode = strdup("dock"))) goto cleanup;
- if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
- bar->modifier = WLC_BIT_MOD_LOGO;
- bar->outputs = NULL;
- bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
- if (!(bar->bindings = create_list())) goto cleanup;
- if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
- bar->pango_markup = false;
- bar->swaybar_command = NULL;
- bar->font = NULL;
- bar->height = -1;
- bar->workspace_buttons = true;
- bar->wrap_scroll = false;
- bar->separator_symbol = NULL;
- bar->strip_workspace_numbers = false;
- bar->binding_mode_indicator = true;
-#ifdef ENABLE_TRAY
- bar->tray_output = NULL;
- bar->icon_theme = NULL;
- bar->tray_padding = 2;
- bar->activate_button = 0x110; /* BTN_LEFT */
- bar->context_button = 0x111; /* BTN_RIGHT */
- bar->secondary_button = 0x112; /* BTN_MIDDLE */
-#endif
- bar->verbose = false;
- bar->pid = 0;
- // set default colors
- if (!(bar->colors.background = strndup("#000000ff", 9))) goto cleanup;
- if (!(bar->colors.statusline = strndup("#ffffffff", 9))) goto cleanup;
- if (!(bar->colors.separator = strndup("#666666ff", 9))) goto cleanup;
- if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) goto cleanup;
- if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) goto cleanup;
- if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
- if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) goto cleanup;
- if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) goto cleanup;
- if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
- if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) goto cleanup;
- if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) goto cleanup;
- if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) goto cleanup;
- if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) goto cleanup;
- if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) goto cleanup;
- if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) goto cleanup;
- // if the following colors stay undefined, they fall back to background,
- // statusline, separator and urgent_workspace_*.
- bar->colors.focused_background = NULL;
- bar->colors.focused_statusline = NULL;
- bar->colors.focused_separator = NULL;
- bar->colors.binding_mode_border = NULL;
- bar->colors.binding_mode_bg = NULL;
- bar->colors.binding_mode_text = NULL;
-
- list_add(config->bars, bar);
-
- return bar;
-
-cleanup:
- free_bar(bar);
- return NULL;
-}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 700
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wordexp.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "stringop.h"
+#include "list.h"
+#include "log.h"
+
+static void terminate_swaybar(pid_t pid) {
+ wlr_log(L_DEBUG, "Terminating swaybar %d", pid);
+ int ret = kill(pid, SIGTERM);
+ if (ret != 0) {
+ wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid);
+ } else {
+ int status;
+ waitpid(pid, &status, 0);
+ }
+}
+
+void free_bar_config(struct bar_config *bar) {
+ if (!bar) {
+ return;
+ }
+ free(bar->mode);
+ free(bar->position);
+ free(bar->hidden_state);
+ free(bar->status_command);
+ free(bar->font);
+ free(bar->separator_symbol);
+ // TODO: Free mouse bindings
+ list_free(bar->bindings);
+ if (bar->outputs) {
+ free_flat_list(bar->outputs);
+ }
+ if (bar->pid != 0) {
+ terminate_swaybar(bar->pid);
+ }
+ free(bar->colors.background);
+ free(bar->colors.statusline);
+ free(bar->colors.separator);
+ free(bar->colors.focused_background);
+ free(bar->colors.focused_statusline);
+ free(bar->colors.focused_separator);
+ free(bar->colors.focused_workspace_border);
+ free(bar->colors.focused_workspace_bg);
+ free(bar->colors.focused_workspace_text);
+ free(bar->colors.active_workspace_border);
+ free(bar->colors.active_workspace_bg);
+ free(bar->colors.active_workspace_text);
+ free(bar->colors.inactive_workspace_border);
+ free(bar->colors.inactive_workspace_bg);
+ free(bar->colors.inactive_workspace_text);
+ free(bar->colors.urgent_workspace_border);
+ free(bar->colors.urgent_workspace_bg);
+ free(bar->colors.urgent_workspace_text);
+ free(bar->colors.binding_mode_border);
+ free(bar->colors.binding_mode_bg);
+ free(bar->colors.binding_mode_text);
+ free(bar);
+}
+
+struct bar_config *default_bar_config(void) {
+ struct bar_config *bar = NULL;
+ bar = malloc(sizeof(struct bar_config));
+ if (!bar) {
+ return NULL;
+ }
+ if (!(bar->mode = strdup("dock"))) goto cleanup;
+ if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
+ bar->outputs = NULL;
+ bar->position = strdup("bottom");
+ if (!(bar->bindings = create_list())) goto cleanup;
+ if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
+ bar->pango_markup = false;
+ bar->swaybar_command = NULL;
+ bar->font = NULL;
+ bar->height = -1;
+ bar->workspace_buttons = true;
+ bar->wrap_scroll = false;
+ bar->separator_symbol = NULL;
+ bar->strip_workspace_numbers = false;
+ bar->binding_mode_indicator = true;
+ bar->verbose = false;
+ bar->pid = 0;
+ // set default colors
+ if (!(bar->colors.background = strndup("#000000ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.statusline = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.separator = strndup("#666666ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) {
+ goto cleanup;
+ }
+ if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) {
+ goto cleanup;
+ }
+ // if the following colors stay undefined, they fall back to background,
+ // statusline, separator and urgent_workspace_*.
+ bar->colors.focused_background = NULL;
+ bar->colors.focused_statusline = NULL;
+ bar->colors.focused_separator = NULL;
+ bar->colors.binding_mode_border = NULL;
+ bar->colors.binding_mode_bg = NULL;
+ bar->colors.binding_mode_text = NULL;
+
+ list_add(config->bars, bar);
+ return bar;
+cleanup:
+ free_bar_config(bar);
+ return NULL;
+}
+
+void invoke_swaybar(struct bar_config *bar) {
+ // Pipe to communicate errors
+ int filedes[2];
+ if (pipe(filedes) == -1) {
+ wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
+ return;
+ }
+
+ bar->pid = fork();
+ if (bar->pid == 0) {
+ close(filedes[0]);
+
+ // run custom swaybar
+ size_t len = snprintf(NULL, 0, "%s -b %s",
+ bar->swaybar_command ? bar->swaybar_command : "swaybar",
+ bar->id);
+ char *command = malloc(len + 1);
+ if (!command) {
+ const char msg[] = "Unable to allocate swaybar command string";
+ size_t len = sizeof(msg);
+ if (write(filedes[1], &len, sizeof(int))) {};
+ if (write(filedes[1], msg, len)) {};
+ close(filedes[1]);
+ exit(1);
+ }
+ snprintf(command, len + 1, "%s -b %s",
+ bar->swaybar_command ? bar->swaybar_command : "swaybar",
+ bar->id);
+ char *const cmd[] = { "sh", "-c", command, NULL, };
+ close(filedes[1]);
+ execvp(cmd[0], cmd);
+ exit(1);
+ }
+ wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid);
+ close(filedes[0]);
+ ssize_t len;
+ if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
+ char *buf = malloc(len);
+ if(!buf) {
+ wlr_log(L_ERROR, "Cannot allocate error string");
+ return;
+ }
+ if (read(filedes[1], buf, len)) {
+ wlr_log(L_ERROR, "%s", buf);
+ }
+ free(buf);
+ }
+ close(filedes[1]);
+}
+
+static bool active_output(const char *name) {
+ struct sway_container *cont = NULL;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ cont = root_container.children->items[i];
+ if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void load_swaybars() {
+ for (int i = 0; i < config->bars->length; ++i) {
+ struct bar_config *bar = config->bars->items[i];
+ bool apply = false;
+ if (bar->outputs) {
+ for (int j = 0; j < bar->outputs->length; ++j) {
+ char *o = bar->outputs->items[j];
+ if (!strcmp(o, "*") || active_output(o)) {
+ apply = true;
+ break;
+ }
+ }
+ } else {
+ apply = true;
+ }
+ if (apply) {
+ if (bar->pid != 0) {
+ terminate_swaybar(bar->pid);
+ }
+ wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
+ invoke_swaybar(bar);
+ }
+ }
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <stdlib.h>
+#include <limits.h>
+#include <float.h>
+#include "sway/config.h"
+#include "log.h"
+
+struct input_config *new_input_config(const char* identifier) {
+ struct input_config *input = calloc(1, sizeof(struct input_config));
+ if (!input) {
+ wlr_log(L_DEBUG, "Unable to allocate input config");
+ return NULL;
+ }
+ wlr_log(L_DEBUG, "new_input_config(%s)", identifier);
+ if (!(input->identifier = strdup(identifier))) {
+ free(input);
+ wlr_log(L_DEBUG, "Unable to allocate input config");
+ return NULL;
+ }
+
+ input->tap = INT_MIN;
+ input->drag_lock = INT_MIN;
+ input->dwt = INT_MIN;
+ input->send_events = INT_MIN;
+ input->click_method = INT_MIN;
+ input->middle_emulation = INT_MIN;
+ input->natural_scroll = INT_MIN;
+ input->accel_profile = INT_MIN;
+ input->pointer_accel = FLT_MIN;
+ input->scroll_method = INT_MIN;
+ input->left_handed = INT_MIN;
+
+ return input;
+}
+
+void merge_input_config(struct input_config *dst, struct input_config *src) {
+ if (src->identifier) {
+ free(dst->identifier);
+ dst->identifier = strdup(src->identifier);
+ }
+ if (src->accel_profile != INT_MIN) {
+ dst->accel_profile = src->accel_profile;
+ }
+ if (src->click_method != INT_MIN) {
+ dst->click_method = src->click_method;
+ }
+ if (src->drag_lock != INT_MIN) {
+ dst->drag_lock = src->drag_lock;
+ }
+ if (src->dwt != INT_MIN) {
+ dst->dwt = src->dwt;
+ }
+ if (src->middle_emulation != INT_MIN) {
+ dst->middle_emulation = src->middle_emulation;
+ }
+ if (src->natural_scroll != INT_MIN) {
+ dst->natural_scroll = src->natural_scroll;
+ }
+ if (src->pointer_accel != FLT_MIN) {
+ dst->pointer_accel = src->pointer_accel;
+ }
+ if (src->scroll_method != INT_MIN) {
+ dst->scroll_method = src->scroll_method;
+ }
+ if (src->send_events != INT_MIN) {
+ dst->send_events = src->send_events;
+ }
+ if (src->tap != INT_MIN) {
+ dst->tap = src->tap;
+ }
+ if (src->xkb_layout) {
+ free(dst->xkb_layout);
+ dst->xkb_layout = strdup(src->xkb_layout);
+ }
+ if (src->xkb_model) {
+ free(dst->xkb_model);
+ dst->xkb_model = strdup(src->xkb_model);
+ }
+ if (src->xkb_options) {
+ free(dst->xkb_options);
+ dst->xkb_options = strdup(src->xkb_options);
+ }
+ if (src->xkb_rules) {
+ free(dst->xkb_rules);
+ dst->xkb_rules = strdup(src->xkb_rules);
+ }
+ if (src->xkb_variant) {
+ free(dst->xkb_variant);
+ dst->xkb_variant = strdup(src->xkb_variant);
+ }
+ if (src->mapped_output) {
+ free(dst->mapped_output);
+ dst->mapped_output = strdup(src->mapped_output);
+ }
+}
+
+struct input_config *copy_input_config(struct input_config *ic) {
+ struct input_config *copy = calloc(1, sizeof(struct input_config));
+ if (copy == NULL) {
+ wlr_log(L_ERROR, "could not allocate input config");
+ return NULL;
+ }
+ merge_input_config(copy, ic);
+ return copy;
+}
+
+void free_input_config(struct input_config *ic) {
+ if (!ic) {
+ return;
+ }
+ free(ic->identifier);
+ free(ic);
+}
+
+int input_identifier_cmp(const void *item, const void *data) {
+ const struct input_config *ic = item;
+ const char *identifier = data;
+ return strcmp(ic->identifier, identifier);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include "sway/config.h"
+#include "sway/output.h"
+#include "log.h"
+
+int output_name_cmp(const void *item, const void *data) {
+ const struct output_config *output = item;
+ const char *name = data;
+
+ return strcmp(output->name, name);
+}
+
+void output_get_identifier(char *identifier, size_t len,
+ struct sway_output *output) {
+ struct wlr_output *wlr_output = output->wlr_output;
+ snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model,
+ wlr_output->serial);
+}
+
+struct output_config *new_output_config(const char *name) {
+ struct output_config *oc = calloc(1, sizeof(struct output_config));
+ if (oc == NULL) {
+ return NULL;
+ }
+ oc->name = strdup(name);
+ if (oc->name == NULL) {
+ free(oc);
+ return NULL;
+ }
+ oc->enabled = -1;
+ oc->width = oc->height = -1;
+ oc->refresh_rate = -1;
+ oc->x = oc->y = -1;
+ oc->scale = -1;
+ oc->transform = -1;
+ return oc;
+}
+
+void merge_output_config(struct output_config *dst, struct output_config *src) {
+ if (src->name) {
+ free(dst->name);
+ dst->name = strdup(src->name);
+ }
+ if (src->enabled != -1) {
+ dst->enabled = src->enabled;
+ }
+ if (src->width != -1) {
+ dst->width = src->width;
+ }
+ if (src->height != -1) {
+ dst->height = src->height;
+ }
+ if (src->x != -1) {
+ dst->x = src->x;
+ }
+ if (src->y != -1) {
+ dst->y = src->y;
+ }
+ if (src->scale != -1) {
+ dst->scale = src->scale;
+ }
+ if (src->refresh_rate != -1) {
+ dst->refresh_rate = src->refresh_rate;
+ }
+ if (src->transform != -1) {
+ dst->transform = src->transform;
+ }
+ if (src->background) {
+ free(dst->background);
+ dst->background = strdup(src->background);
+ }
+ if (src->background_option) {
+ free(dst->background_option);
+ dst->background_option = strdup(src->background_option);
+ }
+}
+
+static void set_mode(struct wlr_output *output, int width, int height,
+ float refresh_rate) {
+ int mhz = (int)(refresh_rate * 1000);
+ if (wl_list_empty(&output->modes)) {
+ wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name);
+ wlr_output_set_custom_mode(output, width, height, mhz);
+ return;
+ }
+
+ struct wlr_output_mode *mode, *best = NULL;
+ wl_list_for_each(mode, &output->modes, link) {
+ if (mode->width == width && mode->height == height) {
+ if (mode->refresh == mhz) {
+ best = mode;
+ break;
+ }
+ best = mode;
+ }
+ }
+ if (!best) {
+ wlr_log(L_ERROR, "Configured mode for %s not available", output->name);
+ } else {
+ wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name);
+ wlr_output_set_mode(output, best);
+ }
+}
+
+void terminate_swaybg(pid_t pid) {
+ int ret = kill(pid, SIGTERM);
+ if (ret != 0) {
+ wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
+ } else {
+ int status;
+ waitpid(pid, &status, 0);
+ }
+}
+
+void apply_output_config(struct output_config *oc, struct sway_container *output) {
+ assert(output->type == C_OUTPUT);
+
+ struct wlr_output_layout *output_layout =
+ root_container.sway_root->output_layout;
+ struct wlr_output *wlr_output = output->sway_output->wlr_output;
+
+ if (oc && oc->enabled == 0) {
+ container_destroy(output);
+ wlr_output_layout_remove(root_container.sway_root->output_layout,
+ wlr_output);
+ return;
+ }
+
+ if (oc && oc->width > 0 && oc->height > 0) {
+ wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width,
+ oc->height, oc->refresh_rate);
+ set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate);
+ }
+ if (oc && oc->scale > 0) {
+ wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale);
+ wlr_output_set_scale(wlr_output, oc->scale);
+ }
+ if (oc && oc->transform >= 0) {
+ wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform);
+ wlr_output_set_transform(wlr_output, oc->transform);
+ }
+
+ // Find position for it
+ if (oc && (oc->x != -1 || oc->y != -1)) {
+ wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
+ wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y);
+ } else {
+ wlr_output_layout_add_auto(output_layout, wlr_output);
+ }
+
+ if (!oc || !oc->background) {
+ // Look for a * config for background
+ int i = list_seq_find(config->output_configs, output_name_cmp, "*");
+ if (i >= 0) {
+ oc = config->output_configs->items[i];
+ } else {
+ oc = NULL;
+ }
+ }
+
+ int output_i;
+ for (output_i = 0; output_i < root_container.children->length; ++output_i) {
+ if (root_container.children->items[output_i] == output) {
+ break;
+ }
+ }
+
+ if (oc && oc->background) {
+ if (output->sway_output->bg_pid != 0) {
+ terminate_swaybg(output->sway_output->bg_pid);
+ }
+
+ wlr_log(L_DEBUG, "Setting background for output %d to %s",
+ output_i, oc->background);
+
+ size_t len = snprintf(NULL, 0, "%s %d %s %s",
+ config->swaybg_command ? config->swaybg_command : "swaybg",
+ output_i, oc->background, oc->background_option);
+ char *command = malloc(len + 1);
+ if (!command) {
+ wlr_log(L_DEBUG, "Unable to allocate swaybg command");
+ return;
+ }
+ snprintf(command, len + 1, "%s %d %s %s",
+ config->swaybg_command ? config->swaybg_command : "swaybg",
+ output_i, oc->background, oc->background_option);
+ wlr_log(L_DEBUG, "-> %s", command);
+
+ char *const cmd[] = { "sh", "-c", command, NULL };
+ output->sway_output->bg_pid = fork();
+ if (output->sway_output->bg_pid == 0) {
+ execvp(cmd[0], cmd);
+ }
+ }
+}
+
+void free_output_config(struct output_config *oc) {
+ if (!oc) {
+ return;
+ }
+ free(oc->name);
+ free(oc->background);
+ free(oc->background_option);
+ free(oc);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <stdlib.h>
+#include <string.h>
+#include "sway/config.h"
+#include "log.h"
+
+struct seat_config *new_seat_config(const char* name) {
+ struct seat_config *seat = calloc(1, sizeof(struct seat_config));
+ if (!seat) {
+ wlr_log(L_DEBUG, "Unable to allocate seat config");
+ return NULL;
+ }
+
+ wlr_log(L_DEBUG, "new_seat_config(%s)", name);
+ seat->name = strdup(name);
+ if (!sway_assert(seat->name, "could not allocate name for seat")) {
+ free(seat);
+ return NULL;
+ }
+
+ seat->fallback = -1;
+ seat->attachments = create_list();
+ if (!sway_assert(seat->attachments,
+ "could not allocate seat attachments list")) {
+ free(seat->name);
+ free(seat);
+ return NULL;
+ }
+
+ return seat;
+}
+
+struct seat_attachment_config *seat_attachment_config_new() {
+ struct seat_attachment_config *attachment =
+ calloc(1, sizeof(struct seat_attachment_config));
+ if (!attachment) {
+ wlr_log(L_DEBUG, "cannot allocate attachment config");
+ return NULL;
+ }
+ return attachment;
+}
+
+static void seat_attachment_config_free(
+ struct seat_attachment_config *attachment) {
+ free(attachment->identifier);
+ free(attachment);
+ return;
+}
+
+static struct seat_attachment_config *seat_attachment_config_copy(
+ struct seat_attachment_config *attachment) {
+ struct seat_attachment_config *copy = seat_attachment_config_new();
+ if (!copy) {
+ return NULL;
+ }
+
+ copy->identifier = strdup(attachment->identifier);
+
+ return copy;
+}
+
+static void merge_seat_attachment_config(struct seat_attachment_config *dest,
+ struct seat_attachment_config *source) {
+ // nothing to merge yet, but there will be some day
+}
+
+void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
+ if (source->name) {
+ free(dest->name);
+ dest->name = strdup(source->name);
+ }
+
+ if (source->fallback != -1) {
+ dest->fallback = source->fallback;
+ }
+
+ for (int i = 0; i < source->attachments->length; ++i) {
+ struct seat_attachment_config *source_attachment =
+ source->attachments->items[i];
+ bool found = false;
+ for (int j = 0; j < dest->attachments->length; ++j) {
+ struct seat_attachment_config *dest_attachment =
+ dest->attachments->items[j];
+ if (strcmp(source_attachment->identifier,
+ dest_attachment->identifier) == 0) {
+ merge_seat_attachment_config(dest_attachment,
+ source_attachment);
+ found = true;
+ }
+ }
+
+ if (!found) {
+ struct seat_attachment_config *copy =
+ seat_attachment_config_copy(source_attachment);
+ if (copy) {
+ list_add(dest->attachments, copy);
+ }
+ }
+ }
+}
+
+struct seat_config *copy_seat_config(struct seat_config *seat) {
+ struct seat_config *copy = new_seat_config(seat->name);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ merge_seat_config(copy, seat);
+
+ return copy;
+}
+
+void free_seat_config(struct seat_config *seat) {
+ if (!seat) {
+ return;
+ }
+
+ free(seat->name);
+ for (int i = 0; i < seat->attachments->length; ++i) {
+ struct seat_attachment_config *attachment =
+ seat->attachments->items[i];
+ seat_attachment_config_free(attachment);
+ }
+
+ list_free(seat->attachments);
+ free(seat);
+}
+
+int seat_name_cmp(const void *item, const void *data) {
+ const struct seat_config *sc = item;
+ const char *name = data;
+ return strcmp(sc->name, name);
+}
+
+struct seat_attachment_config *seat_config_get_attachment(
+ struct seat_config *seat_config, char *identifier) {
+ for (int i = 0; i < seat_config->attachments->length; ++i) {
+ struct seat_attachment_config *attachment =
+ seat_config->attachments->items[i];
+ if (strcmp(attachment->identifier, identifier) == 0) {
+ return attachment;
+ }
+ }
+
+ return NULL;
+}
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 @@
-#define _XOPEN_SOURCE 500
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <strings.h>
-#include <string.h>
-#include "sway/config.h"
-#include "sway/container.h"
-#include "sway/workspace.h"
-#include "sway/focus.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "sway/input_state.h"
-#include "sway/ipc-server.h"
-#include "sway/output.h"
-#include "log.h"
-#include "stringop.h"
-
-#define ASSERT_NONNULL(PTR) \
- sway_assert (PTR, #PTR "must be non-null")
-
-static swayc_t *new_swayc(enum swayc_types type) {
- // next id starts at 1 because 0 is assigned to root_container in layout.c
- static size_t next_id = 1;
- swayc_t *c = calloc(1, sizeof(swayc_t));
- if (!c) {
- return NULL;
- }
- c->id = next_id++;
- c->handle = -1;
- c->gaps = -1;
- c->layout = L_NONE;
- c->workspace_layout = L_NONE;
- c->type = type;
- c->nb_master = 1;
- c->nb_slave_groups = 1;
- if (type != C_VIEW) {
- c->children = create_list();
- }
- return c;
-}
-
-static void free_swayc(swayc_t *cont) {
- if (!ASSERT_NONNULL(cont)) {
- return;
- }
- if (cont->children) {
- // remove children until there are no more, free_swayc calls
- // remove_child, which removes child from this container
- while (cont->children->length) {
- free_swayc(cont->children->items[0]);
- }
- list_free(cont->children);
- }
- if (cont->unmanaged) {
- list_free(cont->unmanaged);
- }
- if (cont->floating) {
- while (cont->floating->length) {
- free_swayc(cont->floating->items[0]);
- }
- list_free(cont->floating);
- }
- if (cont->marks) {
- list_foreach(cont->marks, free);
- list_free(cont->marks);
- }
- if (cont->parent) {
- remove_child(cont);
- }
- if (cont->name) {
- free(cont->name);
- }
- if (cont->class) {
- free(cont->class);
- }
- if (cont->instance) {
- free(cont->instance);
- }
- if (cont->app_id) {
- free(cont->app_id);
- }
- if (cont->bg_pid != 0) {
- terminate_swaybg(cont->bg_pid);
- }
- if (cont->border) {
- if (cont->border->buffer) {
- free(cont->border->buffer);
- }
- free(cont->border);
- }
- free(cont);
-}
-
-static void update_root_geometry() {
- int width = 0;
- int height = 0;
- swayc_t *child;
- int child_width;
- int child_height;
-
- for (int i = 0; i < root_container.children->length; ++i) {
- child = root_container.children->items[i];
- child_width = child->width + child->x;
- child_height = child->height + child->y;
- if (child_width > width) {
- width = child_width;
- }
-
- if (child_height > height) {
- height = child_height;
- }
- }
-
- root_container.width = width;
- root_container.height = height;
-}
-
-// New containers
-
-swayc_t *new_output(wlc_handle handle) {
- struct wlc_size size;
- output_get_scaled_size(handle, &size);
- const char *name = wlc_output_get_name(handle);
- // Find current outputs to see if this already exists
- {
- int i, len = root_container.children->length;
- for (i = 0; i < len; ++i) {
- swayc_t *op = root_container.children->items[i];
- const char *op_name = op->name;
- if (op_name && name && strcmp(op_name, name) == 0) {
- sway_log(L_DEBUG, "restoring output %" PRIuPTR ":%s", handle, op_name);
- return op;
- }
- }
- }
-
- sway_log(L_DEBUG, "New output %" PRIuPTR ":%s", handle, name);
-
- struct output_config *oc = NULL, *all = NULL;
- int i;
- for (i = 0; i < config->output_configs->length; ++i) {
- struct output_config *cur = config->output_configs->items[i];
- if (strcasecmp(name, cur->name) == 0) {
- sway_log(L_DEBUG, "Matched output config for %s", name);
- oc = cur;
- }
- if (strcasecmp("*", cur->name) == 0) {
- sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
- all = cur;
- }
-
- if (oc && all) {
- break;
- }
- }
-
- if (!oc) {
- oc = all;
- }
-
- if (oc && !oc->enabled) {
- return NULL;
- }
-
- swayc_t *output = new_swayc(C_OUTPUT);
- output->handle = handle;
- output->name = name ? strdup(name) : NULL;
- output->width = size.w;
- output->height = size.h;
- output->unmanaged = create_list();
- output->bg_pid = 0;
-
- apply_output_config(oc, output);
- add_child(&root_container, output);
- load_swaybars();
-
- // Create workspace
- char *ws_name = NULL;
- swayc_t *ws = NULL;
-
- if (name) {
- for (i = 0; i < config->workspace_outputs->length; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->output, name) == 0) {
- sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output);
- // Check if any other workspaces are using this name
- if ((ws = workspace_by_name(wso->workspace))) {
- // if yes, move those to this output, because they should be here
- move_workspace_to(ws, output);
- } else if (!ws_name) {
- // set a workspace name in case we need to create a default one
- ws_name = strdup(wso->workspace);
- }
- }
- }
- }
-
- if (output->children->length == 0) {
- if (!ws_name) {
- ws_name = workspace_next_name(output->name);
- }
- // create and initialize default workspace
- sway_log(L_DEBUG, "Creating default workspace %s", ws_name);
- ws = new_workspace(output, ws_name);
- ws->is_focused = true;
- } else {
- sort_workspaces(output);
- set_focused_container(output->children->items[0]);
- }
-
- free(ws_name);
- update_root_geometry();
- return output;
-}
-
-swayc_t *new_workspace(swayc_t *output, const char *name) {
- if (!ASSERT_NONNULL(output)) {
- return NULL;
- }
- sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle);
- swayc_t *workspace = new_swayc(C_WORKSPACE);
-
- workspace->prev_layout = L_NONE;
- workspace->layout = default_layout(output);
- workspace->workspace_layout = default_layout(output);
-
- workspace->x = output->x;
- workspace->y = output->y;
- workspace->width = output->width;
- workspace->height = output->height;
- workspace->name = !name ? NULL : strdup(name);
- workspace->visible = false;
- workspace->floating = create_list();
-
- add_child(output, workspace);
- sort_workspaces(output);
-
- return workspace;
-}
-
-swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(child)
- && !sway_assert(!child->is_floating, "cannot create container around floating window")) {
- return NULL;
- }
- swayc_t *cont = new_swayc(C_CONTAINER);
-
- sway_log(L_DEBUG, "creating container %p around %p", cont, child);
-
- cont->prev_layout = L_NONE;
- cont->layout = layout;
- cont->width = child->width;
- cont->height = child->height;
- cont->x = child->x;
- cont->y = child->y;
- cont->visible = child->visible;
- cont->cached_geometry = child->cached_geometry;
- cont->gaps = child->gaps;
-
- /* Container inherits all of workspaces children, layout and whatnot */
- if (child->type == C_WORKSPACE) {
- swayc_t *workspace = child;
- // reorder focus
- cont->focused = workspace->focused;
- workspace->focused = cont;
- // set all children focu to container
- int i;
- for (i = 0; i < workspace->children->length; ++i) {
- ((swayc_t *)workspace->children->items[i])->parent = cont;
- }
- // Swap children
- list_t *tmp_list = workspace->children;
- workspace->children = cont->children;
- cont->children = tmp_list;
- // add container to workspace chidren
- add_child(workspace, cont);
- // give them proper layouts
- cont->layout = workspace->workspace_layout;
- cont->prev_layout = workspace->prev_layout;
- /* TODO: might break shit in move_container!!! workspace->layout = layout; */
- set_focused_container_for(workspace, get_focused_view(workspace));
- } else { // Or is built around container
- swayc_t *parent = replace_child(child, cont);
- if (parent) {
- add_child(cont, child);
- }
- }
- return cont;
-}
-
-swayc_t *new_view(swayc_t *sibling, wlc_handle handle) {
- if (!ASSERT_NONNULL(sibling)) {
- return NULL;
- }
- const char *title = wlc_view_get_title(handle);
- swayc_t *view = new_swayc(C_VIEW);
- sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%s to container %p %d",
- handle, title, sibling, sibling ? sibling->type : 0);
- // Setup values
- view->handle = handle;
- view->name = title ? strdup(title) : NULL;
- const char *class = wlc_view_get_class(handle);
- view->class = class ? strdup(class) : NULL;
- const char *instance = wlc_view_get_instance(handle);
- view->instance = instance ? strdup(instance) : NULL;
- const char *app_id = wlc_view_get_app_id(handle);
- view->app_id = app_id ? strdup(app_id) : NULL;
- view->visible = true;
- view->is_focused = true;
- view->sticky = false;
- view->width = 0;
- view->height = 0;
- view->desired_width = -1;
- view->desired_height = -1;
- // setup border
- view->border_type = config->border;
- view->border_thickness = config->border_thickness;
-
- view->is_floating = false;
-
- if (sibling->type == C_WORKSPACE) {
- // Case of focused workspace, just create as child of it
- add_child(sibling, view);
- } else {
- // Regular case, create as sibling of current container
- add_sibling(sibling, view);
- }
- return view;
-}
-
-swayc_t *new_floating_view(wlc_handle handle) {
- if (swayc_active_workspace() == NULL) {
- return NULL;
- }
- const char *title = wlc_view_get_title(handle);
- swayc_t *view = new_swayc(C_VIEW);
- sway_log(L_DEBUG, "Adding new view %" PRIuPTR ":%x:%s as a floating view",
- handle, wlc_view_get_type(handle), title);
- // Setup values
- view->handle = handle;
- view->name = title ? strdup(title) : NULL;
- const char *class = wlc_view_get_class(handle);
- view->class = class ? strdup(class) : NULL;
- const char *instance = wlc_view_get_instance(handle);
- view->instance = instance ? strdup(instance) : NULL;
- const char *app_id = wlc_view_get_app_id(handle);
- view->app_id = app_id ? strdup(app_id) : NULL;
- view->visible = true;
- view->sticky = false;
-
- // Set the geometry of the floating view
- const struct wlc_geometry *geometry = wlc_view_get_geometry(handle);
-
- // give it requested geometry, but place in center if possible
- // in top left otherwise
- if (geometry->size.w != 0) {
- view->x = (swayc_active_workspace()->width - geometry->size.w) / 2;
- } else {
- view->x = 0;
- }
- if (geometry->size.h != 0) {
- view->y = (swayc_active_workspace()->height - geometry->size.h) / 2;
- } else {
- view->y = 0;
- }
-
- view->width = geometry->size.w;
- view->height = geometry->size.h;
-
- view->desired_width = view->width;
- view->desired_height = view->height;
-
- // setup border
- view->border_type = config->floating_border;
- view->border_thickness = config->floating_border_thickness;
-
- view->is_floating = true;
-
- // Case of focused workspace, just create as child of it
- list_add(swayc_active_workspace()->floating, view);
- view->parent = swayc_active_workspace();
- if (swayc_active_workspace()->focused == NULL) {
- set_focused_container_for(swayc_active_workspace(), view);
- }
- return view;
-}
-
-void floating_view_sane_size(swayc_t *view) {
- // floating_minimum is used as sane value.
- // floating_maximum has priority in case of conflict
- // TODO: implement total_outputs_dimensions()
- if (config->floating_minimum_height != -1 &&
- view->desired_height < config->floating_minimum_height) {
- view->desired_height = config->floating_minimum_height;
- }
- if (config->floating_minimum_width != -1 &&
- view->desired_width < config->floating_minimum_width) {
- view->desired_width = config->floating_minimum_width;
- }
-
- // if 0 do not resize, only enforce max value
- if (config->floating_maximum_height == 0) {
- // Missing total_outputs_dimensions() using swayc_active_workspace()
- config->floating_maximum_height = swayc_active_workspace()->height;
-
- } else if (config->floating_maximum_height != -1 &&
- view->desired_height > config->floating_maximum_height) {
- view->desired_height = config->floating_maximum_height;
- }
-
- // if 0 do not resize, only enforce max value
- if (config->floating_maximum_width == 0) {
- // Missing total_outputs_dimensions() using swayc_active_workspace()
- config->floating_maximum_width = swayc_active_workspace()->width;
-
- } else if (config->floating_maximum_width != -1 &&
- view->desired_width > config->floating_maximum_width) {
- view->desired_width = config->floating_maximum_width;
- }
-
- sway_log(L_DEBUG, "Sane values for view to %d x %d @ %.f, %.f",
- view->desired_width, view->desired_height, view->x, view->y);
-
- return;
-}
-
-
-// Destroy container
-
-swayc_t *destroy_output(swayc_t *output) {
- if (!ASSERT_NONNULL(output)) {
- return NULL;
- }
- if (output->children->length > 0) {
- // TODO save workspaces when there are no outputs.
- // TODO also check if there will ever be no outputs except for exiting
- // program
- if (root_container.children->length > 1) {
- int p = root_container.children->items[0] == output;
- // Move workspace from this output to another output
- while (output->children->length) {
- swayc_t *child = output->children->items[0];
- remove_child(child);
- add_child(root_container.children->items[p], child);
- }
- sort_workspaces(root_container.children->items[p]);
- update_visibility(root_container.children->items[p]);
- arrange_windows(root_container.children->items[p], -1, -1);
- }
- }
- sway_log(L_DEBUG, "OUTPUT: Destroying output '%" PRIuPTR "'", output->handle);
- free_swayc(output);
- update_root_geometry();
- return &root_container;
-}
-
-swayc_t *destroy_workspace(swayc_t *workspace) {
- if (!ASSERT_NONNULL(workspace)) {
- return NULL;
- }
-
- // Do not destroy this if it's the last workspace on this output
- swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
- if (output && output->children->length == 1) {
- return NULL;
- }
-
- swayc_t *parent = workspace->parent;
- // destroy the WS if there are no children
- if (workspace->children->length == 0 && workspace->floating->length == 0) {
- sway_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
- ipc_event_workspace(workspace, NULL, "empty");
- } else {
- // Move children to a different workspace on this output
- swayc_t *new_workspace = NULL;
- int i;
- for(i = 0; i < output->children->length; i++) {
- if(output->children->items[i] != workspace) {
- break;
- }
- }
- new_workspace = output->children->items[i];
-
- sway_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
- workspace->name, new_workspace->name);
-
- for(i = 0; i < workspace->children->length; i++) {
- move_container_to(workspace->children->items[i], new_workspace);
- }
-
- for(i = 0; i < workspace->floating->length; i++) {
- move_container_to(workspace->floating->items[i], new_workspace);
- }
- }
-
- free_swayc(workspace);
- return parent;
-}
-
-swayc_t *destroy_container(swayc_t *container) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- while (container->children->length == 0 && container->type == C_CONTAINER) {
- sway_log(L_DEBUG, "Container: Destroying container '%p'", container);
- swayc_t *parent = container->parent;
- free_swayc(container);
- container = parent;
- }
- return container;
-}
-
-swayc_t *destroy_view(swayc_t *view) {
- if (!ASSERT_NONNULL(view)) {
- return NULL;
- }
- sway_log(L_DEBUG, "Destroying view '%p'", view);
- swayc_t *parent = view->parent;
- free_swayc(view);
-
- // Destroy empty containers
- if (parent && parent->type == C_CONTAINER) {
- return destroy_container(parent);
- }
- return parent;
-}
-
-// Container lookup
-
-
-swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) {
- if (!container->children) {
- return NULL;
- }
- // Special case for checking floating stuff
- int i;
- if (container->type == C_WORKSPACE) {
- for (i = 0; i < container->floating->length; ++i) {
- swayc_t *child = container->floating->items[i];
- if (test(child, data)) {
- return child;
- }
- }
- }
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- if (test(child, data)) {
- return child;
- } else {
- swayc_t *res = swayc_by_test(child, test, data);
- if (res) {
- return res;
- }
- }
- }
- return NULL;
-}
-
-static bool test_name(swayc_t *view, void *data) {
- if (!view || !view->name) {
- return false;
- }
- return strcmp(view->name, data) == 0;
-}
-
-swayc_t *swayc_by_name(const char *name) {
- return swayc_by_test(&root_container, test_name, (void *)name);
-}
-
-swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
- return NULL;
- }
- do {
- container = container->parent;
- } while (container && container->type != type);
- return container;
-}
-
-swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
- return NULL;
- }
- do {
- container = container->parent;
- } while (container && container->layout != layout);
- return container;
-}
-
-swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types type) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
- return NULL;
- }
- do {
- container = container->focused;
- } while (container && container->type != type);
- return container;
-}
-
-swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) {
- if (!ASSERT_NONNULL(container)) {
- return NULL;
- }
- if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "invalid layout")) {
- return NULL;
- }
- do {
- container = container->focused;
- } while (container && container->layout != layout);
- return container;
-}
-
-
-static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) {
- if (!parent || !parent->children) {
- return NULL;
- }
- int i, len;
- swayc_t **child;
- if (parent->type == C_WORKSPACE) {
- len = parent->floating->length;
- child = (swayc_t **)parent->floating->items;
- for (i = 0; i < len; ++i, ++child) {
- if ((*child)->handle == handle) {
- return *child;
- }
- }
- }
-
- len = parent->children->length;
- child = (swayc_t**)parent->children->items;
- for (i = 0; i < len; ++i, ++child) {
- if ((*child)->handle == handle) {
- return *child;
- } else {
- swayc_t *res;
- if ((res = _swayc_by_handle_helper(handle, *child))) {
- return res;
- }
- }
- }
- return NULL;
-}
-
-swayc_t *swayc_by_handle(wlc_handle handle) {
- return _swayc_by_handle_helper(handle, &root_container);
-}
-
-swayc_t *swayc_active_output(void) {
- return root_container.focused;
-}
-
-swayc_t *swayc_active_workspace(void) {
- return root_container.focused ? root_container.focused->focused : NULL;
-}
-
-swayc_t *swayc_active_workspace_for(swayc_t *cont) {
- if (!cont) {
- return NULL;
- }
- switch (cont->type) {
- case C_ROOT:
- cont = cont->focused;
- /* Fallthrough */
-
- case C_OUTPUT:
- cont = cont ? cont->focused : NULL;
- /* Fallthrough */
-
- case C_WORKSPACE:
- return cont;
-
- default:
- return swayc_parent_by_type(cont, C_WORKSPACE);
- }
-}
-
-static bool pointer_test(swayc_t *view, void *_origin) {
- const struct wlc_point *origin = _origin;
- // Determine the output that the view is under
- swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT);
- if (origin->x >= view->x && origin->y >= view->y
- && origin->x < view->x + view->width && origin->y < view->y + view->height
- && view->visible && parent == root_container.focused) {
- return true;
- }
- return false;
-}
-
-swayc_t *container_under_pointer(void) {
- // root.output->workspace
- if (!root_container.focused) {
- return NULL;
- }
- swayc_t *lookup = root_container.focused;
- // Case of empty workspace
- if (lookup->children && !lookup->unmanaged) {
- return NULL;
- }
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- struct wlc_point origin = { .x = x, .y = y };
-
- while (lookup && lookup->type != C_VIEW) {
- int i;
- int len;
- for (int _i = 0; lookup->unmanaged && _i < lookup->unmanaged->length; ++_i) {
- wlc_handle *handle = lookup->unmanaged->items[_i];
- const struct wlc_geometry *geo = wlc_view_get_geometry(*handle);
- if (origin.x >= geo->origin.x && origin.y >= geo->origin.y
- && origin.x < geo->origin.x + (int)geo->size.w
- && origin.y < geo->origin.y + (int)geo->size.h) {
- // Hack: we force focus upon unmanaged views here
- wlc_view_focus(*handle);
- return NULL;
- }
- }
- // if tabbed/stacked go directly to focused container, otherwise search
- // children
- if (lookup->layout == L_TABBED || lookup->layout == L_STACKED) {
- lookup = lookup->focused;
- continue;
- }
- // if workspace, search floating
- if (lookup->type == C_WORKSPACE) {
- i = len = lookup->floating->length;
- bool got_floating = false;
- while (--i > -1) {
- if (pointer_test(lookup->floating->items[i], &origin)) {
- lookup = lookup->floating->items[i];
- got_floating = true;
- break;
- }
- }
- if (got_floating) {
- continue;
- }
- }
- // search children
- len = lookup->children->length;
- for (i = 0; i < len; ++i) {
- if (pointer_test(lookup->children->items[i], &origin)) {
- lookup = lookup->children->items[i];
- break;
- }
- }
- // when border and titles are done, this could happen
- if (i == len) {
- break;
- }
- }
- return lookup;
-}
-
-swayc_t *container_find(swayc_t *container, bool (*f)(swayc_t *, const void *), const void *data) {
- if (container->children == NULL || container->children->length == 0) {
- return NULL;
- }
-
- swayc_t *con;
- if (container->type == C_WORKSPACE) {
- for (int i = 0; i < container->floating->length; ++i) {
- con = container->floating->items[i];
- if (f(con, data)) {
- return con;
- }
- con = container_find(con, f, data);
- if (con != NULL) {
- return con;
- }
- }
- }
-
- for (int i = 0; i < container->children->length; ++i) {
- con = container->children->items[i];
- if (f(con, data)) {
- return con;
- }
-
- con = container_find(con, f, data);
- if (con != NULL) {
- return con;
- }
- }
-
- return NULL;
-}
-
-// Container information
-
-bool swayc_is_fullscreen(swayc_t *view) {
- return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN);
-}
-
-bool swayc_is_active(swayc_t *view) {
- return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED);
-}
-
-bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) {
- while (child != &root_container) {
- child = child->parent;
- if (child == parent) {
- return true;
- }
- }
- return false;
-}
-
-bool swayc_is_child_of(swayc_t *child, swayc_t *parent) {
- return swayc_is_parent_of(parent, child);
-}
-
-bool swayc_is_empty_workspace(swayc_t *container) {
- return container->type == C_WORKSPACE && container->children->length == 0;
-}
-
-int swayc_gap(swayc_t *container) {
- if (container->type == C_VIEW || container->type == C_CONTAINER) {
- return container->gaps >= 0 ? container->gaps : config->gaps_inner;
- } else if (container->type == C_WORKSPACE) {
- int base = container->gaps >= 0 ? container->gaps : config->gaps_outer;
- if (config->edge_gaps && !(config->smart_gaps && container->children->length == 1)) {
- // the inner gap is created via a margin around each window which
- // is half the gap size, so the workspace also needs half a gap
- // size to make the outermost gap the same size (excluding the
- // actual "outer gap" size which is handled independently)
- return base + config->gaps_inner / 2;
- } else if (config->smart_gaps && container->children->length == 1) {
- return 0;
- } else {
- return base;
- }
- } else {
- return 0;
- }
-}
-
-// Mapping
-
-void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) {
- if (container) {
- int i;
- if (container->children) {
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- container_map(child, f, data);
- }
- }
- if (container->floating) {
- for (i = 0; i < container->floating->length; ++i) {
- swayc_t *child = container->floating->items[i];
- container_map(child, f, data);
- }
- }
- f(container, data);
- }
-}
-
-void update_visibility_output(swayc_t *container, wlc_handle output) {
- // Inherit visibility
- swayc_t *parent = container->parent;
- container->visible = parent->visible;
- // special cases where visibility depends on focus
- if (parent->type == C_OUTPUT || parent->layout == L_TABBED ||
- parent->layout == L_STACKED) {
- container->visible = parent->focused == container && parent->visible;
- }
- // Set visibility and output for view
- if (container->type == C_VIEW) {
- wlc_view_set_output(container->handle, output);
- wlc_view_set_mask(container->handle, container->visible ? VISIBLE : 0);
- }
- // Update visibility for children
- else {
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->children->items[i], output);
- }
- }
- if (container->floating) {
- int i, len = container->floating->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->floating->items[i], output);
- }
- }
- }
-}
-
-void update_visibility(swayc_t *container) {
- if (!container) return;
- switch (container->type) {
- case C_ROOT:
- container->visible = true;
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility(container->children->items[i]);
- }
- }
- return;
-
- case C_OUTPUT:
- container->visible = true;
- if (container->children) {
- int i, len = container->children->length;
- for (i = 0; i < len; ++i) {
- update_visibility_output(container->children->items[i], container->handle);
- }
- }
- return;
-
- default:
- {
- swayc_t *op = swayc_parent_by_type(container, C_OUTPUT);
- update_visibility_output(container, op->handle);
- }
- }
-}
-
-void set_gaps(swayc_t *view, void *_data) {
- int *data = _data;
- if (!ASSERT_NONNULL(view)) {
- return;
- }
- if (view->type == C_WORKSPACE || view->type == C_VIEW) {
- view->gaps = *data;
- }
-}
-
-void add_gaps(swayc_t *view, void *_data) {
- int *data = _data;
- if (!ASSERT_NONNULL(view)) {
- return;
- }
- if (view->type == C_WORKSPACE || view->type == C_VIEW) {
- if ((view->gaps += *data) < 0) {
- view->gaps = 0;
- }
- }
-}
-
-static void close_view(swayc_t *container, void *data) {
- if (container->type == C_VIEW) {
- wlc_view_close(container->handle);
- }
-}
-
-void close_views(swayc_t *container) {
- container_map(container, close_view, NULL);
-}
-
-swayc_t *swayc_tabbed_stacked_ancestor(swayc_t *view) {
- swayc_t *parent = NULL;
- if (!ASSERT_NONNULL(view)) {
- return NULL;
- }
- while (view->type != C_WORKSPACE && view->parent && view->parent->type != C_WORKSPACE) {
- view = view->parent;
- if (view->layout == L_TABBED || view->layout == L_STACKED) {
- parent = view;
- }
- }
-
- return parent;
-}
-
-swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
- if (!ASSERT_NONNULL(con)) {
- return NULL;
- }
- if (con->parent && (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED)) {
- return con->parent;
- }
- return NULL;
-}
-
-swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
- // if layout change modifies the auto layout's major axis, swap width and height
- // to preserve current ratios.
- if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
- enum swayc_layouts prev_major =
- container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT
- ? L_HORIZ : L_VERT;
- enum swayc_layouts new_major =
- layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT
- ? L_HORIZ : L_VERT;
- if (new_major != prev_major) {
- for (int i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- double h = child->height;
- child->height = child->width;
- child->width = h;
- }
- }
- }
- if (container->type == C_WORKSPACE) {
- container->workspace_layout = layout;
- if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
- container->layout = layout;
- }
- } else {
- container->layout = layout;
- }
- return container;
-}
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 @@
#include <stdbool.h>
#include <pcre.h>
#include "sway/criteria.h"
-#include "sway/container.h"
+#include "sway/tree/container.h"
#include "sway/config.h"
+#include "sway/tree/view.h"
#include "stringop.h"
#include "list.h"
#include "log.h"
enum criteria_type { // *must* keep in sync with criteria_strings[]
+ CRIT_APP_ID,
CRIT_CLASS,
CRIT_CON_ID,
CRIT_CON_MARK,
@@ -27,6 +29,7 @@ enum criteria_type { // *must* keep in sync with criteria_strings[]
};
static const char * const criteria_strings[CRIT_LAST] = {
+ [CRIT_APP_ID] = "app_id",
[CRIT_CLASS] = "class",
[CRIT_CON_ID] = "con_id",
[CRIT_CON_MARK] = "con_mark",
@@ -100,8 +103,9 @@ static int countchr(char *str, char c) {
// of buf.
//
// Returns error string or NULL if successful.
-static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) {
- sway_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
+static char *crit_tokens(int *argc, char ***buf,
+ const char * const criteria_str) {
+ wlr_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
char *base = criteria_from(criteria_str);
char *head = base;
char *namep = head; // start of criteria name
@@ -247,13 +251,13 @@ char *extract_crit_tokens(list_t *tokens, const char * const criteria) {
free_crit_token(token);
goto ect_cleanup;
} else if (token->type == CRIT_URGENT || crit_is_focused(value)) {
- sway_log(L_DEBUG, "%s -> \"%s\"", name, value);
+ wlr_log(L_DEBUG, "%s -> \"%s\"", name, value);
list_add(tokens, token);
} else if((error = generate_regex(&token->regex, value))) {
free_crit_token(token);
goto ect_cleanup;
} else {
- sway_log(L_DEBUG, "%s -> /%s/", name, value);
+ wlr_log(L_DEBUG, "%s -> /%s/", name, value);
list_add(tokens, token);
}
}
@@ -268,8 +272,8 @@ static int regex_cmp(const char *item, const pcre *regex) {
}
// test a single view if it matches list of criteria tokens (all of them).
-static bool criteria_test(swayc_t *cont, list_t *tokens) {
- if (cont->type != C_VIEW) {
+static bool criteria_test(struct sway_container *cont, list_t *tokens) {
+ if (cont->type != C_CONTAINER && cont->type != C_VIEW) {
return false;
}
int matches = 0;
@@ -277,94 +281,85 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
struct crit_token *crit = tokens->items[i];
switch (crit->type) {
case CRIT_CLASS:
- if (!cont->class) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->class && strcmp(cont->class, focused->class) == 0) {
+ {
+ const char *class = view_get_class(cont->sway_view);
+ if (!class) {
+ break;
+ }
+ if (crit->regex && regex_cmp(class, crit->regex) == 0) {
matches++;
}
- } else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) {
- matches++;
+ break;
}
- break;
- case CRIT_CON_ID: {
- char *endptr;
- size_t crit_id = strtoul(crit->raw, &endptr, 10);
+ case CRIT_CON_ID:
+ {
+ char *endptr;
+ size_t crit_id = strtoul(crit->raw, &endptr, 10);
- if (*endptr == 0 && cont->id == crit_id) {
- ++matches;
- }
- break;
- }
- case CRIT_CON_MARK:
- if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
- // Make sure it isn't matching the NUL string
- if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
+ if (*endptr == 0 && cont->id == crit_id) {
++matches;
}
+ break;
}
+ case CRIT_CON_MARK:
+ // TODO
break;
case CRIT_FLOATING:
- if (cont->is_floating) {
- matches++;
- }
+ // TODO
break;
case CRIT_ID:
- if (!cont->app_id) {
- // ignore
- } else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) {
- matches++;
- }
+ // TODO
break;
+ case CRIT_APP_ID:
+ {
+ const char *app_id = view_get_app_id(cont->sway_view);
+ if (!app_id) {
+ break;
+ }
+
+ if (crit->regex && regex_cmp(app_id, crit->regex) == 0) {
+ matches++;
+ }
+ break;
+ }
case CRIT_INSTANCE:
- if (!cont->instance) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->instance && strcmp(cont->instance, focused->instance) == 0) {
+ {
+ const char *instance = view_get_instance(cont->sway_view);
+ if (!instance) {
+ break;
+ }
+
+ if (crit->regex && regex_cmp(instance, crit->regex) == 0) {
matches++;
}
- } else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) {
- matches++;
+ break;
}
- break;
case CRIT_TILING:
- if (!cont->is_floating) {
- matches++;
- }
+ // TODO
break;
case CRIT_TITLE:
- if (!cont->name) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused = get_focused_view(&root_container);
- if (focused->name && strcmp(cont->name, focused->name) == 0) {
+ {
+ const char *title = view_get_title(cont->sway_view);
+ if (!title) {
+ break;
+ }
+
+ if (crit->regex && regex_cmp(title, crit->regex) == 0) {
matches++;
}
- } else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) {
- matches++;
+ break;
}
- break;
- case CRIT_URGENT: // "latest" or "oldest"
+ case CRIT_URGENT:
+ // TODO "latest" or "oldest"
break;
case CRIT_WINDOW_ROLE:
+ // TODO
break;
case CRIT_WINDOW_TYPE:
- // TODO wlc indeed exposes this information
+ // TODO
break;
- case CRIT_WORKSPACE: ;
- swayc_t *cont_ws = swayc_parent_by_type(cont, C_WORKSPACE);
- if (!cont_ws || !cont_ws->name) {
- // ignore
- } else if (crit_is_focused(crit->raw)) {
- swayc_t *focused_ws = swayc_active_workspace();
- if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) {
- matches++;
- }
- } else if (crit->regex && regex_cmp(cont_ws->name, crit->regex) == 0) {
- matches++;
- }
+ case CRIT_WORKSPACE:
+ // TODO
break;
default:
sway_abort("Invalid criteria type (%i)", crit->type);
@@ -403,7 +398,7 @@ void free_criteria(struct criteria *crit) {
free(crit);
}
-bool criteria_any(swayc_t *cont, list_t *criteria) {
+bool criteria_any(struct sway_container *cont, list_t *criteria) {
for (int i = 0; i < criteria->length; i++) {
struct criteria *bc = criteria->items[i];
if (criteria_test(cont, bc->tokens)) {
@@ -413,7 +408,7 @@ bool criteria_any(swayc_t *cont, list_t *criteria) {
return false;
}
-list_t *criteria_for(swayc_t *cont) {
+list_t *criteria_for(struct sway_container *cont) {
list_t *criteria = config->criteria, *matches = create_list();
for (int i = 0; i < criteria->length; i++) {
struct criteria *bc = criteria->items[i];
@@ -429,23 +424,22 @@ struct list_tokens {
list_t *tokens;
};
-static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) {
+static void container_match_add(struct sway_container *container,
+ struct list_tokens *list_tokens) {
if (criteria_test(container, list_tokens->tokens)) {
list_add(list_tokens->list, container);
}
}
-list_t *container_for(list_t *tokens) {
- struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens};
+list_t *container_for_crit_tokens(list_t *tokens) {
+ struct list_tokens list_tokens =
+ (struct list_tokens){create_list(), tokens};
- container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens);
-
- for (int i = 0; i < scratchpad->length; ++i) {
- swayc_t *c = scratchpad->items[i];
- if (criteria_test(c, tokens)) {
- list_add(list_tokens.list, c);
- }
- }
+ container_for_each_descendant_dfs(&root_container,
+ (void (*)(struct sway_container *, void *))container_match_add,
+ &list_tokens);
+ // TODO look in the scratchpad
+
return list_tokens.list;
}
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 @@
+#include <pango/pangocairo.h>
+#include <wlr/backend.h>
+#include <wlr/render/wlr_texture.h>
+#include <wlr/util/log.h>
+#include "config.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "cairo.h"
+#include "config.h"
+#include "pango.h"
+
+static const char *layout_to_str(enum sway_container_layout layout) {
+ switch (layout) {
+ case L_HORIZ:
+ return "L_HORIZ";
+ case L_VERT:
+ return "L_VERT";
+ case L_STACKED:
+ return "L_STACKED";
+ case L_TABBED:
+ return "L_TABBED";
+ case L_FLOATING:
+ return "L_FLOATING";
+ case L_NONE:
+ default:
+ return "L_NONE";
+ }
+}
+
+static int draw_container(cairo_t *cairo, struct sway_container *container,
+ struct sway_container *focus, int x, int y) {
+ int text_width, text_height;
+ get_text_size(cairo, "monospace", &text_width, &text_height,
+ 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
+ container_type_to_str(container->type), container->id, container->name,
+ layout_to_str(container->layout),
+ container->width, container->height, container->x, container->y);
+ cairo_save(cairo);
+ cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
+ cairo_set_source_u32(cairo, 0xFFFFFFE0);
+ cairo_fill(cairo);
+ int height = text_height;
+ if (container->children) {
+ for (int i = 0; i < container->children->length; ++i) {
+ struct sway_container *child = container->children->items[i];
+ if (child->parent == container) {
+ cairo_set_source_u32(cairo, 0x000000FF);
+ } else {
+ cairo_set_source_u32(cairo, 0xFF0000FF);
+ }
+ height += draw_container(cairo, child, focus, x + 10, y + height);
+ }
+ }
+ cairo_set_source_u32(cairo, 0xFFFFFFE0);
+ cairo_rectangle(cairo, x, y, 2, height);
+ cairo_fill(cairo);
+ cairo_restore(cairo);
+ cairo_move_to(cairo, x, y);
+ if (focus == container) {
+ cairo_set_source_u32(cairo, 0x0000FFFF);
+ }
+ pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
+ container_type_to_str(container->type), container->id, container->name,
+ layout_to_str(container->layout),
+ container->width, container->height, container->x, container->y);
+ return height;
+}
+
+bool enable_debug_tree = false;
+
+void update_debug_tree() {
+ if (!enable_debug_tree) {
+ return;
+ }
+
+ int width = 640, height = 480;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *container = root_container.children->items[i];
+ if (container->width > width) {
+ width = container->width;
+ }
+ if (container->height > height) {
+ height = container->height;
+ }
+ }
+ cairo_surface_t *surface =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ cairo_t *cairo = cairo_create(surface);
+ PangoContext *pango = pango_cairo_create_context(cairo);
+
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ break;
+ }
+
+ struct sway_container *focus = NULL;
+ if (seat != NULL) {
+ focus = seat_get_focus(seat);
+ }
+ cairo_set_source_u32(cairo, 0x000000FF);
+ draw_container(cairo, &root_container, focus, 0, 0);
+
+ cairo_surface_flush(surface);
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
+ if (root_container.sway_root->debug_tree) {
+ wlr_texture_destroy(root_container.sway_root->debug_tree);
+ }
+ unsigned char *data = cairo_image_surface_get_data(surface);
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
+ struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
+ WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
+ root_container.sway_root->debug_tree = texture;
+ cairo_surface_destroy(surface);
+ g_object_unref(pango);
+ cairo_destroy(cairo);
+}
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 @@
-#include "log.h"
-#include "sway.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <libgen.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <string.h>
-#include <stringop.h>
-#include "sway/workspace.h"
-
-/* XXX:DEBUG:XXX */
-static void container_log(const swayc_t *c, int depth) {
- fprintf(stderr, "focus:%c",
- c == get_focused_view(&root_container) ? 'K':
- c == get_focused_container(&root_container) ? 'F' : // Focused
- c == swayc_active_workspace() ? 'W' : // active workspace
- c == &root_container ? 'R' : // root
- 'X');// not any others
- for (int i = 6; i > depth; i--) { fprintf(stderr, " "); }
- fprintf(stderr,"|(%p)",c);
- fprintf(stderr,"(p:%-8p)",c->parent);
- fprintf(stderr,"(f:%-8p)",c->focused);
- fprintf(stderr,"(h:%2" PRIuPTR ")",c->handle);
- fprintf(stderr,"Type:%-4s|",
- c->type == C_ROOT ? "root" :
- c->type == C_OUTPUT ? "op" :
- c->type == C_WORKSPACE ? "ws" :
- c->type == C_CONTAINER ? "cont" :
- c->type == C_VIEW ? "view" : "?");
- fprintf(stderr,"layout:%-5s|",
- c->layout == L_NONE ? "-" :
- c->layout == L_HORIZ ? "Horiz":
- c->layout == L_VERT ? "Vert":
- c->layout == L_STACKED ? "Stack":
- c->layout == L_TABBED ? "Tab":
- c->layout == L_FLOATING ? "Float":
- c->layout == L_AUTO_LEFT ? "A_lft":
- c->layout == L_AUTO_RIGHT ? "A_rgt":
- c->layout == L_AUTO_TOP ? "A_top":
- c->layout == L_AUTO_BOTTOM ? "A_bot":
- "Unknown");
- fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height);
- fprintf(stderr, "x:%4.f|y:%4.f|", c->x, c->y);
- fprintf(stderr, "g:%3d|",c->gaps);
- fprintf(stderr, "vis:%c|", c->visible?'t':'f');
- fprintf(stderr, "children:%2d|",c->children?c->children->length:0);
- fprintf(stderr, "name:%.16s\n", c->name);
-}
-void layout_log(const swayc_t *c, int depth) {
- if (L_DEBUG > get_log_level()) return;
- int i, d;
- int e = c->children ? c->children->length : 0;
- container_log(c, depth);
- if (e) {
- for (i = 0; i < e; ++i) {
- fputc('|',stderr);
- for (d = 0; d < depth; ++d) fputc('-', stderr);
- layout_log(c->children->items[i], depth + 1);
- }
- }
- if (c->type == C_WORKSPACE) {
- e = c->floating?c->floating->length:0;
- if (e) {
- for (i = 0; i < e; ++i) {
- fputc('|',stderr);
- for (d = 0; d < depth; ++d) fputc('=', stderr);
- layout_log(c->floating->items[i], depth + 1);
- }
- }
- }
-}
-
-const char *swayc_type_string(enum swayc_types type) {
- return type == C_ROOT ? "ROOT" :
- type == C_OUTPUT ? "OUTPUT" :
- type == C_WORKSPACE ? "WORKSPACE" :
- type == C_CONTAINER ? "CONTAINER" :
- type == C_VIEW ? "VIEW" :
- "UNKNOWN";
-}
-
-// Like sway_log, but also appends some info about given container to log output.
-void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ...) {
- sway_assert(cont, "swayc_log: no container ...");
- va_list args;
- va_start(args, format);
- char *txt = malloc(128);
- vsprintf(txt, format, args);
- va_end(args);
-
- char *debug_txt = malloc(32);
- snprintf(debug_txt, 32, "%s '%s'", swayc_type_string(cont->type), cont->name);
-
- sway_log(verbosity, "%s (%s)", txt, debug_txt);
- free(txt);
- free(debug_txt);
-}
-
-/* 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 @@
+#include "sway/tree/container.h"
+#include "sway/desktop.h"
+#include "sway/output.h"
+
+void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
+ bool whole) {
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *cont = root_container.children->items[i];
+ if (cont->type == C_OUTPUT) {
+ output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y,
+ surface, whole);
+ }
+ }
+}
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 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_layer_shell.h>
+#include <wlr/types/wlr_output_damage.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/util/log.h>
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
+
+static void apply_exclusive(struct wlr_box *usable_area,
+ uint32_t anchor, int32_t exclusive,
+ int32_t margin_top, int32_t margin_right,
+ int32_t margin_bottom, int32_t margin_left) {
+ if (exclusive <= 0) {
+ return;
+ }
+ struct {
+ uint32_t anchors;
+ int *positive_axis;
+ int *negative_axis;
+ int margin;
+ } edges[] = {
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
+ .positive_axis = &usable_area->y,
+ .negative_axis = &usable_area->height,
+ .margin = margin_top,
+ },
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = NULL,
+ .negative_axis = &usable_area->height,
+ .margin = margin_bottom,
+ },
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = &usable_area->x,
+ .negative_axis = &usable_area->width,
+ .margin = margin_left,
+ },
+ {
+ .anchors =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = NULL,
+ .negative_axis = &usable_area->width,
+ .margin = margin_right,
+ },
+ };
+ for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
+ if ((anchor & edges[i].anchors) == edges[i].anchors) {
+ if (edges[i].positive_axis) {
+ *edges[i].positive_axis += exclusive + edges[i].margin;
+ }
+ if (edges[i].negative_axis) {
+ *edges[i].negative_axis -= exclusive + edges[i].margin;
+ }
+ }
+ }
+}
+
+static void arrange_layer(struct sway_output *output, struct wl_list *list,
+ struct wlr_box *usable_area, bool exclusive) {
+ struct sway_layer_surface *sway_layer;
+ struct wlr_box full_area = { 0 };
+ wlr_output_effective_resolution(output->wlr_output,
+ &full_area.width, &full_area.height);
+ wl_list_for_each(sway_layer, list, link) {
+ struct wlr_layer_surface *layer = sway_layer->layer_surface;
+ struct wlr_layer_surface_state *state = &layer->current;
+ if (exclusive != (state->exclusive_zone > 0)) {
+ continue;
+ }
+ struct wlr_box bounds;
+ if (state->exclusive_zone == -1) {
+ bounds = full_area;
+ } else {
+ bounds = *usable_area;
+ }
+ struct wlr_box box = {
+ .width = state->desired_width,
+ .height = state->desired_height
+ };
+ // Horizontal axis
+ const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
+ if ((state->anchor & both_horiz) && box.width == 0) {
+ box.x = bounds.x;
+ box.width = bounds.width;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
+ box.x = bounds.x;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
+ box.x = bounds.x + (bounds.width - box.width);
+ } else {
+ box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
+ }
+ // Vertical axis
+ const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+ if ((state->anchor & both_vert) && box.height == 0) {
+ box.y = bounds.y;
+ box.height = bounds.height;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
+ box.y = bounds.y;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
+ box.y = bounds.y + (bounds.height - box.height);
+ } else {
+ box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
+ }
+ // Margin
+ if ((state->anchor & both_horiz) == both_horiz) {
+ box.x += state->margin.left;
+ box.width -= state->margin.left + state->margin.right;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
+ box.x += state->margin.left;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
+ box.x -= state->margin.right;
+ }
+ if ((state->anchor & both_vert) == both_vert) {
+ box.y += state->margin.top;
+ box.height -= state->margin.top + state->margin.bottom;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
+ box.y += state->margin.top;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
+ box.y -= state->margin.bottom;
+ }
+ if (box.width < 0 || box.height < 0) {
+ // TODO: Bubble up a protocol error?
+ wlr_layer_surface_close(layer);
+ continue;
+ }
+ // Apply
+ sway_layer->geo = box;
+ apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
+ state->margin.top, state->margin.right,
+ state->margin.bottom, state->margin.left);
+ wlr_layer_surface_configure(layer, box.width, box.height);
+ }
+}
+
+void arrange_layers(struct sway_output *output) {
+ struct wlr_box usable_area = { 0 };
+ wlr_output_effective_resolution(output->wlr_output,
+ &usable_area.width, &usable_area.height);
+
+ // Arrange exclusive surfaces from top->bottom
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ &usable_area, true);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
+ &usable_area, true);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
+ &usable_area, true);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
+ &usable_area, true);
+
+ if (memcmp(&usable_area, &output->usable_area,
+ sizeof(struct wlr_box)) != 0) {
+ wlr_log(L_DEBUG, "Usable area changed, rearranging output");
+ memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
+ arrange_windows(output->swayc, -1, -1);
+ }
+
+ // Arrange non-exlusive surfaces from top->bottom
+ usable_area.x = usable_area.y = 0;
+ wlr_output_effective_resolution(output->wlr_output,
+ &usable_area.width, &usable_area.height);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ &usable_area, false);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
+ &usable_area, false);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
+ &usable_area, false);
+ arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
+ &usable_area, false);
+
+ // Find topmost keyboard interactive layer, if such a layer exists
+ uint32_t layers_above_shell[] = {
+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+ size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
+ struct sway_layer_surface *layer, *topmost = NULL;
+ for (size_t i = 0; i < nlayers; ++i) {
+ wl_list_for_each_reverse(layer,
+ &output->layers[layers_above_shell[i]], link) {
+ if (layer->layer_surface->current.keyboard_interactive) {
+ topmost = layer;
+ break;
+ }
+ }
+ if (topmost != NULL) {
+ break;
+ }
+ }
+
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL);
+ }
+}
+
+static void handle_output_destroy(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer =
+ wl_container_of(listener, sway_layer, output_destroy);
+ wl_list_remove(&sway_layer->output_destroy.link);
+ sway_layer->layer_surface->output = NULL;
+ wlr_layer_surface_close(sway_layer->layer_surface);
+}
+
+static void handle_surface_commit(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *layer =
+ wl_container_of(listener, layer, surface_commit);
+ struct wlr_layer_surface *layer_surface = layer->layer_surface;
+ struct wlr_output *wlr_output = layer_surface->output;
+ if (wlr_output == NULL) {
+ return;
+ }
+
+ struct sway_output *output = wlr_output->data;
+ struct wlr_box old_geo = layer->geo;
+ arrange_layers(output);
+ if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) {
+ output_damage_surface(output, old_geo.x, old_geo.y,
+ layer_surface->surface, true);
+ output_damage_surface(output, layer->geo.x, layer->geo.y,
+ layer_surface->surface, true);
+ } else {
+ output_damage_surface(output, layer->geo.x, layer->geo.y,
+ layer_surface->surface, false);
+ }
+}
+
+static void unmap(struct sway_layer_surface *sway_layer) {
+ struct wlr_output *wlr_output = sway_layer->layer_surface->output;
+ if (wlr_output == NULL) {
+ return;
+ }
+ struct sway_output *output = wlr_output->data;
+ output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
+ sway_layer->layer_surface->surface, true);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer =
+ wl_container_of(listener, sway_layer, destroy);
+ wlr_log(L_DEBUG, "Layer surface destroyed (%s)",
+ sway_layer->layer_surface->namespace);
+ if (sway_layer->layer_surface->mapped) {
+ unmap(sway_layer);
+ }
+ wl_list_remove(&sway_layer->link);
+ wl_list_remove(&sway_layer->destroy.link);
+ wl_list_remove(&sway_layer->map.link);
+ wl_list_remove(&sway_layer->unmap.link);
+ wl_list_remove(&sway_layer->surface_commit.link);
+ if (sway_layer->layer_surface->output != NULL) {
+ struct sway_output *output = sway_layer->layer_surface->output->data;
+ arrange_layers(output);
+
+ wl_list_remove(&sway_layer->output_destroy.link);
+ }
+ free(sway_layer);
+}
+
+static void handle_map(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer = wl_container_of(listener,
+ sway_layer, map);
+ struct sway_output *output = sway_layer->layer_surface->output->data;
+ output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
+ sway_layer->layer_surface->surface, true);
+ // TODO: send enter to subsurfaces and popups
+ wlr_surface_send_enter(sway_layer->layer_surface->surface,
+ sway_layer->layer_surface->output);
+}
+
+static void handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_layer_surface *sway_layer = wl_container_of(
+ listener, sway_layer, unmap);
+ unmap(sway_layer);
+}
+
+void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
+ struct wlr_layer_surface *layer_surface = data;
+ struct sway_server *server =
+ wl_container_of(listener, server, layer_shell_surface);
+ wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
+ "size %dx%d margin %d,%d,%d,%d",
+ layer_surface->namespace, layer_surface->layer, layer_surface->layer,
+ layer_surface->client_pending.desired_width,
+ layer_surface->client_pending.desired_height,
+ layer_surface->client_pending.margin.top,
+ layer_surface->client_pending.margin.right,
+ layer_surface->client_pending.margin.bottom,
+ layer_surface->client_pending.margin.left);
+
+ struct sway_layer_surface *sway_layer =
+ calloc(1, sizeof(struct sway_layer_surface));
+ if (!sway_layer) {
+ return;
+ }
+
+ sway_layer->surface_commit.notify = handle_surface_commit;
+ wl_signal_add(&layer_surface->surface->events.commit,
+ &sway_layer->surface_commit);
+
+ sway_layer->output_destroy.notify = handle_output_destroy;
+ wl_signal_add(&layer_surface->output->events.destroy,
+ &sway_layer->output_destroy);
+
+ sway_layer->destroy.notify = handle_destroy;
+ wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
+ sway_layer->map.notify = handle_map;
+ wl_signal_add(&layer_surface->events.map, &sway_layer->map);
+ sway_layer->unmap.notify = handle_unmap;
+ wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
+ // TODO: Listen for subsurfaces
+
+ sway_layer->layer_surface = layer_surface;
+ layer_surface->data = sway_layer;
+
+ struct sway_output *output = layer_surface->output->data;
+ wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->link);
+
+ // Temporarily set the layer's current state to client_pending
+ // So that we can easily arrange it
+ struct wlr_layer_surface_state old_state = layer_surface->current;
+ layer_surface->current = layer_surface->client_pending;
+ arrange_layers(output);
+ layer_surface->current = old_state;
+}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <wayland-server.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_output_damage.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include <wlr/util/region.h>
+#include "log.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+
+struct sway_container *output_by_name(const char *name) {
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *output = root_container.children->items[i];
+ if (strcasecmp(output->name, name) == 0) {
+ return output;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Rotate a child's position relative to a parent. The parent size is (pw, ph),
+ * the child position is (*sx, *sy) and its size is (sw, sh).
+ */
+static void rotate_child_position(double *sx, double *sy, double sw, double sh,
+ double pw, double ph, float rotation) {
+ if (rotation == 0.0f) {
+ return;
+ }
+
+ // Coordinates relative to the center of the subsurface
+ double ox = *sx - pw/2 + sw/2,
+ oy = *sy - ph/2 + sh/2;
+ // Rotated coordinates
+ double rx = cos(-rotation)*ox - sin(-rotation)*oy,
+ ry = cos(-rotation)*oy + sin(-rotation)*ox;
+ *sx = rx + pw/2 - sw/2;
+ *sy = ry + ph/2 - sh/2;
+}
+
+/**
+ * Contains a surface's root geometry information. For instance, when rendering
+ * a popup, this will contain the parent view's position and size.
+ */
+struct root_geometry {
+ double x, y;
+ int width, height;
+ float rotation;
+};
+
+static bool get_surface_box(struct root_geometry *geo,
+ struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
+ struct wlr_box *surface_box) {
+ if (!wlr_surface_has_buffer(surface)) {
+ return false;
+ }
+
+ int sw = surface->current->width;
+ int sh = surface->current->height;
+
+ double _sx = sx, _sy = sy;
+ rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
+ geo->rotation);
+
+ struct wlr_box box = {
+ .x = geo->x + _sx,
+ .y = geo->y + _sy,
+ .width = sw,
+ .height = sh,
+ };
+ if (surface_box != NULL) {
+ memcpy(surface_box, &box, sizeof(struct wlr_box));
+ }
+
+ struct wlr_box rotated_box;
+ wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
+
+ struct wlr_box output_box = {
+ .width = output->swayc->width,
+ .height = output->swayc->height,
+ };
+
+ struct wlr_box intersection;
+ return wlr_box_intersection(&output_box, &rotated_box, &intersection);
+}
+
+static void surface_for_each_surface(struct wlr_surface *surface,
+ double ox, double oy, struct root_geometry *geo,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ geo->x = ox;
+ geo->y = oy;
+ geo->width = surface->current->width;
+ geo->height = surface->current->height;
+ geo->rotation = 0;
+
+ wlr_surface_for_each_surface(surface, iterator, user_data);
+}
+
+static void output_view_for_each_surface(struct sway_view *view,
+ struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
+ void *user_data) {
+ geo->x = view->swayc->x;
+ geo->y = view->swayc->y;
+ geo->width = view->surface->current->width;
+ geo->height = view->surface->current->height;
+ geo->rotation = 0; // TODO
+
+ view_for_each_surface(view, iterator, user_data);
+}
+
+static void layer_for_each_surface(struct wl_list *layer_surfaces,
+ struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
+ void *user_data) {
+ struct sway_layer_surface *layer_surface;
+ wl_list_for_each(layer_surface, layer_surfaces, link) {
+ struct wlr_layer_surface *wlr_layer_surface =
+ layer_surface->layer_surface;
+ surface_for_each_surface(wlr_layer_surface->surface,
+ layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
+ user_data);
+ }
+}
+
+static void unmanaged_for_each_surface(struct wl_list *unmanaged,
+ struct sway_output *output, struct root_geometry *geo,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ struct sway_xwayland_unmanaged *unmanaged_surface;
+ wl_list_for_each(unmanaged_surface, unmanaged, link) {
+ struct wlr_xwayland_surface *xsurface =
+ unmanaged_surface->wlr_xwayland_surface;
+ double ox = unmanaged_surface->lx - output->swayc->x;
+ double oy = unmanaged_surface->ly - output->swayc->y;
+
+ surface_for_each_surface(xsurface->surface, ox, oy, geo,
+ iterator, user_data);
+ }
+}
+
+static void scale_box(struct wlr_box *box, float scale) {
+ box->x *= scale;
+ box->y *= scale;
+ box->width *= scale;
+ box->height *= scale;
+}
+
+struct render_data {
+ struct root_geometry root_geo;
+ struct sway_output *output;
+ float alpha;
+};
+
+static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+ void *_data) {
+ struct render_data *data = _data;
+ struct wlr_output *wlr_output = data->output->wlr_output;
+ float rotation = data->root_geo.rotation;
+ float alpha = data->alpha;
+
+ if (!wlr_surface_has_buffer(surface)) {
+ return;
+ }
+
+ struct wlr_box box;
+ bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ sx, sy, &box);
+ if (!intersects) {
+ return;
+ }
+
+ struct wlr_renderer *renderer =
+ wlr_backend_get_renderer(wlr_output->backend);
+ if (!sway_assert(renderer != NULL,
+ "expected the output backend to have a renderer")) {
+ return;
+ }
+
+ scale_box(&box, wlr_output->scale);
+
+ float matrix[9];
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(surface->current->transform);
+ wlr_matrix_project_box(matrix, &box, transform, rotation,
+ wlr_output->transform_matrix);
+
+ wlr_render_texture_with_matrix(renderer, surface->texture,
+ matrix, alpha);
+}
+
+static void render_layer(struct sway_output *output,
+ struct wl_list *layer_surfaces) {
+ struct render_data data = { .output = output, .alpha = 1.0f };
+ layer_for_each_surface(layer_surfaces, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_unmanaged(struct sway_output *output,
+ struct wl_list *unmanaged) {
+ struct render_data data = { .output = output, .alpha = 1.0f };
+ unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_container_iterator(struct sway_container *con,
+ void *_data) {
+ struct sway_output *output = _data;
+ if (!sway_assert(con->type == C_VIEW, "expected a view")) {
+ return;
+ }
+ struct render_data data = { .output = output, .alpha = con->alpha };
+ output_view_for_each_surface(con->sway_view, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_container(struct sway_output *output,
+ struct sway_container *con) {
+ container_descendants(con, C_VIEW, render_container_iterator, output);
+}
+
+static struct sway_container *output_get_active_workspace(
+ struct sway_output *output) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, output->swayc);
+ if (!focus) {
+ // We've never been to this output before
+ focus = output->swayc->children->items[0];
+ }
+ struct sway_container *workspace = focus;
+ if (workspace->type != C_WORKSPACE) {
+ workspace = container_parent(workspace, C_WORKSPACE);
+ }
+ return workspace;
+}
+
+static void render_output(struct sway_output *output, struct timespec *when,
+ pixman_region32_t *damage) {
+ struct wlr_output *wlr_output = output->wlr_output;
+
+ struct wlr_renderer *renderer =
+ wlr_backend_get_renderer(wlr_output->backend);
+ if (!sway_assert(renderer != NULL,
+ "expected the output backend to have a renderer")) {
+ return;
+ }
+
+ wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
+
+ if (!pixman_region32_not_empty(damage)) {
+ // Output isn't damaged but needs buffer swap
+ goto renderer_end;
+ }
+
+ // TODO: don't damage the whole output
+ int width, height;
+ wlr_output_transformed_resolution(wlr_output, &width, &height);
+ pixman_region32_union_rect(damage, damage, 0, 0, width, height);
+
+ float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
+ wlr_renderer_clear(renderer, clear_color);
+
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
+
+ struct sway_container *workspace = output_get_active_workspace(output);
+ render_container(output, workspace);
+
+ render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged);
+
+ // TODO: consider revising this when fullscreen windows are supported
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+ render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
+
+renderer_end:
+ if (root_container.sway_root->debug_tree) {
+ wlr_render_texture(renderer, root_container.sway_root->debug_tree,
+ wlr_output->transform_matrix, 0, 0, 1);
+ }
+
+ wlr_renderer_end(renderer);
+ if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
+ return;
+ }
+ output->last_frame = *when;
+}
+
+struct send_frame_done_data {
+ struct root_geometry root_geo;
+ struct sway_output *output;
+ struct timespec *when;
+};
+
+static void send_frame_done_iterator(struct wlr_surface *surface,
+ int sx, int sy, void *_data) {
+ struct send_frame_done_data *data = _data;
+
+ bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ sx, sy, NULL);
+ if (intersects) {
+ wlr_surface_send_frame_done(surface, data->when);
+ }
+}
+
+static void send_frame_done_layer(struct send_frame_done_data *data,
+ struct wl_list *layer_surfaces) {
+ layer_for_each_surface(layer_surfaces, &data->root_geo,
+ send_frame_done_iterator, data);
+}
+
+static void send_frame_done_unmanaged(struct send_frame_done_data *data,
+ struct wl_list *unmanaged) {
+ unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
+ send_frame_done_iterator, data);
+}
+
+static void send_frame_done_container_iterator(struct sway_container *con,
+ void *_data) {
+ struct send_frame_done_data *data = _data;
+ if (!sway_assert(con->type == C_VIEW, "expected a view")) {
+ return;
+ }
+ output_view_for_each_surface(con->sway_view, &data->root_geo,
+ send_frame_done_iterator, data);
+}
+
+static void send_frame_done_container(struct send_frame_done_data *data,
+ struct sway_container *con) {
+ container_descendants(con, C_VIEW,
+ send_frame_done_container_iterator, data);
+}
+
+static void send_frame_done(struct sway_output *output, struct timespec *when) {
+ struct send_frame_done_data data = {
+ .output = output,
+ .when = when,
+ };
+
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
+
+ struct sway_container *workspace = output_get_active_workspace(output);
+ send_frame_done_container(&data, workspace);
+
+ send_frame_done_unmanaged(&data,
+ &root_container.sway_root->xwayland_unmanaged);
+
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+ send_frame_done_layer(&data,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
+}
+
+static void damage_handle_frame(struct wl_listener *listener, void *data) {
+ struct sway_output *output =
+ wl_container_of(listener, output, damage_frame);
+
+ if (!output->wlr_output->enabled) {
+ return;
+ }
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ bool needs_swap;
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
+ return;
+ }
+
+ if (needs_swap) {
+ render_output(output, &now, &damage);
+ }
+
+ pixman_region32_fini(&damage);
+
+ // Send frame done to all visible surfaces
+ send_frame_done(output, &now);
+}
+
+void output_damage_whole(struct sway_output *output) {
+ wlr_output_damage_add_whole(output->damage);
+}
+
+struct damage_data {
+ struct root_geometry root_geo;
+ struct sway_output *output;
+ bool whole;
+};
+
+static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+ void *_data) {
+ struct damage_data *data = _data;
+ struct sway_output *output = data->output;
+ float rotation = data->root_geo.rotation;
+ bool whole = data->whole;
+
+ struct wlr_box box;
+ bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ sx, sy, &box);
+ if (!intersects) {
+ return;
+ }
+
+ scale_box(&box, output->wlr_output->scale);
+
+ if (whole) {
+ wlr_box_rotated_bounds(&box, rotation, &box);
+ wlr_output_damage_add_box(output->damage, &box);
+ } else {
+ int center_x = box.x + box.width/2;
+ int center_y = box.y + box.height/2;
+
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ pixman_region32_copy(&damage, &surface->current->surface_damage);
+ wlr_region_scale(&damage, &damage, output->wlr_output->scale);
+ if (ceil(output->wlr_output->scale) > surface->current->scale) {
+ // When scaling up a surface, it'll become blurry so we need to
+ // expand the damage region
+ wlr_region_expand(&damage, &damage,
+ ceil(output->wlr_output->scale) - surface->current->scale);
+ }
+ pixman_region32_translate(&damage, box.x, box.y);
+ wlr_region_rotated_bounds(&damage, &damage, rotation,
+ center_x, center_y);
+ wlr_output_damage_add(output->damage, &damage);
+ pixman_region32_fini(&damage);
+ }
+}
+
+void output_damage_surface(struct sway_output *output, double ox, double oy,
+ struct wlr_surface *surface, bool whole) {
+ struct damage_data data = {
+ .output = output,
+ .whole = whole,
+ };
+
+ surface_for_each_surface(surface, ox, oy, &data.root_geo,
+ damage_surface_iterator, &data);
+}
+
+void output_damage_view(struct sway_output *output, struct sway_view *view,
+ bool whole) {
+ if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
+ return;
+ }
+
+ struct damage_data data = {
+ .output = output,
+ .whole = whole,
+ };
+
+ output_view_for_each_surface(view, &data.root_geo,
+ damage_surface_iterator, &data);
+}
+
+static void output_damage_whole_container_iterator(struct sway_container *con,
+ void *data) {
+ struct sway_output *output = data;
+
+ if (!sway_assert(con->type == C_VIEW, "expected a view")) {
+ return;
+ }
+
+ output_damage_view(output, con->sway_view, true);
+}
+
+void output_damage_whole_container(struct sway_output *output,
+ struct sway_container *con) {
+ float scale = output->wlr_output->scale;
+ struct wlr_box box = {
+ .x = con->x * scale,
+ .y = con->y * scale,
+ .width = con->width * scale,
+ .height = con->height * scale,
+ };
+ wlr_output_damage_add_box(output->damage, &box);
+
+ container_descendants(con, C_VIEW, output_damage_whole_container_iterator,
+ output);
+}
+
+static void damage_handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_output *output =
+ wl_container_of(listener, output, damage_destroy);
+ container_destroy(output->swayc);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, destroy);
+ container_destroy(output->swayc);
+}
+
+static void handle_mode(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, mode);
+ arrange_layers(output);
+ arrange_windows(output->swayc, -1, -1);
+}
+
+static void handle_transform(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, transform);
+ arrange_layers(output);
+ arrange_windows(output->swayc, -1, -1);
+}
+
+static void handle_scale(struct wl_listener *listener, void *data) {
+ struct sway_output *output = wl_container_of(listener, output, scale);
+ arrange_layers(output);
+ arrange_windows(output->swayc, -1, -1);
+}
+
+void handle_new_output(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server, new_output);
+ struct wlr_output *wlr_output = data;
+ wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
+
+ struct sway_output *output = calloc(1, sizeof(struct sway_output));
+ if (!output) {
+ return;
+ }
+ output->wlr_output = wlr_output;
+ wlr_output->data = output;
+ output->server = server;
+
+ if (!wl_list_empty(&wlr_output->modes)) {
+ struct wlr_output_mode *mode =
+ wl_container_of(wlr_output->modes.prev, mode, link);
+ wlr_output_set_mode(wlr_output, mode);
+ }
+
+ output->damage = wlr_output_damage_create(wlr_output);
+
+ output->swayc = output_create(output);
+ if (!output->swayc) {
+ free(output);
+ return;
+ }
+
+ size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
+ for (size_t i = 0; i < len; ++i) {
+ wl_list_init(&output->layers[i]);
+ }
+
+ input_manager_configure_xcursor(input_manager);
+
+ wl_signal_add(&wlr_output->events.destroy, &output->destroy);
+ output->destroy.notify = handle_destroy;
+ wl_signal_add(&wlr_output->events.mode, &output->mode);
+ output->mode.notify = handle_mode;
+ wl_signal_add(&wlr_output->events.transform, &output->transform);
+ output->transform.notify = handle_transform;
+ wl_signal_add(&wlr_output->events.scale, &output->scale);
+ output->scale.notify = handle_scale;
+
+ wl_signal_add(&output->damage->events.frame, &output->damage_frame);
+ output->damage_frame.notify = damage_handle_frame;
+ wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
+ output->damage_destroy.notify = damage_handle_destroy;
+
+ arrange_layers(output);
+ arrange_windows(&root_container, -1, -1);
+}
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 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/server.h"
+#include "sway/tree/view.h"
+#include "sway/input/seat.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+static struct sway_wl_shell_view *wl_shell_view_from_view(
+ struct sway_view *view) {
+ if (!sway_assert(view->type == SWAY_VIEW_WL_SHELL,
+ "Expected wl_shell view")) {
+ return NULL;
+ }
+ return (struct sway_wl_shell_view *)view;
+}
+
+static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
+ if (wl_shell_view_from_view(view) == NULL) {
+ return NULL;
+ }
+ switch (prop) {
+ case VIEW_PROP_TITLE:
+ return view->wlr_wl_shell_surface->title;
+ case VIEW_PROP_CLASS:
+ return view->wlr_wl_shell_surface->class;
+ default:
+ return NULL;
+ }
+}
+
+static void configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
+ if (wl_shell_view == NULL) {
+ return;
+ }
+ view_update_position(view, ox, oy);
+ wl_shell_view->pending_width = width;
+ wl_shell_view->pending_height = height;
+ wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height);
+}
+
+static void _close(struct sway_view *view) {
+ if (wl_shell_view_from_view(view) == NULL) {
+ return;
+ }
+
+ wl_client_destroy(view->wlr_wl_shell_surface->client);
+}
+
+static void destroy(struct sway_view *view) {
+ struct sway_wl_shell_view *wl_shell_view = wl_shell_view_from_view(view);
+ if (wl_shell_view == NULL) {
+ return;
+ }
+ wl_list_remove(&wl_shell_view->commit.link);
+ wl_list_remove(&wl_shell_view->destroy.link);
+ free(wl_shell_view);
+}
+
+static const struct sway_view_impl view_impl = {
+ .get_prop = get_prop,
+ .configure = configure,
+ .close = _close,
+ .destroy = destroy,
+};
+
+static void handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_wl_shell_view *wl_shell_view =
+ wl_container_of(listener, wl_shell_view, commit);
+ struct sway_view *view = &wl_shell_view->view;
+ // NOTE: We intentionally discard the view's desired width here
+ // TODO: Let floating views do whatever
+ view_update_size(view, wl_shell_view->pending_width,
+ wl_shell_view->pending_height);
+ view_damage(view, false);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_wl_shell_view *wl_shell_view =
+ wl_container_of(listener, wl_shell_view, destroy);
+ view_destroy(&wl_shell_view->view);
+}
+
+void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server,
+ wl_shell_surface);
+ struct wlr_wl_shell_surface *shell_surface = data;
+
+ if (shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) {
+ // popups don't get views
+ wlr_log(L_DEBUG, "New wl_shell popup");
+ return;
+ }
+
+ // TODO: make transient windows floating
+
+ wlr_log(L_DEBUG, "New wl_shell toplevel title='%s' app_id='%s'",
+ shell_surface->title, shell_surface->class);
+ wlr_wl_shell_surface_ping(shell_surface);
+
+ struct sway_wl_shell_view *wl_shell_view =
+ calloc(1, sizeof(struct sway_wl_shell_view));
+ if (!sway_assert(wl_shell_view, "Failed to allocate view")) {
+ return;
+ }
+
+ view_init(&wl_shell_view->view, SWAY_VIEW_WL_SHELL, &view_impl);
+ wl_shell_view->view.wlr_wl_shell_surface = shell_surface;
+
+ // TODO:
+ // - Wire up listeners
+ // - Look up pid and open on appropriate workspace
+ // - Set new view to maximized so it behaves nicely
+ // - Criteria
+
+ wl_shell_view->commit.notify = handle_commit;
+ wl_signal_add(&shell_surface->surface->events.commit,
+ &wl_shell_view->commit);
+
+ wl_shell_view->destroy.notify = handle_destroy;
+ wl_signal_add(&shell_surface->events.destroy, &wl_shell_view->destroy);
+
+ view_map(&wl_shell_view->view, shell_surface->surface);
+}
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 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_xdg_shell_v6.h>
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/server.h"
+#include "sway/tree/view.h"
+#include "sway/input/seat.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+static const struct sway_view_child_impl popup_impl;
+
+static void popup_destroy(struct sway_view_child *child) {
+ if (!sway_assert(child->impl == &popup_impl,
+ "Expected an xdg_shell_v6 popup")) {
+ return;
+ }
+ struct sway_xdg_popup_v6 *popup = (struct sway_xdg_popup_v6 *)child;
+ wl_list_remove(&popup->new_popup.link);
+ wl_list_remove(&popup->unmap.link);
+ wl_list_remove(&popup->destroy.link);
+ free(popup);
+}
+
+static const struct sway_view_child_impl popup_impl = {
+ .destroy = popup_destroy,
+};
+
+static struct sway_xdg_popup_v6 *popup_create(
+ struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view);
+
+static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
+ struct sway_xdg_popup_v6 *popup =
+ wl_container_of(listener, popup, new_popup);
+ struct wlr_xdg_popup_v6 *wlr_popup = data;
+ popup_create(wlr_popup, popup->child.view);
+}
+
+static void popup_handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, unmap);
+ view_child_destroy(&popup->child);
+}
+
+static void popup_handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xdg_popup_v6 *popup = wl_container_of(listener, popup, destroy);
+ view_child_destroy(&popup->child);
+}
+
+static struct sway_xdg_popup_v6 *popup_create(
+ struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {
+ struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base;
+
+ struct sway_xdg_popup_v6 *popup =
+ calloc(1, sizeof(struct sway_xdg_popup_v6));
+ if (popup == NULL) {
+ return NULL;
+ }
+ view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
+
+ wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
+ popup->new_popup.notify = popup_handle_new_popup;
+ wl_signal_add(&xdg_surface->events.unmap, &popup->unmap);
+ popup->unmap.notify = popup_handle_unmap;
+ wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
+ popup->destroy.notify = popup_handle_destroy;
+
+ return popup;
+}
+
+
+static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view(
+ struct sway_view *view) {
+ if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6,
+ "Expected xdg_shell_v6 view")) {
+ return NULL;
+ }
+ return (struct sway_xdg_shell_v6_view *)view;
+}
+
+static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return NULL;
+ }
+ switch (prop) {
+ case VIEW_PROP_TITLE:
+ return view->wlr_xdg_surface_v6->toplevel->title;
+ case VIEW_PROP_APP_ID:
+ return view->wlr_xdg_surface_v6->toplevel->app_id;
+ default:
+ return NULL;
+ }
+}
+
+static void configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ xdg_shell_v6_view_from_view(view);
+ if (xdg_shell_v6_view == NULL) {
+ return;
+ }
+
+ view_update_position(view, ox, oy);
+ xdg_shell_v6_view->pending_width = width;
+ xdg_shell_v6_view->pending_height = height;
+ wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);
+}
+
+static void set_activated(struct sway_view *view, bool activated) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return;
+ }
+ struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
+ if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
+ wlr_xdg_toplevel_v6_set_activated(surface, activated);
+ }
+}
+
+static void for_each_surface(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return;
+ }
+ wlr_xdg_surface_v6_for_each_surface(view->wlr_xdg_surface_v6, iterator,
+ user_data);
+}
+
+static void _close(struct sway_view *view) {
+ if (xdg_shell_v6_view_from_view(view) == NULL) {
+ return;
+ }
+ struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6;
+ if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
+ wlr_xdg_surface_v6_send_close(surface);
+ }
+}
+
+static void destroy(struct sway_view *view) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ xdg_shell_v6_view_from_view(view);
+ if (xdg_shell_v6_view == NULL) {
+ return;
+ }
+ wl_list_remove(&xdg_shell_v6_view->destroy.link);
+ wl_list_remove(&xdg_shell_v6_view->map.link);
+ wl_list_remove(&xdg_shell_v6_view->unmap.link);
+ free(xdg_shell_v6_view);
+}
+
+static const struct sway_view_impl view_impl = {
+ .get_prop = get_prop,
+ .configure = configure,
+ .set_activated = set_activated,
+ .for_each_surface = for_each_surface,
+ .close = _close,
+ .destroy = destroy,
+};
+
+static void handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, commit);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ // NOTE: We intentionally discard the view's desired width here
+ // TODO: Store this for restoration when moving to floating plane
+ // TODO: Let floating views do whatever
+ view_update_size(view, xdg_shell_v6_view->pending_width,
+ xdg_shell_v6_view->pending_height);
+ view_damage(view, false);
+}
+
+static void handle_new_popup(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, new_popup);
+ struct wlr_xdg_popup_v6 *wlr_popup = data;
+ popup_create(wlr_popup, &xdg_shell_v6_view->view);
+}
+
+static void handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, unmap);
+
+ view_unmap(&xdg_shell_v6_view->view);
+
+ wl_list_remove(&xdg_shell_v6_view->commit.link);
+ wl_list_remove(&xdg_shell_v6_view->new_popup.link);
+}
+
+static void handle_map(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, map);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ struct wlr_xdg_surface_v6 *xdg_surface = view->wlr_xdg_surface_v6;
+
+ view_map(view, view->wlr_xdg_surface_v6->surface);
+
+ xdg_shell_v6_view->commit.notify = handle_commit;
+ wl_signal_add(&xdg_surface->surface->events.commit,
+ &xdg_shell_v6_view->commit);
+
+ xdg_shell_v6_view->new_popup.notify = handle_new_popup;
+ wl_signal_add(&xdg_surface->events.new_popup,
+ &xdg_shell_v6_view->new_popup);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, destroy);
+ view_destroy(&xdg_shell_v6_view->view);
+}
+
+void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server,
+ xdg_shell_v6_surface);
+ struct wlr_xdg_surface_v6 *xdg_surface = data;
+
+ if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
+ wlr_log(L_DEBUG, "New xdg_shell_v6 popup");
+ return;
+ }
+
+ wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
+ xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
+ wlr_xdg_surface_v6_ping(xdg_surface);
+ wlr_xdg_toplevel_v6_set_maximized(xdg_surface, true);
+
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ calloc(1, sizeof(struct sway_xdg_shell_v6_view));
+ if (!sway_assert(xdg_shell_v6_view, "Failed to allocate view")) {
+ return;
+ }
+
+ view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
+ xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
+
+ // TODO:
+ // - Look up pid and open on appropriate workspace
+ // - Criteria
+
+ xdg_shell_v6_view->map.notify = handle_map;
+ wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
+
+ xdg_shell_v6_view->unmap.notify = handle_unmap;
+ wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_v6_view->unmap);
+
+ xdg_shell_v6_view->destroy.notify = handle_destroy;
+ wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy);
+}
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 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/xwayland.h>
+#include "log.h"
+#include "sway/desktop.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+
+static void unmanaged_handle_request_configure(struct wl_listener *listener,
+ void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, request_configure);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+ struct wlr_xwayland_surface_configure_event *ev = data;
+ wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
+ ev->width, ev->height);
+}
+
+static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, commit);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+
+ if (xsurface->x != surface->lx || xsurface->y != surface->ly) {
+ // Surface has moved
+ desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
+ true);
+ surface->lx = xsurface->x;
+ surface->ly = xsurface->y;
+ desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
+ true);
+ } else {
+ desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y,
+ false);
+ }
+}
+
+static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, map);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+
+ wl_list_insert(&root_container.sway_root->xwayland_unmanaged,
+ &surface->link);
+
+ wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
+ surface->commit.notify = unmanaged_handle_commit;
+
+ surface->lx = xsurface->x;
+ surface->ly = xsurface->y;
+ desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
+
+ if (!wlr_xwayland_surface_is_unmanaged(xsurface)) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct wlr_xwayland *xwayland = seat->input->server->xwayland;
+ wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
+ seat_set_focus_surface(seat, xsurface->surface);
+ }
+
+ // TODO: we don't send surface enter/leave events to xwayland unmanaged
+ // surfaces, but xwayland doesn't support HiDPI anyway
+}
+
+static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, unmap);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+ desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true);
+ wl_list_remove(&surface->link);
+ wl_list_remove(&surface->commit.link);
+}
+
+static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_unmanaged *surface =
+ wl_container_of(listener, surface, destroy);
+ struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
+ if (xsurface->mapped) {
+ unmanaged_handle_unmap(&surface->unmap, xsurface);
+ }
+ wl_list_remove(&surface->map.link);
+ wl_list_remove(&surface->unmap.link);
+ wl_list_remove(&surface->destroy.link);
+ free(surface);
+}
+
+static struct sway_xwayland_unmanaged *create_unmanaged(
+ struct wlr_xwayland_surface *xsurface) {
+ struct sway_xwayland_unmanaged *surface =
+ calloc(1, sizeof(struct sway_xwayland_unmanaged));
+ if (surface == NULL) {
+ wlr_log(L_ERROR, "Allocation failed");
+ return NULL;
+ }
+
+ surface->wlr_xwayland_surface = xsurface;
+
+ wl_signal_add(&xsurface->events.request_configure,
+ &surface->request_configure);
+ surface->request_configure.notify = unmanaged_handle_request_configure;
+ wl_signal_add(&xsurface->events.map, &surface->map);
+ surface->map.notify = unmanaged_handle_map;
+ wl_signal_add(&xsurface->events.unmap, &surface->unmap);
+ surface->unmap.notify = unmanaged_handle_unmap;
+ wl_signal_add(&xsurface->events.destroy, &surface->destroy);
+ surface->destroy.notify = unmanaged_handle_destroy;
+
+ unmanaged_handle_map(&surface->map, xsurface);
+
+ return surface;
+}
+
+
+static struct sway_xwayland_view *xwayland_view_from_view(
+ struct sway_view *view) {
+ if (!sway_assert(view->type == SWAY_VIEW_XWAYLAND,
+ "Expected xwayland view")) {
+ return NULL;
+ }
+ return (struct sway_xwayland_view *)view;
+}
+
+static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
+ if (xwayland_view_from_view(view) == NULL) {
+ return NULL;
+ }
+ switch (prop) {
+ case VIEW_PROP_TITLE:
+ return view->wlr_xwayland_surface->title;
+ case VIEW_PROP_CLASS:
+ return view->wlr_xwayland_surface->class;
+ default:
+ return NULL;
+ }
+}
+
+static void configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
+ if (xwayland_view == NULL) {
+ return;
+ }
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+ if (!sway_assert(output, "view must be within tree to set position")) {
+ return;
+ }
+ struct sway_container *root = container_parent(output, C_ROOT);
+ if (!sway_assert(root, "output must be within tree to set position")) {
+ return;
+ }
+ struct wlr_output_layout *layout = root->sway_root->output_layout;
+ struct wlr_output_layout_output *loutput =
+ wlr_output_layout_get(layout, output->sway_output->wlr_output);
+ if (!sway_assert(loutput, "output must be within layout to set position")) {
+ return;
+ }
+
+ view_update_position(view, ox, oy);
+
+ xwayland_view->pending_width = width;
+ xwayland_view->pending_height = height;
+ wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y,
+ width, height);
+}
+
+static void set_activated(struct sway_view *view, bool activated) {
+ if (xwayland_view_from_view(view) == NULL) {
+ return;
+ }
+ struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
+ wlr_xwayland_surface_activate(surface, activated);
+}
+
+static void _close(struct sway_view *view) {
+ if (xwayland_view_from_view(view) == NULL) {
+ return;
+ }
+ wlr_xwayland_surface_close(view->wlr_xwayland_surface);
+}
+
+static void destroy(struct sway_view *view) {
+ struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
+ if (xwayland_view == NULL) {
+ return;
+ }
+ wl_list_remove(&xwayland_view->destroy.link);
+ wl_list_remove(&xwayland_view->request_configure.link);
+ wl_list_remove(&xwayland_view->map.link);
+ wl_list_remove(&xwayland_view->unmap.link);
+ free(xwayland_view);
+}
+
+static const struct sway_view_impl view_impl = {
+ .get_prop = get_prop,
+ .configure = configure,
+ .set_activated = set_activated,
+ .close = _close,
+ .destroy = destroy,
+};
+
+static void handle_commit(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, commit);
+ struct sway_view *view = &xwayland_view->view;
+ // NOTE: We intentionally discard the view's desired width here
+ // TODO: Let floating views do whatever
+ view_update_size(view, xwayland_view->pending_width,
+ xwayland_view->pending_height);
+ view_damage(view, false);
+}
+
+static void handle_unmap(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, unmap);
+ wl_list_remove(&xwayland_view->commit.link);
+ view_unmap(&xwayland_view->view);
+}
+
+static void handle_map(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, map);
+ struct wlr_xwayland_surface *xsurface = data;
+ struct sway_view *view = &xwayland_view->view;
+
+ // Wire up the commit listener here, because xwayland map/unmap can change
+ // the underlying wlr_surface
+ wl_signal_add(&xsurface->surface->events.commit, &xwayland_view->commit);
+ xwayland_view->commit.notify = handle_commit;
+
+ // Put it back into the tree
+ wlr_xwayland_surface_set_maximized(xsurface, true);
+ view_map(view, xsurface->surface);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, destroy);
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ if (xsurface->mapped) {
+ handle_unmap(&xwayland_view->unmap, xsurface);
+ }
+ view_destroy(&xwayland_view->view);
+}
+
+static void handle_request_configure(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, request_configure);
+ struct wlr_xwayland_surface_configure_event *ev = data;
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ // TODO: floating windows are allowed to move around like this, but make
+ // sure tiling windows always stay in place.
+ wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
+ ev->width, ev->height);
+}
+
+void handle_xwayland_surface(struct wl_listener *listener, void *data) {
+ struct sway_server *server = wl_container_of(listener, server,
+ xwayland_surface);
+ struct wlr_xwayland_surface *xsurface = data;
+
+ if (wlr_xwayland_surface_is_unmanaged(xsurface) ||
+ xsurface->override_redirect) {
+ wlr_log(L_DEBUG, "New xwayland unmanaged surface");
+ create_unmanaged(xsurface);
+ return;
+ }
+
+ wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'",
+ xsurface->title, xsurface->class);
+
+ struct sway_xwayland_view *xwayland_view =
+ calloc(1, sizeof(struct sway_xwayland_view));
+ if (!sway_assert(xwayland_view, "Failed to allocate view")) {
+ return;
+ }
+
+ view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
+ xwayland_view->view.wlr_xwayland_surface = xsurface;
+
+ // TODO:
+ // - Look up pid and open on appropriate workspace
+ // - Criteria
+
+ wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
+ xwayland_view->destroy.notify = handle_destroy;
+
+ wl_signal_add(&xsurface->events.request_configure,
+ &xwayland_view->request_configure);
+ xwayland_view->request_configure.notify = handle_request_configure;
+
+ wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap);
+ xwayland_view->unmap.notify = handle_unmap;
+
+ wl_signal_add(&xsurface->events.map, &xwayland_view->map);
+ xwayland_view->map.notify = handle_map;
+
+ handle_map(&xwayland_view->map, xsurface);
+}
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 @@
-#include <stdlib.h>
-#include <wlc/wlc.h>
-#include <wlc/wlc-wayland.h>
-#include <wlc/wlc-render.h>
-#include "wayland-desktop-shell-server-protocol.h"
-#include "wayland-swaylock-server-protocol.h"
-#include "wayland-gamma-control-server-protocol.h"
-#include "wayland-server-decoration-server-protocol.h"
-#include "sway/layout.h"
-#include "sway/input_state.h"
-#include "sway/extensions.h"
-#include "sway/security.h"
-#include "sway/ipc-server.h"
-#include "log.h"
-
-struct desktop_shell_state desktop_shell;
-struct decoration_state decoration_state;
-
-static struct panel_config *find_or_create_panel_config(struct wl_resource *resource) {
- for (int i = 0; i < desktop_shell.panels->length; i++) {
- struct panel_config *conf = desktop_shell.panels->items[i];
- if (conf->wl_resource == resource) {
- sway_log(L_DEBUG, "Found existing panel config for resource %p", resource);
- return conf;
- }
- }
- sway_log(L_DEBUG, "Creating panel config for resource %p", resource);
- struct panel_config *config = calloc(1, sizeof(struct panel_config));
- if (!config) {
- sway_log(L_ERROR, "Unable to create panel config");
- return NULL;
- }
- list_add(desktop_shell.panels, config);
- config->wl_resource = resource;
- return config;
-}
-
-void background_surface_destructor(struct wl_resource *resource) {
- sway_log(L_DEBUG, "Background surface killed");
- int i;
- for (i = 0; i < desktop_shell.backgrounds->length; ++i) {
- struct background_config *config = desktop_shell.backgrounds->items[i];
- if (config->wl_surface_res == resource) {
- list_del(desktop_shell.backgrounds, i);
- break;
- }
- }
-}
-
-void panel_surface_destructor(struct wl_resource *resource) {
- sway_log(L_DEBUG, "Panel surface killed");
- int i;
- for (i = 0; i < desktop_shell.panels->length; ++i) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->wl_surface_res == resource) {
- list_del(desktop_shell.panels, i);
- arrange_windows(&root_container, -1, -1);
- break;
- }
- }
-}
-
-void lock_surface_destructor(struct wl_resource *resource) {
- sway_log(L_DEBUG, "Lock surface killed");
- int i;
- for (i = 0; i < desktop_shell.lock_surfaces->length; ++i) {
- struct wl_resource *surface = desktop_shell.lock_surfaces->items[i];
- if (surface == resource) {
- list_del(desktop_shell.lock_surfaces, i);
- arrange_windows(&root_container, -1, -1);
- break;
- }
- }
- if (desktop_shell.lock_surfaces->length == 0) {
- sway_log(L_DEBUG, "Desktop shell unlocked");
- desktop_shell.is_locked = false;
-
- // We need to now give focus back to the focus which we internally
- // track, since when we lock sway we don't actually change our internal
- // focus tracking.
- swayc_t *focus = get_focused_container(swayc_active_workspace());
- set_focused_container(focus);
- wlc_view_focus(focus->handle);
- }
-}
-
-static void set_background(struct wl_client *client, struct wl_resource *resource,
- struct wl_resource *_output, struct wl_resource *surface) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_BACKGROUND)) {
- sway_log(L_INFO, "Denying background feature to %d", pid);
- return;
- }
- wlc_handle output = wlc_handle_from_wl_output_resource(_output);
- if (!output) {
- return;
- }
- sway_log(L_DEBUG, "Setting surface %p as background for output %d", surface, (int)output);
- struct background_config *config = malloc(sizeof(struct background_config));
- if (!config) {
- sway_log(L_ERROR, "Unable to allocate background config");
- return;
- }
- config->client = client;
- config->output = output;
- config->surface = wlc_resource_from_wl_surface_resource(surface);
- config->wl_surface_res = surface;
- list_add(desktop_shell.backgrounds, config);
- wl_resource_set_destructor(surface, background_surface_destructor);
- arrange_windows(swayc_by_handle(output), -1, -1);
- wlc_output_schedule_render(config->output);
-}
-
-static void set_panel(struct wl_client *client, struct wl_resource *resource,
- struct wl_resource *_output, struct wl_resource *surface) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
- sway_log(L_INFO, "Denying panel feature to %d", pid);
- return;
- }
- wlc_handle output = wlc_handle_from_wl_output_resource(_output);
- if (!output) {
- return;
- }
- sway_log(L_DEBUG, "Setting surface %p as panel for output %d (wl_resource: %p)", surface, (int)output, resource);
- struct panel_config *config = find_or_create_panel_config(resource);
- config->output = output;
- config->client = client;
- config->surface = wlc_resource_from_wl_surface_resource(surface);
- config->wl_surface_res = surface;
- wl_resource_set_destructor(surface, panel_surface_destructor);
- arrange_windows(&root_container, -1, -1);
- wlc_output_schedule_render(config->output);
-}
-
-static void desktop_set_lock_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) {
- sway_log(L_ERROR, "desktop_set_lock_surface is not currently supported");
-}
-
-static void desktop_unlock(struct wl_client *client, struct wl_resource *resource) {
- sway_log(L_ERROR, "desktop_unlock is not currently supported");
-}
-
-static void set_grab_surface(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface) {
- sway_log(L_ERROR, "desktop_set_grab_surface is not currently supported");
-}
-
-static void desktop_ready(struct wl_client *client, struct wl_resource *resource) {
- // nop
-}
-
-static void set_panel_position(struct wl_client *client, struct wl_resource *resource, uint32_t position) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_PANEL)) {
- sway_log(L_INFO, "Denying panel feature to %d", pid);
- return;
- }
- struct panel_config *config = find_or_create_panel_config(resource);
- sway_log(L_DEBUG, "Panel position for wl_resource %p changed %d => %d", resource, config->panel_position, position);
- config->panel_position = position;
- arrange_windows(&root_container, -1, -1);
-}
-
-static struct desktop_shell_interface desktop_shell_implementation = {
- .set_background = set_background,
- .set_panel = set_panel,
- .set_lock_surface = desktop_set_lock_surface,
- .unlock = desktop_unlock,
- .set_grab_surface = set_grab_surface,
- .desktop_ready = desktop_ready,
- .set_panel_position = set_panel_position
-};
-
-static void desktop_shell_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 3) {
- // Unsupported version
- return;
- }
-
- struct wl_resource *resource = wl_resource_create(client, &desktop_shell_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
-
- wl_resource_set_implementation(resource, &desktop_shell_implementation, NULL, NULL);
-}
-
-static void set_lock_surface(struct wl_client *client, struct wl_resource *resource,
- struct wl_resource *_output, struct wl_resource *surface) {
- pid_t pid;
- wl_client_get_credentials(client, &pid, NULL, NULL);
- if (!(get_feature_policy_mask(pid) & FEATURE_LOCK)) {
- sway_log(L_INFO, "Denying lock feature to %d", pid);
- return;
- }
- swayc_t *output = swayc_by_handle(wlc_handle_from_wl_output_resource(_output));
- swayc_t *view = swayc_by_handle(wlc_handle_from_wl_surface_resource(surface));
- sway_log(L_DEBUG, "Setting lock surface to %p", view);
- if (view && output) {
- swayc_t *workspace = output->focused;
- if (!swayc_is_child_of(view, workspace)) {
- move_container_to(view, workspace);
- }
- // make the view floating so it doesn't rearrange other siblings.
- if (!view->is_floating) {
- destroy_container(remove_child(view));
- add_floating(workspace, view);
- }
- wlc_view_set_state(view->handle, WLC_BIT_FULLSCREEN, true);
- wlc_view_bring_to_front(view->handle);
- wlc_view_focus(view->handle);
- desktop_shell.is_locked = true;
- input_init();
- arrange_windows(workspace, -1, -1);
- list_add(desktop_shell.lock_surfaces, surface);
- wl_resource_set_destructor(surface, lock_surface_destructor);
- } else {
- sway_log(L_ERROR, "Attempted to set lock surface to non-view");
- }
-}
-
-static void unlock(struct wl_client *client, struct wl_resource *resource) {
- sway_log(L_ERROR, "unlock is not currently supported");
- // This isn't really necessary, we just unlock when the client exits.
-}
-
-static struct lock_interface swaylock_implementation = {
- .set_lock_surface = set_lock_surface,
- .unlock = unlock
-};
-
-static void swaylock_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 1) {
- // Unsupported version
- return;
- }
-
- struct wl_resource *resource = wl_resource_create(client, &lock_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
-
- wl_resource_set_implementation(resource, &swaylock_implementation, NULL, NULL);
-}
-
-static void gamma_control_destroy(struct wl_client *client, struct wl_resource *res) {
- wl_resource_destroy(res);
-}
-
-static void gamma_control_set_gamma(struct wl_client *client,
- struct wl_resource *res, struct wl_array *red,
- struct wl_array *green, struct wl_array *blue) {
- if (red->size != green->size || red->size != blue->size) {
- wl_resource_post_error(res, GAMMA_CONTROL_ERROR_INVALID_GAMMA,
- "The gamma ramps don't have the same size");
- return;
- }
- uint16_t *r = (uint16_t *)red->data;
- uint16_t *g = (uint16_t *)green->data;
- uint16_t *b = (uint16_t *)blue->data;
- wlc_handle output = wlc_handle_from_wl_output_resource(
- wl_resource_get_user_data(res));
- if (!output) {
- return;
- }
- sway_log(L_DEBUG, "Setting gamma for output");
- wlc_output_set_gamma(output, red->size / sizeof(uint16_t), r, g, b);
-}
-
-static void gamma_control_reset_gamma(struct wl_client *client,
- struct wl_resource *resource) {
- // This space intentionally left blank
-}
-
-static struct gamma_control_interface gamma_control_implementation = {
- .destroy = gamma_control_destroy,
- .set_gamma = gamma_control_set_gamma,
- .reset_gamma = gamma_control_reset_gamma
-};
-
-static void gamma_control_manager_destroy(struct wl_client *client,
- struct wl_resource *res) {
- wl_resource_destroy(res);
-}
-
-static void gamma_control_manager_get(struct wl_client *client,
- struct wl_resource *res, uint32_t id, struct wl_resource *_output) {
- struct wl_resource *manager_res = wl_resource_create(client,
- &gamma_control_interface, wl_resource_get_version(res), id);
- wlc_handle output = wlc_handle_from_wl_output_resource(_output);
- if (!output) {
- return;
- }
- wl_resource_set_implementation(manager_res, &gamma_control_implementation,
- _output, NULL);
- gamma_control_send_gamma_size(manager_res, wlc_output_get_gamma_size(output));
-}
-
-static struct gamma_control_manager_interface gamma_manager_implementation = {
- .destroy = gamma_control_manager_destroy,
- .get_gamma_control = gamma_control_manager_get
-};
-
-static void gamma_control_manager_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 1) {
- // Unsupported version
- return;
- }
- struct wl_resource *resource = wl_resource_create(client,
- &gamma_control_manager_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
- wl_resource_set_implementation(resource, &gamma_manager_implementation, NULL, NULL);
-}
-
-static void server_decoration_release(struct wl_client *client,
- struct wl_resource *resource) {
- wl_resource_destroy(resource);
-}
-
-void server_decoration_enable_csd(wlc_handle handle) {
- swayc_t *view = swayc_by_handle(handle);
- if (!view) {
- sway_log(L_DEBUG, "view invalid");
- return;
- }
- sway_log(L_DEBUG, "%s requested client side decorations", view->name);
- view->border_type = B_NONE;
- update_geometry(view);
-}
-
-static void server_decoration_request_mode(struct wl_client *client,
- struct wl_resource *resource, uint32_t mode) {
- sway_log(L_DEBUG, "Client requested server decoration mode %d", mode);
- if (mode == ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER) {
- return;
- }
- struct wl_resource *surface = wl_resource_get_user_data(resource);
- if (!surface) {
- sway_log(L_DEBUG, "surface invalid");
- return;
- }
- wlc_handle handle = wlc_handle_from_wl_surface_resource(surface);
- if (!handle) {
- list_add(decoration_state.csd_resources, surface);
- return;
- }
- server_decoration_enable_csd(handle);
-}
-
-static struct org_kde_kwin_server_decoration_interface server_decoration_implementation = {
- .release = server_decoration_release,
- .request_mode = server_decoration_request_mode,
-};
-
-static void server_decoration_manager_create(struct wl_client *client,
- struct wl_resource *resource, uint32_t id, struct wl_resource *surface) {
- sway_log(L_DEBUG, "Client requested server decoration manager");
- struct wl_resource *manager = wl_resource_create(client,
- &org_kde_kwin_server_decoration_interface, 1, id);
- if (!manager) {
- wl_client_post_no_memory(client);
- }
- wl_resource_set_implementation(manager, &server_decoration_implementation, surface, NULL);
-}
-
-// Jesus christ KDE, these names are whack as hell
-static struct org_kde_kwin_server_decoration_manager_interface server_decoration_manager_implementation = {
- .create = server_decoration_manager_create,
-};
-
-static void server_decoration_manager_bind(struct wl_client *client, void *data,
- uint32_t version, uint32_t id) {
- if (version > 1) {
- // Unsupported version
- return;
- }
- struct wl_resource *resource = wl_resource_create(client,
- &org_kde_kwin_server_decoration_manager_interface, version, id);
- if (!resource) {
- wl_client_post_no_memory(client);
- }
- wl_resource_set_implementation(resource, &server_decoration_manager_implementation, NULL, NULL);
- org_kde_kwin_server_decoration_manager_send_default_mode(resource,
- ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER);
-}
-
-void register_extensions(void) {
- wl_global_create(wlc_get_wl_display(), &desktop_shell_interface, 3, NULL, desktop_shell_bind);
- desktop_shell.backgrounds = create_list();
- desktop_shell.panels = create_list();
- desktop_shell.lock_surfaces = create_list();
- desktop_shell.is_locked = false;
- decoration_state.csd_resources = create_list();
- wl_global_create(wlc_get_wl_display(), &lock_interface, 1, NULL, swaylock_bind);
- wl_global_create(wlc_get_wl_display(), &gamma_control_manager_interface, 1,
- NULL, gamma_control_manager_bind);
- wl_global_create(wlc_get_wl_display(), &org_kde_kwin_server_decoration_manager_interface ,
- 1, NULL, server_decoration_manager_bind);
-}
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 @@
-#include "stdbool.h"
-#include <wlc/wlc.h>
-#include "sway/focus.h"
-#include "sway/workspace.h"
-#include "sway/layout.h"
-#include "sway/config.h"
-#include "sway/extensions.h"
-#include "sway/input_state.h"
-#include "sway/ipc-server.h"
-#include "sway/border.h"
-#include "log.h"
-
-bool locked_container_focus = false;
-bool suspend_workspace_cleanup = false;
-
-// switches parent focus to c. will switch it accordingly
-static void update_focus(swayc_t *c) {
- // Handle if focus switches
- swayc_t *parent = c->parent;
- if (!parent) return;
- if (parent->focused != c) {
- // Get previous focus
- swayc_t *prev = parent->focused;
- // Set new focus
- parent->focused = c;
-
- switch (c->type) {
- // Shouldn't happen
- case C_ROOT: return;
-
- // Case where output changes
- case C_OUTPUT:
- wlc_output_focus(c->handle);
- break;
-
- // Case where workspace changes
- case C_WORKSPACE:
- if (prev) {
- ipc_event_workspace(prev, c, "focus");
-
- // if the old workspace has no children, destroy it
- if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) {
- destroy_workspace(prev);
- } else {
- // update visibility of old workspace
- update_visibility(prev);
- }
- }
- // Update visibility of newly focused workspace
- update_visibility(c);
- break;
-
- default:
- case C_VIEW:
- case C_CONTAINER:
- break;
- }
- }
-}
-
-bool move_focus(enum movement_direction direction) {
- swayc_t *old_view = get_focused_container(&root_container);
- swayc_t *new_view = get_swayc_in_direction(old_view, direction);
- if (!new_view) {
- return false;
- } else if (new_view->type == C_ROOT) {
- sway_log(L_DEBUG, "Not setting focus above the workspace level");
- return false;
- } else if (new_view->type == C_OUTPUT) {
- return set_focused_container(swayc_active_workspace_for(new_view));
- } else if (direction == MOVE_PARENT || direction == MOVE_CHILD) {
- return set_focused_container(new_view);
- } else if (config->mouse_warping) {
- swayc_t *old_op = old_view->type == C_OUTPUT ?
- old_view : swayc_parent_by_type(old_view, C_OUTPUT);
- swayc_t *focused = get_focused_view(new_view);
- if (set_focused_container(focused)) {
- if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) {
- center_pointer_on(focused);
- }
- return true;
- }
- } else {
- return set_focused_container(get_focused_view(new_view));
- }
- return false;
-}
-
-swayc_t *get_focused_container(swayc_t *parent) {
- if (!parent) {
- return swayc_active_workspace();
- }
- while (!parent->is_focused && parent->focused) {
- parent = parent->focused;
- }
- return parent;
-}
-
-bool set_focused_container(swayc_t *c) {
- if (locked_container_focus || !c || !c->parent) {
- return false;
- }
-
- // current ("old") workspace for sending workspace change event later
- swayc_t *old_ws = swayc_active_workspace();
- // keep track of child count so we can determine if it gets destroyed
- int old_ws_child_count = 0;
- if (old_ws) {
- old_ws_child_count = old_ws->children->length + old_ws->floating->length;
- }
-
- // current ("old") focused container
- swayc_t *old_focus = get_focused_container(&root_container);
- // if old_focus is a workspace, then it's the same workspace as
- // old_ws, and we'll need to null its pointer too, since it will
- // be destroyed in the update_focus() call
- bool old_focus_was_ws = (old_focus->type == C_WORKSPACE);
-
- // workspace of new focused container
- swayc_t *workspace = swayc_active_workspace_for(c);
-
- if (swayc_is_fullscreen(get_focused_container(workspace))) {
- // if switching to a workspace with a fullscreen view,
- // focus on the fullscreen view
- c = get_focused_container(workspace);
- }
-
- swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle);
-
- if (c->type == C_VIEW) {
- // dispatch a window event
- ipc_event_window(c, "focus");
- }
-
- // update the global pointer
- current_focus = c;
-
- // update container focus from here to root, making necessary changes along
- // the way
- swayc_t *p = c;
- if (p->type != C_OUTPUT && p->type != C_ROOT) {
- p->is_focused = true;
- }
- while (p != &root_container) {
- update_focus(p);
- p = p->parent;
- p->is_focused = false;
- }
-
- if (old_focus_was_ws && old_ws_child_count == 0) {
- // this workspace was destroyed in update_focus(), so null the pointers
- old_focus = NULL;
- old_ws = NULL;
- }
-
- if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) {
- if (old_focus) {
- if (old_focus->type == C_VIEW) {
- wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false);
- }
- update_container_border(old_focus);
- }
- if (c->type == C_VIEW) {
- wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true);
- }
- if (!desktop_shell.is_locked) {
- // If the system is locked, we do everything _but_ actually setting
- // focus. This includes making our internals think that this view is
- // focused.
- wlc_view_focus(c->handle);
- }
- if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) {
- update_container_border(c);
- }
-
- swayc_t *parent = swayc_tabbed_stacked_ancestor(c);
- if (parent != NULL) {
- arrange_backgrounds();
- arrange_windows(parent, -1, -1);
- }
- }
-
- if (old_ws != workspace) {
- // old_ws might be NULL here but that's ok
- ipc_event_workspace(old_ws, workspace, "focus");
- }
-
- return true;
-}
-
-bool set_focused_container_for(swayc_t *a, swayc_t *c) {
- if (locked_container_focus || !c) {
- return false;
- }
- swayc_t *find = c;
- while (find != a && (find = find->parent)) {
- if (find == &root_container) {
- return false;
- }
- }
-
- // Get workspace for c, get that workspaces current focused container.
- swayc_t *workspace = swayc_active_workspace_for(c);
- swayc_t *focused = get_focused_view(workspace);
- // if the workspace we are changing focus to has a fullscreen view return
- if (swayc_is_fullscreen(focused) && c != focused) {
- return false;
- }
-
- // Check if we are changing a parent container that will see change
- bool effective = true;
- while (find != &root_container) {
- if (find->parent->focused != find) {
- effective = false;
- }
- find = find->parent;
- }
- if (effective) {
- // Go to set_focused_container
- return set_focused_container(c);
- }
-
- sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR,
- a, a->handle, c, c->handle);
-
- c->is_focused = true;
- swayc_t *p = c;
- while (p != a) {
- update_focus(p);
- p = p->parent;
- p->is_focused = false;
- }
- return true;
-}
-
-swayc_t *get_focused_view(swayc_t *parent) {
- swayc_t *c = parent;
- while (c && c->type != C_VIEW) {
- if (c->type == C_WORKSPACE && c->focused == NULL) {
- return c;
- }
- c = c->focused;
- }
- if (c == NULL) {
- c = swayc_active_workspace_for(parent);
- }
- return c;
-}
-
-swayc_t *get_focused_float(swayc_t *ws) {
- if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) {
- ws = swayc_active_workspace();
- }
- if (ws->floating->length) {
- return ws->floating->items[ws->floating->length - 1];
- }
- return NULL;
-}
-
-swayc_t *get_focused_view_include_floating(swayc_t *parent) {
- swayc_t *c = parent;
- swayc_t *f = NULL;
-
- while (c && c->type != C_VIEW) {
- if (c->type == C_WORKSPACE && c->focused == NULL) {
- return ((f = get_focused_float(c))) ? f : c;
- }
-
- c = c->focused;
- }
-
- if (c == NULL) {
- c = swayc_active_workspace_for(parent);
- }
-
- return c;
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <xkbcommon/xkbcommon.h>
-#include <strings.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <libinput.h>
-#include <math.h>
-#include <wlc/wlc.h>
-#include <wlc/wlc-render.h>
-#include <wlc/wlc-wayland.h>
-#include <ctype.h>
-#include "sway/handlers.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "sway/config.h"
-#include "sway/commands.h"
-#include "sway/workspace.h"
-#include "sway/container.h"
-#include "sway/output.h"
-#include "sway/focus.h"
-#include "sway/input_state.h"
-#include "sway/extensions.h"
-#include "sway/criteria.h"
-#include "sway/ipc-server.h"
-#include "sway/input.h"
-#include "sway/security.h"
-#include "list.h"
-#include "stringop.h"
-#include "log.h"
-
-// Event should be sent to client
-#define EVENT_PASSTHROUGH false
-
-// Event handled by sway and should not be sent to client
-#define EVENT_HANDLED true
-
-static struct panel_config *if_panel_find_config(struct wl_client *client) {
- int i;
- for (i = 0; i < desktop_shell.panels->length; i++) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->client == client) {
- return config;
- }
- }
- return NULL;
-}
-
-static struct background_config *if_background_find_config(struct wl_client *client) {
- int i;
- for (i = 0; i < desktop_shell.backgrounds->length; i++) {
- struct background_config *config = desktop_shell.backgrounds->items[i];
- if (config->client == client) {
- return config;
- }
- }
- return NULL;
-}
-
-static struct wlc_geometry compute_panel_geometry(struct panel_config *config) {
- struct wlc_size resolution;
- output_get_scaled_size(config->output, &resolution);
- const struct wlc_geometry *old = wlc_view_get_geometry(config->handle);
- struct wlc_geometry new;
-
- switch (config->panel_position) {
- case DESKTOP_SHELL_PANEL_POSITION_TOP:
- new.origin.x = 0;
- new.origin.y = 0;
- new.size.w = resolution.w;
- new.size.h = old->size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
- new.origin.x = 0;
- new.origin.y = resolution.h - old->size.h;
- new.size.w = resolution.w;
- new.size.h = old->size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_LEFT:
- new.origin.x = 0;
- new.origin.y = 0;
- new.size.w = old->size.w;
- new.size.h = resolution.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
- new.origin.x = resolution.w - old->size.w;
- new.origin.y = 0;
- new.size.w = old->size.w;
- new.size.h = resolution.h;
- break;
- }
-
- return new;
-}
-
-static void update_panel_geometry(struct panel_config *config) {
- struct wlc_geometry geometry = compute_panel_geometry(config);
- wlc_view_set_geometry(config->handle, 0, &geometry);
-}
-
-static void update_panel_geometries(wlc_handle output) {
- for (int i = 0; i < desktop_shell.panels->length; i++) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->output == output) {
- update_panel_geometry(config);
- }
- }
-}
-
-static void update_background_geometry(struct background_config *config) {
- struct wlc_geometry geometry = wlc_geometry_zero;
- output_get_scaled_size(config->output, &geometry.size);
- wlc_view_set_geometry(config->handle, 0, &geometry);
-}
-
-static void update_background_geometries(wlc_handle output) {
- for (int i = 0; i < desktop_shell.backgrounds->length; i++) {
- struct background_config *config = desktop_shell.backgrounds->items[i];
- if (config->output == output) {
- update_background_geometry(config);
- }
- }
-}
-
-/* Handles */
-
-static bool handle_input_created(struct libinput_device *device) {
- const char *identifier = libinput_dev_unique_id(device);
- if (!identifier) {
- sway_log(L_ERROR, "Unable to allocate unique name for input device %p",
- device);
- return true;
- }
- sway_log(L_INFO, "Found input device (%s)", identifier);
-
- list_add(input_devices, device);
-
- struct input_config *ic = NULL;
- int i;
- for (i = 0; i < config->input_configs->length; ++i) {
- struct input_config *cur = config->input_configs->items[i];
- if (strcasecmp(identifier, cur->identifier) == 0) {
- sway_log(L_DEBUG, "Matched input config for %s",
- identifier);
- ic = cur;
- break;
- }
- if (strcasecmp("*", cur->identifier) == 0) {
- sway_log(L_DEBUG, "Matched wildcard input config for %s",
- identifier);
- ic = cur;
- break;
- }
- }
-
- apply_input_config(ic, device);
- return true;
-}
-
-static void handle_input_destroyed(struct libinput_device *device) {
- int i;
- list_t *list = input_devices;
- for (i = 0; i < list->length; ++i) {
- if(((struct libinput_device *)list->items[i]) == device) {
- list_del(list, i);
- break;
- }
- }
-}
-
-static bool handle_output_created(wlc_handle output) {
- swayc_t *op = new_output(output);
-
- // Visibility mask to be able to make view invisible
- wlc_output_set_mask(output, VISIBLE);
-
- if (!op) {
- return false;
- }
-
- // Switch to workspace if we need to
- if (swayc_active_workspace() == NULL) {
- swayc_t *ws = op->children->items[0];
- workspace_switch(ws);
- }
-
- // Fixes issues with backgrounds and wlc
- wlc_handle prev = wlc_get_focused_output();
- wlc_output_focus(output);
- wlc_output_focus(prev);
- return true;
-}
-
-static void handle_output_destroyed(wlc_handle output) {
- int i;
- list_t *list = root_container.children;
- for (i = 0; i < list->length; ++i) {
- if (((swayc_t *)list->items[i])->handle == output) {
- break;
- }
- }
- if (i < list->length) {
- destroy_output(list->items[i]);
- } else {
- return;
- }
- if (list->length > 0) {
- // switch to other outputs active workspace
- workspace_switch(((swayc_t *)root_container.children->items[0])->focused);
- }
-}
-
-static void handle_output_post_render(wlc_handle output) {
- ipc_get_pixels(output);
-}
-
-static void handle_view_pre_render(wlc_handle view) {
- render_view_borders(view);
-}
-
-static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) {
- sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h);
-
- swayc_t *c = swayc_by_handle(output);
- if (!c) {
- return;
- }
- c->width = to->w;
- c->height = to->h;
-
- update_panel_geometries(output);
- update_background_geometries(output);
-
- arrange_windows(&root_container, -1, -1);
-}
-
-static void handle_output_focused(wlc_handle output, bool focus) {
- swayc_t *c = swayc_by_handle(output);
- // if for some reason this output doesn't exist, create it.
- if (!c) {
- handle_output_created(output);
- }
- if (focus) {
- set_focused_container(get_focused_container(c));
- }
-}
-
-static void ws_cleanup() {
- swayc_t *op, *ws;
- int i = 0, j;
- if (!root_container.children)
- return;
- while (i < root_container.children->length) {
- op = root_container.children->items[i++];
- if (!op->children)
- continue;
- j = 0;
- while (j < op->children->length) {
- ws = op->children->items[j++];
- if (ws->children->length == 0 && ws->floating->length == 0 && ws != op->focused) {
- if (destroy_workspace(ws)) {
- j--;
- }
- }
- }
- }
-}
-
-static void positioner_place_window(wlc_handle handle) {
- const struct wlc_geometry *anchor = wlc_view_positioner_get_anchor_rect(handle);
- const struct wlc_size *sr = wlc_view_positioner_get_size(handle);
- // a positioner is required to have a non-null anchor and a non-negative size
- if (!anchor || !sr ||
- sr->w <= 0 || sr->h <= 0 ||
- anchor->size.w <= 0 || anchor->size.h <= 0) {
- return;
- }
- const struct wlc_point offset = *wlc_view_positioner_get_offset(handle);
- enum wlc_positioner_anchor_bit anchors = wlc_view_positioner_get_anchor(handle);
- enum wlc_positioner_gravity_bit gravity = wlc_view_positioner_get_gravity(handle);
- struct wlc_geometry geo = {
- .origin = offset,
- .size = *sr
- };
-
- if (anchors & WLC_BIT_ANCHOR_TOP) {
- geo.origin.y += anchor->origin.y;
- } else if (anchors & WLC_BIT_ANCHOR_BOTTOM) {
- geo.origin.y += anchor->origin.y + anchor->size.h;
- } else {
- geo.origin.y += anchor->origin.y + anchor->size.h / 2;
- }
- if (anchors & WLC_BIT_ANCHOR_LEFT) {
- geo.origin.x += anchor->origin.x;
- } else if (anchors & WLC_BIT_ANCHOR_RIGHT) {
- geo.origin.x += anchor->origin.x + anchor->size.w;
- } else {
- geo.origin.x += anchor->origin.x + anchor->size.w / 2;
- }
-
- if (gravity & WLC_BIT_GRAVITY_TOP) {
- geo.origin.y -= geo.size.h;
- } else if (gravity & WLC_BIT_GRAVITY_BOTTOM) {
- /* default */
- } else {
- geo.origin.y -= geo.size.h / 2;
- }
- if (gravity & WLC_BIT_GRAVITY_LEFT) {
- geo.origin.x -= geo.size.w;
- } else if (gravity & WLC_BIT_GRAVITY_RIGHT) {
- /* default */
- } else {
- geo.origin.x -= geo.size.w / 2;
- }
-
- sway_log(L_DEBUG, "xdg-positioner: placing window %" PRIuPTR " "
- "sized (%u,%u) offset by (%d,%d), "
- "anchor rectangle sized (%u,%u) at (%d,%d), "
- "anchor edges: %s %s, gravity: %s %s",
- handle,
- sr->w, sr->h, offset.x, offset.y,
- anchor->size.w, anchor->size.h, anchor->origin.x, anchor->origin.y,
- anchors & WLC_BIT_ANCHOR_TOP ? "top" :
- (anchors & WLC_BIT_ANCHOR_BOTTOM ? "bottom" : "middle"),
- anchors & WLC_BIT_ANCHOR_LEFT ? "left" :
- (anchors & WLC_BIT_ANCHOR_RIGHT ? "right" : "center"),
- gravity & WLC_BIT_GRAVITY_TOP ? "top" :
- (gravity & WLC_BIT_GRAVITY_BOTTOM ? "bottom" : "middle"),
- gravity & WLC_BIT_GRAVITY_LEFT ? "left" :
- (gravity & WLC_BIT_GRAVITY_RIGHT ? "right" : "center"));
-
- wlc_handle parent = wlc_view_get_parent(handle);
- if (parent) {
- const struct wlc_geometry *pg = wlc_view_get_geometry(parent);
- geo.origin.x += pg->origin.x;
- geo.origin.y += pg->origin.y;
- }
- wlc_view_set_geometry(handle, 0, &geo);
-}
-
-static bool handle_view_created(wlc_handle handle) {
- // if view is child of another view, the use that as focused container
- wlc_handle parent = wlc_view_get_parent(handle);
- swayc_t *focused = NULL;
- swayc_t *newview = NULL;
- swayc_t *current_ws = swayc_active_workspace();
- bool return_to_workspace = false;
- struct wl_client *client = wlc_view_get_wl_client(handle);
- struct wl_resource *resource = wlc_surface_get_wl_resource(
- wlc_view_get_surface(handle));
- pid_t pid;
- struct panel_config *panel_config = NULL;
- struct background_config *background_config = NULL;
-
- panel_config = if_panel_find_config(client);
- if (panel_config) {
- panel_config->handle = handle;
- update_panel_geometry(panel_config);
- wlc_view_set_mask(handle, VISIBLE);
- wlc_view_set_output(handle, panel_config->output);
- wlc_view_bring_to_front(handle);
- arrange_windows(&root_container, -1, -1);
- return true;
- }
-
- background_config = if_background_find_config(client);
- if (background_config) {
- background_config->handle = handle;
- update_background_geometry(background_config);
- wlc_view_set_mask(handle, VISIBLE);
- wlc_view_set_output(handle, background_config->output);
- wlc_view_send_to_back(handle);
- return true;
- }
-
- // Get parent container, to add view in
- if (parent) {
- focused = swayc_by_handle(parent);
- }
-
- if (client) {
- pid = wlc_view_get_pid(handle);
-
- if (pid) {
- // using newview as a temp storage location here,
- // rather than adding yet another workspace var
- newview = workspace_for_pid(pid);
- if (newview) {
- focused = get_focused_container(newview);
- return_to_workspace = true;
- }
- newview = NULL;
- }
- }
-
- swayc_t *prev_focus = get_focused_container(&root_container);
-
- if (!focused || focused->type == C_OUTPUT) {
- focused = prev_focus;
- // Move focus from floating view
- if (focused->is_floating) {
- // To workspace if there are no children
- if (focused->parent->children->length == 0) {
- focused = focused->parent;
- }
- // TODO find a better way of doing this
- // Or to focused container
- else {
- focused = get_focused_container(focused->parent->children->items[0]);
- }
- }
- }
-
- positioner_place_window(handle);
-
- sway_log(L_DEBUG, "handle:%" PRIuPTR " type:%x state:%x parent:%" PRIuPTR " "
- "mask:%d (x:%d y:%d w:%d h:%d) title:%s "
- "class:%s appid:%s",
- handle, wlc_view_get_type(handle), wlc_view_get_state(handle), parent,
- wlc_view_get_mask(handle), wlc_view_get_geometry(handle)->origin.x,
- wlc_view_get_geometry(handle)->origin.y,wlc_view_get_geometry(handle)->size.w,
- wlc_view_get_geometry(handle)->size.h, wlc_view_get_title(handle),
- wlc_view_get_class(handle), wlc_view_get_app_id(handle));
-
- // TODO properly figure out how each window should be handled.
- switch (wlc_view_get_type(handle)) {
- // regular view created regularly
- case 0:
- if (parent) {
- newview = new_floating_view(handle);
- } else {
- newview = new_view(focused, handle);
- wlc_view_set_state(handle, WLC_BIT_MAXIMIZED, true);
- }
- break;
-
- // Dmenu keeps viewfocus, but others with this flag don't, for now simulate
- // dmenu
- case WLC_BIT_OVERRIDE_REDIRECT:
- wlc_view_focus(handle);
- wlc_view_set_state(handle, WLC_BIT_ACTIVATED, true);
- wlc_view_bring_to_front(handle);
- break;
-
- // Firefox popups have this flag set.
- case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
- wlc_view_bring_to_front(handle);
- locked_container_focus = true;
- break;
-
- // Modals, get focus, popups do not
- case WLC_BIT_MODAL:
- wlc_view_focus(handle);
- wlc_view_bring_to_front(handle);
- newview = new_floating_view(handle);
- /* fallthrough */
- case WLC_BIT_POPUP:
- wlc_view_bring_to_front(handle);
- break;
- }
-
- // Prevent current ws from being destroyed, if empty
- suspend_workspace_cleanup = true;
-
- if (newview) {
- ipc_event_window(newview, "new");
- set_focused_container(newview);
- wlc_view_set_mask(handle, VISIBLE);
- swayc_t *output = swayc_parent_by_type(newview, C_OUTPUT);
- arrange_windows(output, -1, -1);
- // check if it matches for_window in config and execute if so
- list_t *criteria = criteria_for(newview);
- for (int i = 0; i < criteria->length; i++) {
- struct criteria *crit = criteria->items[i];
- sway_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'",
- crit->crit_raw, newview, crit->cmdlist);
- struct cmd_results *res = handle_command(crit->cmdlist, CONTEXT_CRITERIA);
- if (res->status != CMD_SUCCESS) {
- sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
- }
- free_cmd_results(res);
- // view must be focused for commands to affect it, so always
- // refocus in-between command lists
- set_focused_container(newview);
- }
- swayc_t *workspace = swayc_parent_by_type(focused, C_WORKSPACE);
- if (workspace && workspace->fullscreen) {
- set_focused_container(workspace->fullscreen);
- }
- for (int i = 0; i < decoration_state.csd_resources->length; ++i) {
- struct wl_resource *res = decoration_state.csd_resources->items[i];
- if (res == resource) {
- list_del(decoration_state.csd_resources, i);
- server_decoration_enable_csd(handle);
- break;
- }
- }
- } else {
- swayc_t *output = swayc_parent_by_type(focused, C_OUTPUT);
- wlc_handle *h = malloc(sizeof(wlc_handle));
- if (!h) {
- sway_log(L_ERROR, "Unable to allocate window handle, view handler bailing out");
- return true;
- }
- *h = handle;
- sway_log(L_DEBUG, "Adding unmanaged window %p to %p", h, output->unmanaged);
- list_add(output->unmanaged, h);
- wlc_view_set_mask(handle, VISIBLE);
- }
-
- if (return_to_workspace && current_ws != swayc_active_workspace()) {
- // we were on one workspace, switched to another to add this view,
- // now let's return to where we were
- workspace_switch(current_ws);
- set_focused_container(get_focused_container(current_ws));
- }
- if (prev_focus && prev_focus->type == C_VIEW
- && newview && criteria_any(newview, config->no_focus)) {
- // Restore focus
- swayc_t *ws = swayc_parent_by_type(newview, C_WORKSPACE);
- if (!ws || ws != newview->parent
- || ws->children->length + ws->floating->length != 1) {
- sway_log(L_DEBUG, "no_focus: restoring focus to %s", prev_focus->name);
- set_focused_container(prev_focus);
- }
- }
-
- suspend_workspace_cleanup = false;
- ws_cleanup();
- return true;
-}
-
-static void handle_view_destroyed(wlc_handle handle) {
- sway_log(L_DEBUG, "Destroying window %" PRIuPTR, handle);
- swayc_t *view = swayc_by_handle(handle);
-
- // destroy views by type
- switch (wlc_view_get_type(handle)) {
- // regular view created regularly
- case 0:
- case WLC_BIT_MODAL:
- case WLC_BIT_POPUP:
- break;
- // DMENU has this flag, and takes view_focus, but other things with this
- // flag don't
- case WLC_BIT_OVERRIDE_REDIRECT:
- break;
- case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED:
- locked_container_focus = false;
- break;
- }
-
- if (view) {
- bool fullscreen = swayc_is_fullscreen(view);
- remove_view_from_scratchpad(view);
- swayc_t *parent = destroy_view(view), *iter = NULL;
- if (parent) {
- ipc_event_window(parent, "close");
-
- // Destroy empty workspaces
- if (parent->type == C_WORKSPACE &&
- parent->children->length == 0 &&
- parent->floating->length == 0 &&
- swayc_active_workspace() != parent &&
- !parent->visible) {
- parent = destroy_workspace(parent);
- }
-
- if (fullscreen) {
- iter = parent;
- while (iter) {
- if (iter->fullscreen) {
- iter->fullscreen = NULL;
- break;
- }
- iter = iter->parent;
- }
- }
-
-
- arrange_windows(iter ? iter : parent, -1, -1);
- }
- } else {
- // Is it unmanaged?
- int i;
- for (i = 0; i < root_container.children->length; ++i) {
- swayc_t *output = root_container.children->items[i];
- int j;
- for (j = 0; j < output->unmanaged->length; ++j) {
- wlc_handle *_handle = output->unmanaged->items[j];
- if (*_handle == handle) {
- list_del(output->unmanaged, j);
- free(_handle);
- break;
- }
- }
- }
- // Is it in the scratchpad?
- for (i = 0; i < scratchpad->length; ++i) {
- swayc_t *item = scratchpad->items[i];
- if (item->handle == handle) {
- list_del(scratchpad, i);
- destroy_view(item);
- break;
- }
- }
- }
- set_focused_container(get_focused_view(&root_container));
-}
-
-static void handle_view_focus(wlc_handle view, bool focus) {
- return;
-}
-
-static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geometry *geometry) {
- sway_log(L_DEBUG, "geometry request for %" PRIuPTR " %dx%d @ %d,%d", handle,
- geometry->size.w, geometry->size.h, geometry->origin.x, geometry->origin.y);
- // If the view is floating, then apply the geometry.
- // Otherwise save the desired width/height for the view.
- // This will not do anything for the time being as WLC improperly sends geometry requests
- swayc_t *view = swayc_by_handle(handle);
- if (view) {
- view->desired_width = geometry->size.w;
- view->desired_height = geometry->size.h;
-
- if (view->is_floating) {
- floating_view_sane_size(view);
- view->width = view->desired_width;
- view->height = view->desired_height;
- view->x = geometry->origin.x;
- view->y = geometry->origin.y;
- update_geometry(view);
- }
- } else {
- wlc_view_set_geometry(handle, 0, geometry);
- }
-}
-
-static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) {
- swayc_t *c = swayc_by_handle(view);
- pid_t pid = wlc_view_get_pid(view);
- switch (state) {
- case WLC_BIT_FULLSCREEN:
- if (!(get_feature_policy_mask(pid) & FEATURE_FULLSCREEN)) {
- sway_log(L_INFO, "Denying fullscreen to %d (%s)", pid, c->name);
- break;
- }
- // i3 just lets it become fullscreen
- wlc_view_set_state(view, state, toggle);
- if (c) {
- sway_log(L_DEBUG, "setting view %" PRIuPTR " %s, fullscreen %d", view, c->name, toggle);
- arrange_windows(c->parent, -1, -1);
- // Set it as focused window for that workspace if its going fullscreen
- swayc_t *ws = swayc_parent_by_type(c, C_WORKSPACE);
- if (toggle) {
- // Set ws focus to c
- set_focused_container_for(ws, c);
- ws->fullscreen = c;
- } else {
- ws->fullscreen = NULL;
- }
- }
- break;
- case WLC_BIT_MAXIMIZED:
- case WLC_BIT_RESIZING:
- case WLC_BIT_MOVING:
- break;
- case WLC_BIT_ACTIVATED:
- sway_log(L_DEBUG, "View %p requested to be activated", c);
- break;
- }
- return;
-}
-
-static void handle_view_properties_updated(wlc_handle view, uint32_t mask) {
- if (mask == WLC_BIT_PROPERTY_TITLE) {
- swayc_t *c = swayc_by_handle(view);
- if (!c) {
- return;
- }
-
- // update window title
- const char *new_name = wlc_view_get_title(view);
-
- if (new_name) {
- if (!c->name || strcmp(c->name, new_name) != 0) {
- free(c->name);
- c->name = strdup(new_name);
- swayc_t *p = swayc_tabbed_stacked_ancestor(c);
- if (p) {
- // TODO: we only got the topmost tabbed/stacked container, update borders of all containers on the path
- update_container_border(get_focused_view(p));
- } else if (c->border_type == B_NORMAL) {
- update_container_border(c);
- }
- ipc_event_window(c, "title");
- }
- }
- }
-}
-
-static void handle_binding_command(struct sway_binding *binding) {
- struct sway_binding *binding_copy = binding;
- bool reload = false;
- // if this is a reload command we need to make a duplicate of the
- // binding since it will be gone after the reload has completed.
- if (strcasecmp(binding->command, "reload") == 0) {
- binding_copy = sway_binding_dup(binding);
- if (!binding_copy) {
- sway_log(L_ERROR, "Unable to duplicate binding during reload");
- return;
- }
- reload = true;
- }
-
- struct cmd_results *res = handle_command(binding->command, CONTEXT_BINDING);
- if (res->status != CMD_SUCCESS) {
- sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
- }
- ipc_event_binding_keyboard(binding_copy);
-
- if (reload) { // free the binding if we made a copy
- free_sway_binding(binding_copy);
- }
-
- free_cmd_results(res);
-}
-
-static bool handle_bindsym(struct sway_binding *binding, uint32_t keysym, uint32_t keycode) {
- int i;
- for (i = 0; i < binding->keys->length; ++i) {
- if (binding->bindcode) {
- xkb_keycode_t *key = binding->keys->items[i];
- if (keycode == *key) {
- handle_binding_command(binding);
- return true;
- }
- } else {
- xkb_keysym_t *key = binding->keys->items[i];
- if (keysym == *key) {
- handle_binding_command(binding);
- return true;
- }
- }
- }
-
- return false;
-}
-
-static bool valid_bindsym(struct sway_binding *binding) {
- bool match = false;
- int i;
- for (i = 0; i < binding->keys->length; ++i) {
- if (binding->bindcode) {
- xkb_keycode_t *key = binding->keys->items[i];
- if ((match = check_key(0, *key)) == false) {
- break;
- }
- } else {
- xkb_keysym_t *key = binding->keys->items[i];
- if ((match = check_key(*key, 0)) == false) {
- break;
- }
- }
- }
-
- return match;
-}
-
-static bool handle_bindsym_release(struct sway_binding *binding) {
- if (binding->keys->length == 1) {
- xkb_keysym_t *key = binding->keys->items[0];
- if (check_released_key(*key)) {
- handle_binding_command(binding);
- return true;
- }
- }
-
- return false;
-}
-
-static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
- uint32_t key, enum wlc_key_state state) {
-
- if (desktop_shell.is_locked) {
- return EVENT_PASSTHROUGH;
- }
-
- // reset pointer mode on keypress
- if (state == WLC_KEY_STATE_PRESSED && pointer_state.mode) {
- pointer_mode_reset();
- }
-
- struct sway_mode *mode = config->current_mode;
-
- struct wlc_modifiers no_mods = { 0, 0 };
- uint32_t sym = tolower(wlc_keyboard_get_keysym_for_key(key, &no_mods));
-
- int i;
-
- if (state == WLC_KEY_STATE_PRESSED) {
- press_key(sym, key);
- } else { // WLC_KEY_STATE_RELEASED
- release_key(sym, key);
- }
-
- // handle bar modifiers pressed/released
- uint32_t modifier;
- for (i = 0; i < config->active_bar_modifiers->length; ++i) {
- modifier = *(uint32_t *)config->active_bar_modifiers->items[i];
-
- switch (modifier_state_changed(modifiers->mods, modifier)) {
- case MOD_STATE_PRESSED:
- ipc_event_modifier(modifier, "pressed");
- break;
- case MOD_STATE_RELEASED:
- ipc_event_modifier(modifier, "released");
- break;
- }
- }
- // update modifiers state
- modifiers_state_update(modifiers->mods);
-
- // handle bindings
- list_t *candidates = create_list();
- for (i = 0; i < mode->bindings->length; ++i) {
- struct sway_binding *binding = mode->bindings->items[i];
- if ((modifiers->mods ^ binding->modifiers) == 0) {
- switch (state) {
- case WLC_KEY_STATE_PRESSED:
- if (!binding->release && valid_bindsym(binding)) {
- list_add(candidates, binding);
- }
- break;
- case WLC_KEY_STATE_RELEASED:
- if (binding->release && handle_bindsym_release(binding)) {
- list_free(candidates);
- return EVENT_HANDLED;
- }
- break;
- }
- }
- }
-
- for (i = 0; i < candidates->length; ++i) {
- struct sway_binding *binding = candidates->items[i];
- if (state == WLC_KEY_STATE_PRESSED) {
- if (!binding->release && handle_bindsym(binding, sym, key)) {
- list_free(candidates);
- return EVENT_HANDLED;
- }
- }
- }
-
- list_free(candidates);
-
- swayc_t *focused = get_focused_container(&root_container);
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_KEYBOARD)) {
- return EVENT_HANDLED;
- }
- }
- return EVENT_PASSTHROUGH;
-}
-
-static bool handle_pointer_motion(wlc_handle handle, uint32_t time, double x, double y) {
- if (desktop_shell.is_locked) {
- return EVENT_PASSTHROUGH;
- }
-
- double new_x = x;
- double new_y = y;
- // Switch to adjacent output if touching output edge.
- //
- // Since this doesn't currently support moving windows between outputs we
- // don't do the switch if the pointer is in a mode.
- if (config->seamless_mouse && !pointer_state.mode &&
- !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) {
-
- swayc_t *output = swayc_active_output(), *adjacent = NULL;
- struct wlc_point abs_pos = { .x = x + output->x, .y = y + output->y };
- if (x <= 0) { // Left edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_x = adjacent->width;
- // adjust for differently aligned outputs (well, this is
- // only correct when the two outputs have the same
- // resolution or the same dpi I guess, it should take
- // physical attributes into account)
- new_y += (output->y - adjacent->y);
- }
- }
- } else if (x >= output->width) { // Right edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_x = 0;
- new_y += (output->y - adjacent->y);
- }
- }
- } else if (y <= 0) { // Top edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_y = adjacent->height;
- new_x += (output->x - adjacent->x);
- }
- }
- } else if (y >= output->height) { // Bottom edge
- if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) {
- if (workspace_switch(swayc_active_workspace_for(adjacent))) {
- new_y = 0;
- new_x += (output->x - adjacent->x);
- }
- }
- }
- }
-
- pointer_position_set(new_x, new_y, false);
-
- swayc_t *focused = get_focused_container(&root_container);
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
- return EVENT_HANDLED;
- }
- }
-
- return EVENT_PASSTHROUGH;
-}
-
-static bool swayc_border_check(swayc_t *c, const void *_origin) {
- const struct wlc_point *origin = _origin;
- const struct wlc_geometry title_bar = c->title_bar_geometry;
-
- if (c->border_type != B_NORMAL) {
- return false;
- }
-
- if (origin->x >= title_bar.origin.x && origin->y >= title_bar.origin.y
- && origin->x < title_bar.origin.x + (int32_t)title_bar.size.w
- && origin->y < title_bar.origin.y + (int32_t)title_bar.size.h) {
- return true;
- }
- return false;
-}
-
-static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers,
- uint32_t button, enum wlc_button_state state, const struct wlc_point *origin) {
-
- // Update view pointer is on
- pointer_state.view = container_under_pointer();
-
- struct sway_mode *mode = config->current_mode;
- // handle bindings
- for (int i = 0; i < mode->bindings->length; ++i) {
- struct sway_binding *binding = mode->bindings->items[i];
- if ((modifiers->mods ^ binding->modifiers) == 0) {
- switch (state) {
- case WLC_BUTTON_STATE_PRESSED:
- if (!binding->release && handle_bindsym(binding, button, 0)) {
- return EVENT_HANDLED;
- }
- break;
- case WLC_BUTTON_STATE_RELEASED:
- if (binding->release && handle_bindsym(binding, button, 0)) {
- return EVENT_HANDLED;
- }
- break;
- }
- }
- }
-
- // Update pointer_state
- switch (button) {
- case M_LEFT_CLICK:
- if (state == WLC_BUTTON_STATE_PRESSED) {
- pointer_state.left.held = true;
- pointer_state.left.x = origin->x;
- pointer_state.left.y = origin->y;
- pointer_state.left.view = pointer_state.view;
- } else {
- pointer_state.left.held = false;
- }
- break;
-
- case M_RIGHT_CLICK:
- if (state == WLC_BUTTON_STATE_PRESSED) {
- pointer_state.right.held = true;
- pointer_state.right.x = origin->x;
- pointer_state.right.y = origin->y;
- pointer_state.right.view = pointer_state.view;
- } else {
- pointer_state.right.held = false;
- }
- break;
-
- case M_SCROLL_CLICK:
- if (state == WLC_BUTTON_STATE_PRESSED) {
- pointer_state.scroll.held = true;
- pointer_state.scroll.x = origin->x;
- pointer_state.scroll.y = origin->y;
- pointer_state.scroll.view = pointer_state.view;
- } else {
- pointer_state.scroll.held = false;
- }
- break;
-
- //TODO scrolling behavior
- case M_SCROLL_UP:
- case M_SCROLL_DOWN:
- break;
- }
-
- // get focused window and check if to change focus on mouse click
- swayc_t *focused = get_focused_container(&root_container);
-
- // don't change focus or mode if fullscreen
- if (swayc_is_fullscreen(focused)) {
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
- return EVENT_HANDLED;
- }
- }
- return EVENT_PASSTHROUGH;
- }
-
- // set pointer mode only if floating mod has been set
- if (config->floating_mod) {
- pointer_mode_set(button, !(modifiers->mods ^ config->floating_mod));
- }
-
- // Check whether to change focus
- swayc_t *pointer = pointer_state.view;
- if (pointer) {
- swayc_t *ws = swayc_parent_by_type(focused, C_WORKSPACE);
- if (ws != NULL) {
- swayc_t *find = container_find(ws, &swayc_border_check, origin);
- if (find != NULL) {
- set_focused_container(find);
- return EVENT_HANDLED;
- }
- }
-
- if (focused != pointer) {
- set_focused_container(pointer_state.view);
- }
- // Send to front if floating
- if (pointer->is_floating) {
- int i;
- for (i = 0; i < pointer->parent->floating->length; i++) {
- if (pointer->parent->floating->items[i] == pointer) {
- list_del(pointer->parent->floating, i);
- list_add(pointer->parent->floating, pointer);
- break;
- }
- }
- wlc_view_bring_to_front(pointer->handle);
- }
- }
-
- // Return if mode has been set
- if (pointer_state.mode) {
- return EVENT_HANDLED;
- }
-
- if (focused->type == C_VIEW) {
- pid_t pid = wlc_view_get_pid(focused->handle);
- if (!(get_feature_policy_mask(pid) & FEATURE_MOUSE)) {
- return EVENT_HANDLED;
- }
- }
-
- // Always send mouse release
- if (state == WLC_BUTTON_STATE_RELEASED) {
- return EVENT_PASSTHROUGH;
- }
-
- // Finally send click
- return EVENT_PASSTHROUGH;
-}
-
-bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modifiers* modifiers,
- uint8_t axis_bits, double _amount[2]) {
- if (!(modifiers->mods ^ config->floating_mod)) {
- int x_amount = (int)_amount[0];
- int y_amount = (int)_amount[1];
-
- if (x_amount > 0 && strcmp(config->floating_scroll_up_cmd, "")) {
- handle_command(config->floating_scroll_up_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- } else if (x_amount < 0 && strcmp(config->floating_scroll_down_cmd, "")) {
- handle_command(config->floating_scroll_down_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- }
-
- if (y_amount > 0 && strcmp(config->floating_scroll_right_cmd, "")) {
- handle_command(config->floating_scroll_right_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- } else if (y_amount < 0 && strcmp(config->floating_scroll_left_cmd, "")) {
- handle_command(config->floating_scroll_left_cmd, CONTEXT_BINDING);
- return EVENT_HANDLED;
- }
- }
- return EVENT_PASSTHROUGH;
-}
-
-static void handle_wlc_ready(void) {
- sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
- // Execute commands until there are none left
- config->active = true;
- while (config->cmd_queue->length) {
- char *line = config->cmd_queue->items[0];
- struct cmd_results *res = handle_command(line, CONTEXT_CONFIG);
- if (res->status != CMD_SUCCESS) {
- sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
- }
- free_cmd_results(res);
- free(line);
- list_del(config->cmd_queue, 0);
- }
-}
-
-void register_wlc_handlers() {
- wlc_set_output_created_cb(handle_output_created);
- wlc_set_output_destroyed_cb(handle_output_destroyed);
- wlc_set_output_resolution_cb(handle_output_resolution_change);
- wlc_set_output_focus_cb(handle_output_focused);
- wlc_set_output_render_post_cb(handle_output_post_render);
- wlc_set_view_created_cb(handle_view_created);
- wlc_set_view_destroyed_cb(handle_view_destroyed);
- wlc_set_view_focus_cb(handle_view_focus);
- wlc_set_view_render_pre_cb(handle_view_pre_render);
- wlc_set_view_request_geometry_cb(handle_view_geometry_request);
- wlc_set_view_request_state_cb(handle_view_state_request);
- wlc_set_view_properties_updated_cb(handle_view_properties_updated);
- wlc_set_keyboard_key_cb(handle_key);
- wlc_set_pointer_motion_cb_v2(handle_pointer_motion);
- wlc_set_pointer_button_cb(handle_pointer_button);
- wlc_set_pointer_scroll_cb(handle_pointer_scroll);
- wlc_set_compositor_ready_cb(handle_wlc_ready);
- wlc_set_input_created_cb(handle_input_created);
- wlc_set_input_destroyed_cb(handle_input_destroyed);
-}
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 @@
-#define _XOPEN_SOURCE 700
-#include <ctype.h>
-#include <float.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <libinput.h>
-#include "sway/config.h"
-#include "sway/input.h"
-#include "list.h"
-#include "log.h"
-
-struct input_config *new_input_config(const char* identifier) {
- struct input_config *input = calloc(1, sizeof(struct input_config));
- if (!input) {
- sway_log(L_DEBUG, "Unable to allocate input config");
- return NULL;
- }
- sway_log(L_DEBUG, "new_input_config(%s)", identifier);
- if (!(input->identifier = strdup(identifier))) {
- free(input);
- sway_log(L_DEBUG, "Unable to allocate input config");
- return NULL;
- }
-
- input->tap = INT_MIN;
- input->drag_lock = INT_MIN;
- input->dwt = INT_MIN;
- input->send_events = INT_MIN;
- input->click_method = INT_MIN;
- input->middle_emulation = INT_MIN;
- input->natural_scroll = INT_MIN;
- input->accel_profile = INT_MIN;
- input->pointer_accel = FLT_MIN;
- input->scroll_method = INT_MIN;
- input->left_handed = INT_MIN;
-
- return input;
-}
-
-char *libinput_dev_unique_id(struct libinput_device *device) {
- int vendor = libinput_device_get_id_vendor(device);
- int product = libinput_device_get_id_product(device);
- char *name = strdup(libinput_device_get_name(device));
-
- char *p = name;
- for (; *p; ++p) {
- if (*p == ' ') {
- *p = '_';
- }
- }
-
- sway_log(L_DEBUG, "rewritten name %s", name);
-
- int len = strlen(name) + sizeof(char) * 6;
- char *identifier = malloc(len);
- if (!identifier) {
- sway_log(L_ERROR, "Unable to allocate unique input device name");
- return NULL;
- }
-
- const char *fmt = "%d:%d:%s";
- snprintf(identifier, len, fmt, vendor, product, name);
- free(name);
- return identifier;
-}
-
-list_t *input_devices = NULL;
-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 @@
+#define _XOPEN_SOURCE 700
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include "list.h"
+#include "log.h"
+#include "sway/input/cursor.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/tree/view.h"
+#include "wlr-layer-shell-unstable-v1-protocol.h"
+
+static struct wlr_surface *layer_surface_at(struct sway_output *output,
+ struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
+ struct sway_layer_surface *sway_layer;
+ wl_list_for_each_reverse(sway_layer, layer, link) {
+ struct wlr_surface *wlr_surface =
+ sway_layer->layer_surface->surface;
+ double _sx = ox - sway_layer->geo.x;
+ double _sy = oy - sway_layer->geo.y;
+ // TODO: Test popups/subsurfaces
+ if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) {
+ *sx = _sx;
+ *sy = _sy;
+ return wlr_surface;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Returns the container at the cursor's position. If there is a surface at that
+ * location, it is stored in **surface (it may not be a view).
+ */
+static struct sway_container *container_at_cursor(struct sway_cursor *cursor,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ // check for unmanaged views first
+ struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
+ struct sway_xwayland_unmanaged *unmanaged_surface;
+ wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
+ struct wlr_xwayland_surface *xsurface =
+ unmanaged_surface->wlr_xwayland_surface;
+
+ double _sx = cursor->cursor->x - unmanaged_surface->lx;
+ double _sy = cursor->cursor->y - unmanaged_surface->ly;
+ if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) {
+ *surface = xsurface->surface;
+ *sx = _sx;
+ *sy = _sy;
+ return NULL;
+ }
+ }
+
+ // find the output the cursor is on
+ struct wlr_output_layout *output_layout =
+ root_container.sway_root->output_layout;
+ struct wlr_output *wlr_output =
+ wlr_output_layout_output_at(output_layout,
+ cursor->cursor->x, cursor->cursor->y);
+ if (wlr_output == NULL) {
+ return NULL;
+ }
+ struct sway_output *output = wlr_output->data;
+ double ox = cursor->cursor->x, oy = cursor->cursor->y;
+ wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy);
+
+ // find the focused workspace on the output for this seat
+ struct sway_container *ws =
+ seat_get_focus_inactive(cursor->seat, output->swayc);
+ if (ws && ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+ if (!ws) {
+ return output->swayc;
+ }
+
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+
+ struct sway_container *c;
+ if ((c = container_at(ws, cursor->cursor->x, cursor->cursor->y,
+ surface, sx, sy))) {
+ return c;
+ }
+
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
+ ox, oy, sx, sy))) {
+ return ws;
+ }
+
+ c = seat_get_focus_inactive(cursor->seat, output->swayc);
+ if (c) {
+ return c;
+ }
+ if (!c && output->swayc->children->length) {
+ c = output->swayc->children->items[0];
+ return c;
+ }
+
+ return output->swayc;
+}
+
+void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time) {
+ struct wlr_seat *seat = cursor->seat->wlr_seat;
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+ struct sway_container *c = container_at_cursor(cursor, &surface, &sx, &sy);
+ if (c && config->focus_follows_mouse) {
+ seat_set_focus_warp(cursor->seat, c, false);
+ }
+
+ // reset cursor if switching between clients
+ struct wl_client *client = NULL;
+ if (surface != NULL) {
+ client = wl_resource_get_client(surface->resource);
+ }
+ if (client != cursor->image_client) {
+ wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
+ "left_ptr", cursor->cursor);
+ cursor->image_client = client;
+ }
+
+ // send pointer enter/leave
+ if (surface != NULL) {
+ if (seat_is_input_allowed(cursor->seat, surface)) {
+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+ }
+ } else {
+ wlr_seat_pointer_clear_focus(seat);
+ }
+}
+
+static void handle_cursor_motion(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
+ struct wlr_event_pointer_motion *event = data;
+ wlr_cursor_move(cursor->cursor, event->device,
+ event->delta_x, event->delta_y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+}
+
+static void handle_cursor_motion_absolute(
+ struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor =
+ wl_container_of(listener, cursor, motion_absolute);
+ struct wlr_event_pointer_motion_absolute *event = data;
+ wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+}
+
+void dispatch_cursor_button(struct sway_cursor *cursor,
+ uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+ struct sway_container *cont =
+ container_at_cursor(cursor, &surface, &sx, &sy);
+ if (surface && wlr_surface_is_layer_surface(surface)) {
+ struct wlr_layer_surface *layer =
+ wlr_layer_surface_from_wlr_surface(surface);
+ if (layer->current.keyboard_interactive) {
+ seat_set_focus_layer(cursor->seat, layer);
+ return;
+ }
+ }
+ // Avoid moving keyboard focus from a surface that accepts it to one
+ // that does not unless the change would move us to a new workspace.
+ //
+ // This prevents, for example, losing focus when clicking on swaybar.
+ if (surface && cont && cont->type != C_VIEW) {
+ struct sway_container *new_ws = cont;
+ if (new_ws && new_ws->type != C_WORKSPACE) {
+ new_ws = container_parent(new_ws, C_WORKSPACE);
+ }
+ struct sway_container *old_ws = seat_get_focus(cursor->seat);
+ if (old_ws && old_ws->type != C_WORKSPACE) {
+ old_ws = container_parent(old_ws, C_WORKSPACE);
+ }
+ if (new_ws != old_ws) {
+ seat_set_focus(cursor->seat, cont);
+ }
+ } else {
+ seat_set_focus(cursor->seat, cont);
+ }
+
+ wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
+ time_msec, button, state);
+}
+
+static void handle_cursor_button(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
+ struct wlr_event_pointer_button *event = data;
+ dispatch_cursor_button(cursor,
+ event->time_msec, event->button, event->state);
+}
+
+static void handle_cursor_axis(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
+ struct wlr_event_pointer_axis *event = data;
+ wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
+ event->orientation, event->delta);
+}
+
+static void handle_touch_down(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
+ struct wlr_event_touch_down *event = data;
+ wlr_log(L_DEBUG, "TODO: handle touch down event: %p", event);
+}
+
+static void handle_touch_up(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
+ struct wlr_event_touch_up *event = data;
+ wlr_log(L_DEBUG, "TODO: handle touch up event: %p", event);
+}
+
+static void handle_touch_motion(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor =
+ wl_container_of(listener, cursor, touch_motion);
+ struct wlr_event_touch_motion *event = data;
+ wlr_log(L_DEBUG, "TODO: handle touch motion event: %p", event);
+}
+
+static void handle_tool_axis(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
+ struct wlr_event_tablet_tool_axis *event = data;
+
+ if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
+ (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
+ wlr_cursor_warp_absolute(cursor->cursor, event->device,
+ event->x, event->y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+ } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
+ wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+ } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
+ wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
+ cursor_send_pointer_motion(cursor, event->time_msec);
+ }
+}
+
+static void handle_tool_tip(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
+ struct wlr_event_tablet_tool_tip *event = data;
+ dispatch_cursor_button(cursor, event->time_msec,
+ BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
+ WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED);
+}
+
+static void handle_tool_button(struct wl_listener *listener, void *data) {
+ struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
+ struct wlr_event_tablet_tool_button *event = data;
+ // TODO: the user may want to configure which tool buttons are mapped to
+ // which simulated pointer buttons
+ switch (event->state) {
+ case WLR_BUTTON_PRESSED:
+ if (cursor->tool_buttons == 0) {
+ dispatch_cursor_button(cursor,
+ event->time_msec, BTN_RIGHT, event->state);
+ }
+ cursor->tool_buttons++;
+ break;
+ case WLR_BUTTON_RELEASED:
+ if (cursor->tool_buttons == 1) {
+ dispatch_cursor_button(cursor,
+ event->time_msec, BTN_RIGHT, event->state);
+ }
+ cursor->tool_buttons--;
+ break;
+ }
+}
+
+static void handle_request_set_cursor(struct wl_listener *listener,
+ void *data) {
+ struct sway_cursor *cursor =
+ wl_container_of(listener, cursor, request_set_cursor);
+ struct wlr_seat_pointer_request_set_cursor_event *event = data;
+
+ struct wl_client *focused_client = NULL;
+ struct wlr_surface *focused_surface =
+ cursor->seat->wlr_seat->pointer_state.focused_surface;
+ if (focused_surface != NULL) {
+ focused_client = wl_resource_get_client(focused_surface->resource);
+ }
+
+ // TODO: check cursor mode
+ if (focused_client == NULL ||
+ event->seat_client->client != focused_client) {
+ wlr_log(L_DEBUG, "denying request to set cursor from unfocused client");
+ return;
+ }
+
+ wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
+ event->hotspot_y);
+ cursor->image_client = focused_client;
+}
+
+void sway_cursor_destroy(struct sway_cursor *cursor) {
+ if (!cursor) {
+ return;
+ }
+
+ wlr_xcursor_manager_destroy(cursor->xcursor_manager);
+ wlr_cursor_destroy(cursor->cursor);
+ free(cursor);
+}
+
+struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
+ struct sway_cursor *cursor = calloc(1, sizeof(struct sway_cursor));
+ if (!sway_assert(cursor, "could not allocate sway cursor")) {
+ return NULL;
+ }
+
+ struct wlr_cursor *wlr_cursor = wlr_cursor_create();
+ if (!sway_assert(wlr_cursor, "could not allocate wlr cursor")) {
+ free(cursor);
+ return NULL;
+ }
+
+ cursor->seat = seat;
+ wlr_cursor_attach_output_layout(wlr_cursor,
+ root_container.sway_root->output_layout);
+
+ // input events
+ wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
+ cursor->motion.notify = handle_cursor_motion;
+
+ wl_signal_add(&wlr_cursor->events.motion_absolute,
+ &cursor->motion_absolute);
+ cursor->motion_absolute.notify = handle_cursor_motion_absolute;
+
+ wl_signal_add(&wlr_cursor->events.button, &cursor->button);
+ cursor->button.notify = handle_cursor_button;
+
+ wl_signal_add(&wlr_cursor->events.axis, &cursor->axis);
+ cursor->axis.notify = handle_cursor_axis;
+
+ wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down);
+ cursor->touch_down.notify = handle_touch_down;
+
+ wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
+ cursor->touch_up.notify = handle_touch_up;
+
+ wl_signal_add(&wlr_cursor->events.touch_motion,
+ &cursor->touch_motion);
+ cursor->touch_motion.notify = handle_touch_motion;
+
+ // TODO: tablet protocol support
+ // Note: We should emulate pointer events for clients that don't support the
+ // tablet protocol when the time comes
+ wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
+ &cursor->tool_axis);
+ cursor->tool_axis.notify = handle_tool_axis;
+
+ wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);
+ cursor->tool_tip.notify = handle_tool_tip;
+
+ wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button);
+ cursor->tool_button.notify = handle_tool_button;
+
+ wl_signal_add(&seat->wlr_seat->events.request_set_cursor,
+ &cursor->request_set_cursor);
+ cursor->request_set_cursor.notify = handle_request_set_cursor;
+
+ cursor->cursor = wlr_cursor;
+
+ return cursor;
+}
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 @@
+#define _XOPEN_SOURCE 700
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <libinput.h>
+#include <math.h>
+#include <wlr/backend/libinput.h>
+#include <wlr/types/wlr_input_inhibitor.h>
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/server.h"
+#include "stringop.h"
+#include "list.h"
+#include "log.h"
+
+static const char *default_seat = "seat0";
+
+// TODO make me not global
+struct sway_input_manager *input_manager;
+
+struct input_config *current_input_config = NULL;
+struct seat_config *current_seat_config = NULL;
+
+struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) {
+ struct sway_seat *seat = config->handler_context.seat;
+ if (!seat) {
+ seat = input_manager_get_default_seat(input_manager);
+ }
+ return seat;
+}
+
+struct sway_seat *input_manager_get_seat(
+ struct sway_input_manager *input, const char *seat_name) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ if (strcmp(seat->wlr_seat->name, seat_name) == 0) {
+ return seat;
+ }
+ }
+
+ return seat_create(input, seat_name);
+}
+
+static char *get_device_identifier(struct wlr_input_device *device) {
+ int vendor = device->vendor;
+ int product = device->product;
+ char *name = strdup(device->name);
+ name = strip_whitespace(name);
+
+ char *p = name;
+ for (; *p; ++p) {
+ if (*p == ' ') {
+ *p = '_';
+ }
+ }
+
+ const char *fmt = "%d:%d:%s";
+ int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;
+ char *identifier = malloc(len);
+ if (!identifier) {
+ wlr_log(L_ERROR, "Unable to allocate unique input device name");
+ return NULL;
+ }
+
+ snprintf(identifier, len, fmt, vendor, product, name);
+ free(name);
+ return identifier;
+}
+
+static struct sway_input_device *input_sway_device_from_wlr(
+ struct sway_input_manager *input, struct wlr_input_device *device) {
+ struct sway_input_device *input_device = NULL;
+ wl_list_for_each(input_device, &input->devices, link) {
+ if (input_device->wlr_device == device) {
+ return input_device;
+ }
+ }
+ return NULL;
+}
+
+static bool input_has_seat_configuration(struct sway_input_manager *input) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (seat_config) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void input_manager_libinput_config_pointer(
+ struct sway_input_device *input_device) {
+ struct wlr_input_device *wlr_device = input_device->wlr_device;
+ struct input_config *ic = input_device_get_config(input_device);
+ struct libinput_device *libinput_device;
+
+ if (!ic || !wlr_input_device_is_libinput(wlr_device)) {
+ return;
+ }
+
+ libinput_device = wlr_libinput_get_device_handle(wlr_device);
+ wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)",
+ ic->identifier);
+
+ if (ic->accel_profile != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)",
+ ic->identifier, ic->accel_profile);
+ libinput_device_config_accel_set_profile(libinput_device,
+ ic->accel_profile);
+ }
+ if (ic->click_method != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)",
+ ic->identifier, ic->click_method);
+ libinput_device_config_click_set_method(libinput_device,
+ ic->click_method);
+ }
+ if (ic->drag_lock != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
+ ic->identifier, ic->click_method);
+ libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
+ ic->drag_lock);
+ }
+ if (ic->dwt != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)",
+ ic->identifier, ic->dwt);
+ libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt);
+ }
+ if (ic->left_handed != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) left_handed_set_enabled(%d)",
+ ic->identifier, ic->left_handed);
+ libinput_device_config_left_handed_set(libinput_device,
+ ic->left_handed);
+ }
+ if (ic->middle_emulation != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)",
+ ic->identifier, ic->middle_emulation);
+ libinput_device_config_middle_emulation_set_enabled(libinput_device,
+ ic->middle_emulation);
+ }
+ if (ic->natural_scroll != INT_MIN) {
+ wlr_log(L_DEBUG,
+ "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)",
+ ic->identifier, ic->natural_scroll);
+ libinput_device_config_scroll_set_natural_scroll_enabled(
+ libinput_device, ic->natural_scroll);
+ }
+ if (ic->pointer_accel != FLT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)",
+ ic->identifier, ic->pointer_accel);
+ libinput_device_config_accel_set_speed(libinput_device,
+ ic->pointer_accel);
+ }
+ if (ic->scroll_method != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)",
+ ic->identifier, ic->scroll_method);
+ libinput_device_config_scroll_set_method(libinput_device,
+ ic->scroll_method);
+ }
+ if (ic->send_events != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)",
+ ic->identifier, ic->send_events);
+ libinput_device_config_send_events_set_mode(libinput_device,
+ ic->send_events);
+ }
+ if (ic->tap != INT_MIN) {
+ wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)",
+ ic->identifier, ic->tap);
+ libinput_device_config_tap_set_enabled(libinput_device, ic->tap);
+ }
+}
+
+static void handle_device_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_input_device *device = data;
+
+ struct sway_input_device *input_device =
+ input_sway_device_from_wlr(input_manager, device);
+
+ if (!sway_assert(input_device, "could not find sway device")) {
+ return;
+ }
+
+ wlr_log(L_DEBUG, "removing device: '%s'",
+ input_device->identifier);
+
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_remove_device(seat, input_device);
+ }
+
+ wl_list_remove(&input_device->link);
+ wl_list_remove(&input_device->device_destroy.link);
+ free(input_device->identifier);
+ free(input_device);
+}
+
+static void handle_new_input(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input =
+ wl_container_of(listener, input, new_input);
+ struct wlr_input_device *device = data;
+
+ struct sway_input_device *input_device =
+ calloc(1, sizeof(struct sway_input_device));
+ if (!sway_assert(input_device, "could not allocate input device")) {
+ return;
+ }
+
+ input_device->wlr_device = device;
+ input_device->identifier = get_device_identifier(device);
+ wl_list_insert(&input->devices, &input_device->link);
+
+ wlr_log(L_DEBUG, "adding device: '%s'",
+ input_device->identifier);
+
+ if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
+ input_manager_libinput_config_pointer(input_device);
+ }
+
+ struct sway_seat *seat = NULL;
+ if (!input_has_seat_configuration(input)) {
+ wlr_log(L_DEBUG, "no seat configuration, using default seat");
+ seat = input_manager_get_seat(input, default_seat);
+ seat_add_device(seat, input_device);
+ return;
+ }
+
+ bool added = false;
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ bool has_attachment = seat_config &&
+ (seat_config_get_attachment(seat_config, input_device->identifier) ||
+ seat_config_get_attachment(seat_config, "*"));
+
+ if (has_attachment) {
+ seat_add_device(seat, input_device);
+ added = true;
+ }
+ }
+
+ if (!added) {
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (seat_config && seat_config->fallback == 1) {
+ seat_add_device(seat, input_device);
+ added = true;
+ }
+ }
+ }
+
+ if (!added) {
+ wlr_log(L_DEBUG,
+ "device '%s' is not configured on any seats",
+ input_device->identifier);
+ }
+
+ wl_signal_add(&device->events.destroy, &input_device->device_destroy);
+ input_device->device_destroy.notify = handle_device_destroy;
+}
+
+static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input_manager = wl_container_of(
+ listener, input_manager, inhibit_activate);
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
+ }
+}
+
+static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input_manager = wl_container_of(
+ listener, input_manager, inhibit_deactivate);
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_exclusive_client(seat, NULL);
+ struct sway_container *previous = seat_get_focus(seat);
+ if (previous) {
+ wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
+ container_type_to_str(previous->type), previous->name);
+ // Hack to get seat to re-focus the return value of get_focus
+ seat_set_focus(seat, previous->parent);
+ seat_set_focus(seat, previous);
+ }
+ }
+}
+
+struct sway_input_manager *input_manager_create(
+ struct sway_server *server) {
+ struct sway_input_manager *input =
+ calloc(1, sizeof(struct sway_input_manager));
+ if (!input) {
+ return NULL;
+ }
+ input->server = server;
+
+ wl_list_init(&input->devices);
+ wl_list_init(&input->seats);
+
+ // create the default seat
+ input_manager_get_seat(input, default_seat);
+
+ input->new_input.notify = handle_new_input;
+ wl_signal_add(&server->backend->events.new_input, &input->new_input);
+
+ input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
+ input->inhibit_activate.notify = handle_inhibit_activate;
+ wl_signal_add(&input->inhibit->events.activate,
+ &input->inhibit_activate);
+ input->inhibit_deactivate.notify = handle_inhibit_deactivate;
+ wl_signal_add(&input->inhibit->events.deactivate,
+ &input->inhibit_deactivate);
+
+ return input;
+}
+
+bool input_manager_has_focus(struct sway_input_manager *input,
+ struct sway_container *container) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ if (seat_get_focus(seat) == container) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void input_manager_set_focus(struct sway_input_manager *input,
+ struct sway_container *container) {
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input->seats, link) {
+ seat_set_focus(seat, container);
+ }
+}
+
+void input_manager_apply_input_config(struct sway_input_manager *input,
+ struct input_config *input_config) {
+ struct sway_input_device *input_device = NULL;
+ wl_list_for_each(input_device, &input->devices, link) {
+ if (strcmp(input_device->identifier, input_config->identifier) == 0) {
+ if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
+ input_manager_libinput_config_pointer(input_device);
+ }
+
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ seat_configure_device(seat, input_device);
+ }
+ }
+ }
+}
+
+void input_manager_apply_seat_config(struct sway_input_manager *input,
+ struct seat_config *seat_config) {
+ wlr_log(L_DEBUG, "applying new seat config for seat %s",
+ seat_config->name);
+ struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
+ if (!seat) {
+ return;
+ }
+
+ seat_apply_config(seat, seat_config);
+
+ // for every device, try to add it to a seat and if no seat has it
+ // attached, add it to the fallback seats.
+ struct sway_input_device *input_device = NULL;
+ wl_list_for_each(input_device, &input->devices, link) {
+ list_t *seat_list = create_list();
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (!seat_config) {
+ continue;
+ }
+ if (seat_config_get_attachment(seat_config, "*") ||
+ seat_config_get_attachment(seat_config,
+ input_device->identifier)) {
+ list_add(seat_list, seat);
+ }
+ }
+
+ if (seat_list->length) {
+ wl_list_for_each(seat, &input->seats, link) {
+ bool attached = false;
+ for (int i = 0; i < seat_list->length; ++i) {
+ if (seat == seat_list->items[i]) {
+ attached = true;
+ break;
+ }
+ }
+ if (attached) {
+ seat_add_device(seat, input_device);
+ } else {
+ seat_remove_device(seat, input_device);
+ }
+ }
+ } else {
+ wl_list_for_each(seat, &input->seats, link) {
+ struct seat_config *seat_config = seat_get_config(seat);
+ if (seat_config && seat_config->fallback == 1) {
+ seat_add_device(seat, input_device);
+ } else {
+ seat_remove_device(seat, input_device);
+ }
+ }
+ }
+ list_free(seat_list);
+ }
+}
+
+void input_manager_configure_xcursor(struct sway_input_manager *input) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ seat_configure_xcursor(seat);
+ }
+}
+
+struct sway_seat *input_manager_get_default_seat(
+ struct sway_input_manager *input) {
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input->seats, link) {
+ if (strcmp(seat->wlr_seat->name, "seat0") == 0) {
+ return seat;
+ }
+ }
+ return seat;
+}
+
+struct input_config *input_device_get_config(struct sway_input_device *device) {
+ struct input_config *input_config = NULL;
+ for (int i = 0; i < config->input_configs->length; ++i) {
+ input_config = config->input_configs->items[i];
+ if (strcmp(input_config->identifier, device->identifier) == 0) {
+ return input_config;
+ }
+ }
+
+ return NULL;
+}
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 @@
+#include <assert.h>
+#include <wlr/backend/multi.h>
+#include <wlr/backend/session.h>
+#include "sway/input/seat.h"
+#include "sway/input/keyboard.h"
+#include "sway/input/input-manager.h"
+#include "sway/commands.h"
+#include "log.h"
+
+static bool keysym_is_modifier(xkb_keysym_t keysym) {
+ switch (keysym) {
+ case XKB_KEY_Shift_L: case XKB_KEY_Shift_R:
+ case XKB_KEY_Control_L: case XKB_KEY_Control_R:
+ case XKB_KEY_Caps_Lock:
+ case XKB_KEY_Shift_Lock:
+ case XKB_KEY_Meta_L: case XKB_KEY_Meta_R:
+ case XKB_KEY_Alt_L: case XKB_KEY_Alt_R:
+ case XKB_KEY_Super_L: case XKB_KEY_Super_R:
+ case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) {
+ size_t n = 0;
+ for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
+ if (pressed_keysyms[i] != XKB_KEY_NoSymbol) {
+ ++n;
+ }
+ }
+ return n;
+}
+
+static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms,
+ xkb_keysym_t keysym) {
+ for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
+ if (pressed_keysyms[i] == keysym) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms,
+ xkb_keysym_t keysym) {
+ ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
+ if (i < 0) {
+ i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol);
+ if (i >= 0) {
+ pressed_keysyms[i] = keysym;
+ }
+ }
+}
+
+static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms,
+ xkb_keysym_t keysym) {
+ ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
+ if (i >= 0) {
+ pressed_keysyms[i] = XKB_KEY_NoSymbol;
+ }
+}
+
+static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms,
+ const xkb_keysym_t *keysyms, size_t keysyms_len,
+ enum wlr_key_state state) {
+ for (size_t i = 0; i < keysyms_len; ++i) {
+ if (keysym_is_modifier(keysyms[i])) {
+ continue;
+ }
+ if (state == WLR_KEY_PRESSED) {
+ pressed_keysyms_add(pressed_keysyms, keysyms[i]);
+ } else { // WLR_KEY_RELEASED
+ pressed_keysyms_remove(pressed_keysyms, keysyms[i]);
+ }
+ }
+}
+
+static bool binding_matches_key_state(struct sway_binding *binding,
+ enum wlr_key_state key_state) {
+ if (key_state == WLR_KEY_PRESSED && !binding->release) {
+ return true;
+ }
+ if (key_state == WLR_KEY_RELEASED && binding->release) {
+ return true;
+ }
+
+ return false;
+}
+
+static void keyboard_execute_command(struct sway_keyboard *keyboard,
+ struct sway_binding *binding) {
+ wlr_log(L_DEBUG, "running command for binding: %s",
+ binding->command);
+ config_clear_handler_context(config);
+ config->handler_context.seat = keyboard->seat_device->sway_seat;
+ struct cmd_results *results = execute_command(binding->command, NULL);
+ if (results->status != CMD_SUCCESS) {
+ wlr_log(L_DEBUG, "could not run command for binding: %s (%s)",
+ binding->command, results->error);
+ }
+ free_cmd_results(results);
+}
+
+/**
+ * Execute a built-in, hardcoded compositor binding. These are triggered from a
+ * single keysym.
+ *
+ * Returns true if the keysym was handled by a binding and false if the event
+ * should be propagated to clients.
+ */
+static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
+ xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) {
+ for (size_t i = 0; i < keysyms_len; ++i) {
+ xkb_keysym_t keysym = pressed_keysyms[i];
+ if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
+ keysym <= XKB_KEY_XF86Switch_VT_12) {
+ if (wlr_backend_is_multi(server.backend)) {
+ struct wlr_session *session =
+ wlr_multi_get_session(server.backend);
+ if (session) {
+ unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
+ wlr_session_change_vt(session, vt);
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Execute keyboard bindings bound with `bindysm` for the given keyboard state.
+ *
+ * Returns true if the keysym was handled by a binding and false if the event
+ * should be propagated to clients.
+ */
+static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
+ xkb_keysym_t *pressed_keysyms, uint32_t modifiers,
+ enum wlr_key_state key_state) {
+ // configured bindings
+ int n = pressed_keysyms_length(pressed_keysyms);
+ list_t *keysym_bindings = config->current_mode->keysym_bindings;
+ for (int i = 0; i < keysym_bindings->length; ++i) {
+ struct sway_binding *binding = keysym_bindings->items[i];
+ if (!binding_matches_key_state(binding, key_state) ||
+ modifiers ^ binding->modifiers ||
+ n != binding->keys->length) {
+ continue;
+ }
+
+ bool match = true;
+ for (int j = 0; j < binding->keys->length; ++j) {
+ match =
+ pressed_keysyms_index(pressed_keysyms,
+ *(int*)binding->keys->items[j]) >= 0;
+
+ if (!match) {
+ break;
+ }
+ }
+
+ if (match) {
+ keyboard_execute_command(keyboard, binding);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
+ struct sway_binding *binding, struct wlr_event_keyboard_key *event) {
+ assert(binding->bindcode);
+
+ uint32_t keycode = event->keycode + 8;
+
+ if (!binding_matches_key_state(binding, event->state)) {
+ return false;
+ }
+
+ uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
+ if (modifiers ^ binding->modifiers) {
+ return false;
+ }
+
+ // on release, the released key must be in the binding
+ if (event->state == WLR_KEY_RELEASED) {
+ bool found = false;
+ for (int i = 0; i < binding->keys->length; ++i) {
+ uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
+ if (binding_keycode == keycode) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+
+ // every keycode in the binding must be present in the pressed keys on the
+ // keyboard
+ for (int i = 0; i < binding->keys->length; ++i) {
+ uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
+ if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) {
+ continue;
+ }
+
+ bool found = false;
+ for (size_t j = 0; j < keyboard->num_keycodes; ++j) {
+ xkb_keycode_t keycode = keyboard->keycodes[j] + 8;
+ if (keycode == binding_keycode) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ return false;
+ }
+ }
+
+ // every keycode pressed on the keyboard must be present within the binding
+ // keys (unless it is a modifier)
+ for (size_t i = 0; i < keyboard->num_keycodes; ++i) {
+ xkb_keycode_t keycode = keyboard->keycodes[i] + 8;
+ bool found = false;
+ for (int j = 0; j < binding->keys->length; ++j) {
+ uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8;
+ if (binding_keycode == keycode) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (!binding->modifiers) {
+ return false;
+ }
+
+ // check if it is a modifier, which we know matched from the check
+ // above
+ const xkb_keysym_t *keysyms;
+ int num_keysyms =
+ xkb_state_key_get_syms(keyboard->xkb_state,
+ keycode, &keysyms);
+ if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Execute keyboard bindings bound with `bindcode` for the given keyboard state.
+ *
+ * Returns true if the keysym was handled by a binding and false if the event
+ * should be propagated to clients.
+ */
+static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard,
+ struct wlr_event_keyboard_key *event) {
+ struct wlr_keyboard *wlr_keyboard =
+ keyboard->seat_device->input_device->wlr_device->keyboard;
+ list_t *keycode_bindings = config->current_mode->keycode_bindings;
+ for (int i = 0; i < keycode_bindings->length; ++i) {
+ struct sway_binding *binding = keycode_bindings->items[i];
+ if (binding_matches_keycodes(wlr_keyboard, binding, event)) {
+ keyboard_execute_command(keyboard, binding);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Get keysyms and modifiers from the keyboard as xkb sees them.
+ *
+ * This uses the xkb keysyms translation based on pressed modifiers and clears
+ * the consumed modifiers from the list of modifiers passed to keybind
+ * detection.
+ *
+ * On US layout, pressing Alt+Shift+2 will trigger Alt+@.
+ */
+static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
+ xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
+ uint32_t *modifiers) {
+ struct wlr_input_device *device =
+ keyboard->seat_device->input_device->wlr_device;
+ *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
+ xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
+ device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
+ *modifiers = *modifiers & ~consumed;
+
+ return xkb_state_key_get_syms(device->keyboard->xkb_state,
+ keycode, keysyms);
+}
+
+/**
+ * Get keysyms and modifiers from the keyboard as if modifiers didn't change
+ * keysyms.
+ *
+ * This avoids the xkb keysym translation based on modifiers considered pressed
+ * in the state.
+ *
+ * This will trigger keybinds such as Alt+Shift+2.
+ */
+static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
+ xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
+ uint32_t *modifiers) {
+ struct wlr_input_device *device =
+ keyboard->seat_device->input_device->wlr_device;
+ *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
+
+ xkb_layout_index_t layout_index = xkb_state_key_get_layout(
+ device->keyboard->xkb_state, keycode);
+ return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap,
+ keycode, layout_index, 0, keysyms);
+}
+
+static void handle_keyboard_key(struct wl_listener *listener, void *data) {
+ struct sway_keyboard *keyboard =
+ wl_container_of(listener, keyboard, keyboard_key);
+ struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+ struct wlr_input_device *wlr_device =
+ keyboard->seat_device->input_device->wlr_device;
+ struct wlr_event_keyboard_key *event = data;
+
+ xkb_keycode_t keycode = event->keycode + 8;
+ bool handled = false;
+
+ // handle keycodes
+ handled = keyboard_execute_bindcode(keyboard, event);
+
+ // handle translated keysyms
+ if (!handled && event->state == WLR_KEY_RELEASED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_translated,
+ keyboard->modifiers_translated,
+ event->state);
+ }
+ const xkb_keysym_t *translated_keysyms;
+ size_t translated_keysyms_len =
+ keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms,
+ &keyboard->modifiers_translated);
+ pressed_keysyms_update(keyboard->pressed_keysyms_translated,
+ translated_keysyms, translated_keysyms_len, event->state);
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_translated,
+ keyboard->modifiers_translated,
+ event->state);
+ }
+
+ // Handle raw keysyms
+ if (!handled && event->state == WLR_KEY_RELEASED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+ event->state);
+ }
+ const xkb_keysym_t *raw_keysyms;
+ size_t raw_keysyms_len =
+ keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw);
+ pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms,
+ raw_keysyms_len, event->state);
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled = keyboard_execute_bindsym(keyboard,
+ keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+ event->state);
+ }
+
+ // Compositor bindings
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled =
+ keyboard_execute_compositor_binding(keyboard,
+ keyboard->pressed_keysyms_translated,
+ keyboard->modifiers_translated,
+ translated_keysyms_len);
+ }
+ if (!handled && event->state == WLR_KEY_PRESSED) {
+ handled =
+ keyboard_execute_compositor_binding(keyboard,
+ keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
+ raw_keysyms_len);
+ }
+
+ if (!handled || event->state == WLR_KEY_RELEASED) {
+ wlr_seat_set_keyboard(wlr_seat, wlr_device);
+ wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
+ event->keycode, event->state);
+ }
+}
+
+static void handle_keyboard_modifiers(struct wl_listener *listener,
+ void *data) {
+ struct sway_keyboard *keyboard =
+ wl_container_of(listener, keyboard, keyboard_modifiers);
+ struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+ struct wlr_input_device *wlr_device =
+ keyboard->seat_device->input_device->wlr_device;
+ wlr_seat_set_keyboard(wlr_seat, wlr_device);
+ wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers);
+}
+
+struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
+ struct sway_seat_device *device) {
+ struct sway_keyboard *keyboard =
+ calloc(1, sizeof(struct sway_keyboard));
+ if (!sway_assert(keyboard, "could not allocate sway keyboard")) {
+ return NULL;
+ }
+
+ keyboard->seat_device = device;
+ device->keyboard = keyboard;
+
+ wl_list_init(&keyboard->keyboard_key.link);
+ wl_list_init(&keyboard->keyboard_modifiers.link);
+
+ return keyboard;
+}
+
+void sway_keyboard_configure(struct sway_keyboard *keyboard) {
+ struct xkb_rule_names rules;
+ memset(&rules, 0, sizeof(rules));
+ struct input_config *input_config =
+ input_device_get_config(keyboard->seat_device->input_device);
+ struct wlr_input_device *wlr_device =
+ keyboard->seat_device->input_device->wlr_device;
+
+ if (input_config && input_config->xkb_layout) {
+ rules.layout = input_config->xkb_layout;
+ } else {
+ rules.layout = getenv("XKB_DEFAULT_LAYOUT");
+ }
+ if (input_config && input_config->xkb_model) {
+ rules.model = input_config->xkb_model;
+ } else {
+ rules.model = getenv("XKB_DEFAULT_MODEL");
+ }
+
+ if (input_config && input_config->xkb_options) {
+ rules.options = input_config->xkb_options;
+ } else {
+ rules.options = getenv("XKB_DEFAULT_OPTIONS");
+ }
+
+ if (input_config && input_config->xkb_rules) {
+ rules.rules = input_config->xkb_rules;
+ } else {
+ rules.rules = getenv("XKB_DEFAULT_RULES");
+ }
+
+ if (input_config && input_config->xkb_variant) {
+ rules.variant = input_config->xkb_variant;
+ } else {
+ rules.variant = getenv("XKB_DEFAULT_VARIANT");
+ }
+
+ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ if (!sway_assert(context, "cannot create XKB context")) {
+ return;
+ }
+
+ struct xkb_keymap *keymap =
+ xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ if (!keymap) {
+ wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist");
+ xkb_context_unref(context);
+ return;
+ }
+
+ xkb_keymap_unref(keyboard->keymap);
+ keyboard->keymap = keymap;
+ wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+
+ wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600);
+ xkb_context_unref(context);
+ struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
+ wlr_seat_set_keyboard(seat, wlr_device);
+
+ wl_list_remove(&keyboard->keyboard_key.link);
+ wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);
+ keyboard->keyboard_key.notify = handle_keyboard_key;
+
+ wl_list_remove(&keyboard->keyboard_modifiers.link);
+ wl_signal_add( &wlr_device->keyboard->events.modifiers,
+ &keyboard->keyboard_modifiers);
+ keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
+}
+
+void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
+ if (!keyboard) {
+ return;
+ }
+ wl_list_remove(&keyboard->keyboard_key.link);
+ wl_list_remove(&keyboard->keyboard_modifiers.link);
+ free(keyboard);
+}
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 @@
+#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 199309L
+#include <assert.h>
+#include <strings.h>
+#include <time.h>
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include "sway/debug.h"
+#include "sway/tree/container.h"
+#include "sway/tree/workspace.h"
+#include "sway/input/seat.h"
+#include "sway/input/cursor.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/keyboard.h"
+#include "sway/ipc-server.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/tree/container.h"
+#include "sway/tree/view.h"
+#include "log.h"
+
+static void seat_device_destroy(struct sway_seat_device *seat_device) {
+ if (!seat_device) {
+ return;
+ }
+
+ sway_keyboard_destroy(seat_device->keyboard);
+ wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
+ seat_device->input_device->wlr_device);
+ wl_list_remove(&seat_device->link);
+ free(seat_device);
+}
+
+void seat_destroy(struct sway_seat *seat) {
+ struct sway_seat_device *seat_device, *next;
+ wl_list_for_each_safe(seat_device, next, &seat->devices, link) {
+ seat_device_destroy(seat_device);
+ }
+ sway_cursor_destroy(seat->cursor);
+ wl_list_remove(&seat->link);
+ wlr_seat_destroy(seat->wlr_seat);
+}
+
+static struct sway_seat_container *seat_container_from_container(
+ struct sway_seat *seat, struct sway_container *con);
+
+static void seat_container_destroy(struct sway_seat_container *seat_con) {
+ struct sway_container *con = seat_con->container;
+ struct sway_container *child = NULL;
+
+ if (con->children != NULL) {
+ for (int i = 0; i < con->children->length; ++i) {
+ child = con->children->items[i];
+ struct sway_seat_container *seat_child =
+ seat_container_from_container(seat_con->seat, child);
+ seat_container_destroy(seat_child);
+ }
+ }
+
+ wl_list_remove(&seat_con->destroy.link);
+ wl_list_remove(&seat_con->link);
+ free(seat_con);
+}
+
+static void seat_send_focus(struct sway_seat *seat,
+ struct sway_container *con) {
+ if (con->type != C_VIEW) {
+ return;
+ }
+ struct sway_view *view = con->sway_view;
+ if (view->type == SWAY_VIEW_XWAYLAND) {
+ struct wlr_xwayland *xwayland =
+ seat->input->server->xwayland;
+ wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
+ }
+ view_set_activated(view, true);
+ struct wlr_keyboard *keyboard =
+ wlr_seat_get_keyboard(seat->wlr_seat);
+ if (keyboard) {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat,
+ view->surface, keyboard->keycodes,
+ keyboard->num_keycodes, &keyboard->modifiers);
+ } else {
+ wlr_seat_keyboard_notify_enter(
+ seat->wlr_seat, view->surface, NULL, 0, NULL);
+ }
+}
+
+static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
+ struct sway_container *container, enum sway_container_type type) {
+ if (container->type == C_VIEW || container->children->length == 0) {
+ return container;
+ }
+
+ struct sway_seat_container *current = NULL;
+ wl_list_for_each(current, &seat->focus_stack, link) {
+ if (current->container->type != type && type != C_TYPES) {
+ continue;
+ }
+
+ if (container_has_child(container, current->container)) {
+ return current->container;
+ }
+ }
+
+ return NULL;
+}
+
+void seat_focus_inactive_children_for_each(struct sway_seat *seat,
+ struct sway_container *container,
+ void (*f)(struct sway_container *container, void *data), void *data) {
+ struct sway_seat_container *current = NULL;
+ wl_list_for_each(current, &seat->focus_stack, link) {
+ if (current->container->parent == NULL) {
+ continue;
+ }
+ if (current->container->parent == container) {
+ f(current->container, data);
+ }
+ }
+}
+
+struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
+ struct sway_container *container) {
+ return seat_get_focus_by_type(seat, container, C_VIEW);
+}
+
+static void handle_seat_container_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_seat_container *seat_con =
+ wl_container_of(listener, seat_con, destroy);
+ struct sway_seat *seat = seat_con->seat;
+ struct sway_container *con = seat_con->container;
+ struct sway_container *parent = con->parent;
+ struct sway_container *focus = seat_get_focus(seat);
+
+ bool set_focus =
+ focus != NULL &&
+ (focus == con || container_has_child(con, focus)) &&
+ con->type != C_WORKSPACE;
+
+ seat_container_destroy(seat_con);
+
+ if (set_focus) {
+ struct sway_container *next_focus = NULL;
+ while (next_focus == NULL) {
+ next_focus = seat_get_focus_by_type(seat, parent, C_VIEW);
+
+ if (next_focus == NULL && parent->type == C_WORKSPACE) {
+ next_focus = parent;
+ break;
+ }
+
+ parent = parent->parent;
+ }
+
+ // the structure change might have caused it to move up to the top of
+ // the focus stack without sending focus notifications to the view
+ if (seat_get_focus(seat) == next_focus) {
+ seat_send_focus(seat, next_focus);
+ } else {
+ seat_set_focus(seat, next_focus);
+ }
+ }
+}
+
+static struct sway_seat_container *seat_container_from_container(
+ struct sway_seat *seat, struct sway_container *con) {
+ if (con->type == C_ROOT || con->type == C_OUTPUT) {
+ // these don't get seat containers ever
+ return NULL;
+ }
+
+ struct sway_seat_container *seat_con = NULL;
+ wl_list_for_each(seat_con, &seat->focus_stack, link) {
+ if (seat_con->container == con) {
+ return seat_con;
+ }
+ }
+
+ seat_con = calloc(1, sizeof(struct sway_seat_container));
+ if (seat_con == NULL) {
+ wlr_log(L_ERROR, "could not allocate seat container");
+ return NULL;
+ }
+
+ seat_con->container = con;
+ seat_con->seat = seat;
+ wl_list_insert(seat->focus_stack.prev, &seat_con->link);
+ wl_signal_add(&con->events.destroy, &seat_con->destroy);
+ seat_con->destroy.notify = handle_seat_container_destroy;
+
+ return seat_con;
+}
+
+static void handle_new_container(struct wl_listener *listener, void *data) {
+ struct sway_seat *seat = wl_container_of(listener, seat, new_container);
+ struct sway_container *con = data;
+ seat_container_from_container(seat, con);
+}
+
+static void collect_focus_iter(struct sway_container *con, void *data) {
+ struct sway_seat *seat = data;
+ if (con->type > C_WORKSPACE) {
+ return;
+ }
+ struct sway_seat_container *seat_con =
+ seat_container_from_container(seat, con);
+ if (!seat_con) {
+ return;
+ }
+ wl_list_remove(&seat_con->link);
+ wl_list_insert(&seat->focus_stack, &seat_con->link);
+}
+
+struct sway_seat *seat_create(struct sway_input_manager *input,
+ const char *seat_name) {
+ struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
+ if (!seat) {
+ return NULL;
+ }
+
+ seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name);
+ if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
+ free(seat);
+ return NULL;
+ }
+
+ seat->cursor = sway_cursor_create(seat);
+ if (!seat->cursor) {
+ wlr_seat_destroy(seat->wlr_seat);
+ free(seat);
+ return NULL;
+ }
+
+ // init the focus stack
+ wl_list_init(&seat->focus_stack);
+
+ container_for_each_descendant_dfs(&root_container,
+ collect_focus_iter, seat);
+
+ wl_signal_add(&root_container.sway_root->events.new_container,
+ &seat->new_container);
+ seat->new_container.notify = handle_new_container;
+
+ seat->input = input;
+ wl_list_init(&seat->devices);
+
+ wlr_seat_set_capabilities(seat->wlr_seat,
+ WL_SEAT_CAPABILITY_KEYBOARD |
+ WL_SEAT_CAPABILITY_POINTER |
+ WL_SEAT_CAPABILITY_TOUCH);
+
+ seat_configure_xcursor(seat);
+
+ wl_list_insert(&input->seats, &seat->link);
+
+ return seat;
+}
+
+static void seat_apply_input_config(struct sway_seat *seat,
+ struct sway_seat_device *sway_device) {
+ struct input_config *ic = input_device_get_config(
+ sway_device->input_device);
+ if (!ic) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Applying input config to %s",
+ sway_device->input_device->identifier);
+ if (ic->mapped_output) {
+ struct sway_container *output = NULL;
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *_output = root_container.children->items[i];
+ if (strcasecmp(_output->name, ic->mapped_output) == 0) {
+ output = _output;
+ break;
+ }
+ }
+ if (output) {
+ wlr_cursor_map_input_to_output(seat->cursor->cursor,
+ sway_device->input_device->wlr_device,
+ output->sway_output->wlr_output);
+ wlr_log(L_DEBUG, "Mapped to output %s", output->name);
+ }
+ }
+}
+
+static void seat_configure_pointer(struct sway_seat *seat,
+ struct sway_seat_device *sway_device) {
+ wlr_cursor_attach_input_device(seat->cursor->cursor,
+ sway_device->input_device->wlr_device);
+ seat_apply_input_config(seat, sway_device);
+}
+
+static void seat_configure_keyboard(struct sway_seat *seat,
+ struct sway_seat_device *seat_device) {
+ if (!seat_device->keyboard) {
+ sway_keyboard_create(seat, seat_device);
+ }
+ struct wlr_keyboard *wlr_keyboard =
+ seat_device->input_device->wlr_device->keyboard;
+ sway_keyboard_configure(seat_device->keyboard);
+ wlr_seat_set_keyboard(seat->wlr_seat,
+ seat_device->input_device->wlr_device);
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus && focus->type == C_VIEW) {
+ // force notify reenter to pick up the new configuration
+ wlr_seat_keyboard_clear_focus(seat->wlr_seat);
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat,
+ focus->sway_view->surface, wlr_keyboard->keycodes,
+ wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
+ }
+}
+
+static void seat_configure_tablet_tool(struct sway_seat *seat,
+ struct sway_seat_device *sway_device) {
+ wlr_cursor_attach_input_device(seat->cursor->cursor,
+ sway_device->input_device->wlr_device);
+ seat_apply_input_config(seat, sway_device);
+}
+
+static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ struct sway_seat_device *seat_device = NULL;
+ wl_list_for_each(seat_device, &seat->devices, link) {
+ if (seat_device->input_device == input_device) {
+ return seat_device;
+ }
+ }
+
+ return NULL;
+}
+
+void seat_configure_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ struct sway_seat_device *seat_device =
+ seat_get_device(seat, input_device);
+ if (!seat_device) {
+ return;
+ }
+
+ switch (input_device->wlr_device->type) {
+ case WLR_INPUT_DEVICE_POINTER:
+ seat_configure_pointer(seat, seat_device);
+ break;
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ seat_configure_keyboard(seat, seat_device);
+ break;
+ case WLR_INPUT_DEVICE_TABLET_TOOL:
+ seat_configure_tablet_tool(seat, seat_device);
+ break;
+ case WLR_INPUT_DEVICE_TABLET_PAD:
+ case WLR_INPUT_DEVICE_TOUCH:
+ wlr_log(L_DEBUG, "TODO: configure other devices");
+ break;
+ }
+}
+
+void seat_add_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ if (seat_get_device(seat, input_device)) {
+ seat_configure_device(seat, input_device);
+ return;
+ }
+
+ struct sway_seat_device *seat_device =
+ calloc(1, sizeof(struct sway_seat_device));
+ if (!seat_device) {
+ wlr_log(L_DEBUG, "could not allocate seat device");
+ return;
+ }
+
+ wlr_log(L_DEBUG, "adding device %s to seat %s",
+ input_device->identifier, seat->wlr_seat->name);
+
+ seat_device->sway_seat = seat;
+ seat_device->input_device = input_device;
+ wl_list_insert(&seat->devices, &seat_device->link);
+
+ seat_configure_device(seat, input_device);
+}
+
+void seat_remove_device(struct sway_seat *seat,
+ struct sway_input_device *input_device) {
+ struct sway_seat_device *seat_device =
+ seat_get_device(seat, input_device);
+
+ if (!seat_device) {
+ return;
+ }
+
+ wlr_log(L_DEBUG, "removing device %s from seat %s",
+ input_device->identifier, seat->wlr_seat->name);
+
+ seat_device_destroy(seat_device);
+}
+
+void seat_configure_xcursor(struct sway_seat *seat) {
+ // TODO configure theme and size
+ const char *cursor_theme = NULL;
+
+ if (!seat->cursor->xcursor_manager) {
+ seat->cursor->xcursor_manager =
+ wlr_xcursor_manager_create(cursor_theme, 24);
+ if (sway_assert(seat->cursor->xcursor_manager,
+ "Cannot create XCursor manager for theme %s",
+ cursor_theme)) {
+ return;
+ }
+ }
+
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *output_container =
+ root_container.children->items[i];
+ struct wlr_output *output =
+ output_container->sway_output->wlr_output;
+ bool result =
+ wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
+ output->scale);
+
+ sway_assert(!result,
+ "Cannot load xcursor theme for output '%s' with scale %f",
+ // TODO: Fractional scaling
+ output->name, (double)output->scale);
+ }
+
+ wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
+ "left_ptr", seat->cursor->cursor);
+ wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
+ seat->cursor->cursor->y);
+}
+
+bool seat_is_input_allowed(struct sway_seat *seat,
+ struct wlr_surface *surface) {
+ struct wl_client *client = wl_resource_get_client(surface->resource);
+ return !seat->exclusive_client || seat->exclusive_client == client;
+}
+
+void seat_set_focus_warp(struct sway_seat *seat,
+ struct sway_container *container, bool warp) {
+ if (seat->focused_layer) {
+ return;
+ }
+
+ struct sway_container *last_focus = seat_get_focus(seat);
+ if (container && last_focus == container) {
+ return;
+ }
+
+ if (container) {
+ struct sway_seat_container *seat_con =
+ seat_container_from_container(seat, container);
+ if (seat_con == NULL) {
+ return;
+ }
+
+ // put all the anscestors of this container on top of the focus stack
+ struct sway_seat_container *parent =
+ seat_container_from_container(seat,
+ seat_con->container->parent);
+ while (parent) {
+ wl_list_remove(&parent->link);
+ wl_list_insert(&seat->focus_stack, &parent->link);
+
+ parent =
+ seat_container_from_container(seat,
+ parent->container->parent);
+ }
+
+ wl_list_remove(&seat_con->link);
+ wl_list_insert(&seat->focus_stack, &seat_con->link);
+
+ if (container->type == C_VIEW && !seat_is_input_allowed(
+ seat, container->sway_view->surface)) {
+ wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited");
+ return;
+ }
+
+ if (container->type == C_VIEW) {
+ seat_send_focus(seat, container);
+ }
+ }
+
+ if (last_focus) {
+ struct sway_container *last_ws = last_focus;
+ if (last_ws && last_ws->type != C_WORKSPACE) {
+ last_ws = container_parent(last_ws, C_WORKSPACE);
+ }
+ if (last_ws) {
+ ipc_event_workspace(last_ws, container, "focus");
+ if (!workspace_is_visible(last_ws)
+ && last_ws->children->length == 0) {
+ container_destroy(last_ws);
+ }
+ }
+
+ if (config->mouse_warping && warp) {
+ struct sway_container *last_output = last_focus;
+ if (last_output && last_output->type != C_OUTPUT) {
+ last_output = container_parent(last_output, C_OUTPUT);
+ }
+ struct sway_container *new_output = container;
+ if (new_output && new_output->type != C_OUTPUT) {
+ new_output = container_parent(new_output, C_OUTPUT);
+ }
+ if (new_output && last_output && new_output != last_output) {
+ double x = new_output->x + container->x +
+ container->width / 2.0;
+ double y = new_output->y + container->y +
+ container->height / 2.0;
+ struct wlr_output *wlr_output =
+ new_output->sway_output->wlr_output;
+ if (!wlr_output_layout_contains_point(
+ root_container.sway_root->output_layout,
+ wlr_output, seat->cursor->cursor->x,
+ seat->cursor->cursor->y)) {
+ wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ cursor_send_pointer_motion(seat->cursor, now.tv_nsec / 1000);
+ }
+ }
+ }
+ }
+
+ if (last_focus && last_focus->type == C_VIEW &&
+ !input_manager_has_focus(seat->input, last_focus)) {
+ struct sway_view *view = last_focus->sway_view;
+ view_set_activated(view, false);
+ }
+
+ seat->has_focus = (container != NULL);
+
+ update_debug_tree();
+}
+
+void seat_set_focus(struct sway_seat *seat,
+ struct sway_container *container) {
+ seat_set_focus_warp(seat, container, true);
+}
+
+void seat_set_focus_surface(struct sway_seat *seat,
+ struct wlr_surface *surface) {
+ if (seat->focused_layer != NULL) {
+ return;
+ }
+ if (seat->has_focus) {
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus->type == C_VIEW) {
+ wlr_seat_keyboard_clear_focus(seat->wlr_seat);
+ view_set_activated(focus->sway_view, false);
+ }
+ seat->has_focus = false;
+ }
+ struct wlr_keyboard *keyboard =
+ wlr_seat_get_keyboard(seat->wlr_seat);
+ if (keyboard) {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,
+ keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
+ } else {
+ wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL);
+ }
+}
+
+void seat_set_focus_layer(struct sway_seat *seat,
+ struct wlr_layer_surface *layer) {
+ if (!layer && seat->focused_layer) {
+ seat->focused_layer = NULL;
+ struct sway_container *previous = seat_get_focus(seat);
+ if (previous) {
+ wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
+ container_type_to_str(previous->type), previous->name);
+ // Hack to get seat to re-focus the return value of get_focus
+ seat_set_focus(seat, previous->parent);
+ seat_set_focus(seat, previous);
+ }
+ return;
+ } else if (!layer || seat->focused_layer == layer) {
+ return;
+ }
+ seat_set_focus_surface(seat, layer->surface);
+ if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
+ seat->focused_layer = layer;
+ }
+}
+
+void seat_set_exclusive_client(struct sway_seat *seat,
+ struct wl_client *client) {
+ if (!client) {
+ seat->exclusive_client = client;
+ // Triggers a refocus of the topmost surface layer if necessary
+ // TODO: Make layer surface focus per-output based on cursor position
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *output = root_container.children->items[i];
+ if (!sway_assert(output->type == C_OUTPUT,
+ "root container has non-output child")) {
+ continue;
+ }
+ arrange_layers(output->sway_output);
+ }
+ return;
+ }
+ if (seat->focused_layer) {
+ if (wl_resource_get_client(seat->focused_layer->resource) != client) {
+ seat_set_focus_layer(seat, NULL);
+ }
+ }
+ if (seat->has_focus) {
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus->type == C_VIEW && wl_resource_get_client(
+ focus->sway_view->surface->resource) != client) {
+ seat_set_focus(seat, NULL);
+ }
+ }
+ if (seat->wlr_seat->pointer_state.focused_client) {
+ if (seat->wlr_seat->pointer_state.focused_client->client != client) {
+ wlr_seat_pointer_clear_focus(seat->wlr_seat);
+ }
+ }
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ struct wlr_touch_point *point;
+ wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) {
+ if (point->client->client != client) {
+ wlr_seat_touch_point_clear_focus(seat->wlr_seat,
+ now.tv_nsec / 1000, point->touch_id);
+ }
+ }
+ seat->exclusive_client = client;
+}
+
+struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
+ struct sway_container *container) {
+ return seat_get_focus_by_type(seat, container, C_TYPES);
+}
+
+struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
+ if (!seat->has_focus) {
+ return NULL;
+ }
+ return seat_get_focus_inactive(seat, &root_container);
+}
+
+struct sway_container *seat_get_focus(struct sway_seat *seat) {
+ if (!seat->has_focus) {
+ return NULL;
+ }
+ return seat_get_focus_inactive(seat, &root_container);
+}
+
+void seat_apply_config(struct sway_seat *seat,
+ struct seat_config *seat_config) {
+ struct sway_seat_device *seat_device = NULL;
+
+ if (!seat_config) {
+ return;
+ }
+
+ wl_list_for_each(seat_device, &seat->devices, link) {
+ seat_configure_device(seat, seat_device->input_device);
+ }
+}
+
+struct seat_config *seat_get_config(struct sway_seat *seat) {
+ struct seat_config *seat_config = NULL;
+ for (int i = 0; i < config->seat_configs->length; ++i ) {
+ seat_config = config->seat_configs->items[i];
+ if (strcmp(seat->wlr_seat->name, seat_config->name) == 0) {
+ return seat_config;
+ }
+ }
+
+ return NULL;
+}
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 @@
-#include <string.h>
-#include <stdbool.h>
-#include <ctype.h>
-#include "sway/input_state.h"
-#include "sway/config.h"
-#include "log.h"
-
-#define KEY_STATE_MAX_LENGTH 64
-
-struct key_state {
- /*
- * Aims to store state regardless of modifiers.
- * If you press a key, then hold shift, then release the key, we'll
- * get two different key syms, but the same key code. This handles
- * that scenario and makes sure we can use the right bindings.
- */
- uint32_t key_sym;
- uint32_t alt_sym;
- uint32_t key_code;
-};
-
-static struct key_state key_state_array[KEY_STATE_MAX_LENGTH];
-
-static struct key_state last_released;
-
-static uint32_t modifiers_state;
-
-void input_init(void) {
- int i;
- for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
- struct key_state none = { 0, 0, 0 };
- key_state_array[i] = none;
- }
-
- struct key_state none = { 0, 0, 0 };
- last_released = none;
-
- modifiers_state = 0;
-}
-
-uint32_t modifier_state_changed(uint32_t new_state, uint32_t mod) {
- if ((new_state & mod) != 0) { // pressed
- if ((modifiers_state & mod) != 0) { // already pressed
- return MOD_STATE_UNCHANGED;
- } else { // pressed
- return MOD_STATE_PRESSED;
- }
- } else { // not pressed
- if ((modifiers_state & mod) != 0) { // released
- return MOD_STATE_RELEASED;
- } else { // already released
- return MOD_STATE_UNCHANGED;
- }
- }
-}
-
-void modifiers_state_update(uint32_t new_state) {
- modifiers_state = new_state;
-}
-
-static uint8_t find_key(uint32_t key_sym, uint32_t key_code, bool update) {
- int i;
- for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) {
- if (0 == key_sym && 0 == key_code && key_state_array[i].key_sym == 0) {
- break;
- }
- if (key_sym != 0 && (key_state_array[i].key_sym == key_sym
- || key_state_array[i].alt_sym == key_sym)) {
- break;
- }
- if (update && key_state_array[i].key_code == key_code) {
- key_state_array[i].alt_sym = key_sym;
- break;
- }
- if (key_sym == 0 && key_code != 0 && key_state_array[i].key_code == key_code) {
- break;
- }
- }
- return i;
-}
-
-bool check_key(uint32_t key_sym, uint32_t key_code) {
- return find_key(key_sym, key_code, false) < KEY_STATE_MAX_LENGTH;
-}
-
-bool check_released_key(uint32_t key_sym) {
- return (key_sym != 0
- && (last_released.key_sym == key_sym
- || last_released.alt_sym == key_sym));
-}
-
-void press_key(uint32_t key_sym, uint32_t key_code) {
- if (key_code == 0) {
- return;
- }
- // Check if key exists
- if (!check_key(key_sym, key_code)) {
- // Check that we don't exceed buffer length
- int insert = find_key(0, 0, true);
- if (insert < KEY_STATE_MAX_LENGTH) {
- key_state_array[insert].key_sym = key_sym;
- key_state_array[insert].key_code = key_code;
- }
- }
-}
-
-void release_key(uint32_t key_sym, uint32_t key_code) {
- uint8_t index = find_key(key_sym, key_code, true);
- if (index < KEY_STATE_MAX_LENGTH) {
- last_released.key_sym = key_state_array[index].key_sym;
- last_released.alt_sym = key_state_array[index].alt_sym;
- last_released.key_code = key_state_array[index].key_code;
- struct key_state none = { 0, 0, 0 };
- key_state_array[index] = none;
- }
-}
-
-// Pointer state and mode
-
-struct pointer_state pointer_state;
-
-static struct mode_state {
- // initial view state
- double x, y, w, h;
- swayc_t *ptr;
- // Containers used for resizing horizontally
- struct {
- double w;
- swayc_t *ptr;
- struct {
- double w;
- swayc_t *ptr;
- } parent;
- } horiz;
- // Containers used for resizing vertically
- struct {
- double h;
- swayc_t *ptr;
- struct {
- double h;
- swayc_t *ptr;
- } parent;
- } vert;
-} initial;
-
-static struct {
- bool left;
- bool top;
-} lock;
-
-// initial set/unset
-
-static void set_initial_view(swayc_t *view) {
- initial.ptr = view;
- initial.x = view->x;
- initial.y = view->y;
- initial.w = view->width;
- initial.h = view->height;
-}
-
-static void set_initial_sibling(void) {
- bool reset = true;
- swayc_t *ws = swayc_active_workspace_for(initial.ptr);
- if ((initial.horiz.ptr = get_swayc_in_direction_under(initial.ptr,
- lock.left ? MOVE_RIGHT: MOVE_LEFT, ws))) {
- initial.horiz.w = initial.horiz.ptr->width;
- initial.horiz.parent.ptr = get_swayc_in_direction_under(initial.horiz.ptr,
- lock.left ? MOVE_LEFT : MOVE_RIGHT, ws);
- initial.horiz.parent.w = initial.horiz.parent.ptr->width;
- reset = false;
- }
- if ((initial.vert.ptr = get_swayc_in_direction_under(initial.ptr,
- lock.top ? MOVE_DOWN: MOVE_UP, ws))) {
- initial.vert.h = initial.vert.ptr->height;
- initial.vert.parent.ptr = get_swayc_in_direction_under(initial.vert.ptr,
- lock.top ? MOVE_UP : MOVE_DOWN, ws);
- initial.vert.parent.h = initial.vert.parent.ptr->height;
- reset = false;
- }
- // If nothing will change just undo the mode
- if (reset) {
- pointer_state.mode = 0;
- }
-}
-
-static void reset_initial_view(void) {
- initial.ptr->x = initial.x;
- initial.ptr->y = initial.y;
- initial.ptr->width = initial.w;
- initial.ptr->height = initial.h;
- arrange_windows(initial.ptr, -1, -1);
- pointer_state.mode = 0;
-}
-
-static void reset_initial_sibling(void) {
- initial.horiz.ptr->width = initial.horiz.w;
- initial.horiz.parent.ptr->width = initial.horiz.parent.w;
- initial.vert.ptr->height = initial.vert.h;
- initial.vert.parent.ptr->height = initial.vert.parent.h;
- arrange_windows(initial.horiz.ptr->parent, -1, -1);
- arrange_windows(initial.vert.ptr->parent, -1, -1);
- pointer_state.mode = 0;
-}
-
-void pointer_position_set(double new_x, double new_y, bool force_focus) {
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- pointer_state.delta.x = new_x - x;
- pointer_state.delta.y = new_y - y;
-
- wlc_pointer_set_position_v2(new_x, new_y);
-
- // Update view under pointer
- swayc_t *prev_view = pointer_state.view;
- pointer_state.view = container_under_pointer();
-
- // If pointer is in a mode, update it
- if (pointer_state.mode) {
- pointer_mode_update();
- // Otherwise change focus if config is set
- } else if (force_focus || (prev_view != pointer_state.view && config->focus_follows_mouse)) {
- if (pointer_state.view && pointer_state.view->type == C_VIEW) {
- set_focused_container(pointer_state.view);
- }
- }
-}
-
-void center_pointer_on(swayc_t *view) {
- pointer_position_set(view->x + view->width/2, view->y + view->height/2, true);
-}
-
-// Mode set left/right click
-
-static void pointer_mode_set_dragging(void) {
- switch (config->dragging_key) {
- case M_LEFT_CLICK:
- set_initial_view(pointer_state.left.view);
- break;
- case M_RIGHT_CLICK:
- set_initial_view(pointer_state.right.view);
- break;
- }
-
- if (initial.ptr->is_floating) {
- pointer_state.mode = M_DRAGGING | M_FLOATING;
- } else {
- pointer_state.mode = M_DRAGGING | M_TILING;
- // unset mode if we can't drag tile
- if (initial.ptr->parent->type == C_WORKSPACE &&
- initial.ptr->parent->children->length == 1) {
- pointer_state.mode = 0;
- }
- }
-}
-
-static void pointer_mode_set_resizing(void) {
- switch (config->resizing_key) {
- case M_LEFT_CLICK:
- set_initial_view(pointer_state.left.view);
- break;
- case M_RIGHT_CLICK:
- set_initial_view(pointer_state.right.view);
- break;
- }
- // Setup locking information
- int midway_x = initial.ptr->x + initial.ptr->width/2;
- int midway_y = initial.ptr->y + initial.ptr->height/2;
-
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- lock.left = x > midway_x;
- lock.top = y > midway_y;
-
- if (initial.ptr->is_floating) {
- pointer_state.mode = M_RESIZING | M_FLOATING;
- } else {
- pointer_state.mode = M_RESIZING | M_TILING;
- set_initial_sibling();
- }
-}
-
-// Mode set/update/reset
-
-void pointer_mode_set(uint32_t button, bool condition) {
- // switch on drag/resize mode
- switch (pointer_state.mode & (M_DRAGGING | M_RESIZING)) {
- case M_DRAGGING:
- // end drag mode when 'dragging' click is unpressed
- if (config->dragging_key == M_LEFT_CLICK && !pointer_state.left.held) {
- pointer_state.mode = 0;
- } else if (config->dragging_key == M_RIGHT_CLICK && !pointer_state.right.held) {
- pointer_state.mode = 0;
- }
- break;
-
- case M_RESIZING:
- // end resize mode when 'resizing' click is unpressed
- if (config->resizing_key == M_LEFT_CLICK && !pointer_state.left.held) {
- pointer_state.mode = 0;
- } else if (config->resizing_key == M_RIGHT_CLICK && !pointer_state.right.held) {
- pointer_state.mode = 0;
- }
- break;
-
- // No mode case
- default:
- // return if failed condition, or no view
- if (!condition || !pointer_state.view) {
- break;
- }
-
- // Set mode depending on current button press
- switch (button) {
- // Start left-click mode
- case M_LEFT_CLICK:
- // if button release don't do anything
- if (pointer_state.left.held) {
- if (config->dragging_key == M_LEFT_CLICK) {
- pointer_mode_set_dragging();
- } else if (config->resizing_key == M_LEFT_CLICK) {
- pointer_mode_set_resizing();
- }
- }
- break;
-
- // Start right-click mode
- case M_RIGHT_CLICK:
- // if button release don't do anyhting
- if (pointer_state.right.held) {
- if (config->dragging_key == M_RIGHT_CLICK) {
- pointer_mode_set_dragging();
- } else if (config->resizing_key == M_RIGHT_CLICK) {
- pointer_mode_set_resizing();
- }
- }
- break;
- }
- }
-}
-
-void pointer_mode_update(void) {
- if (initial.ptr->type != C_VIEW) {
- pointer_state.mode = 0;
- return;
- }
- double x, y;
- wlc_pointer_get_position_v2(&x, &y);
- int dx = x;
- int dy = y;
-
- switch (pointer_state.mode) {
- case M_FLOATING | M_DRAGGING:
- // Update position
- switch (config->dragging_key) {
- case M_LEFT_CLICK:
- dx -= pointer_state.left.x;
- dy -= pointer_state.left.y;
- break;
- case M_RIGHT_CLICK:
- dx -= pointer_state.right.x;
- dy -= pointer_state.right.y;
- break;
- }
-
- if (initial.x + dx != initial.ptr->x) {
- initial.ptr->x = initial.x + dx;
- }
- if (initial.y + dy != initial.ptr->y) {
- initial.ptr->y = initial.y + dy;
- }
- update_geometry(initial.ptr);
- break;
-
- case M_FLOATING | M_RESIZING:
- switch (config->resizing_key) {
- case M_LEFT_CLICK:
- dx -= pointer_state.left.x;
- dy -= pointer_state.left.y;
- initial.ptr = pointer_state.left.view;
- break;
- case M_RIGHT_CLICK:
- dx -= pointer_state.right.x;
- dy -= pointer_state.right.y;
- initial.ptr = pointer_state.right.view;
- break;
- }
-
- if (lock.left) {
- if (initial.w + dx > min_sane_w) {
- initial.ptr->width = initial.w + dx;
- }
- } else { // lock.right
- if (initial.w - dx > min_sane_w) {
- initial.ptr->width = initial.w - dx;
- initial.ptr->x = initial.x + dx;
- }
- }
- if (lock.top) {
- if (initial.h + dy > min_sane_h) {
- initial.ptr->height = initial.h + dy;
- }
- } else { // lock.bottom
- if (initial.h - dy > min_sane_h) {
- initial.ptr->height = initial.h - dy;
- initial.ptr->y = initial.y + dy;
- }
- }
- update_geometry(initial.ptr);
- break;
-
- case M_TILING | M_DRAGGING:
- // swap current view under pointer with dragged view
- if (pointer_state.view && pointer_state.view->type == C_VIEW
- && pointer_state.view != initial.ptr
- && !pointer_state.view->is_floating) {
- // Swap them around
- swap_container(pointer_state.view, initial.ptr);
- swap_geometry(pointer_state.view, initial.ptr);
- update_geometry(pointer_state.view);
- update_geometry(initial.ptr);
- // Set focus back to initial view
- set_focused_container(initial.ptr);
- // Arrange the windows
- arrange_windows(&root_container, -1, -1);
- }
- break;
-
- case M_TILING | M_RESIZING:
- switch (config->resizing_key) {
- case M_LEFT_CLICK:
- dx -= pointer_state.left.x;
- dy -= pointer_state.left.y;
- break;
- case M_RIGHT_CLICK:
- dx -= pointer_state.right.x;
- dy -= pointer_state.right.y;
- break;
- }
-
- // resize if we can
- if (initial.horiz.ptr) {
- if (lock.left) {
- // Check whether its fine to resize
- if (initial.w + dx > min_sane_w && initial.horiz.w - dx > min_sane_w) {
- initial.horiz.ptr->width = initial.horiz.w - dx;
- initial.horiz.parent.ptr->width = initial.horiz.parent.w + dx;
- }
- } else { // lock.right
- if (initial.w - dx > min_sane_w && initial.horiz.w + dx > min_sane_w) {
- initial.horiz.ptr->width = initial.horiz.w + dx;
- initial.horiz.parent.ptr->width = initial.horiz.parent.w - dx;
- }
- }
- arrange_windows(initial.horiz.ptr->parent, -1, -1);
- }
- if (initial.vert.ptr) {
- if (lock.top) {
- if (initial.h + dy > min_sane_h && initial.vert.h - dy > min_sane_h) {
- initial.vert.ptr->height = initial.vert.h - dy;
- initial.vert.parent.ptr->height = initial.vert.parent.h + dy;
- }
- } else { // lock.bottom
- if (initial.h - dy > min_sane_h && initial.vert.h + dy > min_sane_h) {
- initial.vert.ptr->height = initial.vert.h + dy;
- initial.vert.parent.ptr->height = initial.vert.parent.h - dy;
- }
- }
- arrange_windows(initial.vert.ptr->parent, -1, -1);
- }
- default:
- return;
- }
-}
-
-void pointer_mode_reset(void) {
- switch (pointer_state.mode) {
- case M_FLOATING | M_RESIZING:
- case M_FLOATING | M_DRAGGING:
- reset_initial_view();
- break;
-
- case M_TILING | M_RESIZING:
- (void) reset_initial_sibling;
- break;
-
- case M_TILING | M_DRAGGING:
- default:
- break;
- }
-}
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 @@
#include <json-c/json.h>
+#include <stdio.h>
#include <ctype.h>
-#include <string.h>
-#include <stdint.h>
-#include <libinput.h>
-#include "sway/container.h"
-#include "sway/input.h"
+#include "log.h"
#include "sway/ipc-json.h"
-#include "util.h"
-
-static json_object *ipc_json_create_rect(swayc_t *c) {
- json_object *rect = json_object_new_object();
+#include "sway/tree/container.h"
+#include "sway/output.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_output.h>
+#include "wlr-layer-shell-unstable-v1-protocol.h"
+
+static const char *ipc_json_layout_description(enum sway_container_layout l) {
+ switch (l) {
+ case L_VERT:
+ return "splitv";
+ case L_HORIZ:
+ return "splith";
+ case L_TABBED:
+ return "tabbed";
+ case L_STACKED:
+ return "stacked";
+ case L_FLOATING:
+ return "floating";
+ case L_NONE:
+ break;
+ }
+ return "none";
+}
- json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x));
- json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y));
+json_object *ipc_json_get_version() {
+ int major = 0, minor = 0, patch = 0;
+ json_object *version = json_object_new_object();
- struct wlc_size size;
- if (c->type == C_OUTPUT) {
- size = *wlc_output_get_resolution(c->handle);
- } else {
- size.w = c->width;
- size.h = c->height;
- }
+ sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch);
- json_object_object_add(rect, "width", json_object_new_int((int32_t)size.w));
- json_object_object_add(rect, "height", json_object_new_int((int32_t)size.h));
+ json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION));
+ json_object_object_add(version, "variant", json_object_new_string("sway"));
+ json_object_object_add(version, "major", json_object_new_int(major));
+ json_object_object_add(version, "minor", json_object_new_int(minor));
+ json_object_object_add(version, "patch", json_object_new_int(patch));
- return rect;
+ return version;
}
-static json_object *ipc_json_create_rect_from_geometry(struct wlc_geometry g) {
+static json_object *ipc_json_create_rect(struct sway_container *c) {
json_object *rect = json_object_new_object();
- json_object_object_add(rect, "x", json_object_new_int(g.origin.x));
- json_object_object_add(rect, "y", json_object_new_int(g.origin.y));
- json_object_object_add(rect, "width", json_object_new_int(g.size.w));
- json_object_object_add(rect, "height", json_object_new_int(g.size.h));
+ json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x));
+ json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y));
+ json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width));
+ json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height));
return rect;
}
-static const char *ipc_json_border_description(swayc_t *c) {
- const char *border;
-
- switch (c->border_type) {
- case B_PIXEL:
- border = "1pixel";
- break;
-
- case B_NORMAL:
- border = "normal";
- break;
-
- case B_NONE: // fallthrough
- default:
- border = "none";
- break;
- }
-
- return border;
+static void ipc_json_describe_root(struct sway_container *root, json_object *object) {
+ json_object_object_add(object, "type", json_object_new_string("root"));
+ json_object_object_add(object, "layout", json_object_new_string("splith"));
}
-static const char *ipc_json_layout_description(enum swayc_layouts l) {
- const char *layout;
-
- switch (l) {
- case L_VERT:
- layout = "splitv";
- break;
-
- case L_HORIZ:
- layout = "splith";
- break;
-
- case L_TABBED:
- layout = "tabbed";
- break;
-
- case L_STACKED:
- layout = "stacked";
- break;
-
- case L_FLOATING:
- layout = "floating";
- break;
-
- case L_NONE: // fallthrough
- case L_LAYOUTS: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
- default:
- layout = "null";
- break;
+static const char *ipc_json_get_output_transform(enum wl_output_transform transform) {
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ return "normal";
+ case WL_OUTPUT_TRANSFORM_90:
+ return "90";
+ case WL_OUTPUT_TRANSFORM_180:
+ return "180";
+ case WL_OUTPUT_TRANSFORM_270:
+ return "270";
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ return "flipped";
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ return "flipped-90";
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ return "flipped-180";
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ return "flipped-270";
}
-
- return layout;
+ return NULL;
}
-static float ipc_json_child_percentage(swayc_t *c) {
- float percent = 0;
- swayc_t *parent = c->parent;
-
- if (parent) {
- switch (parent->layout) {
- case L_VERT:
- percent = c->height / parent->height;
- break;
-
- case L_HORIZ:
- percent = c->width / parent->width;
- break;
-
- case L_STACKED: // fallthrough
- case L_TABBED: // fallthrough
- percent = 1.0;
- break;
-
- default:
- break;
+static void ipc_json_describe_output(struct sway_container *container, json_object *object) {
+ struct wlr_output *wlr_output = container->sway_output->wlr_output;
+ json_object_object_add(object, "type",
+ json_object_new_string("output"));
+ json_object_object_add(object, "active",
+ json_object_new_boolean(true));
+ json_object_object_add(object, "primary",
+ json_object_new_boolean(false));
+ json_object_object_add(object, "layout",
+ json_object_new_string("output"));
+ json_object_object_add(object, "make",
+ json_object_new_string(wlr_output->make));
+ json_object_object_add(object, "model",
+ json_object_new_string(wlr_output->model));
+ json_object_object_add(object, "serial",
+ json_object_new_string(wlr_output->serial));
+ json_object_object_add(object, "scale",
+ json_object_new_double(wlr_output->scale));
+ json_object_object_add(object, "refresh",
+ json_object_new_int(wlr_output->refresh));
+ json_object_object_add(object, "transform",
+ json_object_new_string(
+ ipc_json_get_output_transform(wlr_output->transform)));
+
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+ const char *ws = NULL;
+ if (seat) {
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, container);
+ if (focus && focus->type != C_WORKSPACE) {
+ focus = container_parent(focus, C_WORKSPACE);
+ }
+ if (focus) {
+ ws = focus->name;
}
}
+ json_object_object_add(object, "current_workspace",
+ json_object_new_string(ws));
+
+ json_object *modes_array = json_object_new_array();
+ struct wlr_output_mode *mode;
+ wl_list_for_each(mode, &wlr_output->modes, link) {
+ json_object *mode_object = json_object_new_object();
+ json_object_object_add(mode_object, "width",
+ json_object_new_int(mode->width));
+ json_object_object_add(mode_object, "height",
+ json_object_new_int(mode->height));
+ json_object_object_add(mode_object, "refresh",
+ json_object_new_int(mode->refresh));
+ json_object_array_add(modes_array, mode_object);
+ }
- return percent;
-}
-
-static void ipc_json_describe_output(swayc_t *output, json_object *object) {
- uint32_t scale = wlc_output_get_scale(output->handle);
- json_object_object_add(object, "active", json_object_new_boolean(true));
- json_object_object_add(object, "primary", json_object_new_boolean(false));
+ json_object_object_add(object, "modes", modes_array);
json_object_object_add(object, "layout", json_object_new_string("output"));
- json_object_object_add(object, "type", json_object_new_string("output"));
- json_object_object_add(object, "current_workspace",
- (output->focused) ? json_object_new_string(output->focused->name) : NULL);
- json_object_object_add(object, "scale", json_object_new_int(scale));
}
-static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object) {
- int num = (isdigit(workspace->name[0])) ? atoi(workspace->name) : -1;
- const char *layout = ipc_json_layout_description(workspace->workspace_layout);
+static void ipc_json_describe_workspace(struct sway_container *workspace,
+ json_object *object) {
+ int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1;
json_object_object_add(object, "num", json_object_new_int(num));
- json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL);
+ json_object_object_add(object, "output", workspace->parent ?
+ json_object_new_string(workspace->parent->name) : NULL);
json_object_object_add(object, "type", json_object_new_string("workspace"));
- json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
-}
+ json_object_object_add(object, "urgent", json_object_new_boolean(false));
-// window is in the scratchpad ? changed : none
-static const char *ipc_json_get_scratchpad_state(swayc_t *c) {
- int i;
- for (i = 0; i < scratchpad->length; i++) {
- if (scratchpad->items[i] == c) {
- return "changed";
- }
- }
- return "none"; // we ignore the fresh value
+ const char *layout = ipc_json_layout_description(workspace->workspace_layout);
+ json_object_object_add(object, "layout", json_object_new_string(layout));
}
-static void ipc_json_describe_view(swayc_t *c, json_object *object) {
- json_object *props = json_object_new_object();
- json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con"));
-
- wlc_handle parent = wlc_view_get_parent(c->handle);
- json_object_object_add(object, "scratchpad_state",
- json_object_new_string(ipc_json_get_scratchpad_state(c)));
-
- json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
-
- json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) :
- c->app_id ? json_object_new_string(c->app_id) : NULL);
- json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) :
- c->app_id ? json_object_new_string(c->app_id) : NULL);
- json_object_object_add(props, "title", (c->name) ? json_object_new_string(c->name) : NULL);
- json_object_object_add(props, "transient_for", parent ? json_object_new_int(parent) : NULL);
- json_object_object_add(object, "window_properties", props);
-
- json_object_object_add(object, "fullscreen_mode",
- json_object_new_int(swayc_is_fullscreen(c) ? 1 : 0));
- json_object_object_add(object, "sticky", json_object_new_boolean(c->sticky));
- json_object_object_add(object, "floating", json_object_new_string(
- c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause
-
- json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL);
+static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
+ json_object_object_add(object, "name",
+ c->name ? json_object_new_string(c->name) : NULL);
+ json_object_object_add(object, "type", json_object_new_string("con"));
if (c->parent) {
- const char *layout = (c->parent->type == C_CONTAINER) ?
- ipc_json_layout_description(c->parent->layout) : "none";
- const char *last_layout = (c->parent->type == C_CONTAINER) ?
- ipc_json_layout_description(c->parent->prev_layout) : "none";
+ enum sway_container_layout layout = (c->parent->type == C_CONTAINER) ?
+ c->parent->layout : c->layout;
+
json_object_object_add(object, "layout",
- (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
- json_object_object_add(object, "last_split_layout",
- (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout));
- json_object_object_add(object, "workspace_layout",
- json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout)));
+ json_object_new_string(ipc_json_layout_description(layout)));
}
}
-static void ipc_json_describe_root(swayc_t *c, json_object *object) {
- json_object_object_add(object, "type", json_object_new_string("root"));
- json_object_object_add(object, "layout", json_object_new_string("splith"));
+static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
+ json_object *focus = data;
+ json_object_array_add(focus, json_object_new_int(c->id));
}
-json_object *ipc_json_describe_container(swayc_t *c) {
- float percent = ipc_json_child_percentage(c);
-
+json_object *ipc_json_describe_container(struct sway_container *c) {
if (!(sway_assert(c, "Container must not be null."))) {
return NULL;
}
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+ bool focused = seat_get_focus(seat) == c;
+
json_object *object = json_object_new_object();
json_object_object_add(object, "id", json_object_new_int((int)c->id));
- json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
+ json_object_object_add(object, "name",
+ c->name ? json_object_new_string(c->name) : NULL);
json_object_object_add(object, "rect", ipc_json_create_rect(c));
- json_object_object_add(object, "visible", json_object_new_boolean(c->visible));
- json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus));
-
- json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c)));
- json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry));
- json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry));
- json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry));
- json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL);
- json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat
- // TODO: make urgency actually work once Sway supports it
- json_object_object_add(object, "urgent", json_object_new_boolean(false));
- json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness));
+ json_object_object_add(object, "focused",
+ json_object_new_boolean(focused));
+
+ json_object *focus = json_object_new_array();
+ seat_focus_inactive_children_for_each(seat, c,
+ focus_inactive_children_iterator, focus);
+ json_object_object_add(object, "focus", focus);
switch (c->type) {
case C_ROOT:
ipc_json_describe_root(c, object);
break;
-
case C_OUTPUT:
ipc_json_describe_output(c, object);
break;
-
- case C_CONTAINER: // fallthrough
+ case C_CONTAINER:
case C_VIEW:
ipc_json_describe_view(c, object);
break;
-
case C_WORKSPACE:
ipc_json_describe_workspace(c, object);
break;
-
- case C_TYPES: // fallthrough; this should never happen, I'm just trying to silence compiler warnings
+ case C_TYPES:
default:
break;
}
@@ -249,80 +215,56 @@ json_object *ipc_json_describe_container(swayc_t *c) {
return object;
}
-json_object *ipc_json_describe_input(struct libinput_device *device) {
- char* identifier = libinput_dev_unique_id(device);
- int vendor = libinput_device_get_id_vendor(device);
- int product = libinput_device_get_id_product(device);
- const char *name = libinput_device_get_name(device);
- double width = -1, height = -1;
- int has_size = libinput_device_get_size(device, &width, &height);
-
- json_object *device_object = json_object_new_object();
- json_object_object_add(device_object,"identifier",
- identifier ? json_object_new_string(identifier) : NULL);
- json_object_object_add(device_object,
- "vendor", json_object_new_int(vendor));
- json_object_object_add(device_object,
- "product", json_object_new_int(product));
- json_object_object_add(device_object,
- "name", json_object_new_string(name));
- if (has_size == 0) {
- json_object *size_object = json_object_new_object();
- json_object_object_add(size_object,
- "width", json_object_new_double(width));
- json_object_object_add(size_object,
- "height", json_object_new_double(height));
- } else {
- json_object_object_add(device_object, "size", NULL);
- }
+json_object *ipc_json_describe_container_recursive(struct sway_container *c) {
+ json_object *object = ipc_json_describe_container(c);
+ int i;
- struct {
- enum libinput_device_capability cap;
- const char *name;
- // If anyone feels like implementing device-specific IPC output,
- // be my guest
- json_object *(*describe)(struct libinput_device *);
- } caps[] = {
- { LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard", NULL },
- { LIBINPUT_DEVICE_CAP_POINTER, "pointer", NULL },
- { LIBINPUT_DEVICE_CAP_TOUCH, "touch", NULL },
- { LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet_tool", NULL },
- { LIBINPUT_DEVICE_CAP_TABLET_PAD, "tablet_pad", NULL },
- { LIBINPUT_DEVICE_CAP_GESTURE, "gesture", NULL },
-#ifdef LIBINPUT_DEVICE_CAP_SWITCH // libinput 1.7.0+
- { LIBINPUT_DEVICE_CAP_SWITCH, "switch", NULL },
-#endif
- };
-
- json_object *_caps = json_object_new_array();
- for (size_t i = 0; i < sizeof(caps) / sizeof(caps[0]); ++i) {
- if (libinput_device_has_capability(device, caps[i].cap)) {
- json_object_array_add(_caps, json_object_new_string(caps[i].name));
- if (caps[i].describe) {
- json_object *desc = caps[i].describe(device);
- json_object_object_add(device_object, caps[i].name, desc);
- }
+ json_object *children = json_object_new_array();
+ if (c->type != C_VIEW && c->children) {
+ for (i = 0; i < c->children->length; ++i) {
+ json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
}
}
- json_object_object_add(device_object, "capabilities", _caps);
+ json_object_object_add(object, "nodes", children);
- free(identifier);
- return device_object;
+ return object;
}
-json_object *ipc_json_get_version() {
- int major = 0, minor = 0, patch = 0;
- json_object *version = json_object_new_object();
+static const char *describe_device_type(struct sway_input_device *device) {
+ switch (device->wlr_device->type) {
+ case WLR_INPUT_DEVICE_POINTER:
+ return "pointer";
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ return "keyboard";
+ case WLR_INPUT_DEVICE_TOUCH:
+ return "touch";
+ case WLR_INPUT_DEVICE_TABLET_TOOL:
+ return "tablet_tool";
+ case WLR_INPUT_DEVICE_TABLET_PAD:
+ return "tablet_pad";
+ }
+ return "unknown";
+}
- sscanf(SWAY_VERSION, "%u.%u.%u", &major, &minor, &patch);
+json_object *ipc_json_describe_input(struct sway_input_device *device) {
+ if (!(sway_assert(device, "Device must not be null"))) {
+ return NULL;
+ }
- json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION));
- json_object_object_add(version, "variant", json_object_new_string("sway"));
- json_object_object_add(version, "major", json_object_new_int(major));
- json_object_object_add(version, "minor", json_object_new_int(minor));
- json_object_object_add(version, "patch", json_object_new_int(patch));
+ json_object *object = json_object_new_object();
- return version;
+ json_object_object_add(object, "identifier",
+ json_object_new_string(device->identifier));
+ json_object_object_add(object, "name",
+ json_object_new_string(device->wlr_device->name));
+ json_object_object_add(object, "vendor",
+ json_object_new_int(device->wlr_device->vendor));
+ json_object_object_add(object, "product",
+ json_object_new_int(device->wlr_device->product));
+ json_object_object_add(object, "type",
+ json_object_new_string(describe_device_type(device)));
+
+ return object;
}
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) {
json_object *json = json_object_new_object();
json_object_object_add(json, "id", json_object_new_string(bar->id));
-#ifdef ENABLE_TRAY
- if (bar->tray_output) {
- json_object_object_add(json, "tray_output", json_object_new_string(bar->tray_output));
- } else {
- json_object_object_add(json, "tray_output", NULL);
- }
- if (bar->icon_theme) {
- json_object_object_add(json, "icon_theme", json_object_new_string(bar->icon_theme));
- } else {
- json_object_object_add(json, "icon_theme", NULL);
- }
- json_object_object_add(json, "tray_padding", json_object_new_int(bar->tray_padding));
- json_object_object_add(json, "activate_button", json_object_new_int(bar->activate_button));
- json_object_object_add(json, "context_button", json_object_new_int(bar->context_button));
- json_object_object_add(json, "secondary_button", json_object_new_int(bar->secondary_button));
-#endif
json_object_object_add(json, "mode", json_object_new_string(bar->mode));
- json_object_object_add(json, "hidden_state", json_object_new_string(bar->hidden_state));
- json_object_object_add(json, "modifier", json_object_new_string(get_modifier_name_by_mask(bar->modifier)));
- switch (bar->position) {
- case DESKTOP_SHELL_PANEL_POSITION_TOP:
- json_object_object_add(json, "position", json_object_new_string("top"));
- break;
- case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
- json_object_object_add(json, "position", json_object_new_string("bottom"));
- break;
- case DESKTOP_SHELL_PANEL_POSITION_LEFT:
- json_object_object_add(json, "position", json_object_new_string("left"));
- break;
- case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
- json_object_object_add(json, "position", json_object_new_string("right"));
- break;
- }
- json_object_object_add(json, "status_command", json_object_new_string(bar->status_command));
- json_object_object_add(json, "font", json_object_new_string((bar->font) ? bar->font : config->font));
+ json_object_object_add(json, "hidden_state",
+ json_object_new_string(bar->hidden_state));
+ json_object_object_add(json, "position",
+ json_object_new_string(bar->position));
+ json_object_object_add(json, "status_command",
+ json_object_new_string(bar->status_command));
+ json_object_object_add(json, "font",
+ json_object_new_string((bar->font) ? bar->font : config->font));
if (bar->separator_symbol) {
- json_object_object_add(json, "separator_symbol", json_object_new_string(bar->separator_symbol));
+ json_object_object_add(json, "separator_symbol",
+ json_object_new_string(bar->separator_symbol));
}
- json_object_object_add(json, "bar_height", json_object_new_int(bar->height));
- json_object_object_add(json, "wrap_scroll", json_object_new_boolean(bar->wrap_scroll));
- json_object_object_add(json, "workspace_buttons", json_object_new_boolean(bar->workspace_buttons));
- json_object_object_add(json, "strip_workspace_numbers", json_object_new_boolean(bar->strip_workspace_numbers));
- json_object_object_add(json, "binding_mode_indicator", json_object_new_boolean(bar->binding_mode_indicator));
- json_object_object_add(json, "verbose", json_object_new_boolean(bar->verbose));
- json_object_object_add(json, "pango_markup", json_object_new_boolean(bar->pango_markup));
+ json_object_object_add(json, "bar_height",
+ json_object_new_int(bar->height));
+ json_object_object_add(json, "wrap_scroll",
+ json_object_new_boolean(bar->wrap_scroll));
+ json_object_object_add(json, "workspace_buttons",
+ json_object_new_boolean(bar->workspace_buttons));
+ json_object_object_add(json, "strip_workspace_numbers",
+ json_object_new_boolean(bar->strip_workspace_numbers));
+ json_object_object_add(json, "binding_mode_indicator",
+ json_object_new_boolean(bar->binding_mode_indicator));
+ json_object_object_add(json, "verbose",
+ json_object_new_boolean(bar->verbose));
+ json_object_object_add(json, "pango_markup",
+ json_object_new_boolean(bar->pango_markup));
json_object *colors = json_object_new_object();
- json_object_object_add(colors, "background", json_object_new_string(bar->colors.background));
- json_object_object_add(colors, "statusline", json_object_new_string(bar->colors.statusline));
- json_object_object_add(colors, "separator", json_object_new_string(bar->colors.separator));
+ json_object_object_add(colors, "background",
+ json_object_new_string(bar->colors.background));
+ json_object_object_add(colors, "statusline",
+ json_object_new_string(bar->colors.statusline));
+ json_object_object_add(colors, "separator",
+ json_object_new_string(bar->colors.separator));
if (bar->colors.focused_background) {
- json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.focused_background));
+ json_object_object_add(colors, "focused_background",
+ json_object_new_string(bar->colors.focused_background));
} else {
- json_object_object_add(colors, "focused_background", json_object_new_string(bar->colors.background));
+ json_object_object_add(colors, "focused_background",
+ json_object_new_string(bar->colors.background));
}
if (bar->colors.focused_statusline) {
- json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.focused_statusline));
+ json_object_object_add(colors, "focused_statusline",
+ json_object_new_string(bar->colors.focused_statusline));
} else {
- json_object_object_add(colors, "focused_statusline", json_object_new_string(bar->colors.statusline));
+ json_object_object_add(colors, "focused_statusline",
+ json_object_new_string(bar->colors.statusline));
}
if (bar->colors.focused_separator) {
- json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.focused_separator));
+ json_object_object_add(colors, "focused_separator",
+ json_object_new_string(bar->colors.focused_separator));
} else {
- json_object_object_add(colors, "focused_separator", json_object_new_string(bar->colors.separator));
+ json_object_object_add(colors, "focused_separator",
+ json_object_new_string(bar->colors.separator));
}
- json_object_object_add(colors, "focused_workspace_border", json_object_new_string(bar->colors.focused_workspace_border));
- json_object_object_add(colors, "focused_workspace_bg", json_object_new_string(bar->colors.focused_workspace_bg));
- json_object_object_add(colors, "focused_workspace_text", json_object_new_string(bar->colors.focused_workspace_text));
-
- json_object_object_add(colors, "inactive_workspace_border", json_object_new_string(bar->colors.inactive_workspace_border));
- json_object_object_add(colors, "inactive_workspace_bg", json_object_new_string(bar->colors.inactive_workspace_bg));
- json_object_object_add(colors, "inactive_workspace_text", json_object_new_string(bar->colors.inactive_workspace_text));
-
- json_object_object_add(colors, "active_workspace_border", json_object_new_string(bar->colors.active_workspace_border));
- json_object_object_add(colors, "active_workspace_bg", json_object_new_string(bar->colors.active_workspace_bg));
- json_object_object_add(colors, "active_workspace_text", json_object_new_string(bar->colors.active_workspace_text));
-
- json_object_object_add(colors, "urgent_workspace_border", json_object_new_string(bar->colors.urgent_workspace_border));
- json_object_object_add(colors, "urgent_workspace_bg", json_object_new_string(bar->colors.urgent_workspace_bg));
- json_object_object_add(colors, "urgent_workspace_text", json_object_new_string(bar->colors.urgent_workspace_text));
+ json_object_object_add(colors, "focused_workspace_border",
+ json_object_new_string(bar->colors.focused_workspace_border));
+ json_object_object_add(colors, "focused_workspace_bg",
+ json_object_new_string(bar->colors.focused_workspace_bg));
+ json_object_object_add(colors, "focused_workspace_text",
+ json_object_new_string(bar->colors.focused_workspace_text));
+
+ json_object_object_add(colors, "inactive_workspace_border",
+ json_object_new_string(bar->colors.inactive_workspace_border));
+ json_object_object_add(colors, "inactive_workspace_bg",
+ json_object_new_string(bar->colors.inactive_workspace_bg));
+ json_object_object_add(colors, "inactive_workspace_text",
+ json_object_new_string(bar->colors.inactive_workspace_text));
+
+ json_object_object_add(colors, "active_workspace_border",
+ json_object_new_string(bar->colors.active_workspace_border));
+ json_object_object_add(colors, "active_workspace_bg",
+ json_object_new_string(bar->colors.active_workspace_bg));
+ json_object_object_add(colors, "active_workspace_text",
+ json_object_new_string(bar->colors.active_workspace_text));
+
+ json_object_object_add(colors, "urgent_workspace_border",
+ json_object_new_string(bar->colors.urgent_workspace_border));
+ json_object_object_add(colors, "urgent_workspace_bg",
+ json_object_new_string(bar->colors.urgent_workspace_bg));
+ json_object_object_add(colors, "urgent_workspace_text",
+ json_object_new_string(bar->colors.urgent_workspace_text));
if (bar->colors.binding_mode_border) {
- json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.binding_mode_border));
+ json_object_object_add(colors, "binding_mode_border",
+ json_object_new_string(bar->colors.binding_mode_border));
} else {
- json_object_object_add(colors, "binding_mode_border", json_object_new_string(bar->colors.urgent_workspace_border));
+ json_object_object_add(colors, "binding_mode_border",
+ json_object_new_string(bar->colors.urgent_workspace_border));
}
if (bar->colors.binding_mode_bg) {
- json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.binding_mode_bg));
+ json_object_object_add(colors, "binding_mode_bg",
+ json_object_new_string(bar->colors.binding_mode_bg));
} else {
- json_object_object_add(colors, "binding_mode_bg", json_object_new_string(bar->colors.urgent_workspace_bg));
+ json_object_object_add(colors, "binding_mode_bg",
+ json_object_new_string(bar->colors.urgent_workspace_bg));
}
if (bar->colors.binding_mode_text) {
- json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.binding_mode_text));
+ json_object_object_add(colors, "binding_mode_text",
+ json_object_new_string(bar->colors.binding_mode_text));
} else {
- json_object_object_add(colors, "binding_mode_text", json_object_new_string(bar->colors.urgent_workspace_text));
+ json_object_object_add(colors, "binding_mode_text",
+ json_object_new_string(bar->colors.urgent_workspace_text));
}
json_object_object_add(json, "colors", colors);
@@ -440,75 +391,11 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
// Add outputs if defined
if (bar->outputs && bar->outputs->length > 0) {
json_object *outputs = json_object_new_array();
- int i;
- for (i = 0; i < bar->outputs->length; ++i) {
+ for (int i = 0; i < bar->outputs->length; ++i) {
const char *name = bar->outputs->items[i];
json_object_array_add(outputs, json_object_new_string(name));
}
json_object_object_add(json, "outputs", outputs);
}
-
return json;
}
-
-json_object *ipc_json_describe_container_recursive(swayc_t *c) {
- json_object *object = ipc_json_describe_container(c);
- int i;
-
- json_object *floating = json_object_new_array();
- if (c->type != C_VIEW && c->floating) {
- for (i = 0; i < c->floating->length; ++i) {
- swayc_t *item = c->floating->items[i];
- json_object_array_add(floating, ipc_json_describe_container_recursive(item));
- }
- }
- json_object_object_add(object, "floating_nodes", floating);
-
- json_object *children = json_object_new_array();
- if (c->type != C_VIEW && c->children) {
- for (i = 0; i < c->children->length; ++i) {
- json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
- }
- }
- json_object_object_add(object, "nodes", children);
-
- json_object *focus = json_object_new_array();
- if (c->type != C_VIEW) {
- if (c->focused) {
- json_object_array_add(focus, json_object_new_double(c->focused->id));
- }
- if (c->floating) {
- for (i = 0; i < c->floating->length; ++i) {
- swayc_t *item = c->floating->items[i];
- if (item == c->focused) {
- continue;
- }
-
- json_object_array_add(focus, json_object_new_double(item->id));
- }
- }
- if (c->children) {
- for (i = 0; i < c->children->length; ++i) {
- swayc_t *item = c->children->items[i];
- if (item == c->focused) {
- continue;
- }
-
- json_object_array_add(focus, json_object_new_double(item->id));
- }
- }
- }
- json_object_object_add(object, "focus", focus);
-
- if (c->type == C_ROOT) {
- json_object *scratchpad_json = json_object_new_array();
- if (scratchpad->length > 0) {
- for (i = 0; i < scratchpad->length; ++i) {
- json_object_array_add(scratchpad_json, ipc_json_describe_container_recursive(scratchpad->items[i]));
- }
- }
- json_object_object_add(object, "scratchpad", scratchpad_json);
- }
-
- return object;
-}
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 @@
// See https://i3wm.org/docs/ipc.html for protocol information
-
#ifndef __FreeBSD__
// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
#define _XOPEN_SOURCE 700
#endif
-
+#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
+#include <json-c/json.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <sys/un.h>
-#include <stdbool.h>
-#include <wlc/wlc-render.h>
#include <unistd.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <json-c/json.h>
-#include <list.h>
-#include <libinput.h>
-#ifdef __linux__
-struct ucred {
- pid_t pid;
- uid_t uid;
- gid_t gid;
-};
-#endif
+#include <wayland-server.h>
+#include "sway/commands.h"
#include "sway/ipc-json.h"
#include "sway/ipc-server.h"
-#include "sway/security.h"
-#include "sway/config.h"
-#include "sway/commands.h"
-#include "sway/input.h"
-#include "stringop.h"
-#include "log.h"
+#include "sway/server.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
#include "list.h"
-#include "util.h"
+#include "log.h"
static int ipc_socket = -1;
-static struct wlc_event_source *ipc_event_source = NULL;
+static struct wl_event_source *ipc_event_source = NULL;
static struct sockaddr_un *ipc_sockaddr = NULL;
static list_t *ipc_client_list = NULL;
static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
struct ipc_client {
- struct wlc_event_source *event_source;
- struct wlc_event_source *writable_event_source;
+ struct wl_event_source *event_source;
+ struct wl_event_source *writable_event_source;
+ struct sway_server *server;
int fd;
uint32_t payload_length;
uint32_t security_policy;
@@ -56,27 +46,6 @@ struct ipc_client {
char *write_buffer;
};
-static list_t *ipc_get_pixel_requests = NULL;
-
-struct get_pixels_request {
- struct ipc_client *client;
- wlc_handle output;
- struct wlc_geometry geo;
-};
-
-struct get_clipboard_request {
- struct ipc_client *client;
- json_object *json;
- int fd;
- struct wlc_event_source *fd_event_source;
- struct wlc_event_source *timer_event_source;
- char *type;
- unsigned int *pending;
- char *buf;
- size_t buf_size;
- size_t buf_position;
-};
-
struct sockaddr_un *ipc_user_sockaddr(void);
int ipc_handle_connection(int fd, uint32_t mask, void *data);
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);
void ipc_client_disconnect(struct ipc_client *client);
void ipc_client_handle_command(struct ipc_client *client);
bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
-void ipc_get_workspaces_callback(swayc_t *workspace, void *data);
-void ipc_get_outputs_callback(swayc_t *container, void *data);
-static void ipc_get_marks_callback(swayc_t *container, void *data);
-void ipc_init(void) {
+void ipc_init(struct sway_server *server) {
ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (ipc_socket == -1) {
sway_abort("Unable to create IPC socket");
@@ -116,14 +82,14 @@ void ipc_init(void) {
setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1);
ipc_client_list = create_list();
- ipc_get_pixel_requests = create_list();
- ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL);
+ ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket,
+ WL_EVENT_READABLE, ipc_handle_connection, server);
}
void ipc_terminate(void) {
if (ipc_event_source) {
- wlc_event_source_remove(ipc_event_source);
+ wl_event_source_remove(ipc_event_source);
}
close(ipc_socket);
unlink(ipc_sockaddr->sun_path);
@@ -157,100 +123,82 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
return ipc_sockaddr;
}
-static pid_t get_client_pid(int client_fd) {
-// FreeBSD supports getting uid/gid, but not pid
-#ifdef __linux__
- struct ucred ucred;
- socklen_t len = sizeof(struct ucred);
-
- if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
- return -1;
- }
-
- return ucred.pid;
-#else
- return -1;
-#endif
-}
-
int ipc_handle_connection(int fd, uint32_t mask, void *data) {
- (void) fd; (void) data;
- sway_log(L_DEBUG, "Event on IPC listening socket");
- assert(mask == WLC_EVENT_READABLE);
+ (void) fd;
+ struct sway_server *server = data;
+ wlr_log(L_DEBUG, "Event on IPC listening socket");
+ assert(mask == WL_EVENT_READABLE);
int client_fd = accept(ipc_socket, NULL, NULL);
if (client_fd == -1) {
- sway_log_errno(L_ERROR, "Unable to accept IPC client connection");
+ wlr_log_errno(L_ERROR, "Unable to accept IPC client connection");
return 0;
}
int flags;
if ((flags = fcntl(client_fd, F_GETFD)) == -1
|| fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
- sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
+ wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
close(client_fd);
return 0;
}
if ((flags = fcntl(client_fd, F_GETFL)) == -1
|| fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
- sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
+ wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
close(client_fd);
return 0;
}
- struct ipc_client* client = malloc(sizeof(struct ipc_client));
+ struct ipc_client *client = malloc(sizeof(struct ipc_client));
if (!client) {
- sway_log(L_ERROR, "Unable to allocate ipc client");
+ wlr_log(L_ERROR, "Unable to allocate ipc client");
close(client_fd);
return 0;
}
+ client->server = server;
client->payload_length = 0;
client->fd = client_fd;
client->subscribed_events = 0;
- client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client);
+ client->event_source = wl_event_loop_add_fd(server->wl_event_loop,
+ client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client);
client->writable_event_source = NULL;
client->write_buffer_size = 128;
client->write_buffer_len = 0;
client->write_buffer = malloc(client->write_buffer_size);
if (!client->write_buffer) {
- sway_log(L_ERROR, "Unable to allocate ipc client write buffer");
+ wlr_log(L_ERROR, "Unable to allocate ipc client write buffer");
close(client_fd);
return 0;
}
- pid_t pid = get_client_pid(client->fd);
- client->security_policy = get_ipc_policy_mask(pid);
-
- sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid);
-
+ wlr_log(L_DEBUG, "New client: fd %d", client_fd);
list_add(ipc_client_list, client);
-
return 0;
}
-static const int ipc_header_size = sizeof(ipc_magic)+8;
+static const int ipc_header_size = sizeof(ipc_magic) + 8;
int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
struct ipc_client *client = data;
- if (mask & WLC_EVENT_ERROR) {
- sway_log(L_ERROR, "IPC Client socket error, removing client");
+ if (mask & WL_EVENT_ERROR) {
+ wlr_log(L_ERROR, "IPC Client socket error, removing client");
ipc_client_disconnect(client);
return 0;
}
- if (mask & WLC_EVENT_HANGUP) {
- sway_log(L_DEBUG, "Client %d hung up", client->fd);
+ if (mask & WL_EVENT_HANGUP) {
+ wlr_log(L_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client);
return 0;
}
- sway_log(L_DEBUG, "Client %d readable", client->fd);
+ wlr_log(L_DEBUG, "Client %d readable", client->fd);
int read_available;
if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
- sway_log_errno(L_INFO, "Unable to read IPC socket buffer size");
+ wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size");
ipc_client_disconnect(client);
return 0;
}
@@ -272,13 +220,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
// Should be fully available, because read_available >= ipc_header_size
ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
if (received == -1) {
- sway_log_errno(L_INFO, "Unable to receive header from IPC client");
+ wlr_log_errno(L_INFO, "Unable to receive header from IPC client");
ipc_client_disconnect(client);
return 0;
}
if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
- sway_log(L_DEBUG, "IPC header check failed");
+ wlr_log(L_DEBUG, "IPC header check failed");
ipc_client_disconnect(client);
return 0;
}
@@ -293,17 +241,110 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
return 0;
}
+static bool ipc_has_event_listeners(enum ipc_command_type event) {
+ for (int i = 0; i < ipc_client_list->length; i++) {
+ struct ipc_client *client = ipc_client_list->items[i];
+ if ((client->subscribed_events & event_mask(event)) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void ipc_send_event(const char *json_string, enum ipc_command_type event) {
+ struct ipc_client *client;
+ for (int i = 0; i < ipc_client_list->length; i++) {
+ client = ipc_client_list->items[i];
+ if ((client->subscribed_events & event_mask(event)) == 0) {
+ continue;
+ }
+ client->current_command = event;
+ if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
+ wlr_log_errno(L_INFO, "Unable to send reply to IPC client");
+ ipc_client_disconnect(client);
+ }
+ }
+}
+
+void ipc_event_workspace(struct sway_container *old,
+ struct sway_container *new, const char *change) {
+ if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending workspace::%s event", change);
+ json_object *obj = json_object_new_object();
+ json_object_object_add(obj, "change", json_object_new_string(change));
+ if (strcmp("focus", change) == 0) {
+ if (old) {
+ json_object_object_add(obj, "old",
+ ipc_json_describe_container_recursive(old));
+ } else {
+ json_object_object_add(obj, "old", NULL);
+ }
+ }
+
+ if (new) {
+ json_object_object_add(obj, "current",
+ ipc_json_describe_container_recursive(new));
+ } else {
+ json_object_object_add(obj, "current", NULL);
+ }
+
+ const char *json_string = json_object_to_json_string(obj);
+ ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
+ json_object_put(obj);
+}
+
+void ipc_event_window(struct sway_container *window, const char *change) {
+ if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending window::%s event", change);
+ json_object *obj = json_object_new_object();
+ json_object_object_add(obj, "change", json_object_new_string(change));
+ json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
+
+ const char *json_string = json_object_to_json_string(obj);
+ ipc_send_event(json_string, IPC_EVENT_WINDOW);
+ json_object_put(obj);
+}
+
+void ipc_event_barconfig_update(struct bar_config *bar) {
+ if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending barconfig_update event");
+ json_object *json = ipc_json_describe_bar_config(bar);
+
+ const char *json_string = json_object_to_json_string(json);
+ ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
+ json_object_put(json);
+}
+
+void ipc_event_mode(const char *mode) {
+ if (!ipc_has_event_listeners(IPC_EVENT_MODE)) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Sending mode::%s event", mode);
+ json_object *obj = json_object_new_object();
+ json_object_object_add(obj, "change", json_object_new_string(mode));
+
+ const char *json_string = json_object_to_json_string(obj);
+ ipc_send_event(json_string, IPC_EVENT_MODE);
+ json_object_put(obj);
+}
+
int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
struct ipc_client *client = data;
- if (mask & WLC_EVENT_ERROR) {
- sway_log(L_ERROR, "IPC Client socket error, removing client");
+ if (mask & WL_EVENT_ERROR) {
+ wlr_log(L_ERROR, "IPC Client socket error, removing client");
ipc_client_disconnect(client);
return 0;
}
- if (mask & WLC_EVENT_HANGUP) {
- sway_log(L_DEBUG, "Client %d hung up", client->fd);
+ if (mask & WL_EVENT_HANGUP) {
+ wlr_log(L_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client);
return 0;
}
@@ -312,14 +353,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
return 0;
}
- sway_log(L_DEBUG, "Client %d writable", client->fd);
+ wlr_log(L_DEBUG, "Client %d writable", client->fd);
ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
if (written == -1 && errno == EAGAIN) {
return 0;
} else if (written == -1) {
- sway_log_errno(L_INFO, "Unable to send data from queue to IPC client");
+ wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client");
ipc_client_disconnect(client);
return 0;
}
@@ -328,7 +369,7 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
client->write_buffer_len -= written;
if (client->write_buffer_len == 0 && client->writable_event_source) {
- wlc_event_source_remove(client->writable_event_source);
+ wl_event_source_remove(client->writable_event_source);
client->writable_event_source = NULL;
}
@@ -344,332 +385,48 @@ void ipc_client_disconnect(struct ipc_client *client) {
shutdown(client->fd, SHUT_RDWR);
}
- sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
- wlc_event_source_remove(client->event_source);
+ wlr_log(L_INFO, "IPC Client %d disconnected", client->fd);
+ wl_event_source_remove(client->event_source);
if (client->writable_event_source) {
- wlc_event_source_remove(client->writable_event_source);
+ wl_event_source_remove(client->writable_event_source);
}
int i = 0;
- while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++;
+ while (i < ipc_client_list->length && ipc_client_list->items[i] != client) {
+ i++;
+ }
list_del(ipc_client_list, i);
free(client->write_buffer);
close(client->fd);
free(client);
}
-bool output_by_name_test(swayc_t *view, void *data) {
- char *name = (char *)data;
- if (view->type != C_OUTPUT) {
- return false;
- }
- return !strcmp(name, view->name);
-}
-
-void ipc_get_pixels(wlc_handle output) {
- if (ipc_get_pixel_requests->length == 0) {
+static void ipc_get_workspaces_callback(struct sway_container *workspace,
+ void *data) {
+ if (workspace->type != C_WORKSPACE) {
return;
}
-
- list_t *unhandled = create_list();
-
- struct get_pixels_request *req;
- int i;
- for (i = 0; i < ipc_get_pixel_requests->length; ++i) {
- req = ipc_get_pixel_requests->items[i];
- if (req->output != output) {
- list_add(unhandled, req);
- continue;
- }
-
- const struct wlc_size *size = &req->geo.size;
- struct wlc_geometry g_out;
- char response_header[9];
- memset(response_header, 0, sizeof(response_header));
- char *data = malloc(sizeof(response_header) + size->w * size->h * 4);
- if (!data) {
- sway_log(L_ERROR, "Unable to allocate pixels for get_pixels");
- ipc_client_disconnect(req->client);
- free(req);
- continue;
- }
- wlc_pixels_read(WLC_RGBA8888, &req->geo, &g_out, data + sizeof(response_header));
-
- response_header[0] = 1;
- uint32_t *_size = (uint32_t *)(response_header + 1);
- _size[0] = g_out.size.w;
- _size[1] = g_out.size.h;
- size_t len = sizeof(response_header) + (g_out.size.w * g_out.size.h * 4);
- memcpy(data, response_header, sizeof(response_header));
- ipc_send_reply(req->client, data, len);
- free(data);
- // free the request since it has been handled
- free(req);
- }
-
- // free old list of pixel requests and set new list to all unhandled
- // requests (request for another output).
- list_free(ipc_get_pixel_requests);
- ipc_get_pixel_requests = unhandled;
-}
-
-static bool is_text_target(const char *target) {
- return (strncmp(target, "text/", 5) == 0
- || strcmp(target, "UTF8_STRING") == 0
- || strcmp(target, "STRING") == 0
- || strcmp(target, "TEXT") == 0
- || strcmp(target, "COMPOUND_TEXT") == 0);
-}
-
-static void release_clipboard_request(struct get_clipboard_request *req) {
- if (--(*req->pending) == 0) {
- const char *str = json_object_to_json_string(req->json);
- ipc_send_reply(req->client, str, (uint32_t)strlen(str));
- json_object_put(req->json);
- }
-
- free(req->type);
- free(req->buf);
- wlc_event_source_remove(req->fd_event_source);
- wlc_event_source_remove(req->timer_event_source);
- close(req->fd);
- free(req);
-}
-
-static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) {
- assert(data);
- struct get_clipboard_request *req = (struct get_clipboard_request *)data;
-
- if (mask & WLC_EVENT_ERROR) {
- sway_log(L_ERROR, "Selection data fd error");
- goto error;
- }
-
- if (mask & WLC_EVENT_READABLE) {
- static const unsigned int max_size = 8192 * 1024;
- int amt = 0;
-
- do {
- int size = req->buf_size - req->buf_position;
- int amt = read(fd, req->buf + req->buf_position, size - 1);
- if (amt < 0) {
- if (errno == EAGAIN) {
- return 0;
- }
-
- sway_log_errno(L_INFO, "Failed to read from clipboard data fd");
- goto release;
- }
-
- req->buf_position += amt;
- if (req->buf_position >= req->buf_size - 1) {
- if (req->buf_size >= max_size) {
- sway_log(L_ERROR, "get_clipbard: selection data too large");
- goto error;
- }
- char *next = realloc(req->buf, req->buf_size *= 2);
- if (!next) {
- sway_log_errno(L_ERROR, "get_clipboard: realloc data buffer failed");
- goto error;
- }
-
- req->buf = next;
- }
- } while(amt != 0);
-
- req->buf[req->buf_position] = '\0';
-
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "success", json_object_new_boolean(true));
- if (is_text_target(req->type)) {
- json_object_object_add(obj, "content", json_object_new_string(req->buf));
- json_object_object_add(req->json, req->type, obj);
- } else {
- size_t outlen;
- char *b64 = b64_encode(req->buf, req->buf_position, &outlen);
- json_object_object_add(obj, "content", json_object_new_string(b64));
- free(b64);
-
- char *type = malloc(strlen(req->type) + 8);
- strcat(type, ";base64");
- json_object_object_add(req->json, type, obj);
- free(type);
- }
- }
-
- goto release;
-
-error:;
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "success", json_object_new_boolean(false));
- json_object_object_add(obj, "error",
- json_object_new_string("Failed to retrieve data"));
- json_object_object_add(req->json, req->type, obj);
-
-release:
- release_clipboard_request(req);
- return 0;
-}
-
-static int ipc_selection_timer_cb(void *data) {
- assert(data);
- struct get_clipboard_request *req = (struct get_clipboard_request *)data;
-
- sway_log(L_INFO, "get_clipbard: timeout for type %s", req->type);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "success", json_object_new_boolean(false));
- json_object_object_add(obj, "error", json_object_new_string("Timeout"));
- json_object_object_add(req->json, req->type, obj);
-
- release_clipboard_request(req);
- return 0;
-}
-
-// greedy wildcard (only "*") matching
-bool mime_type_matches(const char *mime_type, const char *pattern) {
- const char *wildcard = NULL;
- while (*mime_type && *pattern) {
- if (*pattern == '*' && !wildcard) {
- wildcard = pattern;
- ++pattern;
- }
-
- if (*mime_type != *pattern) {
- if (!wildcard)
- return false;
-
- pattern = wildcard;
- ++mime_type;
- continue;
- }
-
- ++mime_type;
- ++pattern;
- }
-
- while (*pattern == '*') {
- ++pattern;
- }
-
- return (*mime_type == *pattern);
-}
-
-void ipc_get_clipboard(struct ipc_client *client, char *buf) {
- size_t size;
- const char **types = wlc_get_selection_types(&size);
- if (client->payload_length == 0) {
- json_object *obj = json_object_new_array();
- for (size_t i = 0; i < size; ++i) {
- json_object_array_add(obj, json_object_new_string(types[i]));
- }
-
- const char *str = json_object_to_json_string(obj);
- ipc_send_reply(client, str, strlen(str));
- json_object_put(obj);
- return;
- }
-
- unescape_string(buf);
- strip_quotes(buf);
- list_t *requested = split_string(buf, " ");
- json_object *json = json_object_new_object();
- unsigned int *pending = malloc(sizeof(unsigned int));
- *pending = 0;
-
- for (size_t l = 0; l < (size_t) requested->length; ++l) {
- const char *pattern = requested->items[l];
- bool found = false;
- for (size_t i = 0; i < size; ++i) {
- if (!mime_type_matches(types[i], pattern)) {
- continue;
- }
-
- found = true;
-
- struct get_clipboard_request *req = malloc(sizeof(*req));
- if (!req) {
- sway_log(L_ERROR, "get_clipboard: request malloc failed");
- goto data_error;
- }
-
- int pipes[2];
- if (pipe(pipes) == -1) {
- sway_log_errno(L_ERROR, "get_clipboard: pipe call failed");
- free(req);
- goto data_error;
- }
-
- fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
- fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
-
- if (!wlc_get_selection_data(types[i], pipes[1])) {
- close(pipes[0]);
- close(pipes[1]);
- free(req);
- sway_log(L_ERROR, "get_clipboard: failed to retrieve "
- "selection data");
- goto data_error;
- }
-
- if (!(req->buf = malloc(512))) {
- close(pipes[0]);
- close(pipes[1]);
- free(req);
- sway_log_errno(L_ERROR, "get_clipboard: buf malloc failed");
- goto data_error;
- }
-
- (*pending)++;
-
- req->client = client;
- req->type = strdup(types[i]);
- req->json = json;
- req->pending = pending;
- req->buf_position = 0;
- req->buf_size = 512;
- req->fd = pipes[0];
- req->timer_event_source = wlc_event_loop_add_timer(ipc_selection_timer_cb, req);
- req->fd_event_source = wlc_event_loop_add_fd(pipes[0],
- WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP,
- &ipc_selection_data_cb, req);
-
- wlc_event_source_timer_update(req->timer_event_source, 30000);
-
- // NOTE: remove this goto to enable retrieving multiple
- // targets at once. The whole implementation is already
- // made for it. The only reason it was disabled
- // at the time of writing is that neither wlc's xselection
- // implementation nor (apparently) gtk on wayland supports
- // multiple send requests at the same time which makes
- // every request except the last one fail (and therefore
- // return empty data)
- goto cleanup;
- }
-
- if (!found) {
- sway_log(L_INFO, "Invalid clipboard type %s requested", pattern);
- }
- }
-
- if (*pending == 0) {
- static const char *error_empty = "{ \"success\": false, \"error\": "
- "\"No matching types found\" }";
- ipc_send_reply(client, error_empty, (uint32_t)strlen(error_empty));
- free(json);
- free(pending);
- }
-
- goto cleanup;
-
-data_error:;
- static const char *error_json = "{ \"success\": false, \"error\": "
- "\"Failed to create clipboard data request\" }";
- ipc_send_reply(client, error_json, (uint32_t)strlen(error_json));
- free(json);
- free(pending);
-
-cleanup:
- list_free(requested);
- free(types);
+ json_object *workspace_json = ipc_json_describe_container(workspace);
+ // override the default focused indicator because
+ // it's set differently for the get_workspaces reply
+ struct sway_seat *seat =
+ input_manager_get_default_seat(input_manager);
+ struct sway_container *focused_ws = seat_get_focus(seat);
+ if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) {
+ focused_ws = container_parent(focused_ws, C_WORKSPACE);
+ }
+ bool focused = workspace == focused_ws;
+ json_object_object_del(workspace_json, "focused");
+ json_object_object_add(workspace_json, "focused",
+ json_object_new_boolean(focused));
+ json_object_array_add((json_object *)data, workspace_json);
+
+ focused_ws = seat_get_focus_inactive(seat, workspace->parent);
+ if (focused_ws->type != C_WORKSPACE) {
+ focused_ws = container_parent(focused_ws, C_WORKSPACE);
+ }
+ bool visible = workspace == focused_ws;
+ json_object_object_add(workspace_json, "visible",
+ json_object_new_boolean(visible));
}
void ipc_client_handle_command(struct ipc_client *client) {
@@ -679,7 +436,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
char *buf = malloc(client->payload_length + 1);
if (!buf) {
- sway_log_errno(L_INFO, "Unable to allocate IPC payload");
+ wlr_log_errno(L_INFO, "Unable to allocate IPC payload");
ipc_client_disconnect(client);
return;
}
@@ -688,7 +445,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
ssize_t received = recv(client->fd, buf, client->payload_length, 0);
if (received == -1)
{
- sway_log_errno(L_INFO, "Unable to receive payload from IPC client");
+ wlr_log_errno(L_INFO, "Unable to receive payload from IPC client");
ipc_client_disconnect(client);
free(buf);
return;
@@ -701,10 +458,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
switch (client->current_command) {
case IPC_COMMAND:
{
- if (!(client->security_policy & IPC_FEATURE_COMMAND)) {
- goto exit_denied;
- }
- struct cmd_results *results = handle_command(buf, CONTEXT_IPC);
+ config_clear_handler_context(config);
+ struct cmd_results *results = execute_command(buf, NULL);
const char *json = cmd_results_to_json(results);
char reply[256];
int length = snprintf(reply, sizeof(reply), "%s", json);
@@ -713,18 +468,45 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
+ case IPC_GET_OUTPUTS:
+ {
+ json_object *outputs = json_object_new_array();
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *container = root_container.children->items[i];
+ if (container->type == C_OUTPUT) {
+ json_object_array_add(outputs,
+ ipc_json_describe_container(container));
+ }
+ }
+ const char *json_string = json_object_to_json_string(outputs);
+ ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
+ json_object_put(outputs); // free
+ goto exit_cleanup;
+ }
+
+ case IPC_GET_WORKSPACES:
+ {
+ json_object *workspaces = json_object_new_array();
+ container_for_each_descendant_dfs(&root_container,
+ ipc_get_workspaces_callback, workspaces);
+ const char *json_string = json_object_to_json_string(workspaces);
+ ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
+ json_object_put(workspaces); // free
+ goto exit_cleanup;
+ }
+
case IPC_SUBSCRIBE:
{
// TODO: Check if they're permitted to use these events
struct json_object *request = json_tokener_parse(buf);
if (request == NULL) {
ipc_send_reply(client, "{\"success\": false}", 18);
- sway_log_errno(L_INFO, "Failed to read request");
+ wlr_log_errno(L_INFO, "Failed to read request");
goto exit_cleanup;
}
// parse requested event types
- for (int i = 0; i < json_object_array_length(request); i++) {
+ for (size_t i = 0; i < json_object_array_length(request); i++) {
const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
if (strcmp(event_type, "workspace") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
@@ -741,86 +523,39 @@ void ipc_client_handle_command(struct ipc_client *client) {
} else {
ipc_send_reply(client, "{\"success\": false}", 18);
json_object_put(request);
- sway_log_errno(L_INFO, "Failed to parse request");
+ wlr_log_errno(L_INFO, "Failed to parse request");
goto exit_cleanup;
}
}
json_object_put(request);
-
ipc_send_reply(client, "{\"success\": true}", 17);
goto exit_cleanup;
}
- case IPC_GET_WORKSPACES:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) {
- goto exit_denied;
- }
- json_object *workspaces = json_object_new_array();
- container_map(&root_container, ipc_get_workspaces_callback, workspaces);
- const char *json_string = json_object_to_json_string(workspaces);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(workspaces); // free
- goto exit_cleanup;
- }
-
case IPC_GET_INPUTS:
{
- if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) {
- goto exit_denied;
- }
json_object *inputs = json_object_new_array();
- if (input_devices) {
- for(int i = 0; i<input_devices->length; i++) {
- struct libinput_device *device = input_devices->items[i];
- json_object_array_add(inputs, ipc_json_describe_input(device));
- }
+ struct sway_input_device *device = NULL;
+ wl_list_for_each(device, &input_manager->devices, link) {
+ json_object_array_add(inputs, ipc_json_describe_input(device));
}
const char *json_string = json_object_to_json_string(inputs);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(inputs);
- goto exit_cleanup;
- }
-
- case IPC_GET_OUTPUTS:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) {
- goto exit_denied;
- }
- json_object *outputs = json_object_new_array();
- container_map(&root_container, ipc_get_outputs_callback, outputs);
- const char *json_string = json_object_to_json_string(outputs);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(outputs); // free
+ ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
+ json_object_put(inputs); // free
goto exit_cleanup;
}
case IPC_GET_TREE:
{
- if (!(client->security_policy & IPC_FEATURE_GET_TREE)) {
- goto exit_denied;
- }
- json_object *tree = ipc_json_describe_container_recursive(&root_container);
+ json_object *tree =
+ ipc_json_describe_container_recursive(&root_container);
const char *json_string = json_object_to_json_string(tree);
ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
json_object_put(tree);
goto exit_cleanup;
}
- case IPC_GET_MARKS:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) {
- goto exit_denied;
- }
- json_object *marks = json_object_new_array();
- container_map(&root_container, ipc_get_marks_callback, marks);
- const char *json_string = json_object_to_json_string(marks);
- ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
- json_object_put(marks);
- goto exit_cleanup;
- }
-
case IPC_GET_VERSION:
{
json_object *version = ipc_json_get_version();
@@ -830,62 +565,12 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
- case IPC_SWAY_GET_PIXELS:
- {
- char response_header[9];
- memset(response_header, 0, sizeof(response_header));
-
- json_object *obj = json_tokener_parse(buf);
- json_object *o, *x, *y, *w, *h;
-
- json_object_object_get_ex(obj, "output", &o);
- json_object_object_get_ex(obj, "x", &x);
- json_object_object_get_ex(obj, "y", &y);
- json_object_object_get_ex(obj, "w", &w);
- json_object_object_get_ex(obj, "h", &h);
-
- struct wlc_geometry g = {
- .origin = {
- .x = json_object_get_int(x),
- .y = json_object_get_int(y)
- },
- .size = {
- .w = json_object_get_int(w),
- .h = json_object_get_int(h)
- }
- };
-
- swayc_t *output = swayc_by_test(&root_container, output_by_name_test, (void *)json_object_get_string(o));
- json_object_put(obj);
-
- if (!output) {
- sway_log(L_ERROR, "IPC GET_PIXELS request with unknown output name");
- ipc_send_reply(client, response_header, sizeof(response_header));
- goto exit_cleanup;
- }
- struct get_pixels_request *req = malloc(sizeof(struct get_pixels_request));
- if (!req) {
- sway_log(L_ERROR, "Unable to allocate get_pixels request");
- goto exit_cleanup;
- }
- req->client = client;
- req->output = output->handle;
- req->geo = g;
- list_add(ipc_get_pixel_requests, req);
- wlc_output_schedule_render(output->handle);
- goto exit_cleanup;
- }
-
case IPC_GET_BAR_CONFIG:
{
- if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
- goto exit_denied;
- }
if (!buf[0]) {
// Send list of configured bar IDs
json_object *bars = json_object_new_array();
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *bar = config->bars->items[i];
json_object_array_add(bars, json_object_new_string(bar->id));
}
@@ -895,8 +580,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
} else {
// Send particular bar's details
struct bar_config *bar = NULL;
- int i;
- for (i = 0; i < config->bars->length; ++i) {
+ for (int i = 0; i < config->bars->length; ++i) {
bar = config->bars->items[i];
if (strcmp(buf, bar->id) == 0) {
break;
@@ -916,24 +600,13 @@ void ipc_client_handle_command(struct ipc_client *client) {
goto exit_cleanup;
}
- case IPC_GET_CLIPBOARD:
- {
- if (!(client->security_policy & IPC_FEATURE_GET_CLIPBOARD)) {
- goto exit_denied;
- }
-
- ipc_get_clipboard(client, buf);
- goto exit_cleanup;
- }
-
default:
- sway_log(L_INFO, "Unknown IPC command type %i", client->current_command);
+ wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command);
goto exit_cleanup;
}
-exit_denied:
ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
- sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
+ wlr_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
exit_cleanup:
client->payload_length = 0;
@@ -956,16 +629,15 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
client->write_buffer_size *= 2;
}
- // TODO: reduce the limit back to 4 MB when screenshooter is implemented
- if (client->write_buffer_size > (1 << 28)) { // 256 MB
- sway_log(L_ERROR, "Client write buffer too big, disconnecting client");
+ if (client->write_buffer_size > 4e6) { // 4 MB
+ wlr_log(L_ERROR, "Client write buffer too big, disconnecting client");
ipc_client_disconnect(client);
return false;
}
char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
if (!new_buffer) {
- sway_log(L_ERROR, "Unable to reallocate ipc client write buffer");
+ wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer");
ipc_client_disconnect(client);
return false;
}
@@ -977,212 +649,11 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
client->write_buffer_len += payload_length;
if (!client->writable_event_source) {
- client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client);
+ client->writable_event_source = wl_event_loop_add_fd(
+ server.wl_event_loop, client->fd, WL_EVENT_WRITABLE,
+ ipc_client_handle_writable, client);
}
- sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
-
+ wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
return true;
}
-
-void ipc_get_workspaces_callback(swayc_t *workspace, void *data) {
- if (workspace->type == C_WORKSPACE) {
- json_object *workspace_json = ipc_json_describe_container(workspace);
- // override the default focused indicator because
- // it's set differently for the get_workspaces reply
- bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace;
- json_object_object_del(workspace_json, "focused");
- json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused));
- json_object_array_add((json_object *)data, workspace_json);
- }
-}
-
-void ipc_get_outputs_callback(swayc_t *container, void *data) {
- if (container->type == C_OUTPUT) {
- json_object_array_add((json_object *)data, ipc_json_describe_container(container));
- }
-}
-
-static void ipc_get_marks_callback(swayc_t *container, void *data) {
- json_object *object = (json_object *)data;
- if (container->marks) {
- for (int i = 0; i < container->marks->length; ++i) {
- char *mark = (char *)container->marks->items[i];
- json_object_array_add(object, json_object_new_string(mark));
- }
- }
-}
-
-void ipc_send_event(const char *json_string, enum ipc_command_type event) {
- static struct {
- enum ipc_command_type event;
- enum ipc_feature feature;
- } security_mappings[] = {
- { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE },
- { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT },
- { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE },
- { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW },
- { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING },
- { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT }
- };
-
- uint32_t security_mask = 0;
- for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) {
- if (security_mappings[i].event == event) {
- security_mask = security_mappings[i].feature;
- break;
- }
- }
-
- int i;
- struct ipc_client *client;
- for (i = 0; i < ipc_client_list->length; i++) {
- client = ipc_client_list->items[i];
- if (!(client->security_policy & security_mask)) {
- continue;
- }
- if ((client->subscribed_events & event_mask(event)) == 0) {
- continue;
- }
- client->current_command = event;
- if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
- sway_log_errno(L_INFO, "Unable to send reply to IPC client");
- ipc_client_disconnect(client);
- }
- }
-}
-
-void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) {
- sway_log(L_DEBUG, "Sending workspace::%s event", change);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(change));
- if (strcmp("focus", change) == 0) {
- if (old) {
- json_object_object_add(obj, "old", ipc_json_describe_container_recursive(old));
- } else {
- json_object_object_add(obj, "old", NULL);
- }
- }
-
- if (new) {
- json_object_object_add(obj, "current", ipc_json_describe_container_recursive(new));
- } else {
- json_object_object_add(obj, "current", NULL);
- }
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_window(swayc_t *window, const char *change) {
- sway_log(L_DEBUG, "Sending window::%s event", change);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(change));
- json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_WINDOW);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_barconfig_update(struct bar_config *bar) {
- sway_log(L_DEBUG, "Sending barconfig_update event");
- json_object *json = ipc_json_describe_bar_config(bar);
- const char *json_string = json_object_to_json_string(json);
- ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
-
- json_object_put(json); // free
-}
-
-void ipc_event_mode(const char *mode) {
- sway_log(L_DEBUG, "Sending mode::%s event", mode);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(mode));
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_MODE);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_modifier(uint32_t modifier, const char *state) {
- sway_log(L_DEBUG, "Sending modifier::%s event", state);
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string(state));
-
- const char *modifier_name = get_modifier_name_by_mask(modifier);
- json_object_object_add(obj, "modifier", json_object_new_string(modifier_name));
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_MODIFIER);
-
- json_object_put(obj); // free
-}
-
-static void ipc_event_binding(json_object *sb_obj) {
- sway_log(L_DEBUG, "Sending binding::run event");
- json_object *obj = json_object_new_object();
- json_object_object_add(obj, "change", json_object_new_string("run"));
- json_object_object_add(obj, "binding", sb_obj);
-
- const char *json_string = json_object_to_json_string(obj);
- ipc_send_event(json_string, IPC_EVENT_BINDING);
-
- json_object_put(obj); // free
-}
-
-void ipc_event_binding_keyboard(struct sway_binding *sb) {
- json_object *sb_obj = json_object_new_object();
- json_object_object_add(sb_obj, "command", json_object_new_string(sb->command));
-
- const char *names[10];
-
- int len = get_modifier_names(names, sb->modifiers);
- int i;
- json_object *modifiers = json_object_new_array();
- for (i = 0; i < len; ++i) {
- json_object_array_add(modifiers, json_object_new_string(names[i]));
- }
-
- json_object_object_add(sb_obj, "event_state_mask", modifiers);
-
- json_object *input_codes = json_object_new_array();
- int input_code = 0;
- json_object *symbols = json_object_new_array();
- json_object *symbol = NULL;
-
- if (sb->bindcode) { // bindcode: populate input_codes
- uint32_t keycode;
- for (i = 0; i < sb->keys->length; ++i) {
- keycode = *(uint32_t *)sb->keys->items[i];
- json_object_array_add(input_codes, json_object_new_int(keycode));
- if (i == 0) {
- input_code = keycode;
- }
- }
- } else { // bindsym: populate symbols
- uint32_t keysym;
- char buffer[64];
- for (i = 0; i < sb->keys->length; ++i) {
- keysym = *(uint32_t *)sb->keys->items[i];
- if (xkb_keysym_get_name(keysym, buffer, 64) > 0) {
- json_object *str = json_object_new_string(buffer);
- json_object_array_add(symbols, str);
- if (i == 0) {
- symbol = str;
- }
- }
- }
- }
-
- json_object_object_add(sb_obj, "input_codes", input_codes);
- json_object_object_add(sb_obj, "input_code", json_object_new_int(input_code));
- json_object_object_add(sb_obj, "symbols", symbols);
- json_object_object_add(sb_obj, "symbol", symbol);
- json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard"));
-
- ipc_event_binding(sb_obj);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <stdlib.h>
-#include <stdbool.h>
-#include <math.h>
-#include <wlc/wlc.h>
-#include "sway/extensions.h"
-#include "sway/config.h"
-#include "sway/container.h"
-#include "sway/workspace.h"
-#include "sway/focus.h"
-#include "sway/output.h"
-#include "sway/ipc-server.h"
-#include "sway/border.h"
-#include "sway/layout.h"
-#include "list.h"
-#include "log.h"
-
-swayc_t root_container;
-swayc_t *current_focus;
-list_t *scratchpad;
-
-int min_sane_h = 60;
-int min_sane_w = 100;
-
-void init_layout(void) {
- root_container.id = 0; // normally assigned in new_swayc()
- root_container.type = C_ROOT;
- root_container.layout = L_NONE;
- root_container.name = strdup("root");
- root_container.children = create_list();
- root_container.handle = -1;
- root_container.visible = true;
- current_focus = &root_container;
- scratchpad = create_list();
-}
-
-int index_child(const swayc_t *child) {
- swayc_t *parent = child->parent;
- int i, len;
- if (!child->is_floating) {
- len = parent->children->length;
- for (i = 0; i < len; ++i) {
- if (parent->children->items[i] == child) {
- break;
- }
- }
- } else {
- len = parent->floating->length;
- for (i = 0; i < len; ++i) {
- if (parent->floating->items[i] == child) {
- break;
- }
- }
- }
- if (!sway_assert(i < len, "Stray container")) {
- return -1;
- }
- return i;
-}
-
-void add_child(swayc_t *parent, swayc_t *child) {
- sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
- child->width, child->height, parent, parent->type, parent->width, parent->height);
- list_add(parent->children, child);
- child->parent = parent;
- // set focus for this container
- if (!parent->focused) {
- parent->focused = child;
- }
- if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
- child = new_container(child, parent->workspace_layout);
- }
-}
-
-static double *get_height(swayc_t *cont) {
- return &cont->height;
-}
-
-static double *get_width(swayc_t *cont) {
- return &cont->width;
-}
-
-void insert_child(swayc_t *parent, swayc_t *child, int index) {
- if (index > parent->children->length) {
- index = parent->children->length;
- }
- if (index < 0) {
- index = 0;
- }
- list_insert(parent->children, index, child);
- child->parent = parent;
- if (!parent->focused) {
- parent->focused = child;
- }
- if (parent->type == C_WORKSPACE && child->type == C_VIEW && (parent->workspace_layout == L_TABBED || parent->workspace_layout == L_STACKED)) {
- child = new_container(child, parent->workspace_layout);
- }
- if (is_auto_layout(parent->layout)) {
- /* go through each group, adjust the size of the first child of each group */
- double *(*get_maj_dim)(swayc_t *cont);
- double *(*get_min_dim)(swayc_t *cont);
- if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
- get_maj_dim = get_width;
- get_min_dim = get_height;
- } else {
- get_maj_dim = get_height;
- get_min_dim = get_width;
- }
- for (int i = index; i < parent->children->length;) {
- int start = auto_group_start_index(parent, i);
- int end = auto_group_end_index(parent, i);
- swayc_t *first = parent->children->items[start];
- if (start + 1 < parent->children->length) {
- /* preserve the group's dimension along major axis */
- *get_maj_dim(first) = *get_maj_dim(parent->children->items[start + 1]);
- } else {
- /* new group, let the apply_layout handle it */
- first->height = first->width = 0;
- break;
- }
- double remaining = *get_min_dim(parent);
- for (int j = end - 1; j > start; --j) {
- swayc_t *sibling = parent->children->items[j];
- if (sibling == child) {
- /* the inserted child won't yet have its minor
- dimension set */
- remaining -= *get_min_dim(parent) / (end - start);
- } else {
- remaining -= *get_min_dim(sibling);
- }
- }
- *get_min_dim(first) = remaining;
- i = end;
- }
- }
-}
-
-void add_floating(swayc_t *ws, swayc_t *child) {
- sway_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", child, child->type,
- child->width, child->height, ws, ws->type, ws->width, ws->height);
- if (!sway_assert(ws->type == C_WORKSPACE, "Must be of workspace type")) {
- return;
- }
- list_add(ws->floating, child);
- child->parent = ws;
- child->is_floating = true;
- if (!ws->focused) {
- ws->focused = child;
- }
- ipc_event_window(child, "floating");
-}
-
-swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
- swayc_t *parent = fixed->parent;
- if (fixed->is_floating) {
- if (active->is_floating) {
- int i = index_child(fixed);
- list_insert(parent->floating, i + 1, active);
- } else {
- list_add(parent->children, active);
- }
- } else {
- if (active->is_floating) {
- list_add(parent->floating, active);
- } else {
- int i = index_child(fixed);
- if (is_auto_layout(parent->layout)) {
- list_add(parent->children, active);
- } else {
- list_insert(parent->children, i + 1, active);
- }
- }
- }
- active->parent = parent;
- // focus new child
- parent->focused = active;
- return active->parent;
-}
-
-swayc_t *replace_child(swayc_t *child, swayc_t *new_child) {
- swayc_t *parent = child->parent;
- if (parent == NULL) {
- return NULL;
- }
- int i = index_child(child);
- if (child->is_floating) {
- parent->floating->items[i] = new_child;
- } else {
- parent->children->items[i] = new_child;
- }
- // Set parent and focus for new_child
- new_child->parent = child->parent;
- if (child->parent->focused == child) {
- child->parent->focused = new_child;
- }
- child->parent = NULL;
-
- // Set geometry for new child
- new_child->x = child->x;
- new_child->y = child->y;
- new_child->width = child->width;
- new_child->height = child->height;
-
- // reset geometry for child
- child->width = 0;
- child->height = 0;
-
- // deactivate child
- if (child->type == C_VIEW) {
- wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
- }
- return parent;
-}
-
-swayc_t *remove_child(swayc_t *child) {
- int i;
- swayc_t *parent = child->parent;
- if (child->is_floating) {
- // Special case for floating views
- for (i = 0; i < parent->floating->length; ++i) {
- if (parent->floating->items[i] == child) {
- list_del(parent->floating, i);
- break;
- }
- }
- i = 0;
- } else {
- for (i = 0; i < parent->children->length; ++i) {
- if (parent->children->items[i] == child) {
- list_del(parent->children, i);
- break;
- }
- }
- if (is_auto_layout(parent->layout) && parent->children->length) {
- /* go through each group, adjust the size of the last child of each group */
- double *(*get_maj_dim)(swayc_t *cont);
- double *(*get_min_dim)(swayc_t *cont);
- if (parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT) {
- get_maj_dim = get_width;
- get_min_dim = get_height;
- } else {
- get_maj_dim = get_height;
- get_min_dim = get_width;
- }
- for (int j = parent->children->length - 1; j >= i;) {
- int start = auto_group_start_index(parent, j);
- int end = auto_group_end_index(parent, j);
- swayc_t *first = parent->children->items[start];
- if (i == start) {
- /* removed element was first child in the current group,
- use its size along the major axis */
- *get_maj_dim(first) = *get_maj_dim(child);
- } else if (start > i) {
- /* preserve the group's dimension along major axis */
- *get_maj_dim(first) = *get_maj_dim(parent->children->items[start - 1]);
- }
- if (end != parent->children->length) {
- double remaining = *get_min_dim(parent);
- for (int k = start; k < end - 1; ++k) {
- swayc_t *sibling = parent->children->items[k];
- remaining -= *get_min_dim(sibling);
- }
- /* last element of the group gets remaining size, elements
- that don't change groups keep their ratio */
- *get_min_dim((swayc_t *) parent->children->items[end - 1]) = remaining;
- } /* else last group, let apply_layout handle it */
- j = start - 1;
- }
- }
- }
- // Set focused to new container
- if (parent->focused == child) {
- if (parent->children->length > 0) {
- parent->focused = parent->children->items[i ? i-1:0];
- } else if (parent->floating && parent->floating->length) {
- parent->focused = parent->floating->items[parent->floating->length - 1];
- } else {
- parent->focused = NULL;
- }
- }
- child->parent = NULL;
- // deactivate view
- if (child->type == C_VIEW) {
- wlc_view_set_state(child->handle, WLC_BIT_ACTIVATED, false);
- }
- return parent;
-}
-
-void swap_container(swayc_t *a, swayc_t *b) {
- if (!sway_assert(a&&b, "parameters must be non null") ||
- !sway_assert(a->parent && b->parent, "containers must have parents")) {
- return;
- }
- size_t a_index = index_child(a);
- size_t b_index = index_child(b);
- swayc_t *a_parent = a->parent;
- swayc_t *b_parent = b->parent;
- // Swap the pointers
- if (a->is_floating) {
- a_parent->floating->items[a_index] = b;
- } else {
- a_parent->children->items[a_index] = b;
- }
- if (b->is_floating) {
- b_parent->floating->items[b_index] = a;
- } else {
- b_parent->children->items[b_index] = a;
- }
- a->parent = b_parent;
- b->parent = a_parent;
- if (a_parent->focused == a) {
- a_parent->focused = b;
- }
- // don't want to double switch
- if (b_parent->focused == b && a_parent != b_parent) {
- b_parent->focused = a;
- }
-}
-
-void swap_geometry(swayc_t *a, swayc_t *b) {
- double x = a->x;
- double y = a->y;
- double w = a->width;
- double h = a->height;
- a->x = b->x;
- a->y = b->y;
- a->width = b->width;
- a->height = b->height;
- b->x = x;
- b->y = y;
- b->width = w;
- b->height = h;
-}
-
-static void swap_children(swayc_t *container, int a, int b) {
- if (a >= 0 && b >= 0 && a < container->children->length
- && b < container->children->length
- && a != b) {
- swayc_t *pa = (swayc_t *)container->children->items[a];
- swayc_t *pb = (swayc_t *)container->children->items[b];
- container->children->items[a] = container->children->items[b];
- container->children->items[b] = pa;
- if (is_auto_layout(container->layout)) {
- size_t ga = auto_group_index(container, a);
- size_t gb = auto_group_index(container, b);
- if (ga != gb) {
- swap_geometry(pa, pb);
- }
- }
- }
-}
-
-void move_container(swayc_t *container, enum movement_direction dir, int move_amt) {
- enum swayc_layouts layout = L_NONE;
- swayc_t *parent = container->parent;
- if (container->is_floating) {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- switch(dir) {
- case MOVE_LEFT:
- container->x = MAX(0, container->x - move_amt);
- break;
- case MOVE_RIGHT:
- container->x = MIN(output->width - container->width, container->x + move_amt);
- break;
- case MOVE_UP:
- container->y = MAX(0, container->y - move_amt);
- break;
- case MOVE_DOWN:
- container->y = MIN(output->height - container->height, container->y + move_amt);
- break;
- default:
- break;
- }
- update_geometry(container);
- return;
- }
- if (container->type != C_VIEW && container->type != C_CONTAINER) {
- return;
- }
- if (dir == MOVE_UP || dir == MOVE_DOWN) {
- layout = L_VERT;
- } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
- layout = L_HORIZ;
- } else if (dir == MOVE_FIRST) {
- // swap first child in auto layout with currently focused child
- if (is_auto_layout(parent->layout)) {
- int focused_idx = index_child(container);
- swayc_t *first = parent->children->items[0];
- if (focused_idx > 0) {
- list_swap(parent->children, 0, focused_idx);
- swap_geometry(first, container);
- }
- arrange_windows(parent->parent, -1, -1);
- ipc_event_window(container, "move");
- set_focused_container_for(parent->parent, container);
- }
- return;
- } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
- return;
- }
- swayc_t *child = container;
- bool ascended = false;
-
- // View is wrapped in intermediate container which is needed for displaying
- // the titlebar. Moving only the view outside of its parent container would just
- // wrap it again under worspace. There would effectively be no movement,
- // just a change of wrapping container.
- if (child->type == C_VIEW &&
- parent->type == C_CONTAINER &&
- parent->children->length == 1 &&
- parent->parent->type == C_WORKSPACE) {
- child = parent;
- parent = parent->parent;
- }
-
- while (true) {
- sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
- container,parent,child);
- if (parent->layout == layout
- || (layout == L_NONE && (parent->type == C_CONTAINER || parent->type == C_WORKSPACE)) /* accept any layout for next/prev direction */
- || (parent->layout == L_TABBED && layout == L_HORIZ)
- || (parent->layout == L_STACKED && layout == L_VERT)
- || is_auto_layout(parent->layout)) {
- int diff;
- // If it has ascended (parent has moved up), no container is removed
- // so insert it at index, or index+1.
- // if it has not, the moved container is removed, so it needs to be
- // inserted at index-1, or index+1
- if (ascended) {
- diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
- } else {
- diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
- }
- int idx = index_child(child);
- int desired = idx + diff;
- if (dir == MOVE_NEXT || dir == MOVE_PREV) {
- // Next/Prev always wrap.
- if (desired < 0) {
- desired += parent->children->length;
- } else if (desired >= parent->children->length) {
- desired = 0;
- }
- }
- // when it has ascended, legal insertion position is 0:len
- // when it has not, legal insertion position is 0:len-1
- if (desired >= 0 && desired - ascended < parent->children->length) {
- if (!ascended) {
- child = parent->children->items[desired];
- // Move container into sibling container
- if (child->type == C_CONTAINER) {
- parent = child;
- // Insert it in first/last if matching layout, otherwise
- // insert it next to focused container
- if (parent->layout == layout
- || (parent->layout == L_TABBED && layout == L_HORIZ)
- || (parent->layout == L_STACKED && layout == L_VERT)
- || is_auto_layout(parent->layout)) {
- desired = (diff < 0) * parent->children->length;
- } else {
- desired = index_child(child->focused) + 1;
- }
- //reset geometry
- container->width = container->height = 0;
- }
- }
- if (container->parent == parent) {
- swap_children(parent, idx, desired);
- } else {
- swayc_t *old_parent = remove_child(container);
- insert_child(parent, container, desired);
- destroy_container(old_parent);
- sway_log(L_DEBUG,"Moving to %p %d", parent, desired);
- }
- break;
- }
- }
- // Change parent layout if we need to
- if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
- /* swayc_change_layout(parent, layout); */
- parent->layout = layout;
- continue;
- }
- if (parent->type == C_WORKSPACE) {
- // If moving to an adjacent output we need a starting position (since this
- // output might border to multiple outputs).
- struct wlc_point abs_pos;
- get_absolute_center_position(container, &abs_pos);
-
- swayc_t *output = swayc_adjacent_output(parent->parent, dir, &abs_pos, true);
-
- if (output) {
- sway_log(L_DEBUG, "Moving between outputs");
- swayc_t *old_parent = remove_child(container);
- destroy_container(old_parent);
-
- swayc_t *dest = output->focused;
- switch (dir) {
- case MOVE_LEFT:
- case MOVE_UP:
- // reset container geometry
- container->width = container->height = 0;
- add_child(dest, container);
- break;
- case MOVE_RIGHT:
- case MOVE_DOWN:
- // reset container geometry
- container->width = container->height = 0;
- insert_child(dest, container, 0);
- break;
- default:
- break;
- }
- // arrange new workspace
- arrange_windows(dest, -1, -1);
- set_focused_container(container);
- break;
- }
-
- // We simply cannot move any further.
- if (parent->layout == layout) {
- break;
- }
- // Create container around workspace to insert child into
- parent = new_container(parent, layout);
- // Previous line set the resulting container's layout to
- // workspace_layout. It should have been just layout.
- parent->layout = parent->parent->layout;
- }
- ascended = true;
- child = parent;
- parent = child->parent;
- }
- arrange_windows(parent->parent, -1, -1);
- ipc_event_window(container, "move");
- set_focused_container_for(parent->parent, container);
-}
-
-void move_container_to(swayc_t* container, swayc_t* destination) {
- if (container == destination || swayc_is_parent_of(container, destination)) {
- return;
- }
- swayc_t *parent = remove_child(container);
- // Send to new destination
- if (container->is_floating) {
- swayc_t *ws = swayc_active_workspace_for(destination);
- add_floating(ws, container);
-
- // If the workspace only has one child after adding one, it
- // means that the workspace was just initialized.
- if (ws->children->length + ws->floating->length == 1) {
- ipc_event_workspace(NULL, ws, "init");
- }
- } else if (destination->type == C_WORKSPACE) {
- // reset container geometry
- container->width = container->height = 0;
- add_child(destination, container);
-
- // If the workspace only has one child after adding one, it
- // means that the workspace was just initialized.
- if (destination->children->length + destination->floating->length == 1) {
- ipc_event_workspace(NULL, destination, "init");
- }
- } else {
- // reset container geometry
- container->width = container->height = 0;
- add_sibling(destination, container);
- }
- // Destroy old container if we need to
- parent = destroy_container(parent);
- // Refocus
- swayc_t *op1 = swayc_parent_by_type(destination, C_OUTPUT);
- swayc_t *op2 = swayc_parent_by_type(parent, C_OUTPUT);
- set_focused_container(get_focused_view(op1));
- arrange_windows(op1, -1, -1);
- update_visibility(op1);
- if (op1 != op2) {
- set_focused_container(get_focused_view(op2));
- arrange_windows(op2, -1, -1);
- update_visibility(op2);
- }
-}
-
-void move_workspace_to(swayc_t* workspace, swayc_t* destination) {
- if (workspace == destination || swayc_is_parent_of(workspace, destination)) {
- return;
- }
- swayc_t *src_op = remove_child(workspace);
- // reset container geometry
- workspace->width = workspace->height = 0;
- add_child(destination, workspace);
- sort_workspaces(destination);
- // Refocus destination (change to new workspace)
- set_focused_container(get_focused_view(workspace));
- arrange_windows(destination, -1, -1);
- update_visibility(destination);
-
- // make sure source output has a workspace
- if (src_op->children->length == 0) {
- char *ws_name = workspace_next_name(src_op->name);
- swayc_t *ws = new_workspace(src_op, ws_name);
- ws->is_focused = true;
- free(ws_name);
- }
- set_focused_container(get_focused_view(src_op));
- update_visibility(src_op);
-}
-
-static void adjust_border_geometry(swayc_t *c, struct wlc_geometry *g,
- const struct wlc_size *res, int left, int right, int top, int bottom) {
-
- g->size.w += left + right;
- if (g->origin.x - left < 0) {
- g->size.w += g->origin.x - left;
- } else if (g->origin.x + g->size.w - right > res->w) {
- g->size.w = res->w - g->origin.x + right;
- }
-
- g->size.h += top + bottom;
- if (g->origin.y - top < 0) {
- g->size.h += g->origin.y - top;
- } else if (g->origin.y + g->size.h - top > res->h) {
- g->size.h = res->h - g->origin.y + top;
- }
-
- g->origin.x = MIN((uint32_t)MAX(g->origin.x - left, 0), res->w);
- g->origin.y = MIN((uint32_t)MAX(g->origin.y - top, 0), res->h);
-
-}
-
-static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) {
- struct wlc_geometry g = *geometry;
- c->actual_geometry = g;
-
- swayc_t *output = swayc_parent_by_type(c, C_OUTPUT);
- struct wlc_size res;
- output_get_scaled_size(output->handle, &res);
-
- switch (c->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- adjust_border_geometry(c, &g, &res, c->border_thickness,
- c->border_thickness, c->border_thickness, c->border_thickness);
- break;
- case B_NORMAL:
- {
- int title_bar_height = config->font_height + 4; // borders + padding
-
- adjust_border_geometry(c, &g, &res, c->border_thickness,
- c->border_thickness, title_bar_height, c->border_thickness);
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = c->actual_geometry.origin.x - c->border_thickness,
- .y = c->actual_geometry.origin.y - title_bar_height
- },
- .size = {
- .w = c->actual_geometry.size.w + (2 * c->border_thickness),
- .h = title_bar_height
- }
- };
- c->title_bar_geometry = title_bar;
- break;
- }
- }
-
- c->border_geometry = g;
- *geometry = c->actual_geometry;
-
- update_container_border(c);
-}
-
-void update_layout_geometry(swayc_t *parent, enum swayc_layouts prev_layout) {
- switch (parent->layout) {
- case L_TABBED:
- case L_STACKED:
- if (prev_layout != L_TABBED && prev_layout != L_STACKED) {
- // cache current geometry for all non-float children
- int i;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *child = parent->children->items[i];
- child->cached_geometry.origin.x = child->x;
- child->cached_geometry.origin.y = child->y;
- child->cached_geometry.size.w = child->width;
- child->cached_geometry.size.h = child->height;
- }
- }
- break;
- default:
- if (prev_layout == L_TABBED || prev_layout == L_STACKED) {
- // recover cached geometry for all non-float children
- int i;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *child = parent->children->items[i];
- // only recoverer cached geometry if non-zero
- if (!wlc_geometry_equals(&child->cached_geometry, &wlc_geometry_zero)) {
- child->x = child->cached_geometry.origin.x;
- child->y = child->cached_geometry.origin.y;
- child->width = child->cached_geometry.size.w;
- child->height = child->cached_geometry.size.h;
- }
- }
- }
- break;
- }
-}
-
-static int update_gap_geometry(swayc_t *container, struct wlc_geometry *g) {
- swayc_t *ws = swayc_parent_by_type(container, C_WORKSPACE);
- swayc_t *op = ws->parent;
- int gap = container->is_floating ? 0 : swayc_gap(container);
- if (gap % 2 != 0) {
- // because gaps are implemented as "half sized margins" it's currently
- // not possible to align views properly with odd sized gaps.
- gap -= 1;
- }
-
- g->origin.x = container->x + gap/2 < op->width ? container->x + gap/2 : op->width-1;
- g->origin.y = container->y + gap/2 < op->height ? container->y + gap/2 : op->height-1;
- g->size.w = container->width > gap ? container->width - gap : 1;
- g->size.h = container->height > gap ? container->height - gap : 1;
-
- if ((!config->edge_gaps && gap > 0) || (config->smart_gaps && ws->children->length == 1)) {
- // Remove gap against the workspace edges. Because a pixel is not
- // divisable, depending on gap size and the number of siblings our view
- // might be at the workspace edge without being exactly so (thus test
- // with gap, and align correctly).
- if (container->x - gap <= ws->x) {
- g->origin.x = ws->x;
- g->size.w = container->width - gap/2;
- }
- if (container->y - gap <= ws->y) {
- g->origin.y = ws->y;
- g->size.h = container->height - gap/2;
- }
- if (container->x + container->width + gap >= ws->x + ws->width) {
- g->size.w = ws->x + ws->width - g->origin.x;
- }
- if (container->y + container->height + gap >= ws->y + ws->height) {
- g->size.h = ws->y + ws->height - g->origin.y;
- }
- }
-
- return gap;
-}
-
-void update_geometry(swayc_t *container) {
- if (container->type != C_VIEW && container->type != C_CONTAINER) {
- return;
- }
-
- swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
- swayc_t *op = workspace->parent;
- swayc_t *parent = container->parent;
-
- struct wlc_geometry geometry = {
- .origin = {
- .x = container->x < op->width ? container->x : op->width-1,
- .y = container->y < op->height ? container->y : op->height-1
- },
- .size = {
- .w = container->width,
- .h = container->height,
- }
- };
-
- int gap = 0;
-
- // apply inner gaps to non-tabbed/stacked containers
- swayc_t *p = swayc_tabbed_stacked_ancestor(container);
- if (p == NULL) {
- gap = update_gap_geometry(container, &geometry);
- }
-
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- struct wlc_size size;
- output_get_scaled_size(output->handle, &size);
-
- if (swayc_is_fullscreen(container)) {
- geometry.origin.x = 0;
- geometry.origin.y = 0;
- geometry.size.w = size.w;
- geometry.size.h = size.h;
- if (op->focused == workspace) {
- wlc_view_bring_to_front(container->handle);
- }
-
- container->border_geometry = wlc_geometry_zero;
- container->title_bar_geometry = wlc_geometry_zero;
- border_clear(container->border);
- } else if (container->is_floating) { // allocate border for floating window
- update_border_geometry_floating(container, &geometry);
- } else if (!container->is_floating) { // allocate border for titled window
- container->border_geometry = geometry;
-
- int border_top = container->border_thickness;
- int border_bottom = container->border_thickness;
- int border_left = container->border_thickness;
- int border_right = container->border_thickness;
-
- // handle hide_edge_borders
- if (config->hide_edge_borders != E_NONE && (gap <= 0 || (config->smart_gaps && workspace->children->length == 1))) {
- if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) {
- if (geometry.origin.x == workspace->x) {
- border_left = 0;
- }
-
- if (geometry.origin.x + geometry.size.w == workspace->x + workspace->width) {
- border_right = 0;
- }
- }
-
- if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) {
- if (geometry.origin.y == workspace->y || should_hide_top_border(container, geometry.origin.y)) {
- border_top = 0;
- }
-
- if (geometry.origin.y + geometry.size.h == workspace->y + workspace->height) {
- border_bottom = 0;
- }
- }
-
- if (config->hide_edge_borders == E_SMART && workspace->children->length == 1) {
- border_top = 0;
- border_bottom = 0;
- border_left = 0;
- border_right = 0;
- }
- }
-
- int title_bar_height = config->font_height + 4; //borders + padding
-
- if (parent->layout == L_TABBED && parent->children->length > 1) {
- int i, x = 0, w, l, r;
- l = parent->children->length;
- w = geometry.size.w / l;
- r = geometry.size.w % l;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *view = parent->children->items[i];
- if (view == container) {
- x = w * i;
- if (i == l - 1) {
- w += r;
- }
- break;
- }
- }
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x + x,
- .y = container->border_geometry.origin.y
- },
- .size = {
- .w = w,
- .h = title_bar_height
- }
- };
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar.size.h;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar.size.h);
- container->title_bar_geometry = title_bar;
- } else if (parent->layout == L_STACKED && parent->children->length > 1) {
- int i, y = 0;
- for (i = 0; i < parent->children->length; ++i) {
- swayc_t *view = parent->children->items[i];
- if (view == container) {
- y = title_bar_height * i;
- }
- }
-
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x,
- .y = container->border_geometry.origin.y + y
- },
- .size = {
- .w = container->border_geometry.size.w,
- .h = title_bar_height
- }
- };
- title_bar_height = title_bar_height * parent->children->length;
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar_height;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar_height);
- container->title_bar_geometry = title_bar;
- } else {
- switch (container->border_type) {
- case B_NONE:
- break;
- case B_PIXEL:
- geometry.origin.x += border_left;
- geometry.origin.y += border_top;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_top + border_bottom);
- break;
- case B_NORMAL:
- {
- struct wlc_geometry title_bar = {
- .origin = {
- .x = container->border_geometry.origin.x,
- .y = container->border_geometry.origin.y
- },
- .size = {
- .w = container->border_geometry.size.w,
- .h = title_bar_height
- }
- };
- geometry.origin.x += border_left;
- geometry.origin.y += title_bar.size.h;
- geometry.size.w -= (border_left + border_right);
- geometry.size.h -= (border_bottom + title_bar.size.h);
- container->title_bar_geometry = title_bar;
- break;
- }
- }
- }
-
- container->actual_geometry = geometry;
-
- if (container->type == C_VIEW) {
- update_container_border(container);
- }
- }
-
- if (container->type == C_VIEW) {
- wlc_view_set_geometry(container->handle, 0, &geometry);
- }
-}
-
-/**
- * Layout application prototypes
- */
-static void apply_horiz_layout(swayc_t *container, const double x,
- const double y, const double width,
- const double height, const int start,
- const int end);
-static void apply_vert_layout(swayc_t *container, const double x,
- const double y, const double width,
- const double height, const int start,
- const int end);
-static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
- double y, double width,
- double height);
-
-static void apply_auto_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- enum swayc_layouts group_layout,
- bool master_first);
-
-static void arrange_windows_r(swayc_t *container, double width, double height) {
- int i;
- if (width == -1 || height == -1) {
- swayc_log(L_DEBUG, container, "Arranging layout for %p", container);
- width = container->width;
- height = container->height;
- }
- // pixels are indivisible. if we don't round the pixels, then the view
- // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
- // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
- width = floor(width);
- height = floor(height);
-
- sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
- container->name, container->width, container->height, container->x,
- container->y);
-
- double x = 0, y = 0;
- switch (container->type) {
- case C_ROOT:
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *output = container->children->items[i];
- sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y);
- arrange_windows_r(output, -1, -1);
- }
- return;
- case C_OUTPUT:
- {
- struct wlc_size resolution;
- output_get_scaled_size(container->handle, &resolution);
- width = resolution.w; height = resolution.h;
- // output must have correct size due to e.g. seamless mouse,
- // but a workspace might be smaller depending on panels.
- container->width = width;
- container->height = height;
- }
- // arrange all workspaces:
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- arrange_windows_r(child, -1, -1);
- }
- // Bring all unmanaged views to the front
- for (i = 0; i < container->unmanaged->length; ++i) {
- wlc_handle *handle = container->unmanaged->items[i];
- wlc_view_bring_to_front(*handle);
- }
- return;
- case C_WORKSPACE:
- {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- width = output->width, height = output->height;
- for (i = 0; i < desktop_shell.panels->length; ++i) {
- struct panel_config *config = desktop_shell.panels->items[i];
- if (config->output == output->handle) {
- struct wlc_size size = *wlc_surface_get_size(config->surface);
- sway_log(L_DEBUG, "-> Found panel for this workspace: %ux%u, position: %u", size.w, size.h, config->panel_position);
- switch (config->panel_position) {
- case DESKTOP_SHELL_PANEL_POSITION_TOP:
- y += size.h; height -= size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
- height -= size.h;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_LEFT:
- x += size.w; width -= size.w;
- break;
- case DESKTOP_SHELL_PANEL_POSITION_RIGHT:
- width -= size.w;
- break;
- }
- }
- }
- int gap = swayc_gap(container);
- x = container->x = x + gap;
- y = container->y = y + gap;
- width = container->width = width - gap * 2;
- height = container->height = height - gap * 2;
- sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y);
- }
- // children are properly handled below
- break;
- case C_VIEW:
- {
- container->width = width;
- container->height = height;
- update_geometry(container);
- sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", container->width,
- container->height, container->x, container->y);
- }
- return;
- default:
- container->width = width;
- container->height = height;
- x = container->x;
- y = container->y;
-
- // add gaps to top level tapped/stacked container
- if (container->parent->type == C_WORKSPACE &&
- (container->layout == L_TABBED || container->layout == L_STACKED)) {
- update_geometry(container);
- width = container->border_geometry.size.w;
- height = container->border_geometry.size.h;
- x = container->border_geometry.origin.x;
- y = container->border_geometry.origin.y;
- }
-
- // update container size if it's a direct child in a tabbed/stacked layout
- // if parent is a workspace, its actual_geometry won't be initialized
- if (swayc_tabbed_stacked_parent(container) != NULL &&
- container->parent->type != C_WORKSPACE) {
- // Use parent actual_geometry as a base for calculating
- // container geometry
- container->width = container->parent->actual_geometry.size.w;
- container->height = container->parent->actual_geometry.size.h;
- container->x = container->parent->actual_geometry.origin.x;
- container->y = container->parent->actual_geometry.origin.y;
-
- update_geometry(container);
- width = container->width = container->actual_geometry.size.w;
- height = container->height = container->actual_geometry.size.h;
- x = container->x = container->actual_geometry.origin.x;
- y = container->y = container->actual_geometry.origin.y;
- }
-
- break;
- }
-
- switch (container->layout) {
- case L_HORIZ:
- default:
- apply_horiz_layout(container, x, y, width, height, 0,
- container->children->length);
- break;
- case L_VERT:
- apply_vert_layout(container, x, y, width, height, 0,
- container->children->length);
- break;
- case L_TABBED:
- case L_STACKED:
- apply_tabbed_or_stacked_layout(container, x, y, width, height);
- break;
- case L_AUTO_LEFT:
- apply_auto_layout(container, x, y, width, height, L_VERT, true);
- break;
- case L_AUTO_RIGHT:
- apply_auto_layout(container, x, y, width, height, L_VERT, false);
- break;
- case L_AUTO_TOP:
- apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
- break;
- case L_AUTO_BOTTOM:
- apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
- break;
- }
-
- // Arrage floating layouts for workspaces last
- if (container->type == C_WORKSPACE) {
- for (int i = 0; i < container->floating->length; ++i) {
- swayc_t *view = container->floating->items[i];
- if (view->type == C_VIEW) {
- update_geometry(view);
- sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
- view->width, view->height, view->x, view->y);
- if (swayc_is_fullscreen(view)) {
- wlc_view_bring_to_front(view->handle);
- } else if (!container->focused ||
- !swayc_is_fullscreen(container->focused)) {
- wlc_view_bring_to_front(view->handle);
- }
- }
- }
- }
-}
-
-void apply_horiz_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- const int start, const int end) {
- double scale = 0;
- // Calculate total width
- for (int i = start; i < end; ++i) {
- double *old_width = &((swayc_t *)container->children->items[i])->width;
- if (*old_width <= 0) {
- if (end - start > 1) {
- *old_width = width / (end - start - 1);
- } else {
- *old_width = width;
- }
- }
- scale += *old_width;
- }
- scale = width / scale;
-
- // Resize windows
- double child_x = x;
- if (scale > 0.1) {
- sway_log(L_DEBUG, "Arranging %p horizontally", container);
- swayc_t *focused = NULL;
- for (int i = start; i < end; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)", child,
- child->type, width, scale);
- child->x = child_x;
- child->y = y;
-
- if (child == container->focused) {
- focused = child;
- }
-
- if (i == end - 1) {
- double remaining_width = x + width - child_x;
- arrange_windows_r(child, remaining_width, height);
- } else {
- arrange_windows_r(child, child->width * scale, height);
- }
- child_x += child->width;
- }
-
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
- }
- }
-}
-
-void apply_vert_layout(swayc_t *container, const double x, const double y,
- const double width, const double height, const int start,
- const int end) {
- int i;
- double scale = 0;
- // Calculate total height
- for (i = start; i < end; ++i) {
- double *old_height = &((swayc_t *)container->children->items[i])->height;
- if (*old_height <= 0) {
- if (end - start > 1) {
- *old_height = height / (end - start - 1);
- } else {
- *old_height = height;
- }
- }
- scale += *old_height;
- }
- scale = height / scale;
-
- // Resize
- double child_y = y;
- if (scale > 0.1) {
- sway_log(L_DEBUG, "Arranging %p vertically", container);
- swayc_t *focused = NULL;
- for (i = start; i < end; ++i) {
- swayc_t *child = container->children->items[i];
- sway_log(L_DEBUG,
- "Calculating arrangement for %p:%d (will scale %f by %f)", child,
- child->type, height, scale);
- child->x = x;
- child->y = child_y;
-
- if (child == container->focused) {
- focused = child;
- }
-
- if (i == end - 1) {
- double remaining_height = y + height - child_y;
- arrange_windows_r(child, width, remaining_height);
- } else {
- arrange_windows_r(child, width, child->height * scale);
- }
- child_y += child->height;
- }
-
- // update focused view border last because it may
- // depend on the title bar geometry of its siblings.
- if (focused && container->children->length > 1) {
- update_container_border(focused);
- }
- }
-}
-
-void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
- double width, double height) {
- int i;
- swayc_t *focused = NULL;
- for (i = 0; i < container->children->length; ++i) {
- swayc_t *child = container->children->items[i];
- child->x = x;
- child->y = y;
- if (child == container->focused) {
- focused = child;
- } else {
- arrange_windows_r(child, width, height);
- }
- }
-
- if (focused) {
- arrange_windows_r(focused, width, height);
- }
-}
-
-void apply_auto_layout(swayc_t *container, const double x, const double y,
- const double width, const double height,
- enum swayc_layouts group_layout,
- bool master_first) {
- // Auto layout "container" in width x height @ x, y
- // using "group_layout" for each of the groups in the container.
- // There is one "master" group, plus container->nb_slave_groups.
- // Each group is layed out side by side following the "major" axis.
- // The direction of the layout used for groups is the "minor" axis.
- // Example:
- //
- // ---- major axis -->
- // +---------+-----------+
- // | | | |
- // | master | slave 1 | |
- // | +-----------+ | minor axis (direction of group_layout)
- // | | | |
- // | | slave 2 | V
- // +---------+-----------+
- //
- // container with three children (one master and two slaves) and
- // a single slave group (containing slave 1 and 2). The master
- // group and slave group are layed out using L_VERT.
-
- size_t nb_groups = auto_group_count(container);
-
- // the target dimension of the container along the "major" axis, each
- // group in the container will be layed out using "group_layout" along
- // the "minor" axis.
- double dim_maj;
- double pos_maj;
-
- // x and y coords for the next group to be laid out.
- const double *group_x, *group_y;
-
- // pos of the next group to layout along the major axis
- double pos;
-
- // size of the next group along the major axis.
- double group_dim;
-
- // height and width of next group to be laid out.
- const double *group_h, *group_w;
-
- switch (group_layout) {
- default:
- sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
- group_layout, __func__);
- /* fall through */
- case L_VERT:
- dim_maj = width;
- pos_maj = x;
-
- group_x = &pos;
- group_y = &y;
- group_w = &group_dim;
- group_h = &height;
- break;
- case L_HORIZ:
- dim_maj = height;
- pos_maj = y;
-
- group_x = &x;
- group_y = &pos;
- group_w = &width;
- group_h = &group_dim;
- break;
- }
-
- /* Determine the dimension of each of the groups in the layout.
- * Dimension will be width for a VERT layout and height for a HORIZ
- * layout. */
- double old_group_dim[nb_groups];
- double old_dim = 0;
- for (size_t group = 0; group < nb_groups; ++group) {
- int idx;
- if (auto_group_bounds(container, group, &idx, NULL)) {
- swayc_t *child = container->children->items[idx];
- double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
- if (*dim <= 0) {
- // New child with uninitialized dimension
- *dim = dim_maj;
- if (nb_groups > 1) {
- // child gets a dimension proportional to existing groups,
- // it will be later scaled based on to the available size
- // in the major axis.
- *dim /= (nb_groups - 1);
- }
- }
- old_dim += *dim;
- old_group_dim[group] = *dim;
- }
- }
- double scale = dim_maj / old_dim;
-
- /* Apply layout to each group */
- pos = pos_maj;
-
- for (size_t group = 0; group < nb_groups; ++group) {
- int start, end; // index of first (inclusive) and last (exclusive) child in the group
- if (auto_group_bounds(container, group, &start, &end)) {
- // adjusted size of the group
- group_dim = old_group_dim[group] * scale;
- if (group == nb_groups - 1) {
- group_dim = pos_maj + dim_maj - pos; // remaining width
- }
- sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
- container, group, start, end, *group_w, *group_h, *group_x, *group_y);
- switch (group_layout) {
- default:
- case L_VERT:
- apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
- break;
- case L_HORIZ:
- apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
- break;
- }
-
- /* update position for next group */
- pos += group_dim;
- }
- }
-}
-
-void arrange_windows(swayc_t *container, double width, double height) {
- update_visibility(container);
- arrange_windows_r(container, width, height);
- layout_log(&root_container, 0);
-}
-
-void arrange_backgrounds(void) {
- struct background_config *bg;
- for (int i = 0; i < desktop_shell.backgrounds->length; ++i) {
- bg = desktop_shell.backgrounds->items[i];
- wlc_view_send_to_back(bg->handle);
- }
-}
-
-/**
- * Get swayc in the direction of newly entered output.
- */
-static swayc_t *get_swayc_in_output_direction(swayc_t *output, enum movement_direction dir) {
- if (!output) {
- return NULL;
- }
-
- swayc_t *ws = swayc_focus_by_type(output, C_WORKSPACE);
- if (ws && ws->children->length > 0) {
- switch (dir) {
- case MOVE_LEFT:
- // get most right child of new output
- return ws->children->items[ws->children->length-1];
- case MOVE_RIGHT:
- // get most left child of new output
- return ws->children->items[0];
- case MOVE_UP:
- case MOVE_DOWN:
- {
- swayc_t *focused_view = swayc_focus_by_type(ws, C_VIEW);
- if (focused_view && focused_view->parent) {
- swayc_t *parent = focused_view->parent;
- if (parent->layout == L_VERT) {
- if (dir == MOVE_UP) {
- // get child furthest down on new output
- return parent->children->items[parent->children->length-1];
- } else if (dir == MOVE_DOWN) {
- // get child furthest up on new output
- return parent->children->items[0];
- }
- }
- return focused_view;
- }
- break;
- }
- default:
- break;
- }
- }
-
- return output;
-}
-
-swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_direction dir, swayc_t *limit) {
- if (dir == MOVE_CHILD) {
- return container->focused;
- }
-
- swayc_t *parent = container->parent;
- if (dir == MOVE_PARENT) {
- if (parent->type == C_OUTPUT) {
- return NULL;
- } else {
- return parent;
- }
- }
-
- if (dir == MOVE_PREV || dir == MOVE_NEXT) {
- int focused_idx = index_child(container);
- if (focused_idx == -1) {
- return NULL;
- } else {
- int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
- parent->children->length;
- if (desired < 0) {
- desired += parent->children->length;
- }
- return parent->children->items[desired];
- }
- }
-
- // If moving to an adjacent output we need a starting position (since this
- // output might border to multiple outputs).
- struct wlc_point abs_pos;
- get_absolute_center_position(container, &abs_pos);
-
- if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
- sway_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
- container = swayc_parent_by_type(container, C_OUTPUT);
- get_absolute_center_position(container, &abs_pos);
- swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
- return get_swayc_in_output_direction(output, dir);
- }
-
- if (container->type == C_WORKSPACE && container->fullscreen) {
- sway_log(L_DEBUG, "Moving to fullscreen view");
- return container->fullscreen;
- }
-
- swayc_t *wrap_candidate = NULL;
- while (true) {
- // Test if we can even make a difference here
- bool can_move = false;
- int desired;
- int idx = index_child(container);
- if (parent->type == C_ROOT) {
- swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
- if (!output || output == container) {
- return wrap_candidate;
- }
- sway_log(L_DEBUG, "Moving between outputs");
- return get_swayc_in_output_direction(output, dir);
- } else {
- if (is_auto_layout(parent->layout)) {
- bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
- ? dir == MOVE_LEFT || dir == MOVE_RIGHT
- : dir == MOVE_DOWN || dir == MOVE_UP;
- size_t gidx = auto_group_index(parent, idx);
- if (is_major) {
- size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
- can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
- } else {
- desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
- int start, end;
- can_move = auto_group_bounds(parent, gidx, &start, &end)
- && desired >= start && desired < end;
- }
- } else {
- if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
- if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
- can_move = true;
- desired = idx + (dir == MOVE_LEFT ? -1 : 1);
- }
- } else {
- if (parent->layout == L_VERT || parent->layout == L_STACKED) {
- can_move = true;
- desired = idx + (dir == MOVE_UP ? -1 : 1);
- }
- }
- }
- }
-
- if (can_move) {
- if (container->is_floating) {
- if (desired < 0) {
- wrap_candidate = parent->floating->items[parent->floating->length-1];
- } else if (desired >= parent->floating->length){
- wrap_candidate = parent->floating->items[0];
- } else {
- wrap_candidate = parent->floating->items[desired];
- }
- if (wrap_candidate) {
- wlc_view_bring_to_front(wrap_candidate->handle);
- }
- return wrap_candidate;
- } else if (desired < 0 || desired >= parent->children->length) {
- can_move = false;
- int len = parent->children->length;
- if (!wrap_candidate && len > 1) {
- if (desired < 0) {
- wrap_candidate = parent->children->items[len-1];
- } else {
- wrap_candidate = parent->children->items[0];
- }
- if (config->force_focus_wrapping) {
- return wrap_candidate;
- }
- }
- } else {
- sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
- idx, container, dir, desired, parent->children->items[desired]);
- return parent->children->items[desired];
- }
- }
- if (!can_move) {
- container = parent;
- parent = parent->parent;
- if (!parent || container == limit) {
- // wrapping is the last chance
- return wrap_candidate;
- }
- }
- }
-}
-
-swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) {
- return get_swayc_in_direction_under(container, dir, NULL);
-}
-
-void recursive_resize(swayc_t *container, double amount, enum wlc_resize_edge edge) {
- int i;
- bool layout_match = true;
- sway_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
- if (edge == WLC_RESIZE_EDGE_LEFT || edge == WLC_RESIZE_EDGE_RIGHT) {
- container->width += amount;
- layout_match = container->layout == L_HORIZ;
- } else if (edge == WLC_RESIZE_EDGE_TOP || edge == WLC_RESIZE_EDGE_BOTTOM) {
- container->height += amount;
- layout_match = container->layout == L_VERT;
- }
- if (container->type == C_VIEW) {
- update_geometry(container);
- return;
- }
- if (layout_match) {
- for (i = 0; i < container->children->length; i++) {
- recursive_resize(container->children->items[i], amount/container->children->length, edge);
- }
- } else {
- for (i = 0; i < container->children->length; i++) {
- recursive_resize(container->children->items[i], amount, edge);
- }
- }
-}
-
-enum swayc_layouts default_layout(swayc_t *output) {
- if (config->default_layout != L_NONE) {
- return config->default_layout;
- } else if (config->default_orientation != L_NONE) {
- return config->default_orientation;
- } else if (output->width >= output->height) {
- return L_HORIZ;
- } else {
- return L_VERT;
- }
-}
-
-bool is_auto_layout(enum swayc_layouts layout) {
- return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
-}
-
-/**
- * Return the number of master elements in a container
- */
-static inline size_t auto_master_count(const swayc_t *container) {
- sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
- container, container->children->length);
- return MIN(container->nb_master, (size_t)container->children->length);
-}
-
-/**
- * Return the number of children in the slave groups. This corresponds to the children
- * that are not members of the master group.
- */
-static inline size_t auto_slave_count(const swayc_t *container) {
- return container->children->length - auto_master_count(container);
-}
-
-/**
- * Return the number of slave groups in the container.
- */
-size_t auto_slave_group_count(const swayc_t *container) {
- return MIN(container->nb_slave_groups, auto_slave_count(container));
-}
-
-/**
- * Return the combined number of master and slave groups in the container.
- */
-size_t auto_group_count(const swayc_t *container) {
- return auto_slave_group_count(container)
- + (container->children->length && container->nb_master ? 1 : 0);
-}
-
-/**
- * given the index of a container's child, return the index of the first child of the group
- * which index is a member of.
- */
-int auto_group_start_index(const swayc_t *container, int index) {
- if (index < 0 || ! is_auto_layout(container->layout)
- || (size_t)index < container->nb_master) {
- return 0;
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- int start_idx;
- if (index < idx2) {
- start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
- } else {
- start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
- }
- return MIN(start_idx, container->children->length);
- }
-}
-
-/**
- * given the index of a container's child, return the index of the first child of the group
- * that follows the one which index is a member of.
- * This makes the function usable to walk through the groups in a container.
- */
-int auto_group_end_index(const swayc_t *container, int index) {
- if (index < 0 || ! is_auto_layout(container->layout)) {
- return container->children->length;
- } else {
- int nxt_idx;
- if ((size_t)index < container->nb_master) {
- nxt_idx = auto_master_count(container);
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- if (index < idx2) {
- nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
- } else {
- nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
- }
- }
- return MIN(nxt_idx, container->children->length);
- }
-}
-
-/**
- * return the index of the Group containing <index>th child of <container>.
- * The index is the order of the group along the container's major axis (starting at 0).
- */
-size_t auto_group_index(const swayc_t *container, int index) {
- if (index < 0) {
- return 0;
- }
- bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
- size_t nb_slaves = auto_slave_count(container);
- if ((size_t)index < container->nb_master) {
- if (master_first || nb_slaves <= 0) {
- return 0;
- } else {
- return auto_slave_group_count(container);
- }
- } else {
- size_t nb_slave_grp = auto_slave_group_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
- size_t grp_idx;
- if (index < idx2) {
- grp_idx = (index - container->nb_master) / grp_sz;
- } else {
- grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
- }
- return grp_idx + (master_first && container-> nb_master ? 1 : 0);
- }
-}
-
-/**
- * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
- * an auto layout.
- * If the bounds of the given group can be calculated, they are returned in the start/end
- * parameters (int pointers) and the return value will be true.
- * The indexes are passed by reference and can be NULL.
- */
-bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
- size_t nb_grp = auto_group_count(container);
- if (group_index >= nb_grp) {
- return false;
- }
- bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
- size_t nb_master = auto_master_count(container);
- size_t nb_slave_grp = auto_slave_group_count(container);
- int g_start, g_end;
- if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
- g_start = 0;
- g_end = nb_master;
- } else {
- size_t nb_slaves = auto_slave_count(container);
- size_t grp_sz = nb_slaves / nb_slave_grp;
- size_t remainder = nb_slaves % nb_slave_grp;
- size_t g0 = master_first && container->nb_master ? 1 : 0;
- size_t g1 = g0 + nb_slave_grp - remainder;
- if (group_index < g1) {
- g_start = container->nb_master + (group_index - g0) * grp_sz;
- g_end = g_start + grp_sz;
- } else {
- size_t g2 = group_index - g1;
- g_start = container->nb_master
- + (nb_slave_grp - remainder) * grp_sz
- + g2 * (grp_sz + 1);
- g_end = g_start + grp_sz + 1;
- }
- }
- if (start) {
- *start = g_start;
- }
- if (end) {
- *end = g_end;
- }
- return true;
-}
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 @@
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200112L
-#include <stdio.h>
-#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
#include <stdbool.h>
-#include <wlc/wlc.h>
-#include <sys/wait.h>
-#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <sys/un.h>
-#include <signal.h>
#include <unistd.h>
-#include <getopt.h>
#ifdef __linux__
#include <sys/capability.h>
#include <sys/prctl.h>
#endif
-#include "sway/extensions.h"
-#include "sway/layout.h"
+#include <wlr/util/log.h>
#include "sway/config.h"
-#include "sway/security.h"
-#include "sway/handlers.h"
-#include "sway/input.h"
+#include "sway/debug.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
#include "sway/ipc-server.h"
#include "ipc-client.h"
#include "readline.h"
#include "stringop.h"
-#include "sway.h"
-#include "log.h"
#include "util.h"
static bool terminate_request = false;
static int exit_value = 0;
+struct sway_server server;
void sway_terminate(int exit_code) {
terminate_request = true;
exit_value = exit_code;
- wlc_terminate();
+ wl_display_terminate(server.wl_display);
}
void sig_handler(int signal) {
- close_views(&root_container);
+ //close_views(&root_container);
sway_terminate(EXIT_SUCCESS);
}
-static void wlc_log_handler(enum wlc_log_type type, const char *str) {
- if (type == WLC_LOG_ERROR) {
- sway_log(L_ERROR, "[wlc] %s", str);
- } else if (type == WLC_LOG_WARN) {
- sway_log(L_INFO, "[wlc] %s", str);
- } else {
- sway_log(L_DEBUG, "[wlc] %s", str);
- }
-}
-
void detect_raspi() {
bool raspi = false;
FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
@@ -98,23 +86,16 @@ void detect_proprietary() {
if (!f) {
return;
}
- bool nvidia = false, nvidia_modeset = false, nvidia_uvm = false, nvidia_drm = false;
while (!feof(f)) {
char *line;
if (!(line = read_line(f))) {
break;
}
if (strstr(line, "nvidia")) {
- nvidia = true;
- }
- if (strstr(line, "nvidia_modeset")) {
- nvidia_modeset = true;
- }
- if (strstr(line, "nvidia_uvm")) {
- nvidia_uvm = true;
- }
- if (strstr(line, "nvidia_drm")) {
- nvidia_drm = true;
+ fprintf(stderr, "\x1B[1;31mWarning: Proprietary Nvidia drivers are "
+ "NOT supported. Use Nouveau.\x1B[0m\n");
+ free(line);
+ break;
}
if (strstr(line, "fglrx")) {
fprintf(stderr, "\x1B[1;31mWarning: Proprietary AMD drivers do "
@@ -125,52 +106,6 @@ void detect_proprietary() {
free(line);
}
fclose(f);
- if (nvidia) {
- fprintf(stderr, "\x1B[1;31mWarning: Proprietary nvidia driver support "
- "is considered experimental. Nouveau is strongly recommended."
- "\x1B[0m\n");
- if (!nvidia_modeset || !nvidia_uvm || !nvidia_drm) {
- fprintf(stderr, "\x1B[1;31mWarning: You do not have all of the "
- "necessary kernel modules loaded for nvidia support. "
- "You need nvidia, nvidia_modeset, nvidia_uvm, and nvidia_drm."
- "\x1B[0m\n");
- }
-#ifdef __linux__
- f = fopen("/sys/module/nvidia_drm/parameters/modeset", "r");
- if (f) {
- char *line = read_line(f);
- if (line && strstr(line, "Y")) {
- // nvidia-drm.modeset is set to 0
- fprintf(stderr, "\x1B[1;31mWarning: You must load "
- "nvidia-drm with the modeset option on to use "
- "the proprietary driver. Consider adding "
- "nvidia-drm.modeset=1 to your kernel command line "
- "parameters.\x1B[0m\n");
- }
- fclose(f);
- free(line);
- } else {
- // nvidia-drm.modeset is not set
- fprintf(stderr, "\x1B[1;31mWarning: You must load "
- "nvidia-drm with the modeset option on to use "
- "the proprietary driver. Consider adding "
- "nvidia-drm.modeset=1 to your kernel command line "
- "parameters.\x1B[0m\n");
- }
-#else
- f = fopen("/proc/cmdline", "r");
- if (f) {
- char *line = read_line(f);
- if (line && !strstr(line, "nvidia-drm.modeset=1")) {
- fprintf(stderr, "\x1B[1;31mWarning: You must add "
- "nvidia-drm.modeset=1 to your kernel command line to use "
- "the proprietary driver.\x1B[0m\n");
- }
- fclose(f);
- free(line);
- }
-#endif
- }
}
void run_as_ipc_client(char *command, char *socket_path) {
@@ -184,27 +119,15 @@ void run_as_ipc_client(char *command, char *socket_path) {
static void log_env() {
const char *log_vars[] = {
"PATH",
- "LD_LOAD_PATH",
+ "LD_LIBRARY_PATH",
"LD_PRELOAD_PATH",
"LD_LIBRARY_PATH",
"SWAY_CURSOR_THEME",
"SWAY_CURSOR_SIZE",
- "SWAYSOCK",
- "WLC_DRM_DEVICE",
- "WLC_SHM",
- "WLC_OUTPUTS",
- "WLC_XWAYLAND",
- "WLC_LIBINPUT",
- "WLC_REPEAT_DELAY",
- "WLC_REPEAT_RATE",
- "XKB_DEFAULT_RULES",
- "XKB_DEFAULT_MODEL",
- "XKB_DEFAULT_LAYOUT",
- "XKB_DEFAULT_VARIANT",
- "XKB_DEFAULT_OPTIONS",
+ "SWAYSOCK"
};
for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) {
- sway_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));
+ wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));
}
}
@@ -219,14 +142,14 @@ static void log_distro() {
for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) {
FILE *f = fopen(paths[i], "r");
if (f) {
- sway_log(L_INFO, "Contents of %s:", paths[i]);
+ wlr_log(L_INFO, "Contents of %s:", paths[i]);
while (!feof(f)) {
char *line;
if (!(line = read_line(f))) {
break;
}
if (*line) {
- sway_log(L_INFO, "%s", line);
+ wlr_log(L_INFO, "%s", line);
}
free(line);
}
@@ -236,9 +159,10 @@ static void log_distro() {
}
static void log_kernel() {
+ return;
FILE *f = popen("uname -a", "r");
if (!f) {
- sway_log(L_INFO, "Unable to determine kernel version");
+ wlr_log(L_INFO, "Unable to determine kernel version");
return;
}
while (!feof(f)) {
@@ -247,7 +171,7 @@ static void log_kernel() {
break;
}
if (*line) {
- sway_log(L_INFO, "%s", line);
+ wlr_log(L_INFO, "%s", line);
}
free(line);
}
@@ -258,14 +182,14 @@ static void security_sanity_check() {
// TODO: Notify users visually if this has issues
struct stat s;
if (stat("/proc", &s)) {
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"!! DANGER !! /proc is not available - sway CANNOT enforce security rules!");
}
#ifdef __linux__
cap_flag_value_t v;
cap_t cap = cap_get_proc();
if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) {
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users.");
}
if (cap) {
@@ -281,13 +205,13 @@ static void executable_sanity_check() {
stat(exe, &sb);
// We assume that cap_get_file returning NULL implies ENODATA
if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) {
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"sway executable has both the s(g)uid bit AND file caps set.");
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"This is strongly discouraged (and completely broken).");
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"Please clear one of them (either the suid bit, or the file caps).");
- sway_log(L_ERROR,
+ wlr_log(L_ERROR,
"If unsure, strip the file caps.");
exit(EXIT_FAILURE);
}
@@ -295,6 +219,37 @@ static void executable_sanity_check() {
#endif
}
+static void drop_permissions(bool keep_caps) {
+ if (getuid() != geteuid() || getgid() != getegid()) {
+ if (setgid(getgid()) != 0) {
+ wlr_log(L_ERROR, "Unable to drop root");
+ exit(EXIT_FAILURE);
+ }
+ if (setuid(getuid()) != 0) {
+ wlr_log(L_ERROR, "Unable to drop root");
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (setuid(0) != -1) {
+ wlr_log(L_ERROR, "Root privileges can be restored.");
+ exit(EXIT_FAILURE);
+ }
+#ifdef __linux__
+ if (keep_caps) {
+ // Drop every cap except CAP_SYS_PTRACE
+ cap_t caps = cap_init();
+ cap_value_t keep = CAP_SYS_PTRACE;
+ wlr_log(L_INFO, "Dropping extra capabilities");
+ if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
+ cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
+ cap_set_proc(caps)) {
+ wlr_log(L_ERROR, "Failed to drop extra capabilities");
+ exit(EXIT_FAILURE);
+ }
+ }
+#endif
+}
+
int main(int argc, char **argv) {
static int verbose = 0, debug = 0, validate = 0;
@@ -334,7 +289,7 @@ int main(int argc, char **argv) {
int c;
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hCdvVc:", long_options, &option_index);
+ c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index);
if (c == -1) {
break;
}
@@ -352,6 +307,9 @@ int main(int argc, char **argv) {
case 'd': // debug
debug = 1;
break;
+ case 'D': // extended debug options
+ enable_debug_tree = true;
+ break;
case 'v': // version
fprintf(stdout, "sway version " SWAY_VERSION "\n");
exit(EXIT_SUCCESS);
@@ -374,37 +332,24 @@ int main(int argc, char **argv) {
}
}
- // we need to setup logging before wlc_init in case it fails.
+ // TODO: switch logging over to wlroots?
if (debug) {
- init_log(L_DEBUG);
+ wlr_log_init(L_DEBUG, NULL);
} else if (verbose || validate) {
- init_log(L_INFO);
+ wlr_log_init(L_INFO, NULL);
} else {
- init_log(L_ERROR);
+ wlr_log_init(L_ERROR, NULL);
}
if (optind < argc) { // Behave as IPC client
if(optind != 1) {
- sway_log(L_ERROR, "Don't use options with the IPC client");
- exit(EXIT_FAILURE);
- }
- if (getuid() != geteuid() || getgid() != getegid()) {
- if (setgid(getgid()) != 0) {
- sway_log(L_ERROR, "Unable to drop root");
- exit(EXIT_FAILURE);
- }
- if (setuid(getuid()) != 0) {
- sway_log(L_ERROR, "Unable to drop root");
- exit(EXIT_FAILURE);
- }
- }
- if (setuid(0) != -1) {
- sway_log(L_ERROR, "Root privileges can be restored.");
+ wlr_log(L_ERROR, "Don't use options with the IPC client");
exit(EXIT_FAILURE);
}
+ drop_permissions(false);
char *socket_path = getenv("SWAYSOCK");
if (!socket_path) {
- sway_log(L_ERROR, "Unable to retrieve socket path");
+ wlr_log(L_ERROR, "Unable to retrieve socket path");
exit(EXIT_FAILURE);
}
char *command = join_args(argv + optind, argc - optind);
@@ -413,49 +358,25 @@ int main(int argc, char **argv) {
}
executable_sanity_check();
-#ifdef __linux__
bool suid = false;
+#ifdef __linux__
if (getuid() != geteuid() || getgid() != getegid()) {
// Retain capabilities after setuid()
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
- sway_log(L_ERROR, "Cannot keep caps after setuid()");
+ wlr_log(L_ERROR, "Cannot keep caps after setuid()");
exit(EXIT_FAILURE);
}
suid = true;
}
#endif
- wlc_log_set_handler(wlc_log_handler);
log_kernel();
log_distro();
- log_env();
detect_proprietary();
detect_raspi();
- input_devices = create_list();
-
- /* Changing code earlier than this point requires detailed review */
- /* (That code runs as root on systems without logind, and wlc_init drops to
- * another user.) */
- register_wlc_handlers();
- if (!wlc_init()) {
- return 1;
- }
- register_extensions();
-
#ifdef __linux__
- if (suid) {
- // Drop every cap except CAP_SYS_PTRACE
- cap_t caps = cap_init();
- cap_value_t keep = CAP_SYS_PTRACE;
- sway_log(L_INFO, "Dropping extra capabilities");
- if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
- cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
- cap_set_proc(caps)) {
- sway_log(L_ERROR, "Failed to drop extra capabilities");
- exit(EXIT_FAILURE);
- }
- }
+ drop_permissions(suid);
#endif
// handle SIGTERM signals
signal(SIGTERM, sig_handler);
@@ -463,11 +384,16 @@ int main(int argc, char **argv) {
// prevent ipc from crashing sway
signal(SIGPIPE, SIG_IGN);
- sway_log(L_INFO, "Starting sway version " SWAY_VERSION "\n");
+ wlr_log(L_INFO, "Starting sway version " SWAY_VERSION);
+
+ layout_init();
- init_layout();
+ if (!server_init(&server)) {
+ return 1;
+ }
- ipc_init();
+ ipc_init(&server);
+ log_env();
if (validate) {
bool valid = load_main_config(config_path, false);
@@ -484,11 +410,17 @@ int main(int argc, char **argv) {
security_sanity_check();
+ // TODO: wait for server to be ready
+ // TODO: consume config->cmd_queue
+ config->active = true;
+
if (!terminate_request) {
- wlc_run();
+ server_run(&server);
}
- list_free(input_devices);
+ wlr_log(L_INFO, "Shutting down sway");
+
+ server_fini(&server);
ipc_terminate();
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 @@
+sway_sources = files(
+ 'main.c',
+ 'server.c',
+ 'commands.c',
+ 'config.c',
+ 'criteria.c',
+ 'debug-tree.c',
+ 'ipc-json.c',
+ 'ipc-server.c',
+ 'security.c',
+
+ 'desktop/desktop.c',
+ 'desktop/output.c',
+ 'desktop/layer_shell.c',
+ 'desktop/wl_shell.c',
+ 'desktop/xdg_shell_v6.c',
+ 'desktop/xwayland.c',
+
+ 'input/input-manager.c',
+ 'input/seat.c',
+ 'input/cursor.c',
+ 'input/keyboard.c',
+
+ 'config/bar.c',
+ 'config/output.c',
+ 'config/seat.c',
+ 'config/input.c',
+
+ 'commands/bar.c',
+ 'commands/bind.c',
+ 'commands/default_orientation.c',
+ 'commands/exit.c',
+ 'commands/exec.c',
+ 'commands/exec_always.c',
+ 'commands/focus.c',
+ 'commands/focus_follows_mouse.c',
+ 'commands/kill.c',
+ 'commands/opacity.c',
+ 'commands/include.c',
+ 'commands/input.c',
+ 'commands/layout.c',
+ 'commands/mode.c',
+ 'commands/mouse_warping.c',
+ 'commands/move.c',
+ 'commands/output.c',
+ 'commands/reload.c',
+ 'commands/resize.c',
+ 'commands/seat.c',
+ 'commands/seat/attach.c',
+ 'commands/seat/cursor.c',
+ 'commands/seat/fallback.c',
+ 'commands/set.c',
+ 'commands/split.c',
+ 'commands/swaybg_command.c',
+ 'commands/workspace.c',
+ 'commands/ws_auto_back_and_forth.c',
+
+ 'commands/bar/activate_button.c',
+ 'commands/bar/binding_mode_indicator.c',
+ 'commands/bar/bindsym.c',
+ 'commands/bar/colors.c',
+ 'commands/bar/context_button.c',
+ 'commands/bar/font.c',
+ 'commands/bar/height.c',
+ 'commands/bar/hidden_state.c',
+ 'commands/bar/icon_theme.c',
+ 'commands/bar/id.c',
+ 'commands/bar/mode.c',
+ 'commands/bar/modifier.c',
+ 'commands/bar/output.c',
+ 'commands/bar/pango_markup.c',
+ 'commands/bar/position.c',
+ 'commands/bar/secondary_button.c',
+ 'commands/bar/separator_symbol.c',
+ 'commands/bar/status_command.c',
+ 'commands/bar/strip_workspace_numbers.c',
+ 'commands/bar/swaybar_command.c',
+ 'commands/bar/tray_output.c',
+ 'commands/bar/tray_padding.c',
+ 'commands/bar/workspace_buttons.c',
+ 'commands/bar/wrap_scroll.c',
+
+ 'commands/input/accel_profile.c',
+ 'commands/input/click_method.c',
+ 'commands/input/drag_lock.c',
+ 'commands/input/dwt.c',
+ 'commands/input/events.c',
+ 'commands/input/left_handed.c',
+ 'commands/input/map_to_output.c',
+ 'commands/input/middle_emulation.c',
+ 'commands/input/natural_scroll.c',
+ 'commands/input/pointer_accel.c',
+ 'commands/input/scroll_method.c',
+ 'commands/input/tap.c',
+ 'commands/input/xkb_layout.c',
+ 'commands/input/xkb_model.c',
+ 'commands/input/xkb_options.c',
+ 'commands/input/xkb_rules.c',
+ 'commands/input/xkb_variant.c',
+
+ 'tree/container.c',
+ 'tree/layout.c',
+ 'tree/view.c',
+ 'tree/workspace.c',
+ 'tree/output.c',
+)
+
+sway_deps = [
+ cairo,
+ gdk_pixbuf,
+ jsonc,
+ libcap,
+ libinput,
+ math,
+ pango,
+ pcre,
+ pixman,
+ server_protos,
+ wayland_server,
+ wlroots,
+ xkbcommon,
+]
+
+executable(
+ 'sway',
+ sway_sources,
+ include_directories: [sway_inc],
+ dependencies: sway_deps,
+ link_with: [lib_sway_common],
+ install: true
+)
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 @@
-#include <strings.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include "sway/output.h"
-#include "log.h"
-#include "list.h"
-
-void output_get_scaled_size(wlc_handle handle, struct wlc_size *size) {
- *size = *wlc_output_get_resolution(handle);
- uint32_t scale = wlc_output_get_scale(handle);
- size->w /= scale;
- size->h /= scale;
-}
-
-swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) {
- swayc_t *output = NULL;
- // If there is no output directly next to the current one, use
- // swayc_opposite_output to wrap.
- if (strcasecmp(name, "left") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_RIGHT, abs_pos);
- }
- } else if (strcasecmp(name, "right") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_LEFT, abs_pos);
- }
- } else if (strcasecmp(name, "up") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_DOWN, abs_pos);
- }
- } else if (strcasecmp(name, "down") == 0) {
- output = swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true);
- if (!output) {
- output = swayc_opposite_output(MOVE_UP, abs_pos);
- }
- } else {
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c->type == C_OUTPUT && strcasecmp(c->name, name) == 0) {
- return c;
- }
- }
- }
- return output;
-}
-
-swayc_t *swayc_opposite_output(enum movement_direction dir,
- const struct wlc_point *abs_pos) {
-
- // Search through all the outputs and pick the output whose edge covers the
- // given position, and is at leftmost/rightmost/upmost/downmost side of the
- // screen (decided by the direction given).
- swayc_t *opposite = NULL;
- char *dir_text = NULL;
- switch(dir) {
- case MOVE_LEFT:
- case MOVE_RIGHT: ;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
- if (!opposite) {
- opposite = c;
- } else if ((dir == MOVE_LEFT && c->x < opposite->x)
- || (dir == MOVE_RIGHT && c->x > opposite->x)) {
- opposite = c;
- }
- }
- }
- dir_text = dir == MOVE_LEFT ? "leftmost" : "rightmost";
- break;
- case MOVE_UP:
- case MOVE_DOWN: ;
- for (int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
- if (!opposite) {
- opposite = c;
- } else if ((dir == MOVE_UP && c->y < opposite->y)
- || (dir == MOVE_DOWN && c->y > opposite->y)) {
- opposite = c;
- }
- }
- }
- dir_text = dir == MOVE_UP ? "upmost" : "downmost";
- break;
- default:
- sway_abort("Function called with invalid argument.");
- break;
- }
- if (opposite) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s from y-position %i",
- opposite->name, opposite->width, opposite->height, opposite->x, opposite->y,
- dir_text, abs_pos->y);
- }
- return opposite;
-}
-
-// Position is where on the edge (as absolute position) the adjacent output should be searched for.
-swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir,
- const struct wlc_point *abs_pos, bool pick_closest) {
-
- if (!output) {
- output = swayc_active_output();
- }
- // In order to find adjacent outputs we need to test that the outputs are
- // aligned on one axis (decided by the direction given) and that the given
- // position is within the edge of the adjacent output. If no such output
- // exists we pick the adjacent output within the edge that is closest to
- // the given position, if any.
- swayc_t *adjacent = NULL;
- char *dir_text = NULL;
- switch(dir) {
- case MOVE_LEFT:
- case MOVE_RIGHT: ;
- double delta_y = 0;
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c == output || c->type != C_OUTPUT) {
- continue;
- }
- bool x_aligned = dir == MOVE_LEFT ?
- c->x + c->width == output->x :
- c->x == output->x + output->width;
- if (!x_aligned) {
- continue;
- }
- if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
- delta_y = 0;
- adjacent = c;
- break;
- } else if (pick_closest) {
- // track closest adjacent output
- double top_y = c->y, bottom_y = c->y + c->height;
- if (top_y >= output->y && top_y <= output->y + output->height) {
- double delta = top_y - abs_pos->y;
- if (delta < 0) delta = -delta;
- if (delta < delta_y || !adjacent) {
- delta_y = delta;
- adjacent = c;
- }
- }
- // we check both points and pick the closest
- if (bottom_y >= output->y && bottom_y <= output->y + output->height) {
- double delta = bottom_y - abs_pos->y;
- if (delta < 0) delta = -delta;
- if (delta < delta_y || !adjacent) {
- delta_y = delta;
- adjacent = c;
- }
- }
- }
- }
- dir_text = dir == MOVE_LEFT ? "left of" : "right of";
- if (adjacent && delta_y == 0) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->y);
- } else if (adjacent) {
- // so we end up picking the closest adjacent output because
- // there is no directly adjacent to the given position
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->y, delta_y);
- }
- break;
- case MOVE_UP:
- case MOVE_DOWN: ;
- double delta_x = 0;
- for(int i = 0; i < root_container.children->length; ++i) {
- swayc_t *c = root_container.children->items[i];
- if (c == output || c->type != C_OUTPUT) {
- continue;
- }
- bool y_aligned = dir == MOVE_UP ?
- c->y + c->height == output->y :
- c->y == output->y + output->height;
- if (!y_aligned) {
- continue;
- }
- if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
- delta_x = 0;
- adjacent = c;
- break;
- } else if (pick_closest) {
- // track closest adjacent output
- double left_x = c->x, right_x = c->x + c->width;
- if (left_x >= output->x && left_x <= output->x + output->width) {
- double delta = left_x - abs_pos->x;
- if (delta < 0) delta = -delta;
- if (delta < delta_x || !adjacent) {
- delta_x = delta;
- adjacent = c;
- }
- }
- // we check both points and pick the closest
- if (right_x >= output->x && right_x <= output->x + output->width) {
- double delta = right_x - abs_pos->x;
- if (delta < 0) delta = -delta;
- if (delta < delta_x || !adjacent) {
- delta_x = delta;
- adjacent = c;
- }
- }
- }
- }
- dir_text = dir == MOVE_UP ? "above" : "below";
- if (adjacent && delta_x == 0) {
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->x);
- } else if (adjacent) {
- // so we end up picking the closest adjacent output because
- // there is no directly adjacent to the given position
- sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)",
- adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
- dir_text, output->name, abs_pos->x, delta_x);
- }
- break;
- default:
- sway_abort("Function called with invalid argument.");
- break;
- }
- return adjacent;
-}
-
-void get_absolute_position(swayc_t *container, struct wlc_point *point) {
- if (!container || !point)
- sway_abort("Need container and wlc_point (was %p, %p).", container, point);
-
- if (container->type == C_OUTPUT) {
- // Coordinates are already absolute.
- point->x = container->x;
- point->y = container->y;
- } else {
- swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
- if (container->type == C_WORKSPACE) {
- // Workspace coordinates are actually wrong/arbitrary, but should
- // be same as output.
- point->x = output->x;
- point->y = output->y;
- } else {
- point->x = output->x + container->x;
- point->y = output->y + container->y;
- }
- }
-}
-
-void get_absolute_center_position(swayc_t *container, struct wlc_point *point) {
- get_absolute_position(container, point);
- point->x += container->width/2;
- point->y += container->height/2;
-}
-
-static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
- swayc_t *a = *(void **)_a;
- swayc_t *b = *(void **)_b;
- int retval = 0;
-
- if (isdigit(a->name[0]) && isdigit(b->name[0])) {
- int a_num = strtol(a->name, NULL, 10);
- int b_num = strtol(b->name, NULL, 10);
- retval = (a_num < b_num) ? -1 : (a_num > b_num);
- } else if (isdigit(a->name[0])) {
- retval = -1;
- } else if (isdigit(b->name[0])) {
- retval = 1;
- }
-
- return retval;
-}
-
-void sort_workspaces(swayc_t *output) {
- list_stable_sort(output->children, sort_workspace_cmp_qsort);
-}
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 @@
#define _XOPEN_SOURCE 700
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include "sway/config.h"
#include "sway/security.h"
-#include "log.h"
-
-static bool validate_ipc_target(const char *program) {
- struct stat sb;
-
- sway_log(L_DEBUG, "Validating IPC target '%s'", program);
-
- if (!strcmp(program, "*")) {
- return true;
- }
- if (lstat(program, &sb) == -1) {
- return false;
- }
- if (!S_ISREG(sb.st_mode)) {
- sway_log(L_ERROR,
- "IPC target '%s' MUST be/point at an existing regular file",
- program);
- return false;
- }
- if (sb.st_uid != 0) {
-#ifdef NDEBUG
- sway_log(L_ERROR, "IPC target '%s' MUST be owned by root", program);
- return false;
-#else
- sway_log(L_INFO, "IPC target '%s' MUST be owned by root (waived for debug build)", program);
- return true;
-#endif
- }
- if (sb.st_mode & S_IWOTH) {
- sway_log(L_ERROR, "IPC target '%s' MUST NOT be world writable", program);
- return false;
- }
-
- return true;
-}
-
-struct feature_policy *alloc_feature_policy(const char *program) {
- uint32_t default_policy = 0;
-
- if (!validate_ipc_target(program)) {
- return NULL;
- }
- for (int i = 0; i < config->feature_policies->length; ++i) {
- struct feature_policy *policy = config->feature_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- break;
- }
- }
-
- struct feature_policy *policy = malloc(sizeof(struct feature_policy));
- if (!policy) {
- return NULL;
- }
- policy->program = strdup(program);
- if (!policy->program) {
- free(policy);
- return NULL;
- }
- policy->features = default_policy;
-
- return policy;
-}
-
-struct ipc_policy *alloc_ipc_policy(const char *program) {
- uint32_t default_policy = 0;
-
- if (!validate_ipc_target(program)) {
- return NULL;
- }
- for (int i = 0; i < config->ipc_policies->length; ++i) {
- struct ipc_policy *policy = config->ipc_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- break;
- }
- }
-
- struct ipc_policy *policy = malloc(sizeof(struct ipc_policy));
- if (!policy) {
- return NULL;
- }
- policy->program = strdup(program);
- if (!policy->program) {
- free(policy);
- return NULL;
- }
- policy->features = default_policy;
-
- return policy;
-}
struct command_policy *alloc_command_policy(const char *command) {
struct command_policy *policy = malloc(sizeof(struct command_policy));
@@ -111,118 +16,3 @@ struct command_policy *alloc_command_policy(const char *command) {
policy->context = 0;
return policy;
}
-
-static const char *get_pid_exe(pid_t pid) {
-#ifdef __FreeBSD__
- const char *fmt = "/proc/%d/file";
-#else
- const char *fmt = "/proc/%d/exe";
-#endif
- int pathlen = snprintf(NULL, 0, fmt, pid);
- char *path = malloc(pathlen + 1);
- if (path) {
- snprintf(path, pathlen + 1, fmt, pid);
- }
-
- static char link[2048];
-
- ssize_t len = !path ? -1 : readlink(path, link, sizeof(link));
- if (len < 0) {
- sway_log(L_INFO,
- "WARNING: unable to read %s for security check. Using default policy.",
- path);
- strcpy(link, "*");
- } else {
- link[len] = '\0';
- }
- free(path);
-
- return link;
-}
-
-struct feature_policy *get_feature_policy(const char *name) {
- struct feature_policy *policy = NULL;
-
- for (int i = 0; i < config->feature_policies->length; ++i) {
- struct feature_policy *p = config->feature_policies->items[i];
- if (strcmp(p->program, name) == 0) {
- policy = p;
- break;
- }
- }
- if (!policy) {
- policy = alloc_feature_policy(name);
- sway_assert(policy, "Unable to allocate security policy");
- if (policy) {
- list_add(config->feature_policies, policy);
- }
- }
- return policy;
-}
-
-uint32_t get_feature_policy_mask(pid_t pid) {
- uint32_t default_policy = 0;
- const char *link = get_pid_exe(pid);
-
- for (int i = 0; i < config->feature_policies->length; ++i) {
- struct feature_policy *policy = config->feature_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- }
- if (strcmp(policy->program, link) == 0) {
- return policy->features;
- }
- }
-
- return default_policy;
-}
-
-uint32_t get_ipc_policy_mask(pid_t pid) {
- uint32_t default_policy = 0;
- const char *link = get_pid_exe(pid);
-
- for (int i = 0; i < config->ipc_policies->length; ++i) {
- struct ipc_policy *policy = config->ipc_policies->items[i];
- if (strcmp(policy->program, "*") == 0) {
- default_policy = policy->features;
- }
- if (strcmp(policy->program, link) == 0) {
- return policy->features;
- }
- }
-
- return default_policy;
-}
-
-uint32_t get_command_policy_mask(const char *cmd) {
- uint32_t default_policy = 0;
-
- for (int i = 0; i < config->command_policies->length; ++i) {
- struct command_policy *policy = config->command_policies->items[i];
- if (strcmp(policy->command, "*") == 0) {
- default_policy = policy->context;
- }
- if (strcmp(policy->command, cmd) == 0) {
- return policy->context;
- }
- }
-
- return default_policy;
-}
-
-const char *command_policy_str(enum command_context context) {
- switch (context) {
- case CONTEXT_ALL:
- return "all";
- case CONTEXT_CONFIG:
- return "config";
- case CONTEXT_BINDING:
- return "binding";
- case CONTEXT_IPC:
- return "IPC";
- case CONTEXT_CRITERIA:
- return "criteria";
- default:
- return "unknown";
- }
-}
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 @@
+#define _POSIX_C_SOURCE 200112L
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/backend.h>
+#include <wlr/backend/session.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_gamma_control.h>
+#include <wlr/types/wlr_linux_dmabuf.h>
+#include <wlr/types/wlr_layer_shell.h>
+#include <wlr/types/wlr_primary_selection.h>
+#include <wlr/types/wlr_screenshooter.h>
+#include <wlr/types/wlr_server_decoration.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_output.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include <wlr/util/log.h>
+// TODO WLR: make Xwayland optional
+#include <wlr/xwayland.h>
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
+
+static void server_ready(struct wl_listener *listener, void *data) {
+ wlr_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
+ // Execute commands until there are none left
+ config->active = true;
+ while (config->cmd_queue->length) {
+ char *line = config->cmd_queue->items[0];
+ struct cmd_results *res = execute_command(line, NULL);
+ if (res->status != CMD_SUCCESS) {
+ wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error);
+ }
+ free_cmd_results(res);
+ free(line);
+ list_del(config->cmd_queue, 0);
+ }
+}
+
+bool server_init(struct sway_server *server) {
+ wlr_log(L_DEBUG, "Initializing Wayland server");
+
+ server->wl_display = wl_display_create();
+ server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
+ server->backend = wlr_backend_autocreate(server->wl_display);
+
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend);
+ assert(renderer);
+
+ wl_display_init_shm(server->wl_display);
+
+ server->compositor = wlr_compositor_create(server->wl_display, renderer);
+ server->data_device_manager =
+ wlr_data_device_manager_create(server->wl_display);
+
+ wlr_screenshooter_create(server->wl_display);
+ wlr_gamma_control_manager_create(server->wl_display);
+ wlr_primary_selection_device_manager_create(server->wl_display);
+
+ server->new_output.notify = handle_new_output;
+ wl_signal_add(&server->backend->events.new_output, &server->new_output);
+
+ wlr_xdg_output_manager_create(server->wl_display,
+ root_container.sway_root->output_layout);
+
+ server->layer_shell = wlr_layer_shell_create(server->wl_display);
+ wl_signal_add(&server->layer_shell->events.new_surface,
+ &server->layer_shell_surface);
+ server->layer_shell_surface.notify = handle_layer_shell_surface;
+
+ server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
+ wl_signal_add(&server->xdg_shell_v6->events.new_surface,
+ &server->xdg_shell_v6_surface);
+ server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface;
+
+ server->wl_shell = wlr_wl_shell_create(server->wl_display);
+ wl_signal_add(&server->wl_shell->events.new_surface,
+ &server->wl_shell_surface);
+ server->wl_shell_surface.notify = handle_wl_shell_surface;
+
+ // TODO make xwayland optional
+ server->xwayland =
+ wlr_xwayland_create(server->wl_display, server->compositor);
+ wl_signal_add(&server->xwayland->events.new_surface,
+ &server->xwayland_surface);
+ server->xwayland_surface.notify = handle_xwayland_surface;
+ wl_signal_add(&server->xwayland->events.ready,
+ &server->xwayland_ready);
+ // TODO: call server_ready now if xwayland is not enabled
+ server->xwayland_ready.notify = server_ready;
+
+ // TODO: configurable cursor theme and size
+ server->xcursor_manager = wlr_xcursor_manager_create(NULL, 24);
+ wlr_xcursor_manager_load(server->xcursor_manager, 1);
+ struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
+ server->xcursor_manager, "left_ptr", 1);
+ if (xcursor != NULL) {
+ struct wlr_xcursor_image *image = xcursor->images[0];
+ wlr_xwayland_set_cursor(server->xwayland, image->buffer,
+ image->width * 4, image->width, image->height, image->hotspot_x,
+ image->hotspot_y);
+ }
+
+ // TODO: Integration with sway borders
+ struct wlr_server_decoration_manager *deco_manager =
+ wlr_server_decoration_manager_create(server->wl_display);
+ wlr_server_decoration_manager_set_default_mode(
+ deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
+
+ wlr_linux_dmabuf_create(server->wl_display, renderer);
+
+ server->socket = wl_display_add_socket_auto(server->wl_display);
+ if (!server->socket) {
+ wlr_log(L_ERROR, "Unable to open wayland socket");
+ wlr_backend_destroy(server->backend);
+ return false;
+ }
+
+ input_manager = input_manager_create(server);
+ return true;
+}
+
+void server_fini(struct sway_server *server) {
+ // TODO
+}
+
+void server_run(struct sway_server *server) {
+ wlr_log(L_INFO, "Running compositor on wayland display '%s'",
+ server->socket);
+ setenv("WAYLAND_DISPLAY", server->socket, true);
+ if (!wlr_backend_start(server->backend)) {
+ wlr_log(L_ERROR, "Failed to start backend");
+ wlr_backend_destroy(server->backend);
+ return;
+ }
+ wl_display_run(server->wl_display);
+}
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 @@
/////
-vim:set ts=4 sw=4 tw=82 noet:
+vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
/////
sway-input (5)
==============
@@ -11,12 +11,57 @@ sway-input - input configuration file and commands
Description
-----------
-Sway allows for configuration of libinput devices within the sway configuration file.
+Sway allows for configuration of devices within the sway configuration file.
sway-input commands must be used inside an _input { }_ block in the config.
To obtain a list of available device identifiers, run **swaymsg -t get_inputs**.
-Commands
---------
+Input Commands
+--------------
+
+Keyboard Configuration
+~~~~~~~~~~~~~~~~~~~~~~
+
+For more information on these xkb configuration options, see
+**xkeyboard-config**(7).
+
+**input** <identifier> xkb_layout <layout_name>::
+ Sets the layout of the keyboard like _us_ or _de_.
+
+**input** <identifier> xkb_model <model_name>::
+ Sets the model of the keyboard. This has an influence for some extra keys your
+ keyboard might have.
+
+**input** <identifier> xkb_options <options>::
+ Sets extra xkb configuration options for the keyboard.
+
+**input** <identifier> xkb_rules <rules>::
+ Sets files of rules to be used for keyboard mapping composition.
+
+**input** <identifier> xkb_variant <variant>::
+ Sets the variant of the keyboard like _dvorak_ or _colemak_.
+
+Mapping Configuration
+---------------------
+
+**input** <identifier> map_to_output <identifier>::
+ Maps inputs from this device to the specified output. Only meaningful if the
+ device is a pointer, touch, or drawing tablet device.
+
+**input** <identifier> map_to_region <WxH\@X,Y>::
+ Maps inputs from this device to the specified region of the global output
+ layout. Only meaningful if the device is a pointer, touch, or drawing tablet
+ device.
+
+**input** <identifier> map_region <WxH\@X,Y>::
+ Ignores inputs from this device that do not occur within the specified region.
+ Can be in millimeters (e.g. 10mmx20mm\@10mm,20mm) or in terms of 0..1 (e.g.
+ 0.5x0.5\@0,0). Not all devices support millimeters. Only meaningful if the
+ device is not a keyboard an provides events in absolute terms (such as a
+ drawing tablet or touch screen - most pointers provide events relative to the
+ previous frame).
+
+Libinput Configuration
+~~~~~~~~~~~~~~~~~~~~~~
**input** <identifier> accel_profile <adaptive|flat>::
Sets the pointer acceleration profile for the specified input device.
@@ -53,6 +98,27 @@ Commands
**input** <identifier> tap <enabled|disabled>::
Enables or disables tap for specified input device.
+Seat Configuration
+------------------
+
+Configure options for multiseat mode. sway-seat commands must be used inside a
+_seat { }_ block in the config.
+
+A _seat_ is a collection of input devices that act independently of each other.
+Seats are identified by name and the default seat is _seat0_ if no seats are
+configured. Each seat has an independent keyboard focus and a separate cursor that
+is controlled by the pointer devices of the seat. This is useful for multiple
+people using the desktop at the same time with their own devices (each sitting in
+their own "seat").
+
+**seat** <name> attach <input_identifier>::
+ Attach an input device to this seat by its input identifier. A special value
+ of _*_ will attach all devices to the seat.
+
+**seat** <name> fallback <true|false>::
+ Set this seat as the fallback seat. A fallback seat will attach any device not
+ explicitly attached to another seat (similar to a "default" seat).
+
See Also
--------
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 @@
/////
-vim:set ts=4 sw=4 tw=82 noet:
+vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
/////
:quotes.~:
@@ -93,27 +93,6 @@ The following environment variables have an effect on sway:
*SWAYSOCK*::
Specifies the path to the sway IPC socket.
-*WLC_DRM_DEVICE*::
- Specifies the device to use in DRM mode.
-
-*WLC_SHM*::
- Set 1 to force EGL clients to use shared memory.
-
-*WLC_OUTPUTS*::
- Number of fake outputs to use when running in X11 mode.
-
-*WLC_XWAYLAND*::
- Set to 0 to disable Xwayland support.
-
-*WLC_LIBINPUT*::
- Set to 1 to force libinput (even in X11 mode).
-
-*WLC_REPEAT_DELAY*::
- Configures the keyboard repeat delay.
-
-*WLC_REPEAT_RATE*::
- Configures the keyboard repeat rate.
-
*XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*::
Configures the xkb keyboard settings. See xkeyboard-config(7).
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.
Sets variable $name to _value_. You can use the new variable in the arguments
of future commands.
+**swaybg_command** <command>::
+ Executes custom bg command, default is _swaybg_.
+
The following commands cannot be used directly in the configuration file.
They are expected to be used with **bindsym** or at runtime through **swaymsg**(1).
@@ -126,22 +129,21 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
**resize** <shrink|grow> <width|height> [<amount>] [px|ppt]::
Resizes the currently focused container or view by _amount_. _amount_ is
- optional: the default value is 10 (either px or ppt depending on the view
- type). The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers
- to pixels; _ppt_ specifies that _amount_ refers to percentage points of the
- current dimension. Floating views use px dimensions by default (but can use
- ppt if specified); tiled views use ppt dimensions by default (but can use px
- if specified).
+ optional: the default value is 10 (either px or ppt depending on the view type).
+ The [px|ppt] parameter is optional. _px_ specifies that _amount_ refers to pixels;
+ _ppt_ specifies that _amount_ refers to percentage points of the current
+ size. Floating views use px by default (but can use ppt if specified); tiled
+ views use ppt by default (but can use px if specified).
**resize set** <width> [px] <height> [px]::
Sets the width and height of the currently focused container to _width_ pixels
and _height_ pixels. The [px] parameters are optional and have no effect. This
- command only accepts pixel dimensions.
+ command only accepts a size in pixels.
**resize set** <width|height> <amount> [px] [<width|height> <amount> [px]]::
Sets the _width_ and/or _height_ of the currently focused container to
_amount_. The [px] parameters are optional and have no effect. This command
- only accepts pixel dimensions.
+ only accepts a size in pixels.
**scratchpad show**::
Shows a window from the scratchpad. Repeatedly using this command will cycle
@@ -254,14 +256,14 @@ The default colors are:
*restart* is executed.
**floating_maximum_size** <width> x <height>::
- Specifies the maximum dimensions of floating windows.
- Uses the container dimensions as default.
- -1 x -1 will remove any restriction on dimensions.
+ Specifies the maximum size of floating windows.
+ Uses the container size as default.
+ -1 x -1 will remove any restriction on size.
0 x 0 has the same behavior as not setting any value.
If in conflict, this option has precedence over floating_minimum_size.
**floating_minimum_size** <width> x <height>::
- Specifies the minimum dimensions of floating windows.
+ Specifies the minimum size of floating windows.
Default parameters are 75 x 50.
-1 and 0 are invalid parameters, default will be used instead.
@@ -313,7 +315,7 @@ The default colors are:
**hide_edge_borders** <none|vertical|horizontal|both|smart>::
Hide window borders adjacent to the screen edges. Default is _none_.
-**input** <input device> <block of commands>::
+**input** <input_device> <block of commands>::
Append _{_ to this command, the following lines will be commands to configure
the named input device, and _}_ on its own line will close the block.
+
@@ -321,6 +323,18 @@ The default colors are:
+
See **sway-input**(5) for details.
+**seat** <seat_name> <block of commands>::
+ Append _{_ to this command, the following lines will be commands to configure
+ the named seat, and _}_ on its own line will close the block.
+ See **sway-input**(5) for details.
+
+**seat** <seat_name> cursor <move|set> <x> <y>::
+ Move cursor relatively to current position or set to absolute screen position.
+ A value of 0 causes the axis to be ignored in both commands.
+
+**seat** <seat_name> cursor <press|release> <left|right|1|2|3...>::
+ Simulate press of mouse button specified by left, right, or numerical code.
+
**kill**::
Kills (force-closes) the currently-focused container and all of its children.
@@ -349,35 +363,44 @@ The default colors are:
Prevents windows matching <criteria> from being focused automatically when
they're created. This does not apply to the first window in a workspace.
-**output** <name> <resolution|res> <WIDTHxHEIGHT>::
- Configures the specified output to use the given resolution.
+**output** <name> mode|resolution|res <WIDTHxHEIGHT>[@<RATE>[Hz]]::
+ Configures the specified output to use the given mode. Modes are a combination
+ of width and height (in pixels) and a refresh rate that your display can be
+ configured to use. For a list of available modes, use swaymsg -t get_outputs.
+ +
+ Examples:
+ +
+ output HDMI-A-1 mode 1920x1080
+
- _Note_: sway does not currently support setting the output mode. Your output's native
- resolution will be used and the screen will be scaled from the resolution
- specified to your native resolution.
+ output HDMI-A-1 mode 1920x1080@60Hz
-**output** <name> <position|pos> <X,Y>::
+**output** <name> position|pos <X,Y>::
Configures the specified output to be arranged at the given position.
-**output** <name> <scale> <I>::
+**output** <name> scale <I>::
Configures the specified output to be scaled up by the specified integer
- scaling factor (usually 2 for HiDPI screens).
+ scaling factor (usually 2 for HiDPI screens). Fractional scaling is supported.
-**output** <name> <background|bg> <file> <mode>::
+**output** <name> background|bg <file> <mode>::
Sets the wallpaper for the given output to the specified file, using the given
scaling mode (one of "stretch", "fill", "fit", "center", "tile").
-**output** <name> <background|bg> <color> solid_color::
+**output** <name> background|bg <color> solid_color::
Sets the background of the given output to the specified color. _color_ should
be specified as an _#rrggbb_ (no alpha) color.
+**output** <name> transform <transform>::
+ Sets the background transform to the given value. Can be one of "90", "180",
+ "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270"
+ to apply a rotation and flip, or "normal" to apply no transform.
+
**output** <name> disable::
Disables the specified output.
**NOTES FOR THE OUTPUT COMMAND**::
You may combine output commands into one, like so:
+
- output HDMI-A-1 res 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
+ output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
+
You can get a list of output names like so:
+
@@ -397,6 +420,10 @@ The default colors are:
However, any mark that starts with an underscore will not be drawn even if the
option is on. The default option is _on_.
+**opacity** <value>::
+ Set the opacity of the window between 0 (completely transparent) and 1
+ (completely opaque).
+
**unmark** <identifier>::
**Unmark** will remove _identifier_ from the list of current marks on a window. If
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_wl_shell.h>
+#include "sway/config.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/ipc-server.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+#include "sway/tree/workspace.h"
+#include "log.h"
+
+static list_t *bfs_queue;
+
+static list_t *get_bfs_queue() {
+ if (!bfs_queue) {
+ bfs_queue = create_list();
+ if (!bfs_queue) {
+ wlr_log(L_ERROR, "could not allocate list for bfs queue");
+ return NULL;
+ }
+ }
+ bfs_queue->length = 0;
+
+ return bfs_queue;
+}
+
+const char *container_type_to_str(enum sway_container_type type) {
+ switch (type) {
+ case C_ROOT:
+ return "C_ROOT";
+ case C_OUTPUT:
+ return "C_OUTPUT";
+ case C_WORKSPACE:
+ return "C_WORKSPACE";
+ case C_CONTAINER:
+ return "C_CONTAINER";
+ case C_VIEW:
+ return "C_VIEW";
+ default:
+ return "C_UNKNOWN";
+ }
+}
+
+void container_create_notify(struct sway_container *container) {
+ // TODO send ipc event type based on the container type
+ wl_signal_emit(&root_container.sway_root->events.new_container, container);
+
+ if (container->type == C_VIEW || container->type == C_CONTAINER) {
+ ipc_event_window(container, "new");
+ }
+}
+
+static void container_close_notify(struct sway_container *container) {
+ if (container == NULL) {
+ return;
+ }
+ // TODO send ipc event type based on the container type
+ if (container->type == C_VIEW || container->type == C_WORKSPACE) {
+ ipc_event_window(container, "close");
+ }
+}
+
+struct sway_container *container_create(enum sway_container_type type) {
+ // next id starts at 1 because 0 is assigned to root_container in layout.c
+ static size_t next_id = 1;
+ struct sway_container *c = calloc(1, sizeof(struct sway_container));
+ if (!c) {
+ return NULL;
+ }
+ c->id = next_id++;
+ c->layout = L_NONE;
+ c->workspace_layout = L_NONE;
+ c->type = type;
+ c->alpha = 1.0f;
+
+ if (type != C_VIEW) {
+ c->children = create_list();
+ }
+
+ wl_signal_init(&c->events.destroy);
+ wl_signal_init(&c->events.reparent);
+
+ return c;
+}
+
+static void _container_destroy(struct sway_container *cont) {
+ if (cont == NULL) {
+ return;
+ }
+
+ wl_signal_emit(&cont->events.destroy, cont);
+ container_close_notify(cont);
+
+ struct sway_container *parent = cont->parent;
+ if (cont->children != NULL && cont->children->length) {
+ // remove children until there are no more, container_destroy calls
+ // container_remove_child, which removes child from this container
+ while (cont->children != NULL) {
+ struct sway_container *child = cont->children->items[0];
+ container_remove_child(child);
+ _container_destroy(child);
+ }
+ }
+ if (cont->marks) {
+ list_foreach(cont->marks, free);
+ list_free(cont->marks);
+ }
+ if (parent) {
+ parent = container_remove_child(cont);
+ }
+ if (cont->name) {
+ free(cont->name);
+ }
+ list_free(cont->children);
+ cont->children = NULL;
+ free(cont);
+}
+
+static struct sway_container *container_output_destroy(
+ struct sway_container *output) {
+ if (!sway_assert(output, "cannot destroy null output")) {
+ return NULL;
+ }
+
+ if (output->children->length > 0) {
+ // TODO save workspaces when there are no outputs.
+ // TODO also check if there will ever be no outputs except for exiting
+ // program
+ if (root_container.children->length > 1) {
+ int p = root_container.children->items[0] == output;
+ // Move workspace from this output to another output
+ while (output->children->length) {
+ struct sway_container *child = output->children->items[0];
+ container_remove_child(child);
+ container_add_child(root_container.children->items[p], child);
+ }
+ container_sort_workspaces(root_container.children->items[p]);
+ arrange_windows(root_container.children->items[p],
+ -1, -1);
+ }
+ }
+
+ wl_list_remove(&output->sway_output->destroy.link);
+ wl_list_remove(&output->sway_output->mode.link);
+ wl_list_remove(&output->sway_output->transform.link);
+ wl_list_remove(&output->sway_output->scale.link);
+
+ wl_list_remove(&output->sway_output->damage_destroy.link);
+ wl_list_remove(&output->sway_output->damage_frame.link);
+
+ wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
+ _container_destroy(output);
+ return &root_container;
+}
+
+static struct sway_container *container_workspace_destroy(
+ struct sway_container *workspace) {
+ if (!sway_assert(workspace, "cannot destroy null workspace")) {
+ return NULL;
+ }
+
+ // Do not destroy this if it's the last workspace on this output
+ struct sway_container *output = container_parent(workspace, C_OUTPUT);
+ if (output && output->children->length == 1) {
+ return NULL;
+ }
+
+ struct sway_container *parent = workspace->parent;
+ if (workspace->children->length == 0) {
+ // destroy the WS if there are no children (TODO check for floating)
+ wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
+ ipc_event_workspace(workspace, NULL, "empty");
+ } else {
+ // Move children to a different workspace on this output
+ struct sway_container *new_workspace = NULL;
+ // TODO move floating
+ for (int i = 0; i < output->children->length; i++) {
+ if (output->children->items[i] != workspace) {
+ new_workspace = output->children->items[i];
+ break;
+ }
+ }
+
+ wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'",
+ workspace->name, new_workspace->name);
+ for (int i = 0; i < workspace->children->length; i++) {
+ container_move_to(workspace->children->items[i], new_workspace);
+ }
+ }
+
+ _container_destroy(workspace);
+
+ output_damage_whole(output->sway_output);
+
+ return parent;
+}
+
+static void container_root_finish(struct sway_container *con) {
+ wlr_log(L_ERROR, "TODO: destroy the root container");
+}
+
+bool container_reap_empty(struct sway_container *con) {
+ switch (con->type) {
+ case C_ROOT:
+ case C_OUTPUT:
+ // dont reap these
+ break;
+ case C_WORKSPACE:
+ if (!workspace_is_visible(con) && con->children->length == 0) {
+ wlr_log(L_DEBUG, "Destroying workspace via reaper");
+ container_workspace_destroy(con);
+ return true;
+ }
+ break;
+ case C_CONTAINER:
+ if (con->children->length == 0) {
+ _container_destroy(con);
+ return true;
+ }
+ case C_VIEW:
+ break;
+ case C_TYPES:
+ sway_assert(false, "container_reap_empty called on an invalid "
+ "container");
+ break;
+ }
+
+ return false;
+}
+
+struct sway_container *container_reap_empty_recursive(
+ struct sway_container *con) {
+ while (con) {
+ struct sway_container *next = con->parent;
+ if (!container_reap_empty(con)) {
+ break;
+ }
+ con = next;
+ }
+ return con;
+}
+
+struct sway_container *container_flatten(struct sway_container *container) {
+ while (container->type == C_CONTAINER && container->children->length == 1) {
+ struct sway_container *child = container->children->items[0];
+ struct sway_container *parent = container->parent;
+ container_replace_child(container, child);
+ container_destroy(container);
+ container = parent;
+ }
+ return container;
+}
+
+struct sway_container *container_destroy(struct sway_container *con) {
+ if (con == NULL) {
+ return NULL;
+ }
+
+ struct sway_container *parent = con->parent;
+
+ switch (con->type) {
+ case C_ROOT:
+ container_root_finish(con);
+ break;
+ case C_OUTPUT:
+ // dont try to reap the root after this
+ container_output_destroy(con);
+ break;
+ case C_WORKSPACE:
+ // dont try to reap the output after this
+ container_workspace_destroy(con);
+ break;
+ case C_CONTAINER:
+ if (con->children->length) {
+ for (int i = 0; i < con->children->length; ++i) {
+ struct sway_container *child = con->children->items[0];
+ container_remove_child(child);
+ container_add_child(parent, child);
+ }
+ }
+ _container_destroy(con);
+ break;
+ case C_VIEW:
+ _container_destroy(con);
+ break;
+ case C_TYPES:
+ wlr_log(L_ERROR, "container_destroy called on an invalid "
+ "container");
+ break;
+ }
+
+ return container_reap_empty_recursive(parent);
+}
+
+static void container_close_func(struct sway_container *container, void *data) {
+ if (container->type == C_VIEW) {
+ view_close(container->sway_view);
+ }
+}
+
+struct sway_container *container_close(struct sway_container *con) {
+ if (!sway_assert(con != NULL,
+ "container_close called with a NULL container")) {
+ return NULL;
+ }
+
+ struct sway_container *parent = con->parent;
+
+ if (con->type == C_VIEW) {
+ view_close(con->sway_view);
+ } else {
+ container_for_each_descendant_dfs(con, container_close_func, NULL);
+ }
+
+ return parent;
+}
+
+struct sway_container *container_view_create(struct sway_container *sibling,
+ struct sway_view *sway_view) {
+ if (!sway_assert(sibling,
+ "container_view_create called with NULL sibling/parent")) {
+ return NULL;
+ }
+ const char *title = view_get_title(sway_view);
+ struct sway_container *swayc = container_create(C_VIEW);
+ wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s",
+ swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
+ // Setup values
+ swayc->sway_view = sway_view;
+ swayc->name = title ? strdup(title) : NULL;
+ swayc->width = 0;
+ swayc->height = 0;
+
+ if (sibling->type == C_WORKSPACE) {
+ // Case of focused workspace, just create as child of it
+ container_add_child(sibling, swayc);
+ } else {
+ // Regular case, create as sibling of current container
+ container_add_sibling(sibling, swayc);
+ }
+ container_create_notify(swayc);
+ return swayc;
+}
+
+void container_descendants(struct sway_container *root,
+ enum sway_container_type type,
+ void (*func)(struct sway_container *item, void *data), void *data) {
+ for (int i = 0; i < root->children->length; ++i) {
+ struct sway_container *item = root->children->items[i];
+ if (item->type == type) {
+ func(item, data);
+ }
+ if (item->children && item->children->length) {
+ container_descendants(item, type, func, data);
+ }
+ }
+}
+
+struct sway_container *container_find(struct sway_container *container,
+ bool (*test)(struct sway_container *view, void *data), void *data) {
+ if (!container->children) {
+ return NULL;
+ }
+ // TODO: floating windows
+ for (int i = 0; i < container->children->length; ++i) {
+ struct sway_container *child = container->children->items[i];
+ if (test(child, data)) {
+ return child;
+ } else {
+ struct sway_container *res = container_find(child, test, data);
+ if (res) {
+ return res;
+ }
+ }
+ }
+ return NULL;
+}
+
+struct sway_container *container_parent(struct sway_container *container,
+ enum sway_container_type type) {
+ if (!sway_assert(container, "container is NULL")) {
+ return NULL;
+ }
+ if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
+ return NULL;
+ }
+ do {
+ container = container->parent;
+ } while (container && container->type != type);
+ return container;
+}
+
+struct sway_container *container_at(struct sway_container *parent,
+ double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ list_t *queue = get_bfs_queue();
+ if (!queue) {
+ return NULL;
+ }
+
+ list_add(queue, parent);
+
+ struct sway_container *swayc = NULL;
+ while (queue->length) {
+ swayc = queue->items[0];
+ list_del(queue, 0);
+ if (swayc->type == C_VIEW) {
+ struct sway_view *sview = swayc->sway_view;
+ struct sway_container *soutput = container_parent(swayc, C_OUTPUT);
+ struct wlr_box *output_box =
+ wlr_output_layout_get_box(
+ root_container.sway_root->output_layout,
+ soutput->sway_output->wlr_output);
+ double ox = lx - output_box->x;
+ double oy = ly - output_box->y;
+ double view_sx = ox - swayc->x;
+ double view_sy = oy - swayc->y;
+
+ double _sx, _sy;
+ struct wlr_surface *_surface;
+ switch (sview->type) {
+ case SWAY_VIEW_XWAYLAND:
+ _surface = wlr_surface_surface_at(sview->surface,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ case SWAY_VIEW_WL_SHELL:
+ _surface = wlr_wl_shell_surface_surface_at(
+ sview->wlr_wl_shell_surface,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ case SWAY_VIEW_XDG_SHELL_V6:
+ // the top left corner of the sway container is the
+ // coordinate of the top left corner of the window geometry
+ view_sx += sview->wlr_xdg_surface_v6->geometry.x;
+ view_sy += sview->wlr_xdg_surface_v6->geometry.y;
+
+ _surface = wlr_xdg_surface_v6_surface_at(
+ sview->wlr_xdg_surface_v6,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ }
+ if (_surface) {
+ *sx = _sx;
+ *sy = _sy;
+ *surface = _surface;
+ return swayc;
+ }
+ } else {
+ list_cat(queue, swayc->children);
+ }
+ }
+
+ return NULL;
+}
+
+void container_for_each_descendant_dfs(struct sway_container *container,
+ void (*f)(struct sway_container *container, void *data),
+ void *data) {
+ if (container) {
+ if (container->children) {
+ for (int i = 0; i < container->children->length; ++i) {
+ struct sway_container *child =
+ container->children->items[i];
+ container_for_each_descendant_dfs(child, f, data);
+ }
+ }
+ f(container, data);
+ }
+}
+
+void container_for_each_descendant_bfs(struct sway_container *con,
+ void (*f)(struct sway_container *con, void *data), void *data) {
+ list_t *queue = get_bfs_queue();
+ if (!queue) {
+ return;
+ }
+
+ if (queue == NULL) {
+ wlr_log(L_ERROR, "could not allocate list");
+ return;
+ }
+
+ list_add(queue, con);
+
+ struct sway_container *current = NULL;
+ while (queue->length) {
+ current = queue->items[0];
+ list_del(queue, 0);
+ f(current, data);
+ // TODO floating containers
+ list_cat(queue, current->children);
+ }
+}
+
+bool container_has_anscestor(struct sway_container *descendant,
+ struct sway_container *anscestor) {
+ while (descendant->type != C_ROOT) {
+ descendant = descendant->parent;
+ if (descendant == anscestor) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool find_child_func(struct sway_container *con, void *data) {
+ struct sway_container *child = data;
+ return con == child;
+}
+
+bool container_has_child(struct sway_container *con,
+ struct sway_container *child) {
+ if (con == NULL || con->type == C_VIEW || con->children->length == 0) {
+ return false;
+ }
+ return container_find(con, find_child_func, child);
+}
+
+void container_damage_whole(struct sway_container *con) {
+ struct sway_container *output = con;
+ if (output->type != C_OUTPUT) {
+ output = container_parent(output, C_OUTPUT);
+ }
+ output_damage_whole_container(output->sway_output, con);
+}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <ctype.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include "sway/debug.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/output.h"
+#include "sway/tree/workspace.h"
+#include "sway/tree/view.h"
+#include "sway/input/seat.h"
+#include "sway/ipc-server.h"
+#include "list.h"
+#include "log.h"
+
+struct sway_container root_container;
+
+static void output_layout_handle_change(struct wl_listener *listener,
+ void *data) {
+ struct wlr_output_layout *output_layout =
+ root_container.sway_root->output_layout;
+ const struct wlr_box *layout_box =
+ wlr_output_layout_get_box(output_layout, NULL);
+ root_container.x = layout_box->x;
+ root_container.y = layout_box->y;
+ root_container.width = layout_box->width;
+ root_container.height = layout_box->height;
+
+ arrange_windows(&root_container, layout_box->width, layout_box->height);
+}
+
+struct sway_container *container_set_layout(struct sway_container *container,
+ enum sway_container_layout layout) {
+ if (container->type == C_WORKSPACE) {
+ container->workspace_layout = layout;
+ if (layout == L_HORIZ || layout == L_VERT) {
+ container->layout = layout;
+ }
+ } else {
+ container->layout = layout;
+ }
+ return container;
+}
+
+void layout_init(void) {
+ root_container.id = 0; // normally assigned in new_swayc()
+ root_container.type = C_ROOT;
+ root_container.layout = L_NONE;
+ root_container.name = strdup("root");
+ root_container.children = create_list();
+ wl_signal_init(&root_container.events.destroy);
+
+ root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
+ root_container.sway_root->output_layout = wlr_output_layout_create();
+ wl_list_init(&root_container.sway_root->xwayland_unmanaged);
+ wl_signal_init(&root_container.sway_root->events.new_container);
+
+ root_container.sway_root->output_layout_change.notify =
+ output_layout_handle_change;
+ wl_signal_add(&root_container.sway_root->output_layout->events.change,
+ &root_container.sway_root->output_layout_change);
+}
+
+static int index_child(const struct sway_container *child) {
+ // TODO handle floating
+ struct sway_container *parent = child->parent;
+ int i, len;
+ len = parent->children->length;
+ for (i = 0; i < len; ++i) {
+ if (parent->children->items[i] == child) {
+ break;
+ }
+ }
+
+ if (!sway_assert(i < len, "Stray container")) {
+ return -1;
+ }
+ return i;
+}
+
+void container_insert_child(struct sway_container *parent,
+ struct sway_container *child, int i) {
+ struct sway_container *old_parent = child->parent;
+ if (old_parent) {
+ container_remove_child(child);
+ }
+ wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i);
+ list_insert(parent->children, i, child);
+ child->parent = parent;
+ wl_signal_emit(&child->events.reparent, old_parent);
+}
+
+struct sway_container *container_add_sibling(struct sway_container *fixed,
+ struct sway_container *active) {
+ // TODO handle floating
+ struct sway_container *old_parent = NULL;
+ if (active->parent) {
+ old_parent = active->parent;
+ container_remove_child(active);
+ }
+ struct sway_container *parent = fixed->parent;
+ int i = index_child(fixed);
+ list_insert(parent->children, i + 1, active);
+ active->parent = parent;
+ wl_signal_emit(&active->events.reparent, old_parent);
+ return active->parent;
+}
+
+void container_add_child(struct sway_container *parent,
+ struct sway_container *child) {
+ wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
+ child, child->type, child->width, child->height,
+ parent, parent->type, parent->width, parent->height);
+ list_add(parent->children, child);
+ child->parent = parent;
+}
+
+struct sway_container *container_remove_child(struct sway_container *child) {
+ struct sway_container *parent = child->parent;
+ for (int i = 0; i < parent->children->length; ++i) {
+ if (parent->children->items[i] == child) {
+ list_del(parent->children, i);
+ break;
+ }
+ }
+ child->parent = NULL;
+ return parent;
+}
+
+void container_move_to(struct sway_container *container,
+ struct sway_container *destination) {
+ if (container == destination
+ || container_has_anscestor(container, destination)) {
+ return;
+ }
+ struct sway_container *old_parent = container_remove_child(container);
+ container->width = container->height = 0;
+ struct sway_container *new_parent;
+ if (destination->type == C_VIEW) {
+ new_parent = container_add_sibling(destination, container);
+ } else {
+ new_parent = destination;
+ container_add_child(destination, container);
+ }
+ wl_signal_emit(&container->events.reparent, old_parent);
+ if (container->type == C_WORKSPACE) {
+ struct sway_seat *seat = input_manager_get_default_seat(
+ input_manager);
+ if (old_parent->children->length == 0) {
+ char *ws_name = workspace_next_name(old_parent->name);
+ struct sway_container *ws =
+ workspace_create(old_parent, ws_name);
+ free(ws_name);
+ seat_set_focus(seat, ws);
+ }
+ container_sort_workspaces(new_parent);
+ seat_set_focus(seat, new_parent);
+ }
+ if (old_parent) {
+ arrange_windows(old_parent, -1, -1);
+ }
+ arrange_windows(new_parent, -1, -1);
+}
+
+static bool sway_dir_to_wlr(enum movement_direction dir,
+ enum wlr_direction *out) {
+ switch (dir) {
+ case MOVE_UP:
+ *out = WLR_DIRECTION_UP;
+ break;
+ case MOVE_DOWN:
+ *out = WLR_DIRECTION_DOWN;
+ break;
+ case MOVE_LEFT:
+ *out = WLR_DIRECTION_LEFT;
+ break;
+ case MOVE_RIGHT:
+ *out = WLR_DIRECTION_RIGHT;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_parallel(enum sway_container_layout layout,
+ enum movement_direction dir) {
+ switch (layout) {
+ case L_TABBED:
+ case L_STACKED:
+ case L_HORIZ:
+ return dir == MOVE_LEFT || dir == MOVE_RIGHT;
+ case L_VERT:
+ return dir == MOVE_UP || dir == MOVE_DOWN;
+ default:
+ return false;
+ }
+}
+
+static enum movement_direction invert_movement(enum movement_direction dir) {
+ switch (dir) {
+ case MOVE_LEFT:
+ return MOVE_RIGHT;
+ case MOVE_RIGHT:
+ return MOVE_LEFT;
+ case MOVE_UP:
+ return MOVE_DOWN;
+ case MOVE_DOWN:
+ return MOVE_UP;
+ default:
+ sway_assert(0, "This function expects left|right|up|down");
+ return MOVE_LEFT;
+ }
+}
+
+static int move_offs(enum movement_direction move_dir) {
+ return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
+}
+
+/* Gets the index of the most extreme member based on the movement offset */
+static int container_limit(struct sway_container *container,
+ enum movement_direction move_dir) {
+ return move_offs(move_dir) < 0 ? 0 : container->children->length;
+}
+
+/* Takes one child, sets it aside, wraps the rest of the children in a new
+ * container, switches the layout of the workspace, and drops the child back in.
+ * In other words, rejigger it. */
+static void workspace_rejigger(struct sway_container *ws,
+ struct sway_container *child, enum movement_direction move_dir) {
+ struct sway_container *original_parent = child->parent;
+ struct sway_container *new_parent =
+ container_split(ws, ws->layout);
+
+ container_remove_child(child);
+ for (int i = 0; i < ws->children->length; ++i) {
+ struct sway_container *_child = ws->children->items[i];
+ container_move_to(new_parent, _child);
+ }
+
+ int index = move_offs(move_dir);
+ container_insert_child(ws, child, index < 0 ? 0 : 1);
+ container_set_layout(ws,
+ move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT);
+
+ container_flatten(ws);
+ container_reap_empty_recursive(original_parent);
+ wl_signal_emit(&child->events.reparent, original_parent);
+ container_create_notify(new_parent);
+ arrange_windows(ws, -1, -1);
+}
+
+void container_move(struct sway_container *container,
+ enum movement_direction move_dir, int move_amt) {
+ if (!sway_assert(
+ container->type != C_CONTAINER || container->type != C_VIEW,
+ "Can only move containers and views")) {
+ return;
+ }
+ int offs = move_offs(move_dir);
+
+ struct sway_container *sibling = NULL;
+ struct sway_container *current = container;
+ struct sway_container *parent = current->parent;
+
+ if (parent != container_flatten(parent)) {
+ // Special case: we were the last one in this container, so flatten it
+ // and leave
+ update_debug_tree();
+ return;
+ }
+
+ while (!sibling) {
+ if (current->type == C_ROOT) {
+ return;
+ }
+
+ parent = current->parent;
+ wlr_log(L_DEBUG, "Visiting %p %s '%s'", current,
+ container_type_to_str(current->type), current->name);
+
+ int index = index_child(current);
+
+ switch (current->type) {
+ case C_OUTPUT: {
+ enum wlr_direction wlr_dir;
+ sway_dir_to_wlr(move_dir, &wlr_dir);
+ double ref_lx = current->x + current->width / 2;
+ double ref_ly = current->y + current->height / 2;
+ struct wlr_output *next = wlr_output_layout_adjacent_output(
+ root_container.sway_root->output_layout, wlr_dir,
+ current->sway_output->wlr_output, ref_lx, ref_ly);
+ if (!next) {
+ wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go");
+ return;
+ }
+ struct sway_output *next_output = next->data;
+ current = next_output->swayc;
+ wlr_log(L_DEBUG, "Selected next output (%s)", current->name);
+ // Select workspace and get outta here
+ current = seat_get_focus_inactive(
+ config->handler_context.seat, current);
+ if (current->type != C_WORKSPACE) {
+ current = container_parent(current, C_WORKSPACE);
+ }
+ sibling = current;
+ break;
+ }
+ case C_WORKSPACE:
+ if (!is_parallel(current->layout, move_dir)) {
+ if (current->children->length > 2) {
+ wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)",
+ current->children->length);
+ workspace_rejigger(current, container, move_dir);
+ } else if (current->children->length == 2) {
+ wlr_log(L_DEBUG, "Changing workspace layout");
+ container_set_layout(current,
+ move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ?
+ L_HORIZ : L_VERT);
+ container_insert_child(current, container, offs < 0 ? 0 : 1);
+ arrange_windows(current, -1, -1);
+ }
+ return;
+ } else {
+ wlr_log(L_DEBUG, "Selecting output");
+ current = current->parent;
+ }
+ break;
+ case C_CONTAINER:
+ case C_VIEW:
+ if (is_parallel(parent->layout, move_dir)) {
+ if ((index == parent->children->length - 1 && offs > 0)
+ || (index == 0 && offs < 0)) {
+ if (current->parent == container->parent) {
+ wlr_log(L_DEBUG, "Hit limit, selecting parent");
+ current = current->parent;
+ } else {
+ wlr_log(L_DEBUG, "Hit limit, "
+ "promoting descendant to sibling");
+ // Special case
+ struct sway_container *old_parent = container->parent;
+ container_insert_child(current->parent, container,
+ index + (offs < 0 ? 0 : 1));
+ container->width = container->height = 0;
+ arrange_windows(current->parent, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ return;
+ }
+ } else {
+ sibling = parent->children->items[index + offs];
+ wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id);
+ }
+ } else {
+ wlr_log(L_DEBUG, "Moving up to find a parallel container");
+ current = current->parent;
+ }
+ break;
+ default:
+ sway_assert(0, "Not expecting to see container of type %s here",
+ container_type_to_str(current->type));
+ return;
+ }
+ }
+
+ // Part two: move stuff around
+ int index = index_child(container);
+ struct sway_container *old_parent = container->parent;
+
+ while (sibling) {
+ switch (sibling->type) {
+ case C_VIEW:
+ if (sibling->parent == container->parent) {
+ wlr_log(L_DEBUG, "Swapping siblings");
+ sibling->parent->children->items[index + offs] = container;
+ sibling->parent->children->items[index] = sibling;
+ arrange_windows(sibling->parent, -1, -1);
+ } else {
+ wlr_log(L_DEBUG, "Promoting to sibling of cousin");
+ container_insert_child(sibling->parent, container,
+ index_child(sibling) + (offs > 0 ? 0 : 1));
+ container->width = container->height = 0;
+ arrange_windows(sibling->parent, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ }
+ sibling = NULL;
+ break;
+ case C_WORKSPACE: // Note: only in the case of moving between outputs
+ case C_CONTAINER:
+ if (is_parallel(sibling->layout, move_dir)) {
+ int limit = container_limit(sibling, invert_movement(move_dir));
+ wlr_log(L_DEBUG, "limit: %d", limit);
+ wlr_log(L_DEBUG,
+ "Reparenting container (parallel) to index %d "
+ "(move dir: %d)", limit, move_dir);
+ container_insert_child(sibling, container, limit);
+ container->width = container->height = 0;
+ arrange_windows(sibling, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ sibling = NULL;
+ } else {
+ wlr_log(L_DEBUG, "Reparenting container (perpendicular)");
+ container_remove_child(container);
+ struct sway_container *focus_inactive = seat_get_focus_inactive(
+ config->handler_context.seat, sibling);
+ if (focus_inactive) {
+ while (focus_inactive->parent != sibling) {
+ focus_inactive = focus_inactive->parent;
+ }
+ wlr_log(L_DEBUG, "Focus inactive: id:%zd",
+ focus_inactive->id);
+ sibling = focus_inactive;
+ continue;
+ } else if (sibling->children->length) {
+ wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily");
+ container_add_sibling(sibling->children->items[0], container);
+ } else {
+ wlr_log(L_DEBUG, "No kiddos, adding container alone");
+ container_add_child(sibling, container);
+ }
+ container->width = container->height = 0;
+ arrange_windows(sibling, -1, -1);
+ arrange_windows(old_parent, -1, -1);
+ sibling = NULL;
+ }
+ break;
+ default:
+ sway_assert(0, "Not expecting to see container of type %s here",
+ container_type_to_str(sibling->type));
+ return;
+ }
+ }
+
+ if (old_parent) {
+ seat_set_focus(config->handler_context.seat, old_parent);
+ seat_set_focus(config->handler_context.seat, container);
+ }
+
+ struct sway_container *last_ws = old_parent;
+ struct sway_container *next_ws = container->parent;
+ if (last_ws && last_ws->type != C_WORKSPACE) {
+ last_ws = container_parent(last_ws, C_WORKSPACE);
+ }
+ if (next_ws && next_ws->type != C_WORKSPACE) {
+ next_ws = container_parent(next_ws, C_WORKSPACE);
+ }
+ if (last_ws && next_ws && last_ws != next_ws) {
+ ipc_event_workspace(last_ws, container, "focus");
+ }
+}
+
+enum sway_container_layout container_get_default_layout(
+ struct sway_container *con) {
+ if (con->type != C_OUTPUT) {
+ con = container_parent(con, C_OUTPUT);
+ }
+
+ if (!sway_assert(con != NULL,
+ "container_get_default_layout must be called on an attached"
+ " container below the root container")) {
+ return 0;
+ }
+
+ if (config->default_layout != L_NONE) {
+ return config->default_layout;
+ } else if (config->default_orientation != L_NONE) {
+ return config->default_orientation;
+ } else if (con->width >= con->height) {
+ return L_HORIZ;
+ } else {
+ return L_VERT;
+ }
+}
+
+static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
+ struct sway_container *a = *(void **)_a;
+ struct sway_container *b = *(void **)_b;
+ int retval = 0;
+
+ if (isdigit(a->name[0]) && isdigit(b->name[0])) {
+ int a_num = strtol(a->name, NULL, 10);
+ int b_num = strtol(b->name, NULL, 10);
+ retval = (a_num < b_num) ? -1 : (a_num > b_num);
+ } else if (isdigit(a->name[0])) {
+ retval = -1;
+ } else if (isdigit(b->name[0])) {
+ retval = 1;
+ }
+
+ return retval;
+}
+
+void container_sort_workspaces(struct sway_container *output) {
+ list_stable_sort(output->children, sort_workspace_cmp_qsort);
+}
+
+static void apply_horiz_layout(struct sway_container *container, const double x,
+ const double y, const double width,
+ const double height, const int start,
+ const int end);
+
+static void apply_vert_layout(struct sway_container *container, const double x,
+ const double y, const double width,
+ const double height, const int start,
+ const int end);
+
+void arrange_windows(struct sway_container *container,
+ double width, double height) {
+ if (config->reloading) {
+ return;
+ }
+ int i;
+ if (width == -1 || height == -1) {
+ width = container->width;
+ height = container->height;
+ }
+ // pixels are indivisible. if we don't round the pixels, then the view
+ // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
+ // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
+ width = floor(width);
+ height = floor(height);
+
+ wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
+ container->name, container->width, container->height, container->x,
+ container->y);
+
+ double x = 0, y = 0;
+ switch (container->type) {
+ case C_ROOT:
+ for (i = 0; i < container->children->length; ++i) {
+ struct sway_container *output = container->children->items[i];
+ const struct wlr_box *output_box = wlr_output_layout_get_box(
+ container->sway_root->output_layout,
+ output->sway_output->wlr_output);
+ output->x = output_box->x;
+ output->y = output_box->y;
+ output->width = output_box->width;
+ output->height = output_box->height;
+ wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f",
+ output->name, output->x, output->y);
+ arrange_windows(output, output_box->width, output_box->height);
+ }
+ return;
+ case C_OUTPUT:
+ // arrange all workspaces:
+ for (i = 0; i < container->children->length; ++i) {
+ struct sway_container *child = container->children->items[i];
+ arrange_windows(child, -1, -1);
+ }
+ return;
+ case C_WORKSPACE:
+ {
+ struct sway_container *output =
+ container_parent(container, C_OUTPUT);
+ struct wlr_box *area = &output->sway_output->usable_area;
+ wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d",
+ area->width, area->height, area->x, area->y);
+ container->width = width = area->width;
+ container->height = height = area->height;
+ container->x = x = area->x;
+ container->y = y = area->y;
+ wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f",
+ container->name, container->x, container->y);
+ }
+ // children are properly handled below
+ break;
+ case C_VIEW:
+ {
+ container->width = width;
+ container->height = height;
+ view_configure(container->sway_view, container->x, container->y,
+ container->width, container->height);
+ wlr_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f",
+ container->width, container->height,
+ container->x, container->y);
+ }
+ return;
+ default:
+ container->width = width;
+ container->height = height;
+ x = container->x;
+ y = container->y;
+ break;
+ }
+
+ switch (container->layout) {
+ case L_HORIZ:
+ apply_horiz_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ case L_VERT:
+ apply_vert_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ default:
+ wlr_log(L_DEBUG, "TODO: arrange layout type %d", container->layout);
+ apply_horiz_layout(container, x, y, width, height, 0,
+ container->children->length);
+ break;
+ }
+ container_damage_whole(container);
+ // TODO: Make this less shitty
+ update_debug_tree();
+}
+
+static void apply_horiz_layout(struct sway_container *container,
+ const double x, const double y,
+ const double width, const double height,
+ const int start, const int end) {
+ double scale = 0;
+ // Calculate total width
+ for (int i = start; i < end; ++i) {
+ double *old_width =
+ &((struct sway_container *)container->children->items[i])->width;
+ if (*old_width <= 0) {
+ if (end - start > 1) {
+ *old_width = width / (end - start - 1);
+ } else {
+ *old_width = width;
+ }
+ }
+ scale += *old_width;
+ }
+ scale = width / scale;
+
+ // Resize windows
+ double child_x = x;
+ if (scale > 0.1) {
+ wlr_log(L_DEBUG, "Arranging %p horizontally", container);
+ for (int i = start; i < end; ++i) {
+ struct sway_container *child = container->children->items[i];
+ wlr_log(L_DEBUG,
+ "Calculating arrangement for %p:%d (will scale %f by %f)",
+ child, child->type, width, scale);
+
+ if (child->type == C_VIEW) {
+ view_configure(child->sway_view, child_x, y, child->width,
+ child->height);
+ } else {
+ child->x = child_x;
+ child->y = y;
+ }
+
+ if (i == end - 1) {
+ double remaining_width = x + width - child_x;
+ arrange_windows(child, remaining_width, height);
+ } else {
+ arrange_windows(child, child->width * scale, height);
+ }
+ child_x += child->width;
+ }
+
+ // update focused view border last because it may
+ // depend on the title bar geometry of its siblings.
+ /* TODO WLR
+ if (focused && container->children->length > 1) {
+ update_container_border(focused);
+ }
+ */
+ }
+}
+
+void apply_vert_layout(struct sway_container *container,
+ const double x, const double y,
+ const double width, const double height, const int start,
+ const int end) {
+ int i;
+ double scale = 0;
+ // Calculate total height
+ for (i = start; i < end; ++i) {
+ double *old_height =
+ &((struct sway_container *)container->children->items[i])->height;
+ if (*old_height <= 0) {
+ if (end - start > 1) {
+ *old_height = height / (end - start - 1);
+ } else {
+ *old_height = height;
+ }
+ }
+ scale += *old_height;
+ }
+ scale = height / scale;
+
+ // Resize
+ double child_y = y;
+ if (scale > 0.1) {
+ wlr_log(L_DEBUG, "Arranging %p vertically", container);
+ for (i = start; i < end; ++i) {
+ struct sway_container *child = container->children->items[i];
+ wlr_log(L_DEBUG,
+ "Calculating arrangement for %p:%d (will scale %f by %f)",
+ child, child->type, height, scale);
+ if (child->type == C_VIEW) {
+ view_configure(child->sway_view, x, child_y, child->width,
+ child->height);
+ } else {
+ child->x = x;
+ child->y = child_y;
+ }
+
+ if (i == end - 1) {
+ double remaining_height = y + height - child_y;
+ arrange_windows(child, width, remaining_height);
+ } else {
+ arrange_windows(child, width, child->height * scale);
+ }
+ child_y += child->height;
+ }
+
+ // update focused view border last because it may
+ // depend on the title bar geometry of its siblings.
+ /* TODO WLR
+ if (focused && container->children->length > 1) {
+ update_container_border(focused);
+ }
+ */
+ }
+}
+
+/**
+ * Get swayc in the direction of newly entered output.
+ */
+static struct sway_container *get_swayc_in_output_direction(
+ struct sway_container *output, enum movement_direction dir,
+ struct sway_seat *seat) {
+ if (!output) {
+ return NULL;
+ }
+
+ struct sway_container *ws = seat_get_focus_inactive(seat, output);
+ if (ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+
+ if (ws == NULL) {
+ wlr_log(L_ERROR, "got an output without a workspace");
+ return NULL;
+ }
+
+ if (ws->children->length > 0) {
+ switch (dir) {
+ case MOVE_LEFT:
+ // get most right child of new output
+ return ws->children->items[ws->children->length-1];
+ case MOVE_RIGHT:
+ // get most left child of new output
+ return ws->children->items[0];
+ case MOVE_UP:
+ case MOVE_DOWN: {
+ struct sway_container *focused =
+ seat_get_focus_inactive(seat, ws);
+ if (focused && focused->parent) {
+ struct sway_container *parent = focused->parent;
+ if (parent->layout == L_VERT) {
+ if (dir == MOVE_UP) {
+ // get child furthest down on new output
+ int idx = parent->children->length - 1;
+ return parent->children->items[idx];
+ } else if (dir == MOVE_DOWN) {
+ // get child furthest up on new output
+ return parent->children->items[0];
+ }
+ }
+ return focused;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return ws;
+}
+
+static void get_layout_center_position(struct sway_container *container,
+ int *x, int *y) {
+ // FIXME view coords are inconsistently referred to in layout/output systems
+ if (container->type == C_OUTPUT) {
+ *x = container->x + container->width/2;
+ *y = container->y + container->height/2;
+ } else {
+ struct sway_container *output = container_parent(container, C_OUTPUT);
+ if (container->type == C_WORKSPACE) {
+ // Workspace coordinates are actually wrong/arbitrary, but should
+ // be same as output.
+ *x = output->x;
+ *y = output->y;
+ } else {
+ *x = output->x + container->x;
+ *y = output->y + container->y;
+ }
+ }
+}
+
+static struct sway_container *sway_output_from_wlr(struct wlr_output *output) {
+ if (output == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *o = root_container.children->items[i];
+ if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
+ return o;
+ }
+ }
+ return NULL;
+}
+
+struct sway_container *container_get_in_direction(
+ struct sway_container *container, struct sway_seat *seat,
+ enum movement_direction dir) {
+ if (dir == MOVE_CHILD) {
+ return seat_get_focus_inactive(seat, container);
+ }
+
+ struct sway_container *parent = container->parent;
+ if (dir == MOVE_PARENT) {
+ if (parent->type == C_OUTPUT) {
+ return NULL;
+ } else {
+ return parent;
+ }
+ }
+
+ // TODO WLR fullscreen
+ /*
+ if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
+ wlr_log(L_DEBUG, "Moving from fullscreen view, skipping to output");
+ container = container_parent(container, C_OUTPUT);
+ get_layout_center_position(container, &abs_pos);
+ struct sway_container *output =
+ swayc_adjacent_output(container, dir, &abs_pos, true);
+ return get_swayc_in_output_direction(output, dir);
+ }
+ if (container->type == C_WORKSPACE && container->fullscreen) {
+ sway_log(L_DEBUG, "Moving to fullscreen view");
+ return container->fullscreen;
+ }
+ */
+
+ struct sway_container *wrap_candidate = NULL;
+ while (true) {
+ bool can_move = false;
+ int desired;
+ int idx = index_child(container);
+ if (parent->type == C_ROOT) {
+ enum wlr_direction wlr_dir = 0;
+ if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
+ "got invalid direction: %d", dir)) {
+ return NULL;
+ }
+ int lx, ly;
+ get_layout_center_position(container, &lx, &ly);
+ struct wlr_output_layout *layout =
+ root_container.sway_root->output_layout;
+ struct wlr_output *wlr_adjacent =
+ wlr_output_layout_adjacent_output(layout, wlr_dir,
+ container->sway_output->wlr_output, lx, ly);
+ struct sway_container *adjacent =
+ sway_output_from_wlr(wlr_adjacent);
+
+ if (!adjacent || adjacent == container) {
+ return wrap_candidate;
+ }
+ struct sway_container *next =
+ get_swayc_in_output_direction(adjacent, dir, seat);
+ if (next == NULL) {
+ return NULL;
+ }
+ if (next->children && next->children->length) {
+ // TODO consider floating children as well
+ return seat_get_focus_inactive_view(seat, next);
+ } else {
+ return next;
+ }
+ } else {
+ if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
+ if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_LEFT ? -1 : 1);
+ }
+ } else {
+ if (parent->layout == L_VERT || parent->layout == L_STACKED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_UP ? -1 : 1);
+ }
+ }
+ }
+
+ if (can_move) {
+ // TODO handle floating
+ if (desired < 0 || desired >= parent->children->length) {
+ can_move = false;
+ int len = parent->children->length;
+ if (!wrap_candidate && len > 1) {
+ if (desired < 0) {
+ wrap_candidate = parent->children->items[len-1];
+ } else {
+ wrap_candidate = parent->children->items[0];
+ }
+ if (config->force_focus_wrapping) {
+ return wrap_candidate;
+ }
+ }
+ } else {
+ struct sway_container *desired_con = parent->children->items[desired];
+ wlr_log(L_DEBUG,
+ "cont %d-%p dir %i sibling %d: %p", idx,
+ container, dir, desired, desired_con);
+ struct sway_container *next = seat_get_focus_inactive_view(seat, desired_con);
+ return next;
+ }
+ }
+
+ if (!can_move) {
+ container = parent;
+ parent = parent->parent;
+ if (!parent) {
+ // wrapping is the last chance
+ return wrap_candidate;
+ }
+ }
+ }
+}
+
+struct sway_container *container_replace_child(struct sway_container *child,
+ struct sway_container *new_child) {
+ struct sway_container *parent = child->parent;
+ if (parent == NULL) {
+ return NULL;
+ }
+ int i = index_child(child);
+
+ // TODO floating
+ if (new_child->parent) {
+ container_remove_child(new_child);
+ }
+ parent->children->items[i] = new_child;
+ new_child->parent = parent;
+ child->parent = NULL;
+
+ // Set geometry for new child
+ new_child->x = child->x;
+ new_child->y = child->y;
+ new_child->width = child->width;
+ new_child->height = child->height;
+
+ // reset geometry for child
+ child->width = 0;
+ child->height = 0;
+
+ return parent;
+}
+
+struct sway_container *container_split(struct sway_container *child,
+ enum sway_container_layout layout) {
+ // TODO floating: cannot split a floating container
+ if (!sway_assert(child, "child cannot be null")) {
+ return NULL;
+ }
+ if (child->type == C_WORKSPACE && child->children->length == 0) {
+ // Special case: this just behaves like splitt
+ child->prev_layout = child->layout;
+ child->layout = layout;
+ arrange_windows(child, -1, -1);
+ return child;
+ }
+
+ struct sway_container *cont = container_create(C_CONTAINER);
+
+ wlr_log(L_DEBUG, "creating container %p around %p", cont, child);
+
+ cont->prev_layout = L_NONE;
+ cont->width = child->width;
+ cont->height = child->height;
+ cont->x = child->x;
+ cont->y = child->y;
+
+ if (child->type == C_WORKSPACE) {
+ struct sway_seat *seat = input_manager_get_default_seat(input_manager);
+ struct sway_container *workspace = child;
+ bool set_focus = (seat_get_focus(seat) == workspace);
+
+ while (workspace->children->length) {
+ struct sway_container *ws_child = workspace->children->items[0];
+ container_remove_child(ws_child);
+ container_add_child(cont, ws_child);
+ }
+
+ container_add_child(workspace, cont);
+ enum sway_container_layout old_layout = workspace->layout;
+ container_set_layout(workspace, layout);
+ cont->layout = old_layout;
+
+ if (set_focus) {
+ seat_set_focus(seat, cont);
+ }
+ } else {
+ cont->layout = layout;
+ container_replace_child(child, cont);
+ container_add_child(cont, child);
+ }
+
+ return cont;
+}
+
+void container_recursive_resize(struct sway_container *container,
+ double amount, enum resize_edge edge) {
+ bool layout_match = true;
+ wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount);
+ if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) {
+ container->width += amount;
+ layout_match = container->layout == L_HORIZ;
+ } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) {
+ container->height += amount;
+ layout_match = container->layout == L_VERT;
+ }
+ if (container->children) {
+ for (int i = 0; i < container->children->length; i++) {
+ struct sway_container *child = container->children->items[i];
+ double amt = layout_match ?
+ amount / container->children->length : amount;
+ container_recursive_resize(child, amt, edge);
+ }
+ }
+}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <string.h>
+#include <strings.h>
+#include "sway/output.h"
+#include "sway/tree/output.h"
+#include "sway/tree/workspace.h"
+#include "log.h"
+
+struct sway_container *output_create(
+ struct sway_output *sway_output) {
+ struct wlr_box size;
+ wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
+ &size.height);
+
+ const char *name = sway_output->wlr_output->name;
+ char identifier[128];
+ output_get_identifier(identifier, sizeof(identifier), sway_output);
+
+ struct output_config *oc = NULL, *all = NULL;
+ for (int i = 0; i < config->output_configs->length; ++i) {
+ struct output_config *cur = config->output_configs->items[i];
+
+ if (strcasecmp(name, cur->name) == 0 ||
+ strcasecmp(identifier, cur->name) == 0) {
+ wlr_log(L_DEBUG, "Matched output config for %s", name);
+ oc = cur;
+ }
+ if (strcasecmp("*", cur->name) == 0) {
+ wlr_log(L_DEBUG, "Matched wildcard output config for %s", name);
+ all = cur;
+ }
+
+ if (oc && all) {
+ break;
+ }
+ }
+ if (!oc) {
+ oc = all;
+ }
+
+ if (oc && !oc->enabled) {
+ return NULL;
+ }
+
+ struct sway_container *output = container_create(C_OUTPUT);
+ output->sway_output = sway_output;
+ output->name = strdup(name);
+ if (output->name == NULL) {
+ container_destroy(output);
+ return NULL;
+ }
+
+ apply_output_config(oc, output);
+ container_add_child(&root_container, output);
+ load_swaybars();
+
+ // Create workspace
+ char *ws_name = workspace_next_name(output->name);
+ wlr_log(L_DEBUG, "Creating default workspace %s", ws_name);
+ struct sway_container *ws = workspace_create(output, ws_name);
+ // Set each seat's focus if not already set
+ struct sway_seat *seat = NULL;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ if (!seat->has_focus) {
+ seat_set_focus(seat, ws);
+ }
+ }
+
+ free(ws_name);
+ container_create_notify(output);
+ return output;
+}
+
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 @@
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_output_layout.h>
+#include "log.h"
+#include "sway/output.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+
+void view_init(struct sway_view *view, enum sway_view_type type,
+ const struct sway_view_impl *impl) {
+ view->type = type;
+ view->impl = impl;
+ wl_signal_init(&view->events.unmap);
+}
+
+void view_destroy(struct sway_view *view) {
+ if (view == NULL) {
+ return;
+ }
+
+ if (view->surface != NULL) {
+ view_unmap(view);
+ }
+
+ container_destroy(view->swayc);
+
+ if (view->impl->destroy) {
+ view->impl->destroy(view);
+ } else {
+ free(view);
+ }
+}
+
+const char *view_get_title(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_TITLE);
+ }
+ return NULL;
+}
+
+const char *view_get_app_id(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_APP_ID);
+ }
+ return NULL;
+}
+
+const char *view_get_class(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_CLASS);
+ }
+ return NULL;
+}
+
+const char *view_get_instance(struct sway_view *view) {
+ if (view->impl->get_prop) {
+ return view->impl->get_prop(view, VIEW_PROP_INSTANCE);
+ }
+ return NULL;
+}
+
+void view_configure(struct sway_view *view, double ox, double oy, int width,
+ int height) {
+ if (view->impl->configure) {
+ view->impl->configure(view, ox, oy, width, height);
+ }
+}
+
+void view_set_activated(struct sway_view *view, bool activated) {
+ if (view->impl->set_activated) {
+ view->impl->set_activated(view, activated);
+ }
+}
+
+void view_close(struct sway_view *view) {
+ if (view->impl->close) {
+ view->impl->close(view);
+ }
+}
+
+void view_damage(struct sway_view *view, bool whole) {
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *cont = root_container.children->items[i];
+ if (cont->type == C_OUTPUT) {
+ output_damage_view(cont->sway_output, view, whole);
+ }
+ }
+}
+
+static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+
+ box->x = output->x + view->swayc->x;
+ box->y = output->y + view->swayc->y;
+ box->width = view->width;
+ box->height = view->height;
+}
+
+void view_for_each_surface(struct sway_view *view,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ if (view->impl->for_each_surface) {
+ view->impl->for_each_surface(view, iterator, user_data);
+ } else {
+ wlr_surface_for_each_surface(view->surface, iterator, user_data);
+ }
+}
+
+static void view_subsurface_create(struct sway_view *view,
+ struct wlr_subsurface *subsurface);
+
+static void view_init_subsurfaces(struct sway_view *view,
+ struct wlr_surface *surface);
+
+static void view_handle_surface_new_subsurface(struct wl_listener *listener,
+ void *data) {
+ struct sway_view *view =
+ wl_container_of(listener, view, surface_new_subsurface);
+ struct wlr_subsurface *subsurface = data;
+ view_subsurface_create(view, subsurface);
+}
+
+static void surface_send_enter_iterator(struct wlr_surface *surface,
+ int x, int y, void *data) {
+ struct wlr_output *wlr_output = data;
+ wlr_surface_send_enter(surface, wlr_output);
+}
+
+static void surface_send_leave_iterator(struct wlr_surface *surface,
+ int x, int y, void *data) {
+ struct wlr_output *wlr_output = data;
+ wlr_surface_send_leave(surface, wlr_output);
+}
+
+static void view_handle_container_reparent(struct wl_listener *listener,
+ void *data) {
+ struct sway_view *view =
+ wl_container_of(listener, view, container_reparent);
+ struct sway_container *old_parent = data;
+
+ struct sway_container *old_output = old_parent;
+ if (old_output != NULL && old_output->type != C_OUTPUT) {
+ old_output = container_parent(old_output, C_OUTPUT);
+ }
+
+ struct sway_container *new_output = view->swayc->parent;
+ if (new_output != NULL && new_output->type != C_OUTPUT) {
+ new_output = container_parent(new_output, C_OUTPUT);
+ }
+
+ if (old_output == new_output) {
+ return;
+ }
+
+ if (old_output != NULL) {
+ view_for_each_surface(view, surface_send_leave_iterator,
+ old_output->sway_output->wlr_output);
+ }
+ if (new_output != NULL) {
+ view_for_each_surface(view, surface_send_enter_iterator,
+ new_output->sway_output->wlr_output);
+ }
+}
+
+void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
+ if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
+ return;
+ }
+
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus_inactive(seat,
+ &root_container);
+ struct sway_container *cont = container_view_create(focus, view);
+
+ view->surface = wlr_surface;
+ view->swayc = cont;
+
+ view_init_subsurfaces(view, wlr_surface);
+ wl_signal_add(&wlr_surface->events.new_subsurface,
+ &view->surface_new_subsurface);
+ view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
+
+ wl_signal_add(&view->swayc->events.reparent, &view->container_reparent);
+ view->container_reparent.notify = view_handle_container_reparent;
+
+ arrange_windows(cont->parent, -1, -1);
+ input_manager_set_focus(input_manager, cont);
+
+ view_damage(view, true);
+ view_handle_container_reparent(&view->container_reparent, NULL);
+}
+
+void view_unmap(struct sway_view *view) {
+ if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
+ return;
+ }
+
+ wl_signal_emit(&view->events.unmap, view);
+
+ view_damage(view, true);
+
+ wl_list_remove(&view->surface_new_subsurface.link);
+ wl_list_remove(&view->container_reparent.link);
+
+ struct sway_container *parent = container_destroy(view->swayc);
+
+ view->swayc = NULL;
+ view->surface = NULL;
+
+ arrange_windows(parent, -1, -1);
+}
+
+void view_update_position(struct sway_view *view, double ox, double oy) {
+ if (view->swayc->x == ox && view->swayc->y == oy) {
+ return;
+ }
+
+ view_damage(view, true);
+ view->swayc->x = ox;
+ view->swayc->y = oy;
+ view_damage(view, true);
+}
+
+void view_update_size(struct sway_view *view, int width, int height) {
+ if (view->width == width && view->height == height) {
+ return;
+ }
+
+ view_damage(view, true);
+ view->width = width;
+ view->height = height;
+ view_damage(view, true);
+}
+
+
+static void view_subsurface_create(struct sway_view *view,
+ struct wlr_subsurface *subsurface) {
+ struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child));
+ if (child == NULL) {
+ wlr_log(L_ERROR, "Allocation failed");
+ return;
+ }
+ view_child_init(child, NULL, view, subsurface->surface);
+}
+
+static void view_child_handle_surface_commit(struct wl_listener *listener,
+ void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, surface_commit);
+ // TODO: only accumulate damage from the child
+ view_damage(child->view, false);
+}
+
+static void view_child_handle_surface_new_subsurface(
+ struct wl_listener *listener, void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, surface_new_subsurface);
+ struct wlr_subsurface *subsurface = data;
+ view_subsurface_create(child->view, subsurface);
+}
+
+static void view_child_handle_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, surface_destroy);
+ view_child_destroy(child);
+}
+
+static void view_child_handle_view_unmap(struct wl_listener *listener,
+ void *data) {
+ struct sway_view_child *child =
+ wl_container_of(listener, child, view_unmap);
+ view_child_destroy(child);
+}
+
+static void view_init_subsurfaces(struct sway_view *view,
+ struct wlr_surface *surface) {
+ struct wlr_subsurface *subsurface;
+ wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
+ view_subsurface_create(view, subsurface);
+ }
+}
+
+void view_child_init(struct sway_view_child *child,
+ const struct sway_view_child_impl *impl, struct sway_view *view,
+ struct wlr_surface *surface) {
+ child->impl = impl;
+ child->view = view;
+ child->surface = surface;
+
+ wl_signal_add(&surface->events.commit, &child->surface_commit);
+ child->surface_commit.notify = view_child_handle_surface_commit;
+ wl_signal_add(&surface->events.new_subsurface,
+ &child->surface_new_subsurface);
+ child->surface_new_subsurface.notify =
+ view_child_handle_surface_new_subsurface;
+ wl_signal_add(&surface->events.destroy, &child->surface_destroy);
+ child->surface_destroy.notify = view_child_handle_surface_destroy;
+ wl_signal_add(&view->events.unmap, &child->view_unmap);
+ child->view_unmap.notify = view_child_handle_view_unmap;
+
+ struct sway_container *output = child->view->swayc->parent;
+ if (output != NULL) {
+ if (output->type != C_OUTPUT) {
+ output = container_parent(output, C_OUTPUT);
+ }
+ wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
+ }
+
+ view_init_subsurfaces(child->view, surface);
+
+ // TODO: only damage the whole child
+ view_damage(child->view, true);
+}
+
+void view_child_destroy(struct sway_view_child *child) {
+ // TODO: only damage the whole child
+ view_damage(child->view, true);
+
+ wl_list_remove(&child->surface_commit.link);
+ wl_list_remove(&child->surface_destroy.link);
+ wl_list_remove(&child->view_unmap.link);
+
+ if (child->impl && child->impl->destroy) {
+ child->impl->destroy(child);
+ } else {
+ free(child);
+ }
+}
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 @@
+#define _XOPEN_SOURCE 500
+#include <ctype.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include "stringop.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/ipc-server.h"
+#include "sway/tree/container.h"
+#include "sway/tree/workspace.h"
+#include "log.h"
+#include "util.h"
+
+static struct sway_container *get_workspace_initial_output(const char *name) {
+ struct sway_container *parent;
+ // Search for workspace<->output pair
+ int e = config->workspace_outputs->length;
+ for (int i = 0; i < config->workspace_outputs->length; ++i) {
+ struct workspace_output *wso = config->workspace_outputs->items[i];
+ if (strcasecmp(wso->workspace, name) == 0) {
+ // Find output to use if it exists
+ e = root_container.children->length;
+ for (i = 0; i < e; ++i) {
+ parent = root_container.children->items[i];
+ if (strcmp(parent->name, wso->output) == 0) {
+ return parent;
+ }
+ }
+ break;
+ }
+ }
+ // Otherwise put it on the focused output
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, &root_container);
+ parent = focus;
+ parent = container_parent(parent, C_OUTPUT);
+ return parent;
+}
+
+struct sway_container *workspace_create(struct sway_container *output,
+ const char *name) {
+ if (output == NULL) {
+ output = get_workspace_initial_output(name);
+ }
+
+ wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name);
+ struct sway_container *workspace = container_create(C_WORKSPACE);
+
+ workspace->x = output->x;
+ workspace->y = output->y;
+ workspace->width = output->width;
+ workspace->height = output->height;
+ workspace->name = !name ? NULL : strdup(name);
+ workspace->prev_layout = L_NONE;
+ workspace->layout = container_get_default_layout(output);
+ workspace->workspace_layout = workspace->layout;
+
+ container_add_child(output, workspace);
+ container_sort_workspaces(output);
+ container_create_notify(workspace);
+
+ return workspace;
+}
+
+char *prev_workspace_name = NULL;
+struct workspace_by_number_data {
+ int len;
+ const char *cset;
+ const char *name;
+};
+
+void next_name_map(struct sway_container *ws, void *data) {
+ int *count = data;
+ ++count;
+}
+
+static bool workspace_valid_on_output(const char *output_name,
+ const char *ws_name) {
+ int i;
+ for (i = 0; i < config->workspace_outputs->length; ++i) {
+ struct workspace_output *wso = config->workspace_outputs->items[i];
+ if (strcasecmp(wso->workspace, ws_name) == 0) {
+ if (strcasecmp(wso->output, output_name) != 0) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+char *workspace_next_name(const char *output_name) {
+ wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s",
+ output_name);
+ int l = 1;
+ // Scan all workspace bindings to find the next available workspace name,
+ // if none are found/available then default to a number
+ struct sway_mode *mode = config->current_mode;
+
+ // TODO: iterate over keycode bindings too
+ int order = INT_MAX;
+ char *target = NULL;
+ for (int i = 0; i < mode->keysym_bindings->length; ++i) {
+ struct sway_binding *binding = mode->keysym_bindings->items[i];
+ char *cmdlist = strdup(binding->command);
+ char *dup = cmdlist;
+ char *name = NULL;
+
+ // workspace n
+ char *cmd = argsep(&cmdlist, " ");
+ if (cmdlist) {
+ name = argsep(&cmdlist, ",;");
+ }
+
+ if (strcmp("workspace", cmd) == 0 && name) {
+ wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
+ char *_target = strdup(name);
+ strip_quotes(_target);
+ while (isspace(*_target)) {
+ memmove(_target, _target+1, strlen(_target+1));
+ }
+
+ // Make sure that the command references an actual workspace
+ // not a command about workspaces
+ if (strcmp(_target, "next") == 0 ||
+ strcmp(_target, "prev") == 0 ||
+ strcmp(_target, "next_on_output") == 0 ||
+ strcmp(_target, "prev_on_output") == 0 ||
+ strcmp(_target, "number") == 0 ||
+ strcmp(_target, "back_and_forth") == 0 ||
+ strcmp(_target, "current") == 0)
+ {
+ free(_target);
+ free(dup);
+ continue;
+ }
+
+ // If the command is workspace number <name>, isolate the name
+ if (strncmp(_target, "number ", strlen("number ")) == 0) {
+ size_t length = strlen(_target) - strlen("number ") + 1;
+ char *temp = malloc(length);
+ strncpy(temp, _target + strlen("number "), length - 1);
+ temp[length - 1] = '\0';
+ free(_target);
+ _target = temp;
+ wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target);
+
+ // Make sure the workspace number doesn't already exist
+ if (workspace_by_number(_target)) {
+ free(_target);
+ free(dup);
+ continue;
+ }
+ }
+
+ // Make sure that the workspace doesn't already exist
+ if (workspace_by_name(_target)) {
+ free(_target);
+ free(dup);
+ continue;
+ }
+
+ // make sure that the workspace can appear on the given
+ // output
+ if (!workspace_valid_on_output(output_name, _target)) {
+ free(_target);
+ free(dup);
+ continue;
+ }
+
+ if (binding->order < order) {
+ order = binding->order;
+ free(target);
+ target = _target;
+ wlr_log(L_DEBUG, "Workspace: Found free name %s", _target);
+ }
+ }
+ free(dup);
+ }
+ if (target != NULL) {
+ return target;
+ }
+ // As a fall back, get the current number of active workspaces
+ // and return that + 1 for the next workspace's name
+ int ws_num = root_container.children->length;
+ if (ws_num >= 10) {
+ l = 2;
+ } else if (ws_num >= 100) {
+ l = 3;
+ }
+ char *name = malloc(l + 1);
+ if (!name) {
+ wlr_log(L_ERROR, "Could not allocate workspace name");
+ return NULL;
+ }
+ sprintf(name, "%d", ws_num++);
+ return name;
+}
+
+static bool _workspace_by_number(struct sway_container *view, void *data) {
+ if (view->type != C_WORKSPACE) {
+ return false;
+ }
+ struct workspace_by_number_data *wbnd = data;
+ int a = strspn(view->name, wbnd->cset);
+ return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
+}
+
+struct sway_container *workspace_by_number(const char* name) {
+ struct workspace_by_number_data wbnd = {0, "1234567890", name};
+ wbnd.len = strspn(name, wbnd.cset);
+ if (wbnd.len <= 0) {
+ return NULL;
+ }
+ return container_find(&root_container,
+ _workspace_by_number, (void *) &wbnd);
+}
+
+static bool _workspace_by_name(struct sway_container *view, void *data) {
+ return (view->type == C_WORKSPACE) &&
+ (strcasecmp(view->name, (char *) data) == 0);
+}
+
+struct sway_container *workspace_by_name(const char *name) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *current_workspace = NULL, *current_output = NULL;
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus) {
+ current_workspace = container_parent(focus, C_WORKSPACE);
+ current_output = container_parent(focus, C_OUTPUT);
+ }
+ if (strcmp(name, "prev") == 0) {
+ return workspace_prev(current_workspace);
+ } else if (strcmp(name, "prev_on_output") == 0) {
+ return workspace_output_prev(current_output);
+ } else if (strcmp(name, "next") == 0) {
+ return workspace_next(current_workspace);
+ } else if (strcmp(name, "next_on_output") == 0) {
+ return workspace_output_next(current_output);
+ } else if (strcmp(name, "current") == 0) {
+ return current_workspace;
+ } else {
+ return container_find(&root_container, _workspace_by_name,
+ (void *)name);
+ }
+}
+
+/**
+ * Get the previous or next workspace on the specified output. Wraps around at
+ * the end and beginning. If next is false, the previous workspace is returned,
+ * otherwise the next one is returned.
+ */
+struct sway_container *workspace_output_prev_next_impl(
+ struct sway_container *output, bool next) {
+ if (!sway_assert(output->type == C_OUTPUT,
+ "Argument must be an output, is %d", output->type)) {
+ return NULL;
+ }
+
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus_inactive(seat, output);
+ struct sway_container *workspace = (focus->type == C_WORKSPACE ?
+ focus :
+ container_parent(focus, C_WORKSPACE));
+
+ int i;
+ for (i = 0; i < output->children->length; i++) {
+ if (output->children->items[i] == workspace) {
+ return output->children->items[
+ wrap(i + (next ? 1 : -1), output->children->length)];
+ }
+ }
+
+ // Doesn't happen, at worst the for loop returns the previously active
+ // workspace
+ return NULL;
+}
+
+/**
+ * Get the previous or next workspace. If the first/last workspace on an output
+ * is active, proceed to the previous/next output's previous/next workspace. If
+ * next is false, the previous workspace is returned, otherwise the next one is
+ * returned.
+ */
+struct sway_container *workspace_prev_next_impl(
+ struct sway_container *workspace, bool next) {
+ if (!sway_assert(workspace->type == C_WORKSPACE,
+ "Argument must be a workspace, is %d", workspace->type)) {
+ return NULL;
+ }
+
+ struct sway_container *current_output = workspace->parent;
+ int offset = next ? 1 : -1;
+ int start = next ? 0 : 1;
+ int end;
+ if (next) {
+ end = current_output->children->length - 1;
+ } else {
+ end = current_output->children->length;
+ }
+ int i;
+ for (i = start; i < end; i++) {
+ if (current_output->children->items[i] == workspace) {
+ return current_output->children->items[i + offset];
+ }
+ }
+
+ // Given workspace is the first/last on the output, jump to the
+ // previous/next output
+ int num_outputs = root_container.children->length;
+ for (i = 0; i < num_outputs; i++) {
+ if (root_container.children->items[i] == current_output) {
+ struct sway_container *next_output = root_container.children->items[
+ wrap(i + offset, num_outputs)];
+ return workspace_output_prev_next_impl(next_output, next);
+ }
+ }
+
+ // Doesn't happen, at worst the for loop returns the previously active
+ // workspace on the active output
+ return NULL;
+}
+
+struct sway_container *workspace_output_next(struct sway_container *current) {
+ return workspace_output_prev_next_impl(current, true);
+}
+
+struct sway_container *workspace_next(struct sway_container *current) {
+ return workspace_prev_next_impl(current, true);
+}
+
+struct sway_container *workspace_output_prev(struct sway_container *current) {
+ return workspace_output_prev_next_impl(current, false);
+}
+
+struct sway_container *workspace_prev(struct sway_container *current) {
+ return workspace_prev_next_impl(current, false);
+}
+
+bool workspace_switch(struct sway_container *workspace) {
+ if (!workspace) {
+ return false;
+ }
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus =
+ seat_get_focus_inactive(seat, &root_container);
+ if (!seat || !focus) {
+ return false;
+ }
+ struct sway_container *active_ws = focus;
+ if (active_ws->type != C_WORKSPACE) {
+ active_ws = container_parent(focus, C_WORKSPACE);
+ }
+
+ if (config->auto_back_and_forth
+ && active_ws == workspace
+ && prev_workspace_name) {
+ struct sway_container *new_ws = workspace_by_name(prev_workspace_name);
+ workspace = new_ws ?
+ new_ws :
+ workspace_create(NULL, prev_workspace_name);
+ }
+
+ if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name)
+ && active_ws != workspace)) {
+ free(prev_workspace_name);
+ prev_workspace_name = malloc(strlen(active_ws->name) + 1);
+ if (!prev_workspace_name) {
+ wlr_log(L_ERROR, "Unable to allocate previous workspace name");
+ return false;
+ }
+ strcpy(prev_workspace_name, active_ws->name);
+ }
+
+ // TODO: Deal with sticky containers
+
+ wlr_log(L_DEBUG, "Switching to workspace %p:%s",
+ workspace, workspace->name);
+ struct sway_container *next = seat_get_focus_inactive(seat, workspace);
+ if (next == NULL) {
+ next = workspace;
+ }
+ seat_set_focus(seat, next);
+ struct sway_container *output = container_parent(workspace, C_OUTPUT);
+ arrange_windows(output, -1, -1);
+ return true;
+}
+
+bool workspace_is_visible(struct sway_container *ws) {
+ struct sway_container *output = container_parent(ws, C_OUTPUT);
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct sway_container *focus = seat_get_focus_inactive(seat, output);
+ if (focus->type != C_WORKSPACE) {
+ focus = container_parent(focus, C_WORKSPACE);
+ }
+ return focus == ws;
+}
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 @@
-#define _XOPEN_SOURCE 500
-#include <stdlib.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <ctype.h>
-#include <wlc/wlc.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/types.h>
-#include "sway/ipc-server.h"
-#include "sway/extensions.h"
-#include "sway/workspace.h"
-#include "sway/layout.h"
-#include "sway/container.h"
-#include "sway/handlers.h"
-#include "sway/config.h"
-#include "sway/focus.h"
-#include "stringop.h"
-#include "util.h"
-#include "list.h"
-#include "log.h"
-#include "ipc.h"
-
-char *prev_workspace_name = NULL;
-struct workspace_by_number_data {
- int len;
- const char *cset;
- const char *name;
-};
-
-static bool workspace_valid_on_output(const char *output_name, const char *ws_name) {
- int i;
- for (i = 0; i < config->workspace_outputs->length; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->workspace, ws_name) == 0) {
- if (strcasecmp(wso->output, output_name) != 0) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-char *workspace_next_name(const char *output_name) {
- sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name);
- int i;
- int l = 1;
- // Scan all workspace bindings to find the next available workspace name,
- // if none are found/available then default to a number
- struct sway_mode *mode = config->current_mode;
-
- int order = INT_MAX;
- char *target = NULL;
- for (i = 0; i < mode->bindings->length; ++i) {
- struct sway_binding *binding = mode->bindings->items[i];
- char *cmdlist = strdup(binding->command);
- char *dup = cmdlist;
- char *name = NULL;
-
- // workspace n
- char *cmd = argsep(&cmdlist, " ");
- if (cmdlist) {
- name = argsep(&cmdlist, ",;");
- }
-
- if (strcmp("workspace", cmd) == 0 && name) {
- sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
- char *_target = strdup(name);
- strip_quotes(_target);
- while (isspace(*_target))
- _target++;
-
- // Make sure that the command references an actual workspace
- // not a command about workspaces
- if (strcmp(_target, "next") == 0 ||
- strcmp(_target, "prev") == 0 ||
- strcmp(_target, "next_on_output") == 0 ||
- strcmp(_target, "prev_on_output") == 0 ||
- strcmp(_target, "number") == 0 ||
- strcmp(_target, "back_and_forth") == 0 ||
- strcmp(_target, "current") == 0)
- {
- free(_target);
- free(dup);
- continue;
- }
-
- // Make sure that the workspace doesn't already exist
- if (workspace_by_name(_target)) {
- free(_target);
- free(dup);
- continue;
- }
-
- // make sure that the workspace can appear on the given
- // output
- if (!workspace_valid_on_output(output_name, _target)) {
- free(_target);
- free(dup);
- continue;
- }
-
- if (binding->order < order) {
- order = binding->order;
- free(target);
- target = _target;
- sway_log(L_DEBUG, "Workspace: Found free name %s", _target);
- }
- }
- free(dup);
- }
- if (target != NULL) {
- return target;
- }
- // As a fall back, get the current number of active workspaces
- // and return that + 1 for the next workspace's name
- int ws_num = root_container.children->length;
- if (ws_num >= 10) {
- l = 2;
- } else if (ws_num >= 100) {
- l = 3;
- }
- char *name = malloc(l + 1);
- if (!name) {
- sway_log(L_ERROR, "Could not allocate workspace name");
- return NULL;
- }
- sprintf(name, "%d", ws_num++);
- return name;
-}
-
-swayc_t *workspace_create(const char* name) {
- swayc_t *parent;
- // Search for workspace<->output pair
- int i, e = config->workspace_outputs->length;
- for (i = 0; i < e; ++i) {
- struct workspace_output *wso = config->workspace_outputs->items[i];
- if (strcasecmp(wso->workspace, name) == 0)
- {
- // Find output to use if it exists
- e = root_container.children->length;
- for (i = 0; i < e; ++i) {
- parent = root_container.children->items[i];
- if (strcmp(parent->name, wso->output) == 0) {
- return new_workspace(parent, name);
- }
- }
- break;
- }
- }
- // Otherwise create a new one
- parent = get_focused_container(&root_container);
- parent = swayc_parent_by_type(parent, C_OUTPUT);
- return new_workspace(parent, name);
-}
-
-static bool _workspace_by_name(swayc_t *view, void *data) {
- return (view->type == C_WORKSPACE) &&
- (strcasecmp(view->name, (char *) data) == 0);
-}
-
-swayc_t *workspace_by_name(const char* name) {
- if (strcmp(name, "prev") == 0) {
- return workspace_prev();
- }
- else if (strcmp(name, "prev_on_output") == 0) {
- return workspace_output_prev();
- }
- else if (strcmp(name, "next") == 0) {
- return workspace_next();
- }
- else if (strcmp(name, "next_on_output") == 0) {
- return workspace_output_next();
- }
- else if (strcmp(name, "current") == 0) {
- return swayc_active_workspace();
- }
- else {
- return swayc_by_test(&root_container, _workspace_by_name, (void *) name);
- }
-}
-
-static bool _workspace_by_number(swayc_t *view, void *data) {
- if (view->type != C_WORKSPACE) {
- return false;
- }
- struct workspace_by_number_data *wbnd = data;
- int a = strspn(view->name, wbnd->cset);
- return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
-}
-swayc_t *workspace_by_number(const char* name) {
- struct workspace_by_number_data wbnd = {0, "1234567890", name};
- wbnd.len = strspn(name, wbnd.cset);
- if (wbnd.len <= 0) {
- return NULL;
- }
- return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd);
-}
-
-/**
- * Get the previous or next workspace on the specified output.
- * Wraps around at the end and beginning.
- * If next is false, the previous workspace is returned, otherwise the next one is returned.
- */
-swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
- if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) {
- return NULL;
- }
-
- int i;
- for (i = 0; i < output->children->length; i++) {
- if (output->children->items[i] == output->focused) {
- return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)];
- }
- }
-
- // Doesn't happen, at worst the for loop returns the previously active workspace
- return NULL;
-}
-
-/**
- * Get the previous or next workspace. If the first/last workspace on an output is active,
- * proceed to the previous/next output's previous/next workspace.
- * If next is false, the previous workspace is returned, otherwise the next one is returned.
- */
-swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) {
- if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) {
- return NULL;
- }
-
- swayc_t *current_output = workspace->parent;
- int offset = next ? 1 : -1;
- int start = next ? 0 : 1;
- int end = next ? (current_output->children->length) - 1 : current_output->children->length;
- int i;
- for (i = start; i < end; i++) {
- if (current_output->children->items[i] == workspace) {
- return current_output->children->items[i + offset];
- }
- }
-
- // Given workspace is the first/last on the output, jump to the previous/next output
- int num_outputs = root_container.children->length;
- for (i = 0; i < num_outputs; i++) {
- if (root_container.children->items[i] == current_output) {
- swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)];
- return workspace_output_prev_next_impl(next_output, next);
- }
- }
-
- // Doesn't happen, at worst the for loop returns the previously active workspace on the active output
- return NULL;
-}
-
-swayc_t *workspace_output_next() {
- return workspace_output_prev_next_impl(swayc_active_output(), true);
-}
-
-swayc_t *workspace_next() {
- return workspace_prev_next_impl(swayc_active_workspace(), true);
-}
-
-swayc_t *workspace_output_prev() {
- return workspace_output_prev_next_impl(swayc_active_output(), false);
-}
-
-swayc_t *workspace_prev() {
- return workspace_prev_next_impl(swayc_active_workspace(), false);
-}
-
-bool workspace_switch(swayc_t *workspace) {
- if (!workspace) {
- return false;
- }
- swayc_t *active_ws = swayc_active_workspace();
- if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) {
- swayc_t *new_ws = workspace_by_name(prev_workspace_name);
- workspace = new_ws ? new_ws : workspace_create(prev_workspace_name);
- }
-
- if (!prev_workspace_name
- || (strcmp(prev_workspace_name, active_ws->name)
- && active_ws != workspace)) {
- free(prev_workspace_name);
- prev_workspace_name = malloc(strlen(active_ws->name) + 1);
- if (!prev_workspace_name) {
- sway_log(L_ERROR, "Unable to allocate previous workspace name");
- return false;
- }
- strcpy(prev_workspace_name, active_ws->name);
- }
-
- // move sticky containers
- if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) {
- // don't change list while traversing it, use intermediate list instead
- list_t *stickies = create_list();
- for (int i = 0; i < active_ws->floating->length; i++) {
- swayc_t *cont = active_ws->floating->items[i];
- if (cont->sticky) {
- list_add(stickies, cont);
- }
- }
- for (int i = 0; i < stickies->length; i++) {
- swayc_t *cont = stickies->items[i];
- sway_log(L_DEBUG, "Moving sticky container %p to %p:%s",
- cont, workspace, workspace->name);
- swayc_t *parent = remove_child(cont);
- add_floating(workspace, cont);
- // Destroy old container if we need to
- destroy_container(parent);
- }
- list_free(stickies);
- }
- sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
- if (!set_focused_container(get_focused_view(workspace))) {
- return false;
- }
- swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
- arrange_backgrounds();
- arrange_windows(output, -1, -1);
- return true;
-}
-
-swayc_t *workspace_for_pid(pid_t pid) {
- int i;
- swayc_t *ws = NULL;
- struct pid_workspace *pw = NULL;
-
- sway_log(L_DEBUG, "looking for workspace for pid %d", pid);
-
- // leaving this here as it's useful for debugging
- // sway_log(L_DEBUG, "all pid_workspaces");
- // for (int k = 0; k < config->pid_workspaces->length; k++) {
- // pw = config->pid_workspaces->items[k];
- // sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added);
- // }
-
- do {
- for (i = 0; i < config->pid_workspaces->length; i++) {
- pw = config->pid_workspaces->items[i];
- pid_t *pw_pid = pw->pid;
-
- if (pid == *pw_pid) {
- sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace);
- break; // out of for loop
- }
-
- pw = NULL;
- }
-
- if (pw) {
- break; // out of do-while loop
- }
-
- pid = get_parent_pid(pid);
- // no sense in looking for matches for pid 0.
- // also, if pid == getpid(), that is the compositor's
- // pid, which definitely isn't helpful
- } while (pid > 0 && pid != getpid());
-
- if (pw) {
- ws = workspace_by_name(pw->workspace);
-
- if (!ws) {
- sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid);
- ws = workspace_create(pw->workspace);
- }
-
- list_del(config->pid_workspaces, i);
- }
-
- return ws;
-}
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 @@
-include_directories(
- ${PROTOCOLS_INCLUDE_DIRS}
- ${WAYLAND_INCLUDE_DIR}
- ${CAIRO_INCLUDE_DIRS}
- ${PANGO_INCLUDE_DIRS}
- ${JSONC_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
- ${DBUS_INCLUDE_DIRS}
-)
-if (ENABLE_TRAY)
- file(GLOB tray
- tray/*.c
- )
-endif()
-
-add_executable(swaybar
- main.c
- config.c
- render.c
- bar.c
- status_line.c
- ipc.c
- event_loop.c
- ${tray}
-)
-
-target_link_libraries(swaybar
- sway-common
- sway-wayland
- ${WAYLAND_CLIENT_LIBRARIES}
- ${WAYLAND_CURSOR_LIBRARIES}
- ${CAIRO_LIBRARIES}
- ${PANGO_LIBRARIES}
- ${JSONC_LIBRARIES}
- ${DBUS_LIBRARIES}
-)
-
-if (WITH_GDK_PIXBUF)
- include_directories(
- ${GDK_PIXBUF_INCLUDE_DIRS}
- )
-endif()
-
-target_link_libraries(swaybar rt)
-
-install(
- TARGETS swaybar
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
-)
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 @@
#define _XOPEN_SOURCE 500
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
#include <sys/wait.h>
-#include <signal.h>
-#include <poll.h>
+#include <unistd.h>
+#include <wayland-client.h>
+#include <wayland-cursor.h>
+#include <wlr/util/log.h>
#ifdef __FreeBSD__
#include <dev/evdev/input-event-codes.h>
#else
#include <linux/input-event-codes.h>
#endif
-#ifdef ENABLE_TRAY
-#include <dbus/dbus.h>
-#include "swaybar/tray/sni_watcher.h"
-#include "swaybar/tray/tray.h"
-#include "swaybar/tray/sni.h"
-#endif
-#include "swaybar/ipc.h"
#include "swaybar/render.h"
#include "swaybar/config.h"
-#include "swaybar/status_line.h"
#include "swaybar/event_loop.h"
+#include "swaybar/status_line.h"
#include "swaybar/bar.h"
+#include "swaybar/ipc.h"
#include "ipc-client.h"
#include "list.h"
#include "log.h"
+#include "pango.h"
+#include "pool-buffer.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
-static void bar_init(struct bar *bar) {
+static void bar_init(struct swaybar *bar) {
bar->config = init_config();
- bar->status = init_status_line();
- bar->outputs = create_list();
+ wl_list_init(&bar->outputs);
}
-static void spawn_status_cmd_proc(struct bar *bar) {
- if (bar->config->status_command) {
- int pipe_read_fd[2];
- int pipe_write_fd[2];
-
- if (pipe(pipe_read_fd) != 0) {
- sway_log(L_ERROR, "Unable to create pipes for status_command fork");
- return;
- }
- if (pipe(pipe_write_fd) != 0) {
- sway_log(L_ERROR, "Unable to create pipe for status_command fork (write)");
- close(pipe_read_fd[0]);
- close(pipe_read_fd[1]);
- return;
- }
-
- bar->status_command_pid = fork();
- if (bar->status_command_pid == 0) {
- close(pipe_read_fd[0]);
- dup2(pipe_read_fd[1], STDOUT_FILENO);
- close(pipe_read_fd[1]);
-
- dup2(pipe_write_fd[0], STDIN_FILENO);
- close(pipe_write_fd[0]);
- close(pipe_write_fd[1]);
-
- char *const cmd[] = {
- "sh",
- "-c",
- bar->config->status_command,
- NULL,
- };
- execvp(cmd[0], cmd);
- return;
+static void swaybar_output_free(struct swaybar_output *output) {
+ if (!output) {
+ return;
+ }
+ wlr_log(L_DEBUG, "Removing output %s", output->name);
+ zwlr_layer_surface_v1_destroy(output->layer_surface);
+ wl_surface_destroy(output->surface);
+ wl_output_destroy(output->output);
+ destroy_buffer(&output->buffers[0]);
+ destroy_buffer(&output->buffers[1]);
+ struct swaybar_workspace *ws, *ws_tmp;
+ wl_list_for_each_safe(ws, ws_tmp, &output->workspaces, link) {
+ wl_list_remove(&ws->link);
+ free(ws->name);
+ free(ws);
+ }
+ struct swaybar_hotspot *hotspot, *hotspot_tmp;
+ wl_list_for_each_safe(hotspot, hotspot_tmp, &output->hotspots, link) {
+ if (hotspot->destroy) {
+ hotspot->destroy(hotspot->data);
}
-
- close(pipe_read_fd[1]);
- bar->status_read_fd = pipe_read_fd[0];
- fcntl(bar->status_read_fd, F_SETFL, O_NONBLOCK);
-
- close(pipe_write_fd[0]);
- bar->status_write_fd = pipe_write_fd[1];
- fcntl(bar->status_write_fd, F_SETFL, O_NONBLOCK);
+ free(hotspot);
}
+ wl_list_remove(&output->link);
+ free(output->name);
+ free(output);
}
-struct output *new_output(const char *name) {
- struct output *output = malloc(sizeof(struct output));
- output->name = strdup(name);
- output->window = NULL;
- output->registry = NULL;
- output->workspaces = create_list();
-#ifdef ENABLE_TRAY
- output->items = create_list();
-#endif
- return output;
+static void layer_surface_configure(void *data,
+ struct zwlr_layer_surface_v1 *surface,
+ uint32_t serial, uint32_t width, uint32_t height) {
+ struct swaybar_output *output = data;
+ output->width = width;
+ output->height = height;
+ zwlr_layer_surface_v1_ack_configure(surface, serial);
+ render_frame(output->bar, output);
}
-static void mouse_button_notify(struct window *window, int x, int y,
- uint32_t button, uint32_t state_w) {
- sway_log(L_DEBUG, "Mouse button %d clicked at %d %d %d", button, x, y, state_w);
- if (!state_w) {
- return;
- }
+static void layer_surface_closed(void *_output,
+ struct zwlr_layer_surface_v1 *surface) {
+ struct swaybar_output *output = _output;
+ swaybar_output_free(output);
+}
- struct output *clicked_output = NULL;
- for (int i = 0; i < swaybar.outputs->length; i++) {
- struct output *output = swaybar.outputs->items[i];
- if (window == output->window) {
- clicked_output = output;
+struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = layer_surface_configure,
+ .closed = layer_surface_closed,
+};
+
+static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ struct swaybar *bar = data;
+ struct swaybar_pointer *pointer = &bar->pointer;
+ struct swaybar_output *output;
+ wl_list_for_each(output, &bar->outputs, link) {
+ if (output->surface == surface) {
+ pointer->current = output;
break;
}
}
-
- if (!sway_assert(clicked_output != NULL, "Got pointer event for non-existing output")) {
- return;
- }
-
- double button_x = 0.5;
- for (int i = 0; i < clicked_output->workspaces->length; i++) {
- struct workspace *workspace = clicked_output->workspaces->items[i];
- int button_width, button_height;
-
- workspace_button_size(window, workspace->name, &button_width, &button_height);
-
- button_x += button_width;
- if (x <= button_x) {
- ipc_send_workspace_command(workspace->name);
- break;
+ int max_scale = 1;
+ struct swaybar_output *_output;
+ wl_list_for_each(_output, &bar->outputs, link) {
+ if (_output->scale > max_scale) {
+ max_scale = _output->scale;
}
}
+ wl_surface_set_buffer_scale(pointer->cursor_surface, max_scale);
+ wl_surface_attach(pointer->cursor_surface,
+ wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0);
+ wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface,
+ pointer->cursor_image->hotspot_x / max_scale,
+ pointer->cursor_image->hotspot_y / max_scale);
+ wl_surface_commit(pointer->cursor_surface);
+}
- switch (button) {
- case BTN_LEFT:
- status_line_mouse_event(&swaybar, x, y, 1);
- break;
- case BTN_MIDDLE:
- status_line_mouse_event(&swaybar, x, y, 2);
- break;
- case BTN_RIGHT:
- status_line_mouse_event(&swaybar, x, y, 3);
- break;
- }
-
-#ifdef ENABLE_TRAY
- tray_mouse_event(clicked_output, x, y, button, state_w);
-#endif
+static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface) {
+ struct swaybar *bar = data;
+ bar->pointer.current = NULL;
+}
+static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ struct swaybar *bar = data;
+ bar->pointer.x = wl_fixed_to_int(surface_x);
+ bar->pointer.y = wl_fixed_to_int(surface_y);
}
-static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) {
- sway_log(L_DEBUG, "Mouse wheel scrolled %s", direction == SCROLL_UP ? "up" : "down");
-
- // If there are status blocks and click_events are enabled
- // check if the position is within the status area and if so
- // tell the status line to output the event and skip workspace
- // switching below.
- int num_blocks = swaybar.status->block_line->length;
- if (swaybar.status->click_events && num_blocks > 0) {
- struct status_block *first_block = swaybar.status->block_line->items[0];
- int x = window->pointer_input.last_x;
- int y = window->pointer_input.last_y;
- if (x > first_block->x) {
- if (direction == SCROLL_UP) {
- status_line_mouse_event(&swaybar, x, y, 4);
- } else {
- status_line_mouse_event(&swaybar, x, y, 5);
- }
- return;
+static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
+ struct swaybar *bar = data;
+ struct swaybar_pointer *pointer = &bar->pointer;
+ struct swaybar_output *output = pointer->current;
+ if (!sway_assert(output, "button with no active output")) {
+ return;
+ }
+ if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
+ return;
+ }
+ struct swaybar_hotspot *hotspot;
+ wl_list_for_each(hotspot, &output->hotspots, link) {
+ double x = pointer->x * output->scale;
+ double y = pointer->y * output->scale;
+ if (x >= hotspot->x
+ && y >= hotspot->y
+ && x < hotspot->x + hotspot->width
+ && y < hotspot->y + hotspot->height) {
+ hotspot->callback(output, pointer->x, pointer->y,
+ button, hotspot->data);
}
}
+}
- if (!swaybar.config->wrap_scroll) {
- // Find output this window lives on
- int i;
- struct output *output = NULL;
- for (i = 0; i < swaybar.outputs->length; ++i) {
- output = swaybar.outputs->items[i];
- if (output->window == window) {
- break;
- }
- }
- if (!sway_assert(i != swaybar.outputs->length, "Unknown window in scroll event")) {
- return;
- }
- int focused = -1;
- for (i = 0; i < output->workspaces->length; ++i) {
- struct workspace *ws = output->workspaces->items[i];
+static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value) {
+ struct swaybar *bar = data;
+ struct swaybar_output *output = bar->pointer.current;
+ if (!sway_assert(output, "axis with no active output")) {
+ return;
+ }
+ double amt = wl_fixed_to_double(value);
+ if (!bar->config->wrap_scroll) {
+ int i = 0;
+ struct swaybar_workspace *ws = NULL;
+ wl_list_for_each(ws, &output->workspaces, link) {
if (ws->focused) {
- focused = i;
break;
}
+ ++i;
}
- if (!sway_assert(focused != -1, "Scroll wheel event received on inactive output")) {
+ int len = wl_list_length(&output->workspaces);
+ if (!sway_assert(i != len, "axis with null workspace")) {
return;
}
- if ((focused == 0 && direction == SCROLL_UP) ||
- (focused == output->workspaces->length - 1 && direction == SCROLL_DOWN)) {
- // Do not wrap
- return;
+ if (i == 0 && amt > 0) {
+ return; // Do not wrap
+ }
+ if (i == len - 1 && amt < 0) {
+ return; // Do not wrap
}
}
- const char *workspace_name = direction == SCROLL_UP ? "prev_on_output" : "next_on_output";
- ipc_send_workspace_command(workspace_name);
+ const char *workspace_name =
+ amt < 0 ? "prev_on_output" : "next_on_output";
+ ipc_send_workspace_command(bar, workspace_name);
}
-void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) {
- /* initialize bar with default values */
- bar_init(bar);
-
- /* Initialize event loop lists */
- init_event_loop();
-
- /* connect to sway ipc */
- bar->ipc_socketfd = ipc_open_socket(socket_path);
- bar->ipc_event_socketfd = ipc_open_socket(socket_path);
-
- ipc_bar_init(bar, bar_id);
-
- int i;
- for (i = 0; i < bar->outputs->length; ++i) {
- struct output *bar_output = bar->outputs->items[i];
-
- bar_output->registry = registry_poll();
-
- if (!bar_output->registry->desktop_shell) {
- sway_abort("swaybar requires the compositor to support the desktop-shell extension.");
- }
-
- struct output_state *output = bar_output->registry->outputs->items[bar_output->idx];
-
- bar_output->window = window_setup(bar_output->registry,
- output->width / output->scale, 30, output->scale, false);
- if (!bar_output->window) {
- sway_abort("Failed to create window.");
- }
- desktop_shell_set_panel(bar_output->registry->desktop_shell,
- output->output, bar_output->window->surface);
- desktop_shell_set_panel_position(bar_output->registry->desktop_shell,
- bar->config->position);
+static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
+ // Who cares
+}
- window_make_shell(bar_output->window);
+static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis_source) {
+ // Who cares
+}
- /* set font */
- bar_output->window->font = bar->config->font;
+static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis) {
+ // Who cares
+}
- /* set mouse event callbacks */
- bar_output->window->pointer_input.notify_button = mouse_button_notify;
- bar_output->window->pointer_input.notify_scroll = mouse_scroll_notify;
+static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis, int32_t discrete) {
+ // Who cares
+}
- /* set window height */
- set_window_height(bar_output->window, bar->config->height);
+struct wl_pointer_listener pointer_listener = {
+ .enter = wl_pointer_enter,
+ .leave = wl_pointer_leave,
+ .motion = wl_pointer_motion,
+ .button = wl_pointer_button,
+ .axis = wl_pointer_axis,
+ .frame = wl_pointer_frame,
+ .axis_source = wl_pointer_axis_source,
+ .axis_stop = wl_pointer_axis_stop,
+ .axis_discrete = wl_pointer_axis_discrete,
+};
+
+static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
+ enum wl_seat_capability caps) {
+ struct swaybar *bar = data;
+ if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
+ bar->pointer.pointer = wl_seat_get_pointer(wl_seat);
+ wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar);
}
- /* spawn status command */
- spawn_status_cmd_proc(bar);
+}
-#ifdef ENABLE_TRAY
- init_tray(bar);
-#endif
+static void seat_handle_name(void *data, struct wl_seat *wl_seat,
+ const char *name) {
+ // Who cares
}
-bool dirty = true;
+const struct wl_seat_listener seat_listener = {
+ .capabilities = seat_handle_capabilities,
+ .name = seat_handle_name,
+};
-static void respond_ipc(int fd, short mask, void *_bar) {
- struct bar *bar = (struct bar *)_bar;
- sway_log(L_DEBUG, "Got IPC event.");
- dirty = handle_ipc_event(bar);
+static void output_geometry(void *data, struct wl_output *output, int32_t x,
+ int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
+ const char *make, const char *model, int32_t transform) {
+ // Who cares
}
-static void respond_command(int fd, short mask, void *_bar) {
- struct bar *bar = (struct bar *)_bar;
- dirty = handle_status_line(bar);
+static void output_mode(void *data, struct wl_output *output, uint32_t flags,
+ int32_t width, int32_t height, int32_t refresh) {
+ // Who cares
}
-static void respond_output(int fd, short mask, void *_output) {
- struct output *output = (struct output *)_output;
- if (wl_display_dispatch(output->registry->display) == -1) {
- sway_log(L_ERROR, "failed to dispatch wl: %d", errno);
+static void output_done(void *data, struct wl_output *output) {
+ // Who cares
+}
+
+static void output_scale(void *data, struct wl_output *wl_output,
+ int32_t factor) {
+ struct swaybar_output *output = data;
+ output->scale = factor;
+ if (output->surface) {
+ render_frame(output->bar, output);
}
}
-void bar_run(struct bar *bar) {
- add_event(bar->ipc_event_socketfd, POLLIN, respond_ipc, bar);
- add_event(bar->status_read_fd, POLLIN, respond_command, bar);
+struct wl_output_listener output_listener = {
+ .geometry = output_geometry,
+ .mode = output_mode,
+ .done = output_done,
+ .scale = output_scale,
+};
- int i;
- for (i = 0; i < bar->outputs->length; ++i) {
- struct output *output = bar->outputs->items[i];
- add_event(wl_display_get_fd(output->registry->display),
- POLLIN, respond_output, output);
+static bool bar_uses_output(struct swaybar *bar, size_t output_index) {
+ if (bar->config->all_outputs) {
+ return true;
}
-
- while (1) {
- if (dirty) {
- int i;
- for (i = 0; i < bar->outputs->length; ++i) {
- struct output *output = bar->outputs->items[i];
- if (window_prerender(output->window) && output->window->cairo) {
- render(output, bar->config, bar->status);
- window_render(output->window);
- wl_display_flush(output->registry->display);
- }
- }
+ struct config_output *coutput;
+ wl_list_for_each(coutput, &bar->config->outputs, link) {
+ if (coutput->index == output_index) {
+ return true;
}
+ }
+ return false;
+}
- dirty = false;
-
- event_loop_poll();
-#ifdef ENABLE_TRAY
- dispatch_dbus();
-#endif
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ struct swaybar *bar = data;
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ bar->compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 3);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ bar->seat = wl_registry_bind(registry, name,
+ &wl_seat_interface, 1);
+ wl_seat_add_listener(bar->seat, &seat_listener, bar);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ bar->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ static size_t output_index = 0;
+ if (bar_uses_output(bar, output_index)) {
+ struct swaybar_output *output =
+ calloc(1, sizeof(struct swaybar_output));
+ output->bar = bar;
+ output->output = wl_registry_bind(registry, name,
+ &wl_output_interface, 3);
+ wl_output_add_listener(output->output, &output_listener, output);
+ output->scale = 1;
+ output->index = output_index;
+ output->wl_name = name;
+ wl_list_init(&output->workspaces);
+ wl_list_init(&output->hotspots);
+ wl_list_insert(&bar->outputs, &output->link);
+ }
+ ++output_index;
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ bar->layer_shell = wl_registry_bind(
+ registry, name, &zwlr_layer_shell_v1_interface, 1);
}
}
-void free_workspaces(list_t *workspaces) {
- int i;
- for (i = 0; i < workspaces->length; ++i) {
- struct workspace *ws = workspaces->items[i];
- free(ws->name);
- free(ws);
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ struct swaybar *bar = data;
+ struct swaybar_output *output, *tmp;
+ wl_list_for_each_safe(output, tmp, &bar->outputs, link) {
+ if (output->wl_name == name) {
+ swaybar_output_free(output);
+ break;
+ }
}
- list_free(workspaces);
}
-static void free_output(struct output *output) {
- window_teardown(output->window);
- if (output->registry) {
- registry_teardown(output->registry);
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
+
+static void render_all_frames(struct swaybar *bar) {
+ struct swaybar_output *output;
+ wl_list_for_each(output, &bar->outputs, link) {
+ render_frame(bar, output);
}
+}
- free(output->name);
+void bar_setup(struct swaybar *bar,
+ const char *socket_path, const char *bar_id) {
+ bar_init(bar);
+ init_event_loop();
- if (output->workspaces) {
- free_workspaces(output->workspaces);
+ bar->ipc_socketfd = ipc_open_socket(socket_path);
+ bar->ipc_event_socketfd = ipc_open_socket(socket_path);
+ ipc_initialize(bar, bar_id);
+ if (bar->config->status_command) {
+ bar->status = status_line_init(bar->config->status_command);
}
- free(output);
-}
+ bar->display = wl_display_connect(NULL);
+ assert(bar->display);
-static void free_outputs(list_t *outputs) {
- int i;
- for (i = 0; i < outputs->length; ++i) {
- free_output(outputs->items[i]);
+ struct wl_registry *registry = wl_display_get_registry(bar->display);
+ wl_registry_add_listener(registry, &registry_listener, bar);
+ wl_display_roundtrip(bar->display);
+ assert(bar->compositor && bar->layer_shell && bar->shm);
+ wl_display_roundtrip(bar->display);
+
+ struct swaybar_pointer *pointer = &bar->pointer;
+
+ int max_scale = 1;
+ struct swaybar_output *output;
+ wl_list_for_each(output, &bar->outputs, link) {
+ if (output->scale > max_scale) {
+ max_scale = output->scale;
+ }
}
- list_free(outputs);
-}
-static void terminate_status_command(pid_t pid) {
- if (pid) {
- // terminate status_command process
- int ret = kill(pid, SIGTERM);
- if (ret != 0) {
- sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid);
- } else {
- int status;
- waitpid(pid, &status, 0);
+ pointer->cursor_theme = wl_cursor_theme_load(
+ NULL, 24 * max_scale, bar->shm);
+ assert(pointer->cursor_theme);
+ struct wl_cursor *cursor;
+ cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr");
+ assert(cursor);
+ pointer->cursor_image = cursor->images[0];
+ pointer->cursor_surface = wl_compositor_create_surface(bar->compositor);
+ assert(pointer->cursor_surface);
+
+ wl_list_for_each(output, &bar->outputs, link) {
+ struct config_output *coutput;
+ wl_list_for_each(coutput, &bar->config->outputs, link) {
+ if (coutput->index == output->index) {
+ output->name = strdup(coutput->name);
+ break;
+ }
}
+ output->surface = wl_compositor_create_surface(bar->compositor);
+ assert(output->surface);
+ output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
+ bar->layer_shell, output->surface, output->output,
+ ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
+ assert(output->layer_surface);
+ zwlr_layer_surface_v1_add_listener(output->layer_surface,
+ &layer_surface_listener, output);
+ zwlr_layer_surface_v1_set_anchor(output->layer_surface,
+ bar->config->position);
}
+ ipc_get_workspaces(bar);
+ render_all_frames(bar);
}
-void bar_teardown(struct bar *bar) {
- if (bar->config) {
- free_config(bar->config);
+static void display_in(int fd, short mask, void *_bar) {
+ struct swaybar *bar = (struct swaybar *)_bar;
+ if (wl_display_dispatch(bar->display) == -1) {
+ bar_teardown(bar);
+ exit(0);
}
+}
- if (bar->outputs) {
- free_outputs(bar->outputs);
+static void ipc_in(int fd, short mask, void *_bar) {
+ struct swaybar *bar = (struct swaybar *)_bar;
+ if (handle_ipc_readable(bar)) {
+ render_all_frames(bar);
}
+}
- if (bar->status) {
- free_status_line(bar->status);
+static void status_in(int fd, short mask, void *_bar) {
+ struct swaybar *bar = (struct swaybar *)_bar;
+ if (status_handle_readable(bar->status)) {
+ render_all_frames(bar);
}
+}
- /* close sockets/pipes */
- if (bar->status_read_fd) {
- close(bar->status_read_fd);
+void bar_run(struct swaybar *bar) {
+ add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar);
+ add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar);
+ if (bar->status) {
+ add_event(bar->status->read_fd, POLLIN, status_in, bar);
}
-
- if (bar->status_write_fd) {
- close(bar->status_write_fd);
+ while (1) {
+ event_loop_poll();
}
+}
- if (bar->ipc_socketfd) {
- close(bar->ipc_socketfd);
+static void free_outputs(struct wl_list *list) {
+ struct swaybar_output *output, *tmp;
+ wl_list_for_each_safe(output, tmp, list, link) {
+ wl_list_remove(&output->link);
+ free(output->name);
+ free(output);
}
+}
- if (bar->ipc_event_socketfd) {
- close(bar->ipc_event_socketfd);
+void bar_teardown(struct swaybar *bar) {
+ free_outputs(&bar->outputs);
+ if (bar->config) {
+ free_config(bar->config);
+ }
+ close(bar->ipc_event_socketfd);
+ close(bar->ipc_socketfd);
+ if (bar->status) {
+ status_line_free(bar->status);
}
-
- /* terminate status command process */
- terminate_status_command(bar->status_command_pid);
}
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 @@
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <string.h>
-#include "wayland-desktop-shell-client-protocol.h"
-#include "log.h"
#include "swaybar/config.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
uint32_t parse_position(const char *position) {
+ uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
+ uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if (strcmp("top", position) == 0) {
- return DESKTOP_SHELL_PANEL_POSITION_TOP;
+ return ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | horiz;
} else if (strcmp("bottom", position) == 0) {
- return DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
+ return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
} else if (strcmp("left", position) == 0) {
- return DESKTOP_SHELL_PANEL_POSITION_LEFT;
+ return ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | vert;
} else if (strcmp("right", position) == 0) {
- return DESKTOP_SHELL_PANEL_POSITION_RIGHT;
+ return ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | vert;
} else {
- return DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
+ return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
}
}
-char *parse_font(const char *font) {
- char *new_font = NULL;
- if (strncmp("pango:", font, 6) == 0) {
- font += 6;
- }
-
- new_font = strdup(font);
-
- return new_font;
-}
-
-struct config *init_config() {
- struct config *config = calloc(1, sizeof(struct config));
+struct swaybar_config *init_config() {
+ struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config));
config->status_command = NULL;
config->pango_markup = false;
- config->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
+ config->position = parse_position("bottom");
config->font = strdup("monospace 10");
config->mode = NULL;
config->sep_symbol = NULL;
@@ -42,27 +34,16 @@ struct config *init_config() {
config->binding_mode_indicator = true;
config->wrap_scroll = false;
config->workspace_buttons = true;
- config->all_outputs = false;
- config->outputs = create_list();
+ wl_list_init(&config->outputs);
/* height */
config->height = 0;
-#ifdef ENABLE_TRAY
- config->tray_output = NULL;
- config->icon_theme = NULL;
- config->tray_padding = 2;
- /**
- * These constants are used by wayland and are defined in
- * linux/input-event-codes.h
- */
- config->activate_button = 0x110; /* BTN_LEFT */
- config->context_button = 0x111; /* BTN_RIGHT */
-#endif
-
/* colors */
config->colors.background = 0x000000FF;
+ config->colors.focused_background = 0x000000FF;
config->colors.statusline = 0xFFFFFFFF;
+ config->colors.focused_statusline = 0xFFFFFFFF;
config->colors.separator = 0x666666FF;
config->colors.focused_workspace.border = 0x4C7899FF;
@@ -88,7 +69,7 @@ struct config *init_config() {
return config;
}
-void free_config(struct config *config) {
+void free_config(struct swaybar_config *config) {
free(config->status_command);
free(config->font);
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 @@
#include <string.h>
#include <strings.h>
#include <poll.h>
-#include "swaybar/bar.h"
+#include <time.h>
#include "swaybar/event_loop.h"
#include "list.h"
-#include "log.h"
struct event_item {
- void(*cb)(int fd, short mask, void *data);
+ void (*cb)(int fd, short mask, void *data);
void *data;
};
struct timer_item {
timer_t timer;
- void(*cb)(timer_t timer, void *data);
+ void (*cb)(timer_t timer, void *data);
void *data;
};
@@ -138,7 +137,8 @@ void event_loop_poll() {
void init_event_loop() {
event_loop.fds.length = 0;
event_loop.fds.capacity = 10;
- event_loop.fds.items = malloc(event_loop.fds.capacity * sizeof(struct pollfd));
+ event_loop.fds.items = malloc(
+ event_loop.fds.capacity * sizeof(struct pollfd));
event_loop.items = create_list();
event_loop.timers = create_list();
}
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <json-c/json.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include "swaybar/config.h"
+#include "swaybar/status_line.h"
+
+static void i3bar_block_free(struct i3bar_block *block) {
+ if (!block) {
+ return;
+ }
+ wl_list_remove(&block->link);
+ free(block->full_text);
+ free(block->short_text);
+ free(block->align);
+ free(block->name);
+ free(block->instance);
+ free(block->color);
+}
+
+static bool i3bar_parse_json(struct status_line *status, const char *text) {
+ struct i3bar_block *block, *tmp;
+ wl_list_for_each_safe(block, tmp, &status->blocks, link) {
+ i3bar_block_free(block);
+ }
+ json_object *results = json_tokener_parse(text);
+ if (!results) {
+ status_error(status, "[failed to parse i3bar json]");
+ return false;
+ }
+ wlr_log(L_DEBUG, "Got i3bar json: '%s'", text);
+ for (size_t i = 0; i < json_object_array_length(results); ++i) {
+ json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
+ json_object *name, *instance, *separator, *separator_block_width;
+ json_object *background, *border, *border_top, *border_bottom;
+ json_object *border_left, *border_right, *markup;
+ json_object *json = json_object_array_get_idx(results, i);
+ if (!json) {
+ continue;
+ }
+ json_object_object_get_ex(json, "full_text", &full_text);
+ json_object_object_get_ex(json, "short_text", &short_text);
+ json_object_object_get_ex(json, "color", &color);
+ json_object_object_get_ex(json, "min_width", &min_width);
+ json_object_object_get_ex(json, "align", &align);
+ json_object_object_get_ex(json, "urgent", &urgent);
+ json_object_object_get_ex(json, "name", &name);
+ json_object_object_get_ex(json, "instance", &instance);
+ json_object_object_get_ex(json, "markup", &markup);
+ json_object_object_get_ex(json, "separator", &separator);
+ json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
+ json_object_object_get_ex(json, "background", &background);
+ json_object_object_get_ex(json, "border", &border);
+ json_object_object_get_ex(json, "border_top", &border_top);
+ json_object_object_get_ex(json, "border_bottom", &border_bottom);
+ json_object_object_get_ex(json, "border_left", &border_left);
+ json_object_object_get_ex(json, "border_right", &border_right);
+
+ struct i3bar_block *block = calloc(1, sizeof(struct i3bar_block));
+ block->full_text = full_text ?
+ strdup(json_object_get_string(full_text)) : NULL;
+ block->short_text = short_text ?
+ strdup(json_object_get_string(short_text)) : NULL;
+ if (color) {
+ block->color = malloc(sizeof(uint32_t));
+ *block->color = parse_color(json_object_get_string(color));
+ }
+ if (min_width) {
+ json_type type = json_object_get_type(min_width);
+ if (type == json_type_int) {
+ block->min_width = json_object_get_int(min_width);
+ } else if (type == json_type_string) {
+ /* the width will be calculated when rendering */
+ block->min_width = 0;
+ }
+ }
+ block->align = strdup(align ? json_object_get_string(align) : "left");
+ block->urgent = urgent ? json_object_get_int(urgent) : false;
+ block->name = name ? strdup(json_object_get_string(name)) : NULL;
+ block->instance = instance ?
+ strdup(json_object_get_string(instance)) : NULL;
+ if (markup) {
+ block->markup = false;
+ const char *markup_str = json_object_get_string(markup);
+ if (strcmp(markup_str, "pango") == 0) {
+ block->markup = true;
+ }
+ }
+ block->separator = separator ? json_object_get_int(separator) : true;
+ block->separator_block_width = separator_block_width ?
+ json_object_get_int(separator_block_width) : 9;
+ // Airblader features
+ block->background = background ?
+ parse_color(json_object_get_string(background)) : 0;
+ block->border = border ?
+ parse_color(json_object_get_string(border)) : 0;
+ block->border_top = border_top ? json_object_get_int(border_top) : 1;
+ block->border_bottom = border_bottom ?
+ json_object_get_int(border_bottom) : 1;
+ block->border_left = border_left ? json_object_get_int(border_left) : 1;
+ block->border_right = border_right ?
+ json_object_get_int(border_right) : 1;
+ wl_list_insert(&status->blocks, &block->link);
+ }
+ return true;
+}
+
+bool i3bar_handle_readable(struct status_line *status) {
+ struct i3bar_protocol_state *state = &status->i3bar_state;
+
+ char *cur = &state->buffer[state->buffer_index];
+ ssize_t n = read(status->read_fd, cur,
+ state->buffer_size - state->buffer_index);
+ if (n == -1) {
+ status_error(status, "[failed to read from status command]");
+ return false;
+ }
+
+ if (n == (ssize_t)(state->buffer_size - state->buffer_index)) {
+ state->buffer_size = state->buffer_size * 2;
+ char *new_buffer = realloc(state->buffer, state->buffer_size);
+ if (!new_buffer) {
+ free(state->buffer);
+ status_error(status, "[failed to allocate buffer]");
+ return true;
+ }
+ state->current_node += new_buffer - state->buffer;
+ cur += new_buffer - state->buffer;
+ state->buffer = new_buffer;
+ }
+
+ cur[n] = '\0';
+ bool redraw = false;
+ while (*cur) {
+ if (state->nodes[state->depth] == JSON_NODE_STRING) {
+ if (!state->escape && *cur == '"') {
+ --state->depth;
+ }
+ state->escape = !state->escape && *cur == '\\';
+ } else {
+ switch (*cur) {
+ case '[':
+ ++state->depth;
+ if (state->depth >
+ sizeof(state->nodes) / sizeof(state->nodes[0])) {
+ status_error(status, "[i3bar json too deep]");
+ return false;
+ }
+ state->nodes[state->depth] = JSON_NODE_ARRAY;
+ if (state->depth == 1) {
+ state->current_node = cur;
+ }
+ break;
+ case ']':
+ if (state->nodes[state->depth] != JSON_NODE_ARRAY) {
+ status_error(status, "[failed to parse i3bar json]");
+ return false;
+ }
+ --state->depth;
+ if (state->depth == 0) {
+ // cur[1] is valid since cur[0] != '\0'
+ char p = cur[1];
+ cur[1] = '\0';
+ redraw = i3bar_parse_json(
+ status, state->current_node) || redraw;
+ cur[1] = p;
+ memmove(state->buffer, cur,
+ state->buffer_size - (cur - state->buffer));
+ cur = state->buffer;
+ state->current_node = cur + 1;
+ }
+ break;
+ case '"':
+ ++state->depth;
+ if (state->depth >
+ sizeof(state->nodes) / sizeof(state->nodes[0])) {
+ status_error(status, "[i3bar json too deep]");
+ return false;
+ }
+ state->nodes[state->depth] = JSON_NODE_STRING;
+ break;
+ }
+ }
+ ++cur;
+ }
+ state->buffer_index = cur - state->buffer;
+ return redraw;
+}
+
+void i3bar_block_send_click(struct status_line *status,
+ struct i3bar_block *block, int x, int y, uint32_t button) {
+ wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");
+ if (!block->name || !status->i3bar_state.click_events) {
+ return;
+ }
+
+ struct json_object *event_json = json_object_new_object();
+ json_object_object_add(event_json, "name",
+ json_object_new_string(block->name));
+ if (block->instance) {
+ json_object_object_add(event_json, "instance",
+ json_object_new_string(block->instance));
+ }
+
+ json_object_object_add(event_json, "button", json_object_new_int(button));
+ json_object_object_add(event_json, "x", json_object_new_int(x));
+ json_object_object_add(event_json, "y", json_object_new_int(y));
+ if (dprintf(status->write_fd, "%s\n",
+ json_object_to_json_string(event_json)) < 0) {
+ status_error(status, "[failed to write click event]");
+ }
+ json_object_put(event_json);
+}
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 @@
#define _XOPEN_SOURCE 500
+#include <limits.h>
#include <string.h>
#include <strings.h>
#include <json-c/json.h>
+#include <wlr/util/log.h>
#include "swaybar/config.h"
#include "swaybar/ipc.h"
#include "ipc-client.h"
-#include "list.h"
-#include "log.h"
-
-void ipc_send_workspace_command(const char *workspace_name) {
- uint32_t size = strlen("workspace \"\"") + strlen(workspace_name) + 1;
+void ipc_send_workspace_command(struct swaybar *bar, const char *ws) {
+ const char *fmt = "workspace \"%s\"";
+ uint32_t size = snprintf(NULL, 0, fmt, ws);
char command[size];
- sprintf(command, "workspace \"%s\"", workspace_name);
+ snprintf(command, size, fmt, ws);
+ ipc_single_command(bar->ipc_socketfd, IPC_COMMAND, command, &size);
+}
- ipc_single_command(swaybar.ipc_socketfd, IPC_COMMAND, command, &size);
+char *parse_font(const char *font) {
+ char *new_font = NULL;
+ if (strncmp("pango:", font, 6) == 0) {
+ font += 6;
+ }
+ new_font = strdup(font);
+ return new_font;
}
-static void ipc_parse_config(struct config *config, const char *payload) {
+static void ipc_parse_colors(
+ struct swaybar_config *config, json_object *colors) {
+ json_object *background, *statusline, *separator;
+ json_object *focused_background, *focused_statusline, *focused_separator;
+ json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
+ json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
+ json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
+ json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
+ json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
+ json_object_object_get_ex(colors, "background", &background);
+ json_object_object_get_ex(colors, "statusline", &statusline);
+ json_object_object_get_ex(colors, "separator", &separator);
+ json_object_object_get_ex(colors, "focused_background", &focused_background);
+ json_object_object_get_ex(colors, "focused_statusline", &focused_statusline);
+ json_object_object_get_ex(colors, "focused_separator", &focused_separator);
+ json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border);
+ json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg);
+ json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text);
+ json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border);
+ json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg);
+ json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text);
+ json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border);
+ json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg);
+ json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text);
+ json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border);
+ json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg);
+ json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text);
+ json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border);
+ json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg);
+ json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text);
+ if (background) {
+ config->colors.background = parse_color(
+ json_object_get_string(background));
+ }
+ if (statusline) {
+ config->colors.statusline = parse_color(
+ json_object_get_string(statusline));
+ }
+ if (separator) {
+ config->colors.separator = parse_color(
+ json_object_get_string(separator));
+ }
+ if (focused_background) {
+ config->colors.focused_background = parse_color(
+ json_object_get_string(focused_background));
+ }
+ if (focused_statusline) {
+ config->colors.focused_statusline = parse_color(
+ json_object_get_string(focused_statusline));
+ }
+ if (focused_separator) {
+ config->colors.focused_separator = parse_color(
+ json_object_get_string(focused_separator));
+ }
+ if (focused_workspace_border) {
+ config->colors.focused_workspace.border = parse_color(
+ json_object_get_string(focused_workspace_border));
+ }
+ if (focused_workspace_bg) {
+ config->colors.focused_workspace.background = parse_color(
+ json_object_get_string(focused_workspace_bg));
+ }
+ if (focused_workspace_text) {
+ config->colors.focused_workspace.text = parse_color(
+ json_object_get_string(focused_workspace_text));
+ }
+ if (active_workspace_border) {
+ config->colors.active_workspace.border = parse_color(
+ json_object_get_string(active_workspace_border));
+ }
+ if (active_workspace_bg) {
+ config->colors.active_workspace.background = parse_color(
+ json_object_get_string(active_workspace_bg));
+ }
+ if (active_workspace_text) {
+ config->colors.active_workspace.text = parse_color(
+ json_object_get_string(active_workspace_text));
+ }
+ if (inactive_workspace_border) {
+ config->colors.inactive_workspace.border = parse_color(
+ json_object_get_string(inactive_workspace_border));
+ }
+ if (inactive_workspace_bg) {
+ config->colors.inactive_workspace.background = parse_color(
+ json_object_get_string(inactive_workspace_bg));
+ }
+ if (inactive_workspace_text) {
+ config->colors.inactive_workspace.text = parse_color(
+ json_object_get_string(inactive_workspace_text));
+ }
+ if (binding_mode_border) {
+ config->colors.binding_mode.border = parse_color(
+ json_object_get_string(binding_mode_border));
+ }
+ if (binding_mode_bg) {
+ config->colors.binding_mode.background = parse_color(
+ json_object_get_string(binding_mode_bg));
+ }
+ if (binding_mode_text) {
+ config->colors.binding_mode.text = parse_color(
+ json_object_get_string(binding_mode_text));
+ }
+}
+
+static void ipc_parse_config(
+ struct swaybar_config *config, const char *payload) {
json_object *bar_config = json_tokener_parse(payload);
json_object *markup, *mode, *hidden_bar, *position, *status_command;
json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers;
json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs;
-#ifdef ENABLE_TRAY
- json_object *tray_output, *icon_theme, *tray_padding, *activate_button, *context_button;
- json_object *secondary_button;
- json_object_object_get_ex(bar_config, "tray_output", &tray_output);
- json_object_object_get_ex(bar_config, "icon_theme", &icon_theme);
- json_object_object_get_ex(bar_config, "tray_padding", &tray_padding);
- json_object_object_get_ex(bar_config, "activate_button", &activate_button);
- json_object_object_get_ex(bar_config, "context_button", &context_button);
- json_object_object_get_ex(bar_config, "secondary_button", &secondary_button);
-#endif
json_object_object_get_ex(bar_config, "mode", &mode);
json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar);
json_object_object_get_ex(bar_config, "position", &position);
@@ -47,230 +150,100 @@ static void ipc_parse_config(struct config *config, const char *payload) {
json_object_object_get_ex(bar_config, "colors", &colors);
json_object_object_get_ex(bar_config, "outputs", &outputs);
json_object_object_get_ex(bar_config, "pango_markup", &markup);
-
if (status_command) {
free(config->status_command);
config->status_command = strdup(json_object_get_string(status_command));
}
-
if (position) {
config->position = parse_position(json_object_get_string(position));
}
-
if (font) {
free(config->font);
config->font = parse_font(json_object_get_string(font));
}
-
if (sep_symbol) {
free(config->sep_symbol);
config->sep_symbol = strdup(json_object_get_string(sep_symbol));
}
-
if (strip_workspace_numbers) {
config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers);
}
-
if (binding_mode_indicator) {
config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator);
}
-
if (wrap_scroll) {
config->wrap_scroll = json_object_get_boolean(wrap_scroll);
}
-
if (workspace_buttons) {
config->workspace_buttons = json_object_get_boolean(workspace_buttons);
}
-
if (bar_height) {
config->height = json_object_get_int(bar_height);
}
-
if (markup) {
config->pango_markup = json_object_get_boolean(markup);
}
-#ifdef ENABLE_TRAY
- if (tray_output) {
- free(config->tray_output);
- config->tray_output = strdup(json_object_get_string(tray_output));
- }
-
- if (icon_theme) {
- free(config->icon_theme);
- config->icon_theme = strdup(json_object_get_string(icon_theme));
- }
-
- if (tray_padding) {
- config->tray_padding = json_object_get_int(tray_padding);
- }
-
- if (activate_button) {
- config->activate_button = json_object_get_int(activate_button);
- }
-
- if (context_button) {
- config->context_button = json_object_get_int(context_button);
+ struct config_output *output, *tmp;
+ wl_list_for_each_safe(output, tmp, &config->outputs, link) {
+ wl_list_remove(&output->link);
+ free(output->name);
+ free(output);
}
-
- if (secondary_button) {
- config->secondary_button = json_object_get_int(secondary_button);
- }
-#endif
-
- // free previous outputs list
- int i;
- for (i = 0; i < config->outputs->length; ++i) {
- free(config->outputs->items[i]);
- }
- list_free(config->outputs);
- config->outputs = create_list();
-
if (outputs) {
int length = json_object_array_length(outputs);
- json_object *output;
- const char *output_str;
- for (i = 0; i < length; ++i) {
- output = json_object_array_get_idx(outputs, i);
- output_str = json_object_get_string(output);
- if (strcmp("*", output_str) == 0) {
+ for (int i = 0; i < length; ++i) {
+ json_object *output = json_object_array_get_idx(outputs, i);
+ const char *name = json_object_get_string(output);
+ if (strcmp("*", name) == 0) {
config->all_outputs = true;
break;
}
- list_add(config->outputs, strdup(output_str));
+ struct config_output *coutput = calloc(
+ 1, sizeof(struct config_output));
+ coutput->name = strdup(name);
+ coutput->index = SIZE_MAX;
+ wl_list_insert(&config->outputs, &coutput->link);
}
} else {
config->all_outputs = true;
}
if (colors) {
- json_object *background, *statusline, *separator;
- json_object *focused_background, *focused_statusline, *focused_separator;
- json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
- json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
- json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
- json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
- json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
- json_object_object_get_ex(colors, "background", &background);
- json_object_object_get_ex(colors, "statusline", &statusline);
- json_object_object_get_ex(colors, "separator", &separator);
- json_object_object_get_ex(colors, "focused_background", &focused_background);
- json_object_object_get_ex(colors, "focused_statusline", &focused_statusline);
- json_object_object_get_ex(colors, "focused_separator", &focused_separator);
- json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border);
- json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg);
- json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text);
- json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border);
- json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg);
- json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text);
- json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border);
- json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg);
- json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text);
- json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border);
- json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg);
- json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text);
- json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border);
- json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg);
- json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text);
- if (background) {
- config->colors.background = parse_color(json_object_get_string(background));
- }
-
- if (statusline) {
- config->colors.statusline = parse_color(json_object_get_string(statusline));
- }
-
- if (separator) {
- config->colors.separator = parse_color(json_object_get_string(separator));
- }
-
- if (focused_background) {
- config->colors.focused_background = parse_color(json_object_get_string(focused_background));
- }
-
- if (focused_statusline) {
- config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline));
- }
-
- if (focused_separator) {
- config->colors.focused_separator = parse_color(json_object_get_string(focused_separator));
- }
-
- if (focused_workspace_border) {
- config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border));
- }
-
- if (focused_workspace_bg) {
- config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg));
- }
-
- if (focused_workspace_text) {
- config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text));
- }
-
- if (active_workspace_border) {
- config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border));
- }
-
- if (active_workspace_bg) {
- config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg));
- }
-
- if (active_workspace_text) {
- config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text));
- }
-
- if (inactive_workspace_border) {
- config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border));
- }
-
- if (inactive_workspace_bg) {
- config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg));
- }
-
- if (inactive_workspace_text) {
- config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text));
- }
-
- if (binding_mode_border) {
- config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border));
- }
-
- if (binding_mode_bg) {
- config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg));
- }
-
- if (binding_mode_text) {
- config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text));
- }
+ ipc_parse_colors(config, colors);
}
json_object_put(bar_config);
}
-static void ipc_update_workspaces(struct bar *bar) {
- int i;
- for (i = 0; i < bar->outputs->length; ++i) {
- struct output *output = bar->outputs->items[i];
- if (output->workspaces) {
- free_workspaces(output->workspaces);
- }
- output->workspaces = create_list();
+static void free_workspaces(struct wl_list *list) {
+ struct swaybar_workspace *ws, *tmp;
+ wl_list_for_each_safe(ws, tmp, list, link) {
+ wl_list_remove(&ws->link);
+ free(ws->name);
+ free(ws);
}
+}
+void ipc_get_workspaces(struct swaybar *bar) {
+ bar->focused_output = NULL;
+ struct swaybar_output *output;
+ wl_list_for_each(output, &bar->outputs, link) {
+ free_workspaces(&output->workspaces);
+ output->focused = false;
+ }
uint32_t len = 0;
- char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len);
+ char *res = ipc_single_command(bar->ipc_socketfd,
+ IPC_GET_WORKSPACES, NULL, &len);
json_object *results = json_tokener_parse(res);
if (!results) {
free(res);
return;
}
-
- int length = json_object_array_length(results);
+ size_t length = json_object_array_length(results);
json_object *ws_json;
json_object *num, *name, *visible, *focused, *out, *urgent;
- for (i = 0; i < length; ++i) {
+ for (size_t i = 0; i < length; ++i) {
ws_json = json_object_array_get_idx(results, i);
json_object_object_get_ex(ws_json, "num", &num);
@@ -280,113 +253,95 @@ static void ipc_update_workspaces(struct bar *bar) {
json_object_object_get_ex(ws_json, "output", &out);
json_object_object_get_ex(ws_json, "urgent", &urgent);
- int j;
- for (j = 0; j < bar->outputs->length; ++j) {
- struct output *output = bar->outputs->items[j];
- if (strcmp(json_object_get_string(out), output->name) == 0) {
- struct workspace *ws = malloc(sizeof(struct workspace));
+ wl_list_for_each(output, &bar->outputs, link) {
+ const char *ws_output = json_object_get_string(out);
+ if (strcmp(ws_output, output->name) == 0) {
+ struct swaybar_workspace *ws =
+ calloc(1, sizeof(struct swaybar_workspace));
ws->num = json_object_get_int(num);
ws->name = strdup(json_object_get_string(name));
ws->visible = json_object_get_boolean(visible);
ws->focused = json_object_get_boolean(focused);
if (ws->focused) {
- if (bar->focused_output) {
- bar->focused_output->focused = false;
- }
- bar->focused_output = output;
output->focused = true;
}
ws->urgent = json_object_get_boolean(urgent);
- list_add(output->workspaces, ws);
+ wl_list_insert(&output->workspaces, &ws->link);
}
}
}
-
json_object_put(results);
free(res);
}
-void ipc_bar_init(struct bar *bar, const char *bar_id) {
- // Get bar config
- uint32_t len = strlen(bar_id);
- char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len);
-
- ipc_parse_config(bar->config, res);
- free(res);
-
- // Get outputs
- len = 0;
- res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len);
+static void ipc_get_outputs(struct swaybar *bar) {
+ uint32_t len = 0;
+ char *res = ipc_single_command(bar->ipc_socketfd,
+ IPC_GET_OUTPUTS, NULL, &len);
json_object *outputs = json_tokener_parse(res);
- int i;
- int length = json_object_array_length(outputs);
- json_object *output, *output_name, *output_active;
- const char *name;
- bool active;
- for (i = 0; i < length; ++i) {
- output = json_object_array_get_idx(outputs, i);
+ for (size_t i = 0; i < json_object_array_length(outputs); ++i) {
+ json_object *output = json_object_array_get_idx(outputs, i);
+ json_object *output_name, *output_active;
json_object_object_get_ex(output, "name", &output_name);
json_object_object_get_ex(output, "active", &output_active);
- name = json_object_get_string(output_name);
- active = json_object_get_boolean(output_active);
+ const char *name = json_object_get_string(output_name);
+ bool active = json_object_get_boolean(output_active);
if (!active) {
continue;
}
-
- bool use_output = false;
if (bar->config->all_outputs) {
- use_output = true;
+ struct config_output *coutput = calloc(
+ 1, sizeof(struct config_output));
+ coutput->name = strdup(name);
+ coutput->index = i;
+ wl_list_insert(&bar->config->outputs, &coutput->link);
} else {
- int j = 0;
- for (j = 0; j < bar->config->outputs->length; ++j) {
- const char *conf_name = bar->config->outputs->items[j];
- if (strcasecmp(name, conf_name) == 0) {
- use_output = true;
+ struct config_output *coutput;
+ wl_list_for_each(coutput, &bar->config->outputs, link) {
+ if (strcmp(name, coutput->name) == 0) {
+ coutput->index = i;
break;
}
}
}
-
- if (!use_output) {
- continue;
- }
-
- // add bar to the output
- struct output *bar_output = new_output(name);
- bar_output->idx = i;
- list_add(bar->outputs, bar_output);
}
- free(res);
json_object_put(outputs);
+ free(res);
+}
- const char *subscribe_json = "[ \"workspace\", \"mode\" ]";
- len = strlen(subscribe_json);
- res = ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len);
+void ipc_initialize(struct swaybar *bar, const char *bar_id) {
+ uint32_t len = strlen(bar_id);
+ char *res = ipc_single_command(bar->ipc_socketfd,
+ IPC_GET_BAR_CONFIG, bar_id, &len);
+ ipc_parse_config(bar->config, res);
free(res);
+ ipc_get_outputs(bar);
- ipc_update_workspaces(bar);
+ const char *subscribe = "[ \"workspace\", \"mode\" ]";
+ len = strlen(subscribe);
+ free(ipc_single_command(bar->ipc_event_socketfd,
+ IPC_SUBSCRIBE, subscribe, &len));
}
-bool handle_ipc_event(struct bar *bar) {
+bool handle_ipc_readable(struct swaybar *bar) {
struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);
if (!resp) {
return false;
}
switch (resp->type) {
case IPC_EVENT_WORKSPACE:
- ipc_update_workspaces(bar);
+ ipc_get_workspaces(bar);
break;
case IPC_EVENT_MODE: {
json_object *result = json_tokener_parse(resp->payload);
if (!result) {
free_ipc_response(resp);
- sway_log(L_ERROR, "failed to parse payload as json");
+ wlr_log(L_ERROR, "failed to parse payload as json");
return false;
}
- json_object *json_change;
+ json_object *json_change, *json_pango_markup;
if (json_object_object_get_ex(result, "change", &json_change)) {
const char *change = json_object_get_string(json_change);
-
free(bar->config->mode);
if (strcmp(change, "default") == 0) {
bar->config->mode = NULL;
@@ -394,9 +349,16 @@ bool handle_ipc_event(struct bar *bar) {
bar->config->mode = strdup(change);
}
} else {
- sway_log(L_ERROR, "failed to parse response");
+ wlr_log(L_ERROR, "failed to parse response");
+ json_object_put(result);
+ free_ipc_response(resp);
+ return false;
+ }
+ if (json_object_object_get_ex(result,
+ "pango_markup", &json_pango_markup)) {
+ bar->config->mode_pango_markup = json_object_get_boolean(
+ json_pango_markup);
}
-
json_object_put(result);
break;
}
@@ -404,7 +366,6 @@ bool handle_ipc_event(struct bar *bar) {
free_ipc_response(resp);
return false;
}
-
free_ipc_response(resp);
return true;
}
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 @@
#include <string.h>
#include <stdbool.h>
#include <getopt.h>
+#include <wlr/util/log.h>
#include "swaybar/bar.h"
#include "ipc-client.h"
-#include "log.h"
-/* global bar state */
-struct bar swaybar;
+static struct swaybar swaybar;
-void sway_terminate(int exit_code) {
+void sig_handler(int signal) {
bar_teardown(&swaybar);
- exit(exit_code);
+ exit(0);
}
-void sig_handler(int signal) {
+void sway_terminate(int code) {
bar_teardown(&swaybar);
- exit(0);
+ exit(code);
}
int main(int argc, char **argv) {
@@ -75,20 +74,23 @@ int main(int argc, char **argv) {
}
}
- if (!bar_id) {
- sway_abort("No bar_id passed. Provide --bar_id or let sway start swaybar");
- }
-
if (debug) {
- init_log(L_DEBUG);
+ wlr_log_init(L_DEBUG, NULL);
} else {
- init_log(L_ERROR);
+ wlr_log_init(L_ERROR, NULL);
+ }
+
+ if (!bar_id) {
+ wlr_log(L_ERROR, "No bar_id passed. "
+ "Provide --bar_id or let sway start swaybar");
+ return 1;
}
if (!socket_path) {
socket_path = get_socketpath();
if (!socket_path) {
- sway_abort("Unable to retrieve socket path");
+ wlr_log(L_ERROR, "Unable to retrieve socket path");
+ return 1;
}
}
@@ -100,9 +102,6 @@ int main(int argc, char **argv) {
free(bar_id);
bar_run(&swaybar);
-
- // gracefully shutdown swaybar and status_command
bar_teardown(&swaybar);
-
return 0;
}
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 @@
+executable(
+ 'swaybar', [
+ 'bar.c',
+ 'config.c',
+ 'event_loop.c',
+ 'i3bar.c',
+ 'ipc.c',
+ 'main.c',
+ 'render.c',
+ 'status_line.c',
+ ],
+ include_directories: [sway_inc],
+ dependencies: [
+ cairo,
+ client_protos,
+ gdk_pixbuf,
+ jsonc,
+ math,
+ pango,
+ pangocairo,
+ rt,
+ wayland_client,
+ wayland_cursor,
+ wlroots,
+ ],
+ link_with: [lib_sway_common, lib_sway_client],
+ install: true
+)
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 @@
+#define _POSIX_C_SOURCE 200809L
+#include <limits.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
-
-#include "client/cairo.h"
-#include "client/pango.h"
-#include "client/window.h"
+#include <wlr/util/log.h>
+#include "cairo.h"
+#include "pango.h"
+#include "pool-buffer.h"
+#include "swaybar/bar.h"
#include "swaybar/config.h"
-#include "swaybar/status_line.h"
+#include "swaybar/ipc.h"
#include "swaybar/render.h"
-#ifdef ENABLE_TRAY
-#include "swaybar/tray/tray.h"
-#include "swaybar/tray/sni.h"
-#endif
-#include "log.h"
-
-
-/* internal spacing */
-static int margin = 3;
-static int ws_horizontal_padding = 5;
-static double ws_vertical_padding = 1.5;
-static int ws_spacing = 1;
-
-/**
- * Renders a sharp line of any width and height.
- *
- * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
- * if the line has a width/height of one pixel, respectively.
- */
-static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
- cairo_set_source_u32(cairo, color);
+#include "swaybar/status_line.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
+
+static const int WS_HORIZONTAL_PADDING = 5;
+static const double WS_VERTICAL_PADDING = 1.5;
+static const double BORDER_WIDTH = 1;
+
+static uint32_t render_status_line_error(cairo_t *cairo,
+ struct swaybar_output *output, struct swaybar_config *config,
+ const char *error, double *x, uint32_t surface_height) {
+ if (!error) {
+ return 0;
+ }
+
+ uint32_t height = surface_height * output->scale;
+
+ cairo_set_source_u32(cairo, 0xFF0000FF);
+
+ int margin = 3 * output->scale;
+ int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
+
+ int text_width, text_height;
+ get_text_size(cairo, config->font,
+ &text_width, &text_height, output->scale, false, "%s", error);
+
+ uint32_t ideal_height = text_height + ws_vertical_padding * 2;
+ uint32_t ideal_surface_height = ideal_height / output->scale;
+ if (surface_height < ideal_surface_height) {
+ return ideal_surface_height;
+ }
+ *x -= text_width + margin;
+
+ double text_y = height / 2.0 - text_height / 2.0;
+ cairo_move_to(cairo, *x, (int)floor(text_y));
+ pango_printf(cairo, config->font, output->scale, false, "%s", error);
+ *x -= margin;
+ return surface_height;
+}
+
+static uint32_t render_status_line_text(cairo_t *cairo,
+ struct swaybar_output *output, struct swaybar_config *config,
+ const char *text, bool focused, double *x, uint32_t surface_height) {
+ if (!text) {
+ return 0;
+ }
+
+ uint32_t height = surface_height * output->scale;
+
+ cairo_set_source_u32(cairo, focused ?
+ config->colors.focused_statusline : config->colors.statusline);
+ int text_width, text_height;
+ get_text_size(cairo, config->font, &text_width, &text_height,
+ output->scale, config->pango_markup, "%s", text);
+
+ int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
+ int margin = 3 * output->scale;
+
+ uint32_t ideal_height = text_height + ws_vertical_padding * 2;
+ uint32_t ideal_surface_height = ideal_height / output->scale;
+ if (surface_height < ideal_surface_height) {
+ return ideal_surface_height;
+ }
+
+ *x -= text_width + margin;
+ double text_y = height / 2.0 - text_height / 2.0;
+ cairo_move_to(cairo, *x, (int)floor(text_y));
+ pango_printf(cairo, config->font, output->scale,
+ config->pango_markup, "%s", text);
+ *x -= margin;
+ return surface_height;
+}
+
+static void render_sharp_line(cairo_t *cairo, uint32_t color,
+ double x, double y, double width, double height) {
+ cairo_set_source_u32(cairo, color);
if (width > 1 && height > 1) {
cairo_rectangle(cairo, x, y, width, height);
cairo_fill(cairo);
@@ -39,13 +96,11 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y
height += y;
width = x;
}
-
if (height == 1) {
y += 0.5;
width += x;
height = y;
}
-
cairo_move_to(cairo, x, y);
cairo_set_line_width(cairo, 1.0);
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
}
}
-static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) {
- int width, height, sep_width;
- get_text_size(window->cairo, window->font, &width, &height,
- window->scale, block->markup, "%s", block->full_text);
+static void block_hotspot_callback(struct swaybar_output *output,
+ int x, int y, uint32_t button, void *data) {
+ struct i3bar_block *block = data;
+ struct status_line *status = output->bar->status;
+ i3bar_block_send_click(status, block, x, y, button);
+}
- int textwidth = width;
- double block_width = width;
+static uint32_t render_status_block(cairo_t *cairo,
+ struct swaybar_config *config, struct swaybar_output *output,
+ struct i3bar_block *block, double *x,
+ uint32_t surface_height, bool focused, bool edge) {
+ if (!block->full_text || !*block->full_text) {
+ return 0;
+ }
+ uint32_t height = surface_height * output->scale;
+
+ int text_width, text_height;
+ get_text_size(cairo, config->font, &text_width, &text_height,
+ output->scale, block->markup, "%s", block->full_text);
+
+ int margin = 3 * output->scale;
+ int ws_vertical_padding = WS_VERTICAL_PADDING * 2;
+
+ int width = text_width;
if (width < block->min_width) {
width = block->min_width;
}
- *x -= width;
+ double block_width = width;
+ uint32_t ideal_height = text_height + ws_vertical_padding * 2;
+ uint32_t ideal_surface_height = ideal_height / output->scale;
+ if (surface_height < ideal_surface_height) {
+ return ideal_surface_height;
+ }
- if (block->border != 0 && block->border_left > 0) {
+ *x -= width;
+ if (block->border && block->border_left > 0) {
*x -= (block->border_left + margin);
block_width += block->border_left + margin;
}
-
- if (block->border != 0 && block->border_right > 0) {
+ if (block->border && block->border_right > 0) {
*x -= (block->border_right + margin);
block_width += block->border_right + margin;
}
- // Add separator
+ int sep_width, sep_height;
if (!edge) {
if (config->sep_symbol) {
- get_text_size(window->cairo, window->font, &sep_width, &height,
- window->scale, false, "%s", config->sep_symbol);
+ get_text_size(cairo, config->font, &sep_width, &sep_height,
+ output->scale, false, "%s", config->sep_symbol);
+ uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
+ uint32_t _ideal_surface_height = _ideal_height / output->scale;
+ if (surface_height < _ideal_surface_height) {
+ return _ideal_surface_height;
+ }
if (sep_width > block->separator_block_width) {
block->separator_block_width = sep_width + margin * 2;
}
}
-
*x -= block->separator_block_width;
} else {
*x -= margin;
}
- double pos = *x;
-
- block->x = (int)pos;
- block->width = (int)block_width;
+ struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
+ hotspot->x = *x;
+ hotspot->y = 0;
+ hotspot->width = width;
+ hotspot->height = height;
+ hotspot->callback = block_hotspot_callback;
+ hotspot->destroy = NULL;
+ hotspot->data = block;
+ wl_list_insert(&output->hotspots, &hotspot->link);
- // render background
- if (block->background != 0x0) {
- cairo_set_source_u32(window->cairo, block->background);
- cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2);
- cairo_fill(window->cairo);
+ double pos = *x;
+ if (block->background) {
+ cairo_set_source_u32(cairo, block->background);
+ cairo_rectangle(cairo, pos - 0.5 * output->scale,
+ output->scale, block_width, height);
+ cairo_fill(cairo);
}
- // render top border
- if (block->border != 0 && block->border_top > 0) {
- render_sharp_line(window->cairo, block->border,
- pos - 0.5,
- 1,
- block_width,
- block->border_top);
+ if (block->border && block->border_top > 0) {
+ render_sharp_line(cairo, block->border,
+ pos - 0.5 * output->scale, output->scale,
+ block_width, block->border_top);
}
-
- // render bottom border
- if (block->border != 0 && block->border_bottom > 0) {
- render_sharp_line(window->cairo, block->border,
- pos - 0.5,
- (window->height * window->scale) - 1 - block->border_bottom,
- block_width,
- block->border_bottom);
+ if (block->border && block->border_bottom > 0) {
+ render_sharp_line(cairo, block->border,
+ pos - 0.5 * output->scale,
+ height - output->scale - block->border_bottom,
+ block_width, block->border_bottom);
}
-
- // render left border
if (block->border != 0 && block->border_left > 0) {
- render_sharp_line(window->cairo, block->border,
- pos - 0.5,
- 1,
- block->border_left,
- (window->height * window->scale) - 2);
-
+ render_sharp_line(cairo, block->border,
+ pos - 0.5 * output->scale, output->scale,
+ block->border_left, height);
pos += block->border_left + margin;
}
- // render text
double offset = 0;
-
if (strncmp(block->align, "left", 5) == 0) {
offset = pos;
} else if (strncmp(block->align, "right", 5) == 0) {
- offset = pos + width - textwidth;
+ offset = pos + width - text_width;
} else if (strncmp(block->align, "center", 6) == 0) {
- offset = pos + (width - textwidth) / 2;
+ offset = pos + (width - text_width) / 2;
}
-
- cairo_move_to(window->cairo, offset, margin);
- cairo_set_source_u32(window->cairo, block->color);
- pango_printf(window->cairo, window->font, window->scale,
+ cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0);
+ uint32_t color = block->color ? *block->color : config->colors.statusline;
+ cairo_set_source_u32(cairo, color);
+ pango_printf(cairo, config->font, output->scale,
block->markup, "%s", block->full_text);
-
pos += width;
- // render right border
- if (block->border != 0 && block->border_right > 0) {
+ if (block->border && block->border_right > 0) {
pos += margin;
-
- render_sharp_line(window->cairo, block->border,
- pos - 0.5,
- 1,
- block->border_right,
- (window->height * window->scale) - 2);
-
+ render_sharp_line(cairo, block->border,
+ pos - 0.5 * output->scale, output->scale,
+ block->border_right, height);
pos += block->border_right;
}
- // render separator
if (!edge && block->separator) {
- if (is_focused) {
- cairo_set_source_u32(window->cairo, config->colors.focused_separator);
+ if (focused) {
+ cairo_set_source_u32(cairo, config->colors.focused_separator);
} else {
- cairo_set_source_u32(window->cairo, config->colors.separator);
+ cairo_set_source_u32(cairo, config->colors.separator);
}
if (config->sep_symbol) {
offset = pos + (block->separator_block_width - sep_width) / 2;
- cairo_move_to(window->cairo, offset, margin);
- pango_printf(window->cairo, window->font, window->scale,
- false, "%s", config->sep_symbol);
+ cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0);
+ pango_printf(cairo, config->font, output->scale, false,
+ "%s", config->sep_symbol);
} else {
- cairo_set_line_width(window->cairo, 1);
- cairo_move_to(window->cairo, pos + block->separator_block_width/2,
- margin);
- cairo_line_to(window->cairo, pos + block->separator_block_width/2,
- (window->height * window->scale) - margin);
- cairo_stroke(window->cairo);
+ cairo_set_line_width(cairo, 1);
+ cairo_move_to(cairo,
+ pos + block->separator_block_width / 2, margin);
+ cairo_line_to(cairo,
+ pos + block->separator_block_width / 2, height - margin);
+ cairo_stroke(cairo);
}
}
+ return surface_height;
+}
+static uint32_t render_status_line_i3bar(cairo_t *cairo,
+ struct swaybar_config *config, struct swaybar_output *output,
+ struct status_line *status, bool focused,
+ double *x, uint32_t surface_height) {
+ uint32_t max_height = 0;
+ bool edge = true;
+ struct i3bar_block *block;
+ wl_list_for_each(block, &status->blocks, link) {
+ uint32_t h = render_status_block(cairo, config, output,
+ block, x, surface_height, focused, edge);
+ max_height = h > max_height ? h : max_height;
+ edge = false;
+ }
+ return max_height;
}
-static const char *strip_workspace_name(bool strip_num, const char *ws_name) {
- bool strip = false;
- int i;
-
- if (strip_num) {
- int len = strlen(ws_name);
- for (i = 0; i < len; ++i) {
- if (!('0' <= ws_name[i] && ws_name[i] <= '9')) {
- if (':' == ws_name[i] && i < len-1 && i > 0) {
- strip = true;
- ++i;
- }
- break;
- }
- }
+static uint32_t render_status_line(cairo_t *cairo,
+ struct swaybar_config *config, struct swaybar_output *output,
+ struct status_line *status, bool focused,
+ double *x, uint32_t surface_height) {
+ switch (status->protocol) {
+ case PROTOCOL_ERROR:
+ return render_status_line_error(cairo, output, config,
+ status->text, x, surface_height);
+ case PROTOCOL_TEXT:
+ return render_status_line_text(cairo, output, config,
+ status->text, focused, x, surface_height);
+ case PROTOCOL_I3BAR:
+ return render_status_line_i3bar(cairo, config, output,
+ status, focused, x, surface_height);
+ case PROTOCOL_UNDEF:
+ return 0;
}
+ return 0;
+}
+
+static uint32_t render_binding_mode_indicator(cairo_t *cairo,
+ struct swaybar_output *output, struct swaybar_config *config,
+ const char *mode, double x, uint32_t surface_height) {
+ uint32_t height = surface_height * output->scale;
- if (strip) {
- return ws_name + i;
+ int text_width, text_height;
+ get_text_size(cairo, config->font, &text_width, &text_height,
+ output->scale, true, "%s", mode);
+
+ int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
+ int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale;
+ int border_width = BORDER_WIDTH * output->scale;
+
+ uint32_t ideal_height = text_height + ws_vertical_padding * 2
+ + border_width * 2;
+ uint32_t ideal_surface_height = ideal_height / output->scale;
+ if (surface_height < ideal_surface_height) {
+ return ideal_surface_height;
}
+ uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
+
+ cairo_set_source_u32(cairo, config->colors.binding_mode.background);
+ cairo_rectangle(cairo, x, 0, width, height);
+ cairo_fill(cairo);
+
+ cairo_set_source_u32(cairo, config->colors.binding_mode.border);
+ cairo_rectangle(cairo, x, 0, width, border_width);
+ cairo_fill(cairo);
+ cairo_rectangle(cairo, x, 0, border_width, height);
+ cairo_fill(cairo);
+ cairo_rectangle(cairo, x + width - border_width, 0, border_width, height);
+ cairo_fill(cairo);
+ cairo_rectangle(cairo, x, height - border_width, width, border_width);
+ cairo_fill(cairo);
+
+ double text_y = height / 2.0 - text_height / 2.0;
+ cairo_set_source_u32(cairo, config->colors.binding_mode.text);
+ cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
+ pango_printf(cairo, config->font, output->scale, true, "%s", mode);
+ return surface_height;
+}
+static const char *strip_workspace_number(const char *ws_name) {
+ size_t len = strlen(ws_name);
+ for (size_t i = 0; i < len; ++i) {
+ if (ws_name[i] < '0' || ws_name[i] > '9') {
+ if (':' == ws_name[i] && i < len - 1 && i > 0) {
+ return ws_name + i + 1;
+ }
+ return ws_name;
+ }
+ }
return ws_name;
}
-void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) {
- const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name);
-
- get_text_size(window->cairo, window->font, width, height,
- window->scale, true, "%s", stripped_name);
- *width += 2 * ws_horizontal_padding;
- *height += 2 * ws_vertical_padding;
+static void workspace_hotspot_callback(struct swaybar_output *output,
+ int x, int y, uint32_t button, void *data) {
+ ipc_send_workspace_command(output->bar, (const char *)data);
}
-static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) {
- const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name);
+static uint32_t render_workspace_button(cairo_t *cairo,
+ struct swaybar_output *output, struct swaybar_config *config,
+ struct swaybar_workspace *ws, double *x, uint32_t surface_height) {
+ const char *name = ws->name;
+ if (config->strip_workspace_numbers) {
+ name = strip_workspace_number(ws->name);
+ }
struct box_colors box_colors;
if (ws->urgent) {
@@ -235,133 +370,154 @@ static void render_workspace_button(struct window *window, struct config *config
box_colors = config->colors.inactive_workspace;
}
- int width, height;
- workspace_button_size(window, stripped_name, &width, &height);
+ uint32_t height = surface_height * output->scale;
- // background
- cairo_set_source_u32(window->cairo, box_colors.background);
- cairo_rectangle(window->cairo, *x, 1.5, width - 1, height);
- cairo_fill(window->cairo);
-
- // border
- cairo_set_source_u32(window->cairo, box_colors.border);
- cairo_rectangle(window->cairo, *x, 1.5, width - 1, height);
- cairo_stroke(window->cairo);
-
- // text
- cairo_set_source_u32(window->cairo, box_colors.text);
- cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin);
- pango_printf(window->cairo, window->font, window->scale,
- true, "%s", stripped_name);
-
- *x += width + ws_spacing;
-}
+ int text_width, text_height;
+ get_text_size(cairo, config->font, &text_width, &text_height,
+ output->scale, true, "%s", name);
+
+ int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
+ int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale;
+ int border_width = BORDER_WIDTH * output->scale;
+
+ uint32_t ideal_height = ws_vertical_padding * 2 + text_height
+ + border_width * 2;
+ uint32_t ideal_surface_height = ideal_height / output->scale;
+ if (surface_height < ideal_surface_height) {
+ return ideal_surface_height;
+ }
-static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) {
- int width, height;
- get_text_size(window->cairo, window->font, &width, &height,
- window->scale, false, "%s", config->mode);
-
- // background
- cairo_set_source_u32(window->cairo, config->colors.binding_mode.background);
- cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1,
- height + ws_vertical_padding * 2);
- cairo_fill(window->cairo);
-
- // border
- cairo_set_source_u32(window->cairo, config->colors.binding_mode.border);
- cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1,
- height + ws_vertical_padding * 2);
- cairo_stroke(window->cairo);
-
- // text
- cairo_set_source_u32(window->cairo, config->colors.binding_mode.text);
- cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin);
- pango_printf(window->cairo, window->font, window->scale,
- false, "%s", config->mode);
+ uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2;
+
+ cairo_set_source_u32(cairo, box_colors.background);
+ cairo_rectangle(cairo, *x, 0, width, height);
+ cairo_fill(cairo);
+
+ cairo_set_source_u32(cairo, box_colors.border);
+ cairo_rectangle(cairo, *x, 0, width, border_width);
+ cairo_fill(cairo);
+ cairo_rectangle(cairo, *x, 0, border_width, height);
+ cairo_fill(cairo);
+ cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height);
+ cairo_fill(cairo);
+ cairo_rectangle(cairo, *x, height - border_width, width, border_width);
+ cairo_fill(cairo);
+
+ double text_y = height / 2.0 - text_height / 2.0;
+ cairo_set_source_u32(cairo, box_colors.text);
+ cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
+ pango_printf(cairo, config->font, output->scale, true, "%s", name);
+
+ struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
+ hotspot->x = *x;
+ hotspot->y = 0;
+ hotspot->width = width;
+ hotspot->height = height;
+ hotspot->callback = workspace_hotspot_callback;
+ hotspot->destroy = free;
+ hotspot->data = strdup(ws->name);
+ wl_list_insert(&output->hotspots, &hotspot->link);
+
+ *x += width;
+ return surface_height;
}
-void render(struct output *output, struct config *config, struct status_line *line) {
- int i;
-
- struct window *window = output->window;
- cairo_t *cairo = window->cairo;
- bool is_focused = output->focused;
-
- // Clear
- cairo_save(cairo);
- cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
- cairo_paint(cairo);
- cairo_restore(cairo);
-
+static uint32_t render_to_cairo(cairo_t *cairo,
+ struct swaybar *bar, struct swaybar_output *output) {
+ struct swaybar_config *config = bar->config;
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
-
- // Background
- if (is_focused) {
+ if (output->focused) {
cairo_set_source_u32(cairo, config->colors.focused_background);
} else {
cairo_set_source_u32(cairo, config->colors.background);
}
cairo_paint(cairo);
-#ifdef ENABLE_TRAY
- uint32_t tray_width = tray_render(output, config);
-#else
- const uint32_t tray_width = window->width * window->scale;
-#endif
-
- // Command output
- if (is_focused) {
- cairo_set_source_u32(cairo, config->colors.focused_statusline);
- } else {
- cairo_set_source_u32(cairo, config->colors.statusline);
- }
-
- int width, height;
-
- if (line->protocol == TEXT) {
- get_text_size(window->cairo, window->font, &width, &height,
- window->scale, config->pango_markup, "%s", line->text_line);
- cairo_move_to(cairo, tray_width - margin - width, margin);
- pango_printf(window->cairo, window->font, window->scale,
- config->pango_markup, "%s", line->text_line);
- } else if (line->protocol == I3BAR && line->block_line) {
- double pos = tray_width - 0.5;
- bool edge = true;
- for (i = line->block_line->length - 1; i >= 0; --i) {
- struct status_block *block = line->block_line->items[i];
- if (block->full_text && block->full_text[0]) {
- render_block(window, config, block, &pos, edge, is_focused);
- edge = false;
- }
+ uint32_t max_height = 0;
+ /*
+ * Each render_* function takes the actual height of the bar, and returns
+ * the ideal height. If the actual height is too short, the render function
+ * can do whatever it wants - the buffer won't be committed. If the actual
+ * height is too tall, the render function should adapt its drawing to
+ * utilize the available space.
+ */
+ double x = output->width * output->scale;
+ if (bar->status) {
+ uint32_t h = render_status_line(cairo, config, output,
+ bar->status, output->focused, &x, output->height);
+ max_height = h > max_height ? h : max_height;
+ }
+ x = 0;
+ if (config->workspace_buttons) {
+ struct swaybar_workspace *ws;
+ wl_list_for_each_reverse(ws, &output->workspaces, link) {
+ uint32_t h = render_workspace_button(cairo,
+ output, config, ws, &x, output->height);
+ max_height = h > max_height ? h : max_height;
}
}
+ if (config->binding_mode_indicator && config->mode) {
+ uint32_t h = render_binding_mode_indicator(cairo,
+ output, config, config->mode, x, output->height);
+ max_height = h > max_height ? h : max_height;
+ }
- cairo_set_line_width(cairo, 1.0);
- double x = 0.5;
+ return max_height > output->height ? max_height : output->height;
+}
- // Workspaces
- if (config->workspace_buttons) {
- for (i = 0; i < output->workspaces->length; ++i) {
- struct workspace *ws = output->workspaces->items[i];
- render_workspace_button(window, config, ws, &x);
+void render_frame(struct swaybar *bar, struct swaybar_output *output) {
+ struct swaybar_hotspot *hotspot, *tmp;
+ wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) {
+ if (hotspot->destroy) {
+ hotspot->destroy(hotspot->data);
}
+ wl_list_remove(&hotspot->link);
+ free(hotspot);
}
- // binding mode indicator
- if (config->mode && config->binding_mode_indicator) {
- render_binding_mode_indicator(window, config, x);
+ cairo_surface_t *recorder = cairo_recording_surface_create(
+ CAIRO_CONTENT_COLOR_ALPHA, NULL);
+ cairo_t *cairo = cairo_create(recorder);
+ cairo_save(cairo);
+ cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cairo);
+ cairo_restore(cairo);
+ uint32_t height = render_to_cairo(cairo, bar, output);
+ if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) {
+ height = bar->config->height;
}
-}
-
-void set_window_height(struct window *window, int height) {
- int text_width, text_height;
- get_text_size(window->cairo, window->font,
- &text_width, &text_height, window->scale, false,
- "Test string for measuring purposes");
- if (height > 0) {
- margin = (height - text_height) / 2;
- ws_vertical_padding = margin - 1.5;
- }
- window->height = (text_height + margin * 2) / window->scale;
+ if (height != output->height) {
+ // Reconfigure surface
+ zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height);
+ zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height);
+ // TODO: this could infinite loop if the compositor assigns us a
+ // different height than what we asked for
+ wl_surface_commit(output->surface);
+ wl_display_roundtrip(bar->display);
+ } else {
+ // Replay recording into shm and send it off
+ output->current_buffer = get_next_buffer(bar->shm,
+ output->buffers,
+ output->width * output->scale,
+ output->height * output->scale);
+ cairo_t *shm = output->current_buffer->cairo;
+
+ cairo_save(shm);
+ cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(shm);
+ cairo_restore(shm);
+
+ cairo_set_source_surface(shm, recorder, 0.0, 0.0);
+ cairo_paint(shm);
+
+ wl_surface_set_buffer_scale(output->surface, output->scale);
+ wl_surface_attach(output->surface,
+ output->current_buffer->buffer, 0, 0);
+ wl_surface_damage(output->surface, 0, 0,
+ output->width, output->height);
+ wl_surface_commit(output->surface);
+ wl_display_roundtrip(bar->display);
+ }
+ cairo_surface_destroy(recorder);
+ cairo_destroy(cairo);
}
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 @@
-#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE
+#include <fcntl.h>
+#include <json-c/json.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
#include <unistd.h>
-#include <json-c/json.h>
-
+#include <wlr/util/log.h>
#include "swaybar/config.h"
#include "swaybar/status_line.h"
-#include "log.h"
-#include "util.h"
-
-#define I3JSON_MAXDEPTH 4
-#define I3JSON_UNKNOWN 0
-#define I3JSON_ARRAY 1
-#define I3JSON_STRING 2
-
-struct {
- int bufsize;
- char *buffer;
- char *line_start;
- char *parserpos;
- bool escape;
- int depth;
- int bar[I3JSON_MAXDEPTH+1];
-} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } };
-
-static char line[1024];
-static char line_rest[1024];
-
-static char event_buff[1024];
-
-static void free_status_block(void *item) {
- if (!item) {
- return;
- }
- struct status_block *sb = (struct status_block*)item;
- if (sb->full_text) {
- free(sb->full_text);
- }
- if (sb->short_text) {
- free(sb->short_text);
- }
- if (sb->align) {
- free(sb->align);
- }
- if (sb->name) {
- free(sb->name);
- }
- if (sb->instance) {
- free(sb->instance);
- }
- free(sb);
-}
-
-static void parse_json(struct bar *bar, const char *text) {
- json_object *results = json_tokener_parse(text);
- if (!results) {
- sway_log(L_DEBUG, "Failed to parse json");
- return;
- }
-
- if (json_object_array_length(results) < 1) {
- return;
- }
-
- if (bar->status->block_line) {
- list_foreach(bar->status->block_line, free_status_block);
- list_free(bar->status->block_line);
- }
-
- bar->status->block_line = create_list();
-
- int i;
- for (i = 0; i < json_object_array_length(results); ++i) {
- json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
- json_object *name, *instance, *separator, *separator_block_width;
- json_object *background, *border, *border_top, *border_bottom;
- json_object *border_left, *border_right, *markup;
-
- json_object *json = json_object_array_get_idx(results, i);
- if (!json) {
- continue;
- }
-
- json_object_object_get_ex(json, "full_text", &full_text);
- json_object_object_get_ex(json, "short_text", &short_text);
- json_object_object_get_ex(json, "color", &color);
- json_object_object_get_ex(json, "min_width", &min_width);
- json_object_object_get_ex(json, "align", &align);
- json_object_object_get_ex(json, "urgent", &urgent);
- json_object_object_get_ex(json, "name", &name);
- json_object_object_get_ex(json, "instance", &instance);
- json_object_object_get_ex(json, "markup", &markup);
- json_object_object_get_ex(json, "separator", &separator);
- json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
- json_object_object_get_ex(json, "background", &background);
- json_object_object_get_ex(json, "border", &border);
- json_object_object_get_ex(json, "border_top", &border_top);
- json_object_object_get_ex(json, "border_bottom", &border_bottom);
- json_object_object_get_ex(json, "border_left", &border_left);
- json_object_object_get_ex(json, "border_right", &border_right);
-
- struct status_block *new = calloc(1, sizeof(struct status_block));
-
- if (full_text) {
- new->full_text = strdup(json_object_get_string(full_text));
- }
-
- if (short_text) {
- new->short_text = strdup(json_object_get_string(short_text));
- }
-
- if (color) {
- new->color = parse_color(json_object_get_string(color));
- } else {
- new->color = bar->config->colors.statusline;
- }
-
- if (min_width) {
- json_type type = json_object_get_type(min_width);
- if (type == json_type_int) {
- new->min_width = json_object_get_int(min_width);
- } else if (type == json_type_string) {
- /* the width will be calculated when rendering */
- new->min_width = 0;
- }
- }
-
- if (align) {
- new->align = strdup(json_object_get_string(align));
- } else {
- new->align = strdup("left");
- }
-
- if (urgent) {
- new->urgent = json_object_get_int(urgent);
- }
+#include "readline.h"
- if (name) {
- new->name = strdup(json_object_get_string(name));
- }
-
- if (instance) {
- new->instance = strdup(json_object_get_string(instance));
- }
-
- if (markup) {
- new->markup = false;
- const char *markup_str = json_object_get_string(markup);
- if (strcmp(markup_str, "pango") == 0) {
- new->markup = true;
- }
- }
-
- if (separator) {
- new->separator = json_object_get_int(separator);
- } else {
- new->separator = true; // i3bar spec
- }
-
- if (separator_block_width) {
- new->separator_block_width = json_object_get_int(separator_block_width);
- } else {
- new->separator_block_width = 9; // i3bar spec
- }
-
- // Airblader features
- if (background) {
- new->background = parse_color(json_object_get_string(background));
- } else {
- new->background = 0x0; // transparent
- }
-
- if (border) {
- new->border = parse_color(json_object_get_string(border));
- } else {
- new->border = 0x0; // transparent
- }
-
- if (border_top) {
- new->border_top = json_object_get_int(border_top);
- } else {
- new->border_top = 1;
- }
-
- if (border_bottom) {
- new->border_bottom = json_object_get_int(border_bottom);
- } else {
- new->border_bottom = 1;
- }
-
- if (border_left) {
- new->border_left = json_object_get_int(border_left);
- } else {
- new->border_left = 1;
- }
-
- if (border_right) {
- new->border_right = json_object_get_int(border_right);
- } else {
- new->border_right = 1;
- }
-
- list_add(bar->status->block_line, new);
- }
-
- json_object_put(results);
+void status_error(struct status_line *status, const char *text) {
+ close(status->read_fd);
+ close(status->write_fd);
+ status->protocol = PROTOCOL_ERROR;
+ status->text = text;
}
-// continue parsing from last parserpos
-static int i3json_parse(struct bar *bar) {
- char *c = i3json_state.parserpos;
- int handled = 0;
- while (*c) {
- if (i3json_state.bar[i3json_state.depth] == I3JSON_STRING) {
- if (!i3json_state.escape && *c == '"') {
- --i3json_state.depth;
- }
- i3json_state.escape = !i3json_state.escape && *c == '\\';
- } else {
- switch (*c) {
- case '[':
- ++i3json_state.depth;
- if (i3json_state.depth > I3JSON_MAXDEPTH) {
- sway_abort("JSON too deep");
- }
- i3json_state.bar[i3json_state.depth] = I3JSON_ARRAY;
- if (i3json_state.depth == 2) {
- i3json_state.line_start = c;
- }
- break;
- case ']':
- if (i3json_state.bar[i3json_state.depth] != I3JSON_ARRAY) {
- sway_abort("JSON malformed");
- }
- --i3json_state.depth;
- if (i3json_state.depth == 1) {
- // c[1] is valid since c[0] != '\0'
- char p = c[1];
- c[1] = '\0';
- parse_json(bar, i3json_state.line_start);
- c[1] = p;
- ++handled;
- i3json_state.line_start = c+1;
- }
- break;
- case '"':
- ++i3json_state.depth;
- if (i3json_state.depth > I3JSON_MAXDEPTH) {
- sway_abort("JSON too deep");
- }
- i3json_state.bar[i3json_state.depth] = I3JSON_STRING;
- break;
- }
- }
- ++c;
- }
- i3json_state.parserpos = c;
- return handled;
-}
-
-// Read line from file descriptor, only show the line tail if it is too long.
-// In non-blocking mode treat "no more data" as a linebreak.
-// If data after a line break has been read, return it in rest.
-// If rest is non-empty, then use that as the start of the next line.
-static int read_line_tail(int fd, char *buf, int nbyte, char *rest) {
- if (fd < 0 || !buf || !nbyte) {
- return -1;
- }
- int l;
- char *buffer = malloc(nbyte*2+1);
- char *readpos = buffer;
- char *lf;
- // prepend old data to new line if necessary
- if (rest) {
- l = strlen(rest);
- if (l > nbyte) {
- strcpy(buffer, rest + l - nbyte);
- readpos += nbyte;
- } else if (l) {
- strcpy(buffer, rest);
- readpos += l;
- }
- }
- // read until a linefeed is found or no more data is available
- while ((l = read(fd, readpos, nbyte)) > 0) {
- readpos[l] = '\0';
- lf = strchr(readpos, '\n');
- if (lf) {
- // linefeed found, replace with \0
- *lf = '\0';
- // give data from the end of the line, try to fill the buffer
- if (lf-buffer > nbyte) {
- strcpy(buf, lf - nbyte + 1);
- } else {
- strcpy(buf, buffer);
- }
- // we may have read data from the next line, save it to rest
- if (rest) {
- rest[0] = '\0';
- strcpy(rest, lf + 1);
- }
- free(buffer);
- return strlen(buf);
- } else {
- // no linefeed found, slide data back.
- int overflow = readpos - buffer + l - nbyte;
- if (overflow > 0) {
- memmove(buffer, buffer + overflow , nbyte + 1);
- }
- }
- }
- if (l < 0) {
- free(buffer);
- return l;
- }
- readpos[l]='\0';
- if (rest) {
- rest[0] = '\0';
- }
- if (nbyte < readpos - buffer + l - 1) {
- memcpy(buf, readpos - nbyte + l + 1, nbyte);
- } else {
- strncpy(buf, buffer, nbyte);
- }
- buf[nbyte-1] = '\0';
- free(buffer);
- return strlen(buf);
-}
-
-// make sure that enough buffer space is available starting from parserpos
-static void i3json_ensure_free(int min_free) {
- int _step = 10240;
- int r = min_free % _step;
- if (r) {
- min_free += _step - r;
- }
- if (!i3json_state.buffer) {
- i3json_state.buffer = malloc(min_free);
- i3json_state.bufsize = min_free;
- i3json_state.parserpos = i3json_state.buffer;
- } else {
- int len = 0;
- int pos = 0;
- if (i3json_state.line_start) {
- len = strlen(i3json_state.line_start);
- pos = i3json_state.parserpos - i3json_state.line_start;
- if (i3json_state.line_start != i3json_state.buffer) {
- memmove(i3json_state.buffer, i3json_state.line_start, len+1);
- }
- } else {
- len = strlen(i3json_state.buffer);
- }
- if (i3json_state.bufsize < len+min_free) {
- i3json_state.bufsize += min_free;
- if (i3json_state.bufsize > 1024000) {
- sway_abort("Status line json too long or malformed.");
- }
- i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize);
- if (!i3json_state.buffer) {
- sway_abort("Could not allocate json buffer");
- }
- }
- if (i3json_state.line_start) {
- i3json_state.line_start = i3json_state.buffer;
- i3json_state.parserpos = i3json_state.buffer + pos;
- } else {
- i3json_state.parserpos = i3json_state.buffer;
- }
- }
- if (!i3json_state.buffer) {
- sway_abort("Could not allocate buffer.");
- }
-}
-
-// append data and parse it.
-static int i3json_handle_data(struct bar *bar, char *data) {
- int len = strlen(data);
- i3json_ensure_free(len);
- strcpy(i3json_state.parserpos, data);
- return i3json_parse(bar);
-}
-
-// read data from fd and parse it.
-static int i3json_handle_fd(struct bar *bar) {
- i3json_ensure_free(10240);
- // get fresh data at the end of the buffer
- int readlen = read(bar->status_read_fd, i3json_state.parserpos, 10239);
- if (readlen < 0) {
- return readlen;
- }
- i3json_state.parserpos[readlen] = '\0';
- return i3json_parse(bar);
-}
-
-bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button) {
- sway_log(L_DEBUG, "status_line_mouse_event.");
- if (!bar->status->click_events) {
- sway_log(L_DEBUG, "click_events are not enabled.");
+bool status_handle_readable(struct status_line *status) {
+ char *line;
+ switch (status->protocol) {
+ case PROTOCOL_ERROR:
return false;
- }
-
- if (bar->status->protocol == I3BAR) {
- sway_log(L_DEBUG, "Sending click event.");
-
- // find clicked block
- struct status_block *clicked_block = NULL;
- struct status_block *current_block = NULL;
- int num_blocks = bar->status->block_line->length;
-
- if (num_blocks == 0) {
- return false;
- } else {
- current_block = bar->status->block_line->items[0];
- if (x < current_block->x) {
- return false;
- }
- }
-
- for (int i = 0; i < num_blocks; i++) {
- current_block = bar->status->block_line->items[i];
- if (x < (current_block->x + current_block->width)) {
- clicked_block = current_block;
- break;
- }
- }
-
- if (!clicked_block || !clicked_block->name) {
- return false;
- }
-
- // event example {"name":"capture","instance":"label","button":1,"x":3431,"y":18}
-
- struct json_object *event_json = json_object_new_object();
- json_object_object_add(event_json, "name", json_object_new_string(clicked_block->name));
- if (clicked_block->instance) {
- json_object_object_add(event_json, "instance", json_object_new_string(clicked_block->instance));
- }
- json_object_object_add(event_json, "button", json_object_new_int(button));
- json_object_object_add(event_json, "x", json_object_new_int(x));
- json_object_object_add(event_json, "y", json_object_new_int(y));
-
- int len = snprintf(event_buff, sizeof(event_buff), "%s\n", json_object_to_json_string(event_json));
-
- json_object_put(event_json);
-
- if (len <= (int)sizeof(event_buff)) { // if not truncated
- write(bar->status_write_fd, event_buff, len);
+ case PROTOCOL_I3BAR:
+ if (i3bar_handle_readable(status) > 0) {
return true;
}
- }
-
- return false;
-}
-
-bool handle_status_line(struct bar *bar) {
- bool dirty = false;
-
- switch (bar->status->protocol) {
- case I3BAR:
- sway_log(L_DEBUG, "Got i3bar protocol.");
- if (i3json_handle_fd(bar) > 0) {
- dirty = true;
- }
break;
- case TEXT:
- sway_log(L_DEBUG, "Got text protocol.");
- read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest);
- dirty = true;
- bar->status->text_line = line;
- break;
- case UNDEF:
- sway_log(L_DEBUG, "Detecting protocol...");
- if (read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest) < 0) {
- break;
+ case PROTOCOL_TEXT:
+ line = read_line_buffer(status->read,
+ status->text_state.buffer, status->text_state.buffer_size);
+ if (!line) {
+ status_error(status, "[error reading from status command]");
+ } else {
+ status->text = line;
+ }
+ return true;
+ case PROTOCOL_UNDEF:
+ line = read_line_buffer(status->read,
+ status->text_state.buffer, status->text_state.buffer_size);
+ if (!line) {
+ status_error(status, "[error reading from status command]");
+ return false;
}
- dirty = true;
- bar->status->text_line = line;
- bar->status->protocol = TEXT;
if (line[0] == '{') {
- // detect i3bar json protocol
json_object *proto = json_tokener_parse(line);
if (proto) {
-
json_object *version;
if (json_object_object_get_ex(proto, "version", &version)
- && json_object_get_int(version) == 1
- ) {
- sway_log(L_DEBUG, "Switched to i3bar protocol.");
- bar->status->protocol = I3BAR;
+ && json_object_get_int(version) == 1) {
+ wlr_log(L_DEBUG, "Switched to i3bar protocol.");
+ status->protocol = PROTOCOL_I3BAR;
}
-
json_object *click_events;
- if (json_object_object_get_ex(proto, "click_events", &click_events)
+ if (json_object_object_get_ex(
+ proto, "click_events", &click_events)
&& json_object_get_boolean(click_events)) {
-
- sway_log(L_DEBUG, "Enabling click events.");
- bar->status->click_events = true;
-
+ wlr_log(L_DEBUG, "Enabled click events.");
+ status->i3bar_state.click_events = true;
const char *events_array = "[\n";
- write(bar->status_write_fd, events_array, strlen(events_array));
+ ssize_t len = strlen(events_array);
+ if (write(status->write_fd, events_array, len) != len) {
+ status_error(status,
+ "[failed to write to status command]");
+ }
}
-
- i3json_handle_data(bar, line_rest);
-
json_object_put(proto);
}
+
+ status->protocol = PROTOCOL_I3BAR;
+ free(status->text_state.buffer);
+ wl_list_init(&status->blocks);
+ status->i3bar_state.buffer_size = 4096;
+ status->i3bar_state.buffer =
+ malloc(status->i3bar_state.buffer_size);
+ } else {
+ status->protocol = PROTOCOL_TEXT;
+ status->text = line;
}
- break;
+ return true;
}
-
- return dirty;
+ return false;
}
-struct status_line *init_status_line() {
- struct status_line *line = malloc(sizeof(struct status_line));
- line->block_line = create_list();
- line->text_line = NULL;
- line->protocol = UNDEF;
- line->click_events = false;
+struct status_line *status_line_init(char *cmd) {
+ struct status_line *status = calloc(1, sizeof(struct status_line));
+ status->text_state.buffer_size = 8192;
+ status->text_state.buffer = malloc(status->text_state.buffer_size);
- return line;
-}
+ int pipe_read_fd[2];
+ int pipe_write_fd[2];
+ if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) {
+ wlr_log(L_ERROR, "Unable to create pipes for status_command fork");
+ exit(1);
+ }
+
+ status->pid = fork();
+ if (status->pid == 0) {
+ dup2(pipe_read_fd[1], STDOUT_FILENO);
+ close(pipe_read_fd[0]);
+ close(pipe_read_fd[1]);
-void free_status_line(struct status_line *line) {
- if (line->block_line) {
- list_foreach(line->block_line, free_status_block);
- list_free(line->block_line);
+ dup2(pipe_write_fd[0], STDIN_FILENO);
+ close(pipe_write_fd[0]);
+ close(pipe_write_fd[1]);
+
+ char *const _cmd[] = { "sh", "-c", cmd, NULL, };
+ execvp(_cmd[0], _cmd);
+ exit(1);
}
+
+ close(pipe_read_fd[1]);
+ status->read_fd = pipe_read_fd[0];
+ fcntl(status->read_fd, F_SETFL, O_NONBLOCK);
+ close(pipe_write_fd[0]);
+ status->write_fd = pipe_write_fd[1];
+ fcntl(status->write_fd, F_SETFL, O_NONBLOCK);
+
+ status->read = fdopen(status->read_fd, "r");
+ status->write = fdopen(status->write_fd, "w");
+ return status;
+}
+
+void status_line_free(struct status_line *status) {
+ close(status->read_fd);
+ close(status->write_fd);
+ kill(status->pid, SIGTERM);
+ free(status);
}
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 @@
-#define _XOPEN_SOURCE 700
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <poll.h>
-#include <signal.h>
-#include <time.h>
-#include <dbus/dbus.h>
-#include "swaybar/tray/dbus.h"
-#include "swaybar/event_loop.h"
-#include "log.h"
-
-DBusConnection *conn = NULL;
-
-static void dispatch_watch(int fd, short mask, void *data) {
- sway_log(L_DEBUG, "Dispatching watch");
- DBusWatch *watch = data;
-
- if (!dbus_watch_get_enabled(watch)) {
- return;
- }
-
- uint32_t flags = 0;
-
- if (mask & POLLIN) {
- flags |= DBUS_WATCH_READABLE;
- } if (mask & POLLOUT) {
- flags |= DBUS_WATCH_WRITABLE;
- } if (mask & POLLHUP) {
- flags |= DBUS_WATCH_HANGUP;
- } if (mask & POLLERR) {
- flags |= DBUS_WATCH_ERROR;
- }
-
- dbus_watch_handle(watch, flags);
-}
-
-static dbus_bool_t add_watch(DBusWatch *watch, void *_data) {
- if (!dbus_watch_get_enabled(watch)) {
- // Watch should not be polled
- return TRUE;
- }
-
- short mask = 0;
- uint32_t flags = dbus_watch_get_flags(watch);
-
- if (flags & DBUS_WATCH_READABLE) {
- mask |= POLLIN;
- } if (flags & DBUS_WATCH_WRITABLE) {
- mask |= POLLOUT;
- }
-
- int fd = dbus_watch_get_unix_fd(watch);
-
- sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd);
- add_event(fd, mask, dispatch_watch, watch);
-
- return TRUE;
-}
-
-static void remove_watch(DBusWatch *watch, void *_data) {
- int fd = dbus_watch_get_unix_fd(watch);
-
- remove_event(fd);
-}
-
-static void dispatch_timeout(timer_t timer, void *data) {
- sway_log(L_DEBUG, "Dispatching DBus timeout");
- DBusTimeout *timeout = data;
-
- if (dbus_timeout_get_enabled(timeout)) {
- dbus_timeout_handle(timeout);
- }
-}
-
-static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) {
- if (!dbus_timeout_get_enabled(timeout)) {
- return TRUE;
- }
-
- timer_t *timer = malloc(sizeof(timer_t));
- if (!timer) {
- sway_log(L_ERROR, "Cannot allocate memory");
- return FALSE;
- }
- struct sigevent ev = {
- .sigev_notify = SIGEV_NONE,
- };
-
- if (timer_create(CLOCK_MONOTONIC, &ev, timer)) {
- sway_log(L_ERROR, "Could not create DBus timer");
- return FALSE;
- }
-
- int interval = dbus_timeout_get_interval(timeout);
- int interval_sec = interval / 1000;
- int interval_msec = (interval_sec * 1000) - interval;
-
- struct timespec period = {
- (time_t) interval_sec,
- ((long) interval_msec) * 1000 * 1000,
- };
- struct itimerspec time = {
- period,
- period,
- };
-
- timer_settime(*timer, 0, &time, NULL);
-
- dbus_timeout_set_data(timeout, timer, NULL);
-
- sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec);
- add_timer(*timer, dispatch_timeout, timeout);
-
- return TRUE;
-}
-static void remove_timeout(DBusTimeout *timeout, void *_data) {
- timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout);
- sway_log(L_DEBUG, "Removing DBus timeout.");
-
- if (timer) {
- remove_timer(*timer);
- timer_delete(*timer);
- free(timer);
- }
-}
-
-static bool should_dispatch = true;
-
-static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status,
- void *_data) {
- if (new_status == DBUS_DISPATCH_DATA_REMAINS) {
- should_dispatch = true;
- }
-}
-
-/* Public functions below */
-
-void dispatch_dbus() {
- if (!should_dispatch || !conn) {
- return;
- }
-
- DBusDispatchStatus status;
-
- do {
- status = dbus_connection_dispatch(conn);
- } while (status == DBUS_DISPATCH_DATA_REMAINS);
-
- if (status != DBUS_DISPATCH_COMPLETE) {
- sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status);
- }
-
- should_dispatch = false;
-}
-
-int dbus_init() {
- DBusError error;
- dbus_error_init(&error);
-
- conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
- if (conn == NULL) {
- sway_log(L_INFO, "Compiled with dbus support, but unable to connect to dbus");
- sway_log(L_INFO, "swaybar will be unable to display tray icons.");
- return -1;
- }
-
- dbus_connection_set_exit_on_disconnect(conn, FALSE);
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message);
- conn = NULL;
- return -1;
- }
-
- sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn));
-
- // Will be called if dispatch status changes
- dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL);
-
- if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
- NULL, NULL, NULL)) {
- dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
- sway_log(L_ERROR, "Failed to activate DBUS watch functions");
- return -1;
- }
-
- if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
- NULL, NULL, NULL)) {
- dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
- dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL);
- sway_log(L_ERROR, "Failed to activate DBUS timeout functions");
- return -1;
- }
-
- return 0;
-}
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 @@
-#define _XOPEN_SOURCE 700
-#define _POSIX_C_SOURCE 200809L
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <limits.h>
-#include "swaybar/tray/icon.h"
-#include "swaybar/bar.h"
-#include "swaybar/config.h"
-#include "stringop.h"
-#include "log.h"
-
-/**
- * REVIEW:
- * This file repeats lots of "costly" operations that are the same for every
- * icon. It's possible to create a dictionary or some other structure to cache
- * these, though it may complicate things somewhat.
- *
- * Also parsing (index.theme) is currently pretty messy, so that could be made
- * much better as well. Over all, things work, but are not optimal.
- */
-
-/* Finds all themes that the given theme inherits */
-static list_t *find_inherits(const char *theme_dir) {
- const char inherits[] = "Inherits";
- const char index_name[] = "index.theme";
- list_t *themes = create_list();
- FILE *index = NULL;
- char *path = malloc(strlen(theme_dir) + sizeof(index_name));
- if (!path) {
- goto fail;
- }
- if (!themes) {
- goto fail;
- }
-
- strcpy(path, theme_dir);
- strcat(path, index_name);
-
- index = fopen(path, "r");
- if (!index) {
- goto fail;
- }
-
- char *buf = NULL;
- size_t n = 0;
- while (!feof(index) && getline(&buf, &n, index) != -1) {
- if (n <= sizeof(inherits) + 1) {
- continue;
- }
- if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) {
- char *themestr = buf + sizeof(inherits);
- themes = split_string(themestr, ",");
- break;
- }
- }
- free(buf);
-
-fail:
- free(path);
- if (index) {
- fclose(index);
- }
- return themes;
-}
-
-static bool isdir(const char *path) {
- struct stat statbuf;
- if (stat(path, &statbuf) != -1) {
- if (S_ISDIR(statbuf.st_mode)) {
- return true;
- }
- }
- return false;
-
-}
-
-/**
- * Returns the directory of a given theme if it exists.
- * The returned pointer must be freed.
- */
-static char *find_theme_dir(const char *theme) {
- char *basedir;
- char *icon_dir;
-
- if (!theme) {
- return NULL;
- }
-
- if (!(icon_dir = malloc(1024))) {
- sway_log(L_ERROR, "Out of memory!");
- goto fail;
- }
-
- if ((basedir = getenv("HOME"))) {
- if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) {
- sway_log(L_ERROR, "Path too long to render");
- // XXX perhaps just goto trying in /usr/share? This
- // shouldn't happen anyway, but might with a long global
- goto fail;
- }
-
- if (isdir(icon_dir)) {
- return icon_dir;
- }
- }
-
- if ((basedir = getenv("XDG_DATA_DIRS"))) {
- if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
- sway_log(L_ERROR, "Path too long to render");
- // ditto
- goto fail;
- }
-
- if (isdir(icon_dir)) {
- return icon_dir;
- }
- }
-
- // Spec says use "/usr/share/pixmaps/", but I see everything in
- // "/usr/share/icons/" look it both, I suppose.
- if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) {
- sway_log(L_ERROR, "Path too long to render");
- goto fail;
- }
- if (isdir(icon_dir)) {
- return icon_dir;
- }
-
- if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) {
- sway_log(L_ERROR, "Path too long to render");
- goto fail;
- }
- if (isdir(icon_dir)) {
- return icon_dir;
- }
-
-fail:
- free(icon_dir);
- sway_log(L_ERROR, "Could not find dir for theme: %s", theme);
- return NULL;
-}
-
-/**
- * Returns all theme dirs needed to be looked in for an icon.
- * Does not check for duplicates
- */
-static list_t *find_all_theme_dirs(const char *theme) {
- list_t *dirs = create_list();
- if (!dirs) {
- return NULL;
- }
- char *dir = find_theme_dir(theme);
- if (dir) {
- list_add(dirs, dir);
- list_t *inherits = find_inherits(dir);
- list_cat(dirs, inherits);
- list_free(inherits);
- }
- dir = find_theme_dir("hicolor");
- if (dir) {
- list_add(dirs, dir);
- }
-
- return dirs;
-}
-
-struct subdir {
- int size;
- char name[];
-};
-
-static int subdir_str_cmp(const void *_subdir, const void *_str) {
- const struct subdir *subdir = _subdir;
- const char *str = _str;
- return strcmp(subdir->name, str);
-}
-/**
- * Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but
- * generates a list of struct subdirs
- */
-static list_t *split_subdirs(char *subdir_str) {
- list_t *subdir_list = create_list();
- char *copy = strdup(subdir_str);
- if (!subdir_list || !copy) {
- list_free(subdir_list);
- free(copy);
- return NULL;
- }
-
- char *token;
- token = strtok(copy, ",");
- while(token) {
- int len = strlen(token) + 1;
- struct subdir *subdir =
- malloc(sizeof(struct subdir) + sizeof(char [len]));
- if (!subdir) {
- // Return what we have
- return subdir_list;
- }
- subdir->size = 0;
- strcpy(subdir->name, token);
-
- list_add(subdir_list, subdir);
-
- token = strtok(NULL, ",");
- }
- free(copy);
-
- return subdir_list;
-}
-/**
- * Returns a list of all subdirectories of a theme.
- * Take note: the subdir names are all relative to `theme_dir` and must be
- * combined with it to form a valid directory.
- *
- * Each member of the list is of type (struct subdir *) this struct contains
- * the name of the subdir, along with size information. These must be freed
- * bye the caller.
- *
- * This currently ignores min and max sizes of icons.
- */
-static list_t* find_theme_subdirs(const char *theme_dir) {
- const char index_name[] = "/index.theme";
- list_t *dirs = NULL;
- char *path = malloc(strlen(theme_dir) + sizeof(index_name));
- FILE *index = NULL;
- if (!path) {
- sway_log(L_ERROR, "Failed to allocate memory");
- goto fail;
- }
-
- strcpy(path, theme_dir);
- strcat(path, index_name);
-
- index = fopen(path, "r");
- if (!index) {
- sway_log(L_ERROR, "Could not open file: %s", path);
- goto fail;
- }
-
- char *buf = NULL;
- size_t n = 0;
- const char directories[] = "Directories";
- while (!feof(index) && getline(&buf, &n, index) != -1) {
- if (n <= sizeof(directories) + 1) {
- continue;
- }
- if (strncmp(directories, buf, sizeof(directories) - 1) == 0) {
- char *dirstr = buf + sizeof(directories);
- dirs = split_subdirs(dirstr);
- break;
- }
- }
- // Now, find the size of each dir
- struct subdir *current_subdir = NULL;
- const char size[] = "Size";
- while (!feof(index) && getline(&buf, &n, index) != -1) {
- if (buf[0] == '[') {
- int len = strlen(buf);
- if (buf[len-1] == '\n') {
- len--;
- }
- // replace ']'
- buf[len-1] = '\0';
-
- int index;
- if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) {
- current_subdir = (dirs->items[index]);
- }
- }
-
- if (strncmp(size, buf, sizeof(size) - 1) == 0) {
- if (current_subdir) {
- current_subdir->size = atoi(buf + sizeof(size));
- }
- }
- }
- free(buf);
-fail:
- free(path);
- if (index) {
- fclose(index);
- }
- return dirs;
-}
-
-/* Returns the file of an icon given its name and size */
-static char *find_icon_file(const char *name, int size) {
- int namelen = strlen(name);
- list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme);
- if (!dirs) {
- return NULL;
- }
- int min_size_diff = INT_MAX;
- char *current_file = NULL;
-
- for (int i = 0; i < dirs->length; ++i) {
- char *dir = dirs->items[i];
- list_t *subdirs = find_theme_subdirs(dir);
-
- if (!subdirs) {
- continue;
- }
-
- for (int i = 0; i < subdirs->length; ++i) {
- struct subdir *subdir = subdirs->items[i];
-
- // Only use an unsized if we don't already have a
- // canidate this should probably change to allow svgs
- if (!subdir->size && current_file) {
- continue;
- }
-
- int size_diff = abs(size - subdir->size);
-
- if (size_diff >= min_size_diff) {
- continue;
- }
-
- char *path = malloc(strlen(subdir->name) + strlen(dir) + 2);
-
- strcpy(path, dir);
- path[strlen(dir)] = '/';
- strcpy(path + strlen(dir) + 1, subdir->name);
-
- DIR *icons = opendir(path);
- if (!icons) {
- free(path);
- continue;
- }
-
- struct dirent *direntry;
- while ((direntry = readdir(icons)) != NULL) {
- int len = strlen(direntry->d_name);
- if (len <= namelen + 2) { //must have some ext
- continue;
- }
- if (strncmp(direntry->d_name, name, namelen) == 0) {
- char *ext = direntry->d_name + namelen + 1;
-#ifdef WITH_GDK_PIXBUF
- if (strcmp(ext, "png") == 0 ||
- strcmp(ext, "xpm") == 0 ||
- strcmp(ext, "svg") == 0) {
-#else
- if (strcmp(ext, "png") == 0) {
-#endif
- free(current_file);
- char *icon_path = malloc(strlen(path) + len + 2);
-
- strcpy(icon_path, path);
- icon_path[strlen(path)] = '/';
- strcpy(icon_path + strlen(path) + 1, direntry->d_name);
- current_file = icon_path;
- min_size_diff = size_diff;
- }
- }
- }
- free(path);
- closedir(icons);
- }
- free_flat_list(subdirs);
- }
- free_flat_list(dirs);
-
- return current_file;
-}
-
-cairo_surface_t *find_icon(const char *name, int size) {
- char *image_path = find_icon_file(name, size);
- if (image_path == NULL) {
- return NULL;
- }
-
- cairo_surface_t *image = NULL;
-#ifdef WITH_GDK_PIXBUF
- GError *err = NULL;
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
- if (!pixbuf) {
- sway_log(L_ERROR, "Failed to load icon image: %s", err->message);
- }
- image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
-#else
- // TODO make svg work? cairo supports it. maybe remove gdk alltogether
- image = cairo_image_surface_create_from_png(image_path);
-#endif //WITH_GDK_PIXBUF
- if (!image) {
- sway_log(L_ERROR, "Could not read icon image");
- return NULL;
- }
-
- free(image_path);
- return image;
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <dbus/dbus.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include "swaybar/tray/dbus.h"
-#include "swaybar/tray/sni.h"
-#include "swaybar/tray/icon.h"
-#include "swaybar/bar.h"
-#include "client/cairo.h"
-#include "log.h"
-
-// Not sure what this is but cairo needs it.
-static const cairo_user_data_key_t cairo_user_data_key;
-
-struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item,
- int height) {
- struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref));
- if (!sni_ref) {
- return NULL;
- }
- sni_ref->icon = cairo_image_surface_scale(item->image, height, height);
- sni_ref->ref = item;
-
- return sni_ref;
-}
-
-void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
- if (!sni_ref) {
- return;
- }
- cairo_surface_destroy(sni_ref->icon);
- free(sni_ref);
-}
-
-/* Gets the pixmap of an icon */
-static void reply_icon(DBusPendingCall *pending, void *_data) {
- struct StatusNotifierItem *item = _data;
-
- DBusMessage *reply = dbus_pending_call_steal_reply(pending);
-
- if (!reply) {
- sway_log(L_ERROR, "Did not get reply");
- goto bail;
- }
-
- int message_type = dbus_message_get_type(reply);
-
- if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
- char *msg;
-
- dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &msg,
- DBUS_TYPE_INVALID);
-
- sway_log(L_ERROR, "Message is error: %s", msg);
- goto bail;
- }
-
- DBusMessageIter iter;
- DBusMessageIter variant; /* v[a(iiay)] */
- DBusMessageIter array; /* a(iiay) */
- DBusMessageIter d_struct; /* (iiay) */
- DBusMessageIter icon; /* ay */
-
- dbus_message_iter_init(reply, &iter);
-
- // Each if here checks the types above before recursing
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
- dbus_message_iter_get_signature(&iter));
- goto bail;
- }
- dbus_message_iter_recurse(&iter, &variant);
-
- if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"",
- dbus_message_iter_get_signature(&variant));
- goto bail;
- }
-
- if (dbus_message_iter_get_element_count(&variant) == 0) {
- // Can't recurse if there are no items
- sway_log(L_INFO, "Item has no icon");
- goto bail;
- }
- dbus_message_iter_recurse(&variant, &array);
-
- dbus_message_iter_recurse(&array, &d_struct);
-
- int width;
- dbus_message_iter_get_basic(&d_struct, &width);
- dbus_message_iter_next(&d_struct);
-
- int height;
- dbus_message_iter_get_basic(&d_struct, &height);
- dbus_message_iter_next(&d_struct);
-
- int len = dbus_message_iter_get_element_count(&d_struct);
-
- if (!len) {
- sway_log(L_ERROR, "No icon data");
- goto bail;
- }
-
- // Also implies len % 4 == 0, useful below
- if (len != width * height * 4) {
- sway_log(L_ERROR, "Incorrect array size passed");
- goto bail;
- }
-
- dbus_message_iter_recurse(&d_struct, &icon);
-
- int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
- // FIXME support a variable stride
- // (works on my machine though for all tested widths)
- if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
- goto bail;
- }
-
- // Data is by reference, no need to free
- uint8_t *message_data;
- dbus_message_iter_get_fixed_array(&icon, &message_data, &len);
-
- uint8_t *image_data = malloc(stride * height);
- if (!image_data) {
- sway_log(L_ERROR, "Could not allocate memory for icon");
- goto bail;
- }
-
- // Transform from network byte order to host byte order
- // Assumptions are safe because the equality above
- uint32_t *network = (uint32_t *) message_data;
- uint32_t *host = (uint32_t *)image_data;
- for (int i = 0; i < width * height; ++i) {
- host[i] = ntohl(network[i]);
- }
-
- cairo_surface_t *image = cairo_image_surface_create_for_data(
- image_data, CAIRO_FORMAT_ARGB32,
- width, height, stride);
-
- if (image) {
- if (item->image) {
- cairo_surface_destroy(item->image);
- }
- item->image = image;
- // Free the image data on surface destruction
- cairo_surface_set_user_data(image,
- &cairo_user_data_key,
- image_data,
- free);
- item->dirty = true;
- dirty = true;
-
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
- return;
- } else {
- sway_log(L_ERROR, "Could not create image surface");
- free(image_data);
- }
-
-bail:
- if (reply) {
- dbus_message_unref(reply);
- }
- dbus_pending_call_unref(pending);
- sway_log(L_ERROR, "Could not get icon from item");
- return;
-}
-static void send_icon_msg(struct StatusNotifierItem *item) {
- DBusPendingCall *pending;
- DBusMessage *message = dbus_message_new_method_call(
- item->name,
- "/StatusNotifierItem",
- "org.freedesktop.DBus.Properties",
- "Get");
- const char *iface;
- if (item->kde_special_snowflake) {
- iface = "org.kde.StatusNotifierItem";
- } else {
- iface = "org.freedesktop.StatusNotifierItem";
- }
- const char *prop = "IconPixmap";
-
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_STRING, &prop,
- DBUS_TYPE_INVALID);
-
- bool status =
- dbus_connection_send_with_reply(conn, message, &pending, -1);
-
- dbus_message_unref(message);
-
- if (!(pending || status)) {
- sway_log(L_ERROR, "Could not get item icon");
- return;
- }
-
- dbus_pending_call_set_notify(pending, reply_icon, item, NULL);
-}
-
-/* Get an icon by its name */
-static void reply_icon_name(DBusPendingCall *pending, void *_data) {
- struct StatusNotifierItem *item = _data;
-
- DBusMessage *reply = dbus_pending_call_steal_reply(pending);
-
- if (!reply) {
- sway_log(L_INFO, "Got no icon name reply from item");
- goto bail;
- }
-
- int message_type = dbus_message_get_type(reply);
-
- if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
- char *msg;
-
- dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &msg,
- DBUS_TYPE_INVALID);
-
- sway_log(L_INFO, "Could not get icon name: %s", msg);
- goto bail;
- }
-
- DBusMessageIter iter; /* v[s] */
- DBusMessageIter variant; /* s */
-
- dbus_message_iter_init(reply, &iter);
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
- dbus_message_iter_get_signature(&iter));
- goto bail;
- }
- dbus_message_iter_recurse(&iter, &variant);
-
-
- if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
- sway_log(L_ERROR, "Relpy type incorrect");
- sway_log(L_ERROR, "Should be \"s\", is \"%s\"",
- dbus_message_iter_get_signature(&iter));
- goto bail;
- }
-
- char *icon_name;
- dbus_message_iter_get_basic(&variant, &icon_name);
-
- cairo_surface_t *image = find_icon(icon_name, 256);
-
- if (image) {
- sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name,
- cairo_image_surface_get_width(image));
- if (item->image) {
- cairo_surface_destroy(item->image);
- }
- item->image = image;
- item->dirty = true;
- dirty = true;
-
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
- return;
- }
-
-bail:
- if (reply) {
- dbus_message_unref(reply);
- }
- dbus_pending_call_unref(pending);
- // Now try the pixmap
- send_icon_msg(item);
- return;
-}
-static void send_icon_name_msg(struct StatusNotifierItem *item) {
- DBusPendingCall *pending;
- DBusMessage *message = dbus_message_new_method_call(
- item->name,
- "/StatusNotifierItem",
- "org.freedesktop.DBus.Properties",
- "Get");
- const char *iface;
- if (item->kde_special_snowflake) {
- iface = "org.kde.StatusNotifierItem";
- } else {
- iface = "org.freedesktop.StatusNotifierItem";
- }
- const char *prop = "IconName";
-
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_STRING, &prop,
- DBUS_TYPE_INVALID);
-
- bool status =
- dbus_connection_send_with_reply(conn, message, &pending, -1);
-
- dbus_message_unref(message);
-
- if (!(pending || status)) {
- sway_log(L_ERROR, "Could not get item icon name");
- return;
- }
-
- dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL);
-}
-
-void get_icon(struct StatusNotifierItem *item) {
- send_icon_name_msg(item);
-}
-
-void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
- const char *iface =
- (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
- : "org.freedesktop.StatusNotifierItem");
- DBusMessage *message = dbus_message_new_method_call(
- item->name,
- "/StatusNotifierItem",
- iface,
- "Activate");
-
- dbus_message_append_args(message,
- DBUS_TYPE_INT32, &x,
- DBUS_TYPE_INT32, &y,
- DBUS_TYPE_INVALID);
-
- dbus_connection_send(conn, message, NULL);
-
- dbus_message_unref(message);
-}
-
-void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
- const char *iface =
- (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
- : "org.freedesktop.StatusNotifierItem");
- DBusMessage *message = dbus_message_new_method_call(
- item->name,
- "/StatusNotifierItem",
- iface,
- "ContextMenu");
-
- dbus_message_append_args(message,
- DBUS_TYPE_INT32, &x,
- DBUS_TYPE_INT32, &y,
- DBUS_TYPE_INVALID);
-
- dbus_connection_send(conn, message, NULL);
-
- dbus_message_unref(message);
-}
-void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
- const char *iface =
- (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
- : "org.freedesktop.StatusNotifierItem");
- DBusMessage *message = dbus_message_new_method_call(
- item->name,
- "/StatusNotifierItem",
- iface,
- "SecondaryActivate");
-
- dbus_message_append_args(message,
- DBUS_TYPE_INT32, &x,
- DBUS_TYPE_INT32, &y,
- DBUS_TYPE_INVALID);
-
- dbus_connection_send(conn, message, NULL);
-
- dbus_message_unref(message);
-}
-
-static void get_unique_name(struct StatusNotifierItem *item) {
- // I think that we're fine being sync here becaues the message is
- // directly to the message bus. Could be async though.
- DBusMessage *message = dbus_message_new_method_call(
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "GetNameOwner");
-
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &item->name,
- DBUS_TYPE_INVALID);
-
- DBusMessage *reply = dbus_connection_send_with_reply_and_block(
- conn, message, -1, NULL);
-
- dbus_message_unref(message);
-
- if (!reply) {
- sway_log(L_ERROR, "Could not get unique name for item: %s",
- item->name);
- return;
- }
-
- char *unique_name;
- if (!dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &unique_name,
- DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error parsing method args");
- } else {
- if (item->unique_name) {
- free(item->unique_name);
- }
- item->unique_name = strdup(unique_name);
- }
-
- dbus_message_unref(reply);
-}
-
-struct StatusNotifierItem *sni_create(const char *name) {
- // Make sure `name` is well formed
- if (!dbus_validate_bus_name(name, NULL)) {
- sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name);
- return NULL;
- }
-
- struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
- item->name = strdup(name);
- item->unique_name = NULL;
- item->image = NULL;
- item->dirty = false;
-
- // If it doesn't use this name then assume that it uses the KDE spec
- // This is because xembed-sni-proxy uses neither "org.freedesktop" nor
- // "org.kde" and just gives us the items "unique name"
- //
- // We could use this to our advantage and fill out the "unique name"
- // field with the given name if it is neither freedesktop or kde, but
- // that's makes us rely on KDE hackyness which is bad practice
- const char freedesktop_name[] = "org.freedesktop";
- if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) {
- item->kde_special_snowflake = true;
- } else {
- item->kde_special_snowflake = false;
- }
-
- get_icon(item);
-
- get_unique_name(item);
-
- return item;
-}
-/* Return 0 if `item` has a name of `str` */
-int sni_str_cmp(const void *_item, const void *_str) {
- const struct StatusNotifierItem *item = _item;
- const char *str = _str;
-
- return strcmp(item->name, str);
-}
-/* Returns 0 if `item` has a unique name of `str` */
-int sni_uniq_cmp(const void *_item, const void *_str) {
- const struct StatusNotifierItem *item = _item;
- const char *str = _str;
-
- if (!item->unique_name) {
- return false;
- }
- return strcmp(item->unique_name, str);
-}
-void sni_free(struct StatusNotifierItem *item) {
- if (!item) {
- return;
- }
- free(item->name);
- if (item->unique_name) {
- free(item->unique_name);
- }
- if (item->image) {
- cairo_surface_destroy(item->image);
- }
- free(item);
-}
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 @@
-#define _XOPEN_SOURCE 500
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <dbus/dbus.h>
-#include "swaybar/tray/dbus.h"
-#include "list.h"
-#include "log.h"
-
-static list_t *items = NULL;
-static list_t *hosts = NULL;
-
-/**
- * Describes the function of the StatusNotifierWatcher
- * See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/
- *
- * We also implement KDE's special snowflake protocol, it's like this but with
- * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
- */
-static const char *interface_xml =
- "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
- "'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>"
- "<node>"
- " <interface name='org.freedesktop.DBus.Introspectable'>"
- " <method name='Introspect'>"
- " <arg name='xml_data' direction='out' type='s'/>"
- " </method>"
- " </interface>"
- " <interface name='org.freedesktop.DBus.Properties'>"
- " <method name='Get'>"
- " <arg name='interface' direction='in' type='s'/>"
- " <arg name='propname' direction='in' type='s'/>"
- " <arg name='value' direction='out' type='v'/>"
- " </method>"
- " <method name='Set'>"
- " <arg name='interface' direction='in' type='s'/>"
- " <arg name='propname' direction='in' type='s'/>"
- " <arg name='value' direction='in' type='v'/>"
- " </method>"
- " <method name='GetAll'>"
- " <arg name='interface' direction='in' type='s'/>"
- " <arg name='props' direction='out' type='a{sv}'/>"
- " </method>"
- " </interface>"
- " <interface name='org.freedesktop.StatusNotifierWatcher'>"
- " <method name='RegisterStatusNotifierItem'>"
- " <arg type='s' name='service' direction='in'/>"
- " </method>"
- " <method name='RegisterStatusNotifierHost'>"
- " <arg type='s' name='service' direction='in'/>"
- " </method>"
- " <property name='RegisteredStatusNotifierItems' type='as' access='read'/>"
- " <property name='IsStatusNotifierHostRegistered' type='b' access='read'/>"
- " <property name='ProtocolVersion' type='i' access='read'/>"
- " <signal name='StatusNotifierItemRegistered'>"
- " <arg type='s' name='service' direction='out'/>"
- " </signal>"
- " <signal name='StatusNotifierItemUnregistered'>"
- " <arg type='s' name='service' direction='out'/>"
- " </signal>"
- " <signal name='StatusNotifierHostRegistered'>"
- " <arg type='' name='service' direction='out'/>"
- " </signal>"
- " </interface>"
- "</node>";
-
-static void host_registered_signal(DBusConnection *connection) {
- // Send one signal for each protocol
- DBusMessage *signal = dbus_message_new_signal(
- "/StatusNotifierWatcher",
- "org.freedesktop.StatusNotifierWatcher",
- "StatusNotifierHostRegistered");
-
- dbus_connection_send(connection, signal, NULL);
- dbus_message_unref(signal);
-
-
- signal = dbus_message_new_signal(
- "/StatusNotifierWatcher",
- "org.kde.StatusNotifierWatcher",
- "StatusNotifierHostRegistered");
-
- dbus_connection_send(connection, signal, NULL);
- dbus_message_unref(signal);
-}
-static void item_registered_signal(DBusConnection *connection, const char *name) {
- DBusMessage *signal = dbus_message_new_signal(
- "/StatusNotifierWatcher",
- "org.freedesktop.StatusNotifierWatcher",
- "StatusNotifierItemRegistered");
- dbus_message_append_args(signal,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
- dbus_connection_send(connection, signal, NULL);
- dbus_message_unref(signal);
-
- signal = dbus_message_new_signal(
- "/StatusNotifierWatcher",
- "org.kde.StatusNotifierWatcher",
- "StatusNotifierItemRegistered");
- dbus_message_append_args(signal,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
- dbus_connection_send(connection, signal, NULL);
- dbus_message_unref(signal);
-}
-static void item_unregistered_signal(DBusConnection *connection, const char *name) {
- DBusMessage *signal = dbus_message_new_signal(
- "/StatusNotifierWatcher",
- "org.freedesktop.StatusNotifierWatcher",
- "StatusNotifierItemUnregistered");
- dbus_message_append_args(signal,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
- dbus_connection_send(connection, signal, NULL);
- dbus_message_unref(signal);
-
- signal = dbus_message_new_signal(
- "/StatusNotifierWatcher",
- "org.kde.StatusNotifierWatcher",
- "StatusNotifierItemUnregistered");
- dbus_message_append_args(signal,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
- dbus_connection_send(connection, signal, NULL);
- dbus_message_unref(signal);
-}
-
-static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
- DBusMessage *reply;
-
- reply = dbus_message_new_method_return(request);
- dbus_message_append_args(reply,
- DBUS_TYPE_STRING, &interface_xml,
- DBUS_TYPE_INVALID);
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
-}
-
-static void register_item(DBusConnection *connection, DBusMessage *message) {
- DBusError error;
- char *name;
-
- dbus_error_init(&error);
- if (!dbus_message_get_args(message, &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
- }
-
- sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
-
- // Don't add duplicate or not real item
- if (!dbus_validate_bus_name(name, NULL)) {
- sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
- return;
- }
-
- if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
- return;
- }
- if (!dbus_bus_name_has_owner(connection, name, &error)) {
- return;
- }
-
- list_add(items, strdup(name));
- item_registered_signal(connection, name);
-
- // It's silly, but xembedsniproxy wants a reply for this function
- DBusMessage *reply = dbus_message_new_method_return(message);
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
-}
-
-static void register_host(DBusConnection *connection, DBusMessage *message) {
- DBusError error;
- char *name;
-
- dbus_error_init(&error);
- if (!dbus_message_get_args(message, &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
- }
-
- sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
-
- // Don't add duplicate or not real host
- if (!dbus_validate_bus_name(name, NULL)) {
- sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
- return;
- }
-
-
- if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) {
- return;
- }
- if (!dbus_bus_name_has_owner(connection, name, &error)) {
- return;
- }
-
- list_add(hosts, strdup(name));
- host_registered_signal(connection);
-}
-
-static void get_property(DBusConnection *connection, DBusMessage *message) {
- DBusError error;
- char *interface;
- char *property;
-
- dbus_error_init(&error);
- if (!dbus_message_get_args(message, &error,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
- return;
- }
-
- if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
- sway_log(L_INFO, "Replying with items\n");
- DBusMessage *reply;
- reply = dbus_message_new_method_return(message);
- DBusMessageIter iter;
- DBusMessageIter sub;
- DBusMessageIter subsub;
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "as", &sub);
- dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
- "s", &subsub);
-
- for (int i = 0; i < items->length; ++i) {
- dbus_message_iter_append_basic(&subsub,
- DBUS_TYPE_STRING, &items->items[i]);
- }
-
- dbus_message_iter_close_container(&sub, &subsub);
- dbus_message_iter_close_container(&iter, &sub);
-
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
- } else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) {
- DBusMessage *reply;
- DBusMessageIter iter;
- DBusMessageIter sub;
- int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
-
- reply = dbus_message_new_method_return(message);
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "b", &sub);
- dbus_message_iter_append_basic(&sub,
- DBUS_TYPE_BOOLEAN, &registered);
-
- dbus_message_iter_close_container(&iter, &sub);
-
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
- } else if (strcmp(property, "ProtocolVersion") == 0) {
- DBusMessage *reply;
- DBusMessageIter iter;
- DBusMessageIter sub;
- const int version = 0;
-
- reply = dbus_message_new_method_return(message);
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "i", &sub);
- dbus_message_iter_append_basic(&sub,
- DBUS_TYPE_INT32, &version);
-
- dbus_message_iter_close_container(&iter, &sub);
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
- }
-}
-
-static void set_property(DBusConnection *connection, DBusMessage *message) {
- // All properties are read only and we don't allow new properties
- return;
-}
-
-static void get_all(DBusConnection *connection, DBusMessage *message) {
- DBusMessage *reply;
- reply = dbus_message_new_method_return(message);
- DBusMessageIter iter; /* a{v} */
- DBusMessageIter arr;
- DBusMessageIter dict;
- DBusMessageIter sub;
- DBusMessageIter subsub;
- int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
- const int version = 0;
- const char *prop;
-
- // Could clean this up with a function for each prop
- dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- "{sv}", &arr);
-
- prop = "RegisteredStatusNotifierItems";
- dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
- NULL, &dict);
- dbus_message_iter_append_basic(&dict,
- DBUS_TYPE_STRING, &prop);
- dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
- "as", &sub);
- dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
- "s", &subsub);
- for (int i = 0; i < items->length; ++i) {
- dbus_message_iter_append_basic(&subsub,
- DBUS_TYPE_STRING, &items->items[i]);
- }
- dbus_message_iter_close_container(&sub, &subsub);
- dbus_message_iter_close_container(&dict, &sub);
- dbus_message_iter_close_container(&arr, &dict);
-
- prop = "IsStatusNotifierHostRegistered";
- dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
- NULL, &dict);
- dbus_message_iter_append_basic(&dict,
- DBUS_TYPE_STRING, &prop);
- dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
- "b", &sub);
- dbus_message_iter_append_basic(&sub,
- DBUS_TYPE_BOOLEAN, &registered);
- dbus_message_iter_close_container(&dict, &sub);
- dbus_message_iter_close_container(&arr, &dict);
-
- prop = "ProtocolVersion";
- dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
- NULL, &dict);
- dbus_message_iter_append_basic(&dict,
- DBUS_TYPE_STRING, &prop);
- dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
- "i", &sub);
- dbus_message_iter_append_basic(&sub,
- DBUS_TYPE_INT32, &version);
- dbus_message_iter_close_container(&dict, &sub);
- dbus_message_iter_close_container(&arr, &dict);
-
- dbus_message_iter_close_container(&iter, &arr);
-
- dbus_connection_send(connection, reply, NULL);
- dbus_message_unref(reply);
-}
-
-static DBusHandlerResult message_handler(DBusConnection *connection,
- DBusMessage *message, void *data) {
- const char *interface_name = dbus_message_get_interface(message);
- const char *member_name = dbus_message_get_member(message);
-
- // In order of the xml above
- if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 &&
- strcmp(member_name, "Introspect") == 0) {
- // We don't have an introspect for KDE
- respond_to_introspect(connection, message);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) {
- if (strcmp(member_name, "Get") == 0) {
- get_property(connection, message);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (strcmp(member_name, "Set") == 0) {
- set_property(connection, message);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (strcmp(member_name, "GetAll") == 0) {
- get_all(connection, message);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else {
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- } else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 ||
- strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) {
- if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) {
- register_item(connection, message);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) {
- register_host(connection, message);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else {
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- }
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static DBusHandlerResult signal_handler(DBusConnection *connection,
- DBusMessage *message, void *_data) {
- if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) {
- // Only eat the message if it is name that we are watching
- const char *name;
- const char *old_owner;
- const char *new_owner;
- int index;
- if (!dbus_message_get_args(message, NULL,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &old_owner,
- DBUS_TYPE_STRING, &new_owner,
- DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error getting LostName args");
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- if (strcmp(new_owner, "") != 0) {
- // Name is not lost
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) {
- sway_log(L_INFO, "Status Notifier Item lost %s", name);
- free(items->items[index]);
- list_del(items, index);
- item_unregistered_signal(connection, name);
-
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) {
- sway_log(L_INFO, "Status Notifier Host lost %s", name);
- free(hosts->items[index]);
- list_del(hosts, index);
-
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- }
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static const DBusObjectPathVTable vtable = {
- .message_function = message_handler,
- .unregister_function = NULL,
-};
-
-int init_sni_watcher() {
- DBusError error;
- dbus_error_init(&error);
- if (!conn) {
- sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher");
- return -1;
- }
-
- items = create_list();
- hosts = create_list();
-
- int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
- DBUS_NAME_FLAG_REPLACE_EXISTING,
- &error);
- if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
- sway_log(L_DEBUG, "Got watcher name");
- } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
- sway_log(L_INFO, "Could not get watcher name, it may start later");
- }
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message);
- return -1;
- }
-
- status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher",
- DBUS_NAME_FLAG_REPLACE_EXISTING,
- &error);
- if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
- sway_log(L_DEBUG, "Got kde watcher name");
- } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
- sway_log(L_INFO, "Could not get kde watcher name, it may start later");
- }
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message);
- return -1;
- }
-
- dbus_connection_try_register_object_path(conn,
- "/StatusNotifierWatcher",
- &vtable, NULL, &error);
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus_err: %s\n", error.message);
- return -1;
- }
-
- dbus_bus_add_match(conn,
- "type='signal',\
- sender='org.freedesktop.DBus',\
- interface='org.freedesktop.DBus',\
- member='NameOwnerChanged'",
- &error);
-
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "DBus error getting match args: %s", error.message);
- }
-
- dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
- return 0;
-}
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 @@
-#define _XOPEN_SOURCE 700
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <dbus/dbus.h>
-#include "swaybar/bar.h"
-#include "swaybar/tray/tray.h"
-#include "swaybar/tray/dbus.h"
-#include "swaybar/tray/sni.h"
-#include "swaybar/tray/sni_watcher.h"
-#include "swaybar/bar.h"
-#include "swaybar/config.h"
-#include "list.h"
-#include "log.h"
-
-struct tray *tray;
-
-static void register_host(char *name) {
- DBusMessage *message;
-
- message = dbus_message_new_method_call(
- "org.freedesktop.StatusNotifierWatcher",
- "/StatusNotifierWatcher",
- "org.freedesktop.StatusNotifierWatcher",
- "RegisterStatusNotifierHost");
- if (!message) {
- sway_log(L_ERROR, "Cannot allocate dbus method call");
- return;
- }
-
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
-
- dbus_connection_send(conn, message, NULL);
-
- dbus_message_unref(message);
-}
-
-static void get_items_reply(DBusPendingCall *pending, void *_data) {
- DBusMessage *reply = dbus_pending_call_steal_reply(pending);
-
- if (!reply) {
- sway_log(L_ERROR, "Got no items reply from sni watcher");
- goto bail;
- }
-
- int message_type = dbus_message_get_type(reply);
-
- if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
- char *msg;
-
- dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &msg,
- DBUS_TYPE_INVALID);
-
- sway_log(L_ERROR, "Message is error: %s", msg);
- goto bail;
- }
-
- DBusMessageIter iter;
- DBusMessageIter variant;
- DBusMessageIter array;
-
- dbus_message_iter_init(reply, &iter);
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
- goto bail;
- }
- dbus_message_iter_recurse(&iter, &variant);
- if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
- sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
- goto bail;
- }
-
- // Clear list
- list_foreach(tray->items, (void (*)(void *))sni_free);
- list_free(tray->items);
- tray->items = create_list();
-
- // O(n) function, could be faster dynamically reading values
- int len = dbus_message_iter_get_element_count(&variant);
-
- dbus_message_iter_recurse(&variant, &array);
- for (int i = 0; i < len; i++) {
- const char *name;
- dbus_message_iter_get_basic(&array, &name);
-
- struct StatusNotifierItem *item = sni_create(name);
-
- if (item) {
- sway_log(L_DEBUG, "Item registered with host: %s", name);
- list_add(tray->items, item);
- dirty = true;
- }
- }
-
-bail:
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
- return;
-}
-static void get_items() {
- DBusPendingCall *pending;
- DBusMessage *message = dbus_message_new_method_call(
- "org.freedesktop.StatusNotifierWatcher",
- "/StatusNotifierWatcher",
- "org.freedesktop.DBus.Properties",
- "Get");
-
- const char *iface = "org.freedesktop.StatusNotifierWatcher";
- const char *prop = "RegisteredStatusNotifierItems";
- dbus_message_append_args(message,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_STRING, &prop,
- DBUS_TYPE_INVALID);
-
- bool status =
- dbus_connection_send_with_reply(conn, message, &pending, -1);
- dbus_message_unref(message);
-
- if (!(pending || status)) {
- sway_log(L_ERROR, "Could not get items");
- return;
- }
-
- dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL);
-}
-
-static DBusHandlerResult signal_handler(DBusConnection *connection,
- DBusMessage *message, void *_data) {
- if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
- "StatusNotifierItemRegistered")) {
- const char *name;
- if (!dbus_message_get_args(message, NULL,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args");
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
- struct StatusNotifierItem *item = sni_create(name);
-
- if (item) {
- list_add(tray->items, item);
- dirty = true;
- }
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
- "StatusNotifierItemUnregistered")) {
- const char *name;
- if (!dbus_message_get_args(message, NULL,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID)) {
- sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args");
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- int index;
- if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
- sni_free(tray->items->items[index]);
- list_del(tray->items, index);
- dirty = true;
- } else {
- // If it's not in our list, then our list is incorrect.
- // Fetch all items again
- sway_log(L_INFO, "Host item list incorrect, refreshing");
- get_items();
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem",
- "NewIcon") || dbus_message_is_signal(message,
- "org.kde.StatusNotifierItem", "NewIcon")) {
- const char *name;
- int index;
- struct StatusNotifierItem *item;
-
- name = dbus_message_get_sender(message);
- if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) {
- item = tray->items->items[index];
- sway_log(L_INFO, "NewIcon signal from item %s", item->name);
- get_icon(item);
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static int init_host() {
- tray = (struct tray *)malloc(sizeof(tray));
-
- tray->items = create_list();
-
- DBusError error;
- dbus_error_init(&error);
- char *name = NULL;
- if (!conn) {
- sway_log(L_ERROR, "Connection is null, cannot init SNI host");
- goto err;
- }
- name = calloc(sizeof(char), 256);
-
- if (!name) {
- sway_log(L_ERROR, "Cannot allocate name");
- goto err;
- }
-
- pid_t pid = getpid();
- if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid)
- >= 256) {
- sway_log(L_ERROR, "Cannot get host name because string is too short."
- "This should not happen");
- goto err;
- }
-
- // We want to be the sole owner of this name
- if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE,
- &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
- sway_log(L_ERROR, "Cannot get host name and start the tray");
- goto err;
- }
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message);
- goto err;
- }
- sway_log(L_DEBUG, "Got host name");
-
- register_host(name);
-
- get_items();
-
- // Perhaps use addmatch helper functions like wlc does?
- dbus_bus_add_match(conn,
- "type='signal',\
- sender='org.freedesktop.StatusNotifierWatcher',\
- member='StatusNotifierItemRegistered'",
- &error);
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus_err: %s", error.message);
- goto err;
- }
- dbus_bus_add_match(conn,
- "type='signal',\
- sender='org.freedesktop.StatusNotifierWatcher',\
- member='StatusNotifierItemUnregistered'",
- &error);
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus_err: %s", error.message);
- return -1;
- }
-
- // SNI matches
- dbus_bus_add_match(conn,
- "type='signal',\
- interface='org.freedesktop.StatusNotifierItem',\
- member='NewIcon'",
- &error);
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus_err %s", error.message);
- goto err;
- }
- dbus_bus_add_match(conn,
- "type='signal',\
- interface='org.kde.StatusNotifierItem',\
- member='NewIcon'",
- &error);
- if (dbus_error_is_set(&error)) {
- sway_log(L_ERROR, "dbus_err %s", error.message);
- goto err;
- }
-
- dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
-
- free(name);
- return 0;
-
-err:
- // TODO better handle errors
- free(name);
- return -1;
-}
-
-void tray_mouse_event(struct output *output, int x, int y,
- uint32_t button, uint32_t state) {
-
- struct window *window = output->window;
- uint32_t tray_padding = swaybar.config->tray_padding;
- int tray_width = window->width * window->scale;
-
- for (int i = 0; i < output->items->length; ++i) {
- struct sni_icon_ref *item =
- output->items->items[i];
- int icon_width = cairo_image_surface_get_width(item->icon);
-
- tray_width -= tray_padding;
- if (x <= tray_width && x >= tray_width - icon_width) {
- if (button == swaybar.config->activate_button) {
- sni_activate(item->ref, x, y);
- } else if (button == swaybar.config->context_button) {
- sni_context_menu(item->ref, x, y);
- } else if (button == swaybar.config->secondary_button) {
- sni_secondary(item->ref, x, y);
- }
- break;
- }
- tray_width -= icon_width;
- }
-}
-
-uint32_t tray_render(struct output *output, struct config *config) {
- struct window *window = output->window;
- cairo_t *cairo = window->cairo;
-
- // Tray icons
- uint32_t tray_padding = config->tray_padding;
- uint32_t tray_width = window->width * window->scale;
- const int item_size = (window->height * window->scale) - (2 * tray_padding);
-
- if (item_size < 0) {
- // Can't render items if the padding is too large
- return tray_width;
- }
-
- if (config->tray_output && strcmp(config->tray_output, output->name) != 0) {
- return tray_width;
- }
-
- for (int i = 0; i < tray->items->length; ++i) {
- struct StatusNotifierItem *item =
- tray->items->items[i];
- if (!item->image) {
- continue;
- }
-
- struct sni_icon_ref *render_item = NULL;
- int j;
- for (j = i; j < output->items->length; ++j) {
- struct sni_icon_ref *ref =
- output->items->items[j];
- if (ref->ref == item) {
- render_item = ref;
- break;
- } else {
- sni_icon_ref_free(ref);
- list_del(output->items, j);
- }
- }
-
- if (!render_item) {
- render_item = sni_icon_ref_create(item, item_size);
- list_add(output->items, render_item);
- } else if (item->dirty) {
- // item needs re-render
- sni_icon_ref_free(render_item);
- output->items->items[j] = render_item =
- sni_icon_ref_create(item, item_size);
- }
-
- tray_width -= tray_padding;
- tray_width -= item_size;
-
- cairo_operator_t op = cairo_get_operator(cairo);
- cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
- cairo_set_source_surface(cairo, render_item->icon, tray_width, tray_padding);
- cairo_rectangle(cairo, tray_width, tray_padding, item_size, item_size);
- cairo_fill(cairo);
- cairo_set_operator(cairo, op);
-
- item->dirty = false;
- }
-
-
- if (tray_width != window->width * window->scale) {
- tray_width -= tray_padding;
- }
-
- return tray_width;
-}
-
-void init_tray(struct bar *bar) {
- if (!bar->config->tray_output || strcmp(bar->config->tray_output, "none") != 0) {
- /* Connect to the D-Bus */
- dbus_init();
-
- /* Start the SNI watcher */
- init_sni_watcher();
-
- /* Start the SNI host */
- init_host();
- }
-}
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 @@
-include_directories(
- ${PROTOCOLS_INCLUDE_DIRS}
- ${WAYLAND_INCLUDE_DIR}
- ${CAIRO_INCLUDE_DIRS}
- ${PANGO_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
-)
-
-add_executable(swaybg
- main.c
-)
-
-target_link_libraries(swaybg
- sway-common
- sway-wayland
- ${WAYLAND_CLIENT_LIBRARIES}
- ${WAYLAND_CURSOR_LIBRARIES}
- ${CAIRO_LIBRARIES}
- ${PANGO_LIBRARIES}
- m
-)
-
-if (WITH_GDK_PIXBUF)
- include_directories(
- ${GDK_PIXBUF_INCLUDE_DIRS}
- )
- target_link_libraries(swaybg
- ${GDK_PIXBUF_LIBRARIES}
- )
-endif()
-
-install(
- TARGETS swaybg
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
-)
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 @@
-#include "wayland-desktop-shell-client-protocol.h"
+#include <assert.h>
+#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
-#include <wayland-client.h>
-#include <time.h>
#include <string.h>
-#include "client/window.h"
-#include "client/registry.h"
-#include "client/cairo.h"
-#include "log.h"
-#include "list.h"
+#include <time.h>
+#include <wayland-client.h>
+#include <wlr/util/log.h>
+#include "background-image.h"
+#include "pool-buffer.h"
+#include "cairo.h"
#include "util.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
-list_t *surfaces;
-struct registry *registry;
+struct swaybg_args {
+ int output_idx;
+ const char *path;
+ enum background_mode mode;
+};
-enum scaling_mode {
- SCALING_MODE_STRETCH,
- SCALING_MODE_FILL,
- SCALING_MODE_FIT,
- SCALING_MODE_CENTER,
- SCALING_MODE_TILE,
+struct swaybg_context {
+ uint32_t color;
+ cairo_surface_t *image;
};
-void sway_terminate(int exit_code) {
- int i;
- for (i = 0; i < surfaces->length; ++i) {
- struct window *window = surfaces->items[i];
- window_teardown(window);
- }
- list_free(surfaces);
- registry_teardown(registry);
- exit(exit_code);
-}
+struct swaybg_state {
+ const struct swaybg_args *args;
+ struct swaybg_context context;
+
+ struct wl_display *display;
+ struct wl_compositor *compositor;
+ struct zwlr_layer_shell_v1 *layer_shell;
+ struct wl_shm *shm;
+
+ struct wl_output *output;
+ struct wl_surface *surface;
+ struct wl_region *input_region;
+ struct zwlr_layer_surface_v1 *layer_surface;
+
+ bool run_display;
+ uint32_t width, height;
+ int32_t scale;
+ struct pool_buffer buffers[2];
+ struct pool_buffer *current_buffer;
+};
bool is_valid_color(const char *color) {
int len = strlen(color);
if (len != 7 || color[0] != '#') {
- sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color);
+ wlr_log(L_ERROR, "%s is not a valid color for swaybg. "
+ "Color should be specified as #rrggbb (no alpha).", color);
return false;
}
@@ -52,163 +63,181 @@ bool is_valid_color(const char *color) {
return true;
}
-int main(int argc, const char **argv) {
- init_log(L_INFO);
- surfaces = create_list();
- registry = registry_poll();
-
- if (argc != 4) {
- sway_abort("Do not run this program manually. See man 5 sway and look for output options.");
+static void render_frame(struct swaybg_state *state) {
+ int buffer_width = state->width * state->scale,
+ buffer_height = state->height * state->scale;
+ state->current_buffer = get_next_buffer(state->shm,
+ state->buffers, buffer_width, buffer_height);
+ cairo_t *cairo = state->current_buffer->cairo;
+ if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
+ cairo_set_source_u32(cairo, state->context.color);
+ cairo_paint(cairo);
+ } else {
+ render_background_image(cairo, state->context.image,
+ state->args->mode, buffer_width, buffer_height);
}
- if (!registry->desktop_shell) {
- sway_abort("swaybg requires the compositor to support the desktop-shell extension.");
+ wl_surface_set_buffer_scale(state->surface, state->scale);
+ wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0);
+ wl_surface_damage(state->surface, 0, 0, state->width, state->height);
+ wl_surface_commit(state->surface);
+}
+
+static bool prepare_context(struct swaybg_state *state) {
+ if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
+ state->context.color = parse_color(state->args->path);
+ return is_valid_color(state->args->path);
}
+ if (!(state->context.image = load_background_image(state->args->path))) {
+ return false;
+ }
+ return true;
+}
- int desired_output = atoi(argv[1]);
- sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length);
- int i;
- struct output_state *output = registry->outputs->items[desired_output];
- struct window *window = window_setup(registry,
- output->width, output->height, output->scale, false);
- if (!window) {
- sway_abort("Failed to create surfaces.");
+static void layer_surface_configure(void *data,
+ struct zwlr_layer_surface_v1 *surface,
+ uint32_t serial, uint32_t width, uint32_t height) {
+ struct swaybg_state *state = data;
+ state->width = width;
+ state->height = height;
+ zwlr_layer_surface_v1_ack_configure(surface, serial);
+ render_frame(state);
+}
+
+static void layer_surface_closed(void *data,
+ struct zwlr_layer_surface_v1 *surface) {
+ struct swaybg_state *state = data;
+ zwlr_layer_surface_v1_destroy(state->layer_surface);
+ wl_surface_destroy(state->surface);
+ wl_region_destroy(state->input_region);
+ state->run_display = false;
+}
+
+struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = layer_surface_configure,
+ .closed = layer_surface_closed,
+};
+
+static void output_geometry(void *data, struct wl_output *output, int32_t x,
+ int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
+ const char *make, const char *model, int32_t transform) {
+ // Who cares
+}
+
+static void output_mode(void *data, struct wl_output *output, uint32_t flags,
+ int32_t width, int32_t height, int32_t refresh) {
+ // Who cares
+}
+
+static void output_done(void *data, struct wl_output *output) {
+ // Who cares
+}
+
+static void output_scale(void *data, struct wl_output *output, int32_t factor) {
+ struct swaybg_state *state = data;
+ state->scale = factor;
+ if (state->run_display) {
+ render_frame(state);
}
- desktop_shell_set_background(registry->desktop_shell, output->output, window->surface);
- window_make_shell(window);
- list_add(surfaces, window);
-
- if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) {
- cairo_set_source_u32(window->cairo, parse_color(argv[2]));
- cairo_paint(window->cairo);
- window_render(window);
- } else {
-#ifdef WITH_GDK_PIXBUF
- GError *err = NULL;
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err);
- if (!pixbuf) {
- sway_abort("Failed to load background image.");
- }
- cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
-#else
- cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
-#endif //WITH_GDK_PIXBUF
- if (!image) {
- sway_abort("Failed to read background image.");
- }
- if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
- sway_abort("Failed to read background image: %s."
-#ifndef WITH_GDK_PIXBUF
- "\nSway was compiled without gdk_pixbuf support, so only"
- "\nPNG images can be loaded. This is the likely cause."
-#endif //WITH_GDK_PIXBUF
- , cairo_status_to_string(cairo_surface_status(image)));
- }
- double width = cairo_image_surface_get_width(image);
- double height = cairo_image_surface_get_height(image);
-
- const char *scaling_mode_str = argv[3];
- enum scaling_mode scaling_mode = SCALING_MODE_STRETCH;
- if (strcmp(scaling_mode_str, "stretch") == 0) {
- scaling_mode = SCALING_MODE_STRETCH;
- } else if (strcmp(scaling_mode_str, "fill") == 0) {
- scaling_mode = SCALING_MODE_FILL;
- } else if (strcmp(scaling_mode_str, "fit") == 0) {
- scaling_mode = SCALING_MODE_FIT;
- } else if (strcmp(scaling_mode_str, "center") == 0) {
- scaling_mode = SCALING_MODE_CENTER;
- } else if (strcmp(scaling_mode_str, "tile") == 0) {
- scaling_mode = SCALING_MODE_TILE;
- } else {
- sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
- }
+}
- int wwidth = window->width * window->scale;
- int wheight = window->height * window->scale;
-
- for (i = 0; i < surfaces->length; ++i) {
- struct window *window = surfaces->items[i];
- if (window_prerender(window) && window->cairo) {
- switch (scaling_mode) {
- case SCALING_MODE_STRETCH:
- cairo_scale(window->cairo,
- (double) wwidth / width,
- (double) wheight / height);
- cairo_set_source_surface(window->cairo, image, 0, 0);
- break;
- case SCALING_MODE_FILL:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- } else {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- }
- break;
- }
- case SCALING_MODE_FIT:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- } else {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- }
- break;
- }
- case SCALING_MODE_CENTER:
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 - width/2,
- (double) wheight/2 - height/2);
- break;
- case SCALING_MODE_TILE:
- {
- cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
- cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
- cairo_set_source(window->cairo, pattern);
- break;
- }
- default:
- sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str);
- }
-
- cairo_paint(window->cairo);
-
- window_render(window);
- }
+struct wl_output_listener output_listener = {
+ .geometry = output_geometry,
+ .mode = output_mode,
+ .done = output_done,
+ .scale = output_scale,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ struct swaybg_state *state = data;
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ state->compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 3);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ state->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ static int output_idx = 0;
+ if (output_idx == state->args->output_idx) {
+ state->output = wl_registry_bind(registry, name,
+ &wl_output_interface, 3);
+ wl_output_add_listener(state->output, &output_listener, state);
}
+ output_idx++;
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ state->layer_shell = wl_registry_bind(
+ registry, name, &zwlr_layer_shell_v1_interface, 1);
+ }
+}
- cairo_surface_destroy(image);
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ // who cares
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
+
+int main(int argc, const char **argv) {
+ struct swaybg_args args = {0};
+ struct swaybg_state state = {0};
+ state.args = &args;
+ wlr_log_init(L_DEBUG, NULL);
+
+ if (argc != 4) {
+ wlr_log(L_ERROR, "Do not run this program manually. "
+ "See man 5 sway and look for output options.");
+ return 1;
}
+ args.output_idx = atoi(argv[1]);
+ args.path = argv[2];
- while (wl_display_dispatch(registry->display) != -1);
+ args.mode = parse_background_mode(argv[3]);
+ if (args.mode == BACKGROUND_MODE_INVALID) {
+ return 1;
+ }
+ if (!prepare_context(&state)) {
+ return 1;
+ }
- for (i = 0; i < surfaces->length; ++i) {
- struct window *window = surfaces->items[i];
- window_teardown(window);
+ assert(state.display = wl_display_connect(NULL));
+
+ struct wl_registry *registry = wl_display_get_registry(state.display);
+ wl_registry_add_listener(registry, &registry_listener, &state);
+ wl_display_roundtrip(state.display);
+ assert(state.compositor && state.layer_shell && state.output && state.shm);
+
+ // Second roundtrip to get output properties
+ wl_display_roundtrip(state.display);
+
+ assert(state.surface = wl_compositor_create_surface(state.compositor));
+
+ assert(state.input_region = wl_compositor_create_region(state.compositor));
+ wl_surface_set_input_region(state.surface, state.input_region);
+
+ state.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
+ state.layer_shell, state.surface, state.output,
+ ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");
+ assert(state.layer_surface);
+
+ zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0);
+ zwlr_layer_surface_v1_set_anchor(state.layer_surface,
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
+ zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1);
+ zwlr_layer_surface_v1_add_listener(state.layer_surface,
+ &layer_surface_listener, &state);
+ wl_surface_commit(state.surface);
+ wl_display_roundtrip(state.display);
+
+ state.run_display = true;
+ while (wl_display_dispatch(state.display) != -1 && state.run_display) {
+ // This space intentionally left blank
}
- list_free(surfaces);
- registry_teardown(registry);
return 0;
}
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 @@
+executable(
+ 'swaybg',
+ 'main.c',
+ include_directories: [sway_inc],
+ dependencies: [
+ cairo,
+ client_protos,
+ gdk_pixbuf,
+ jsonc,
+ math,
+ pango,
+ pangocairo,
+ wayland_client,
+ wlroots,
+ ],
+ link_with: [lib_sway_common, lib_sway_client],
+ install: true
+)
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 @@
-include_directories(
- ${JSONC_INCLUDE_DIRS}
- ${WLC_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
-)
-
-add_executable(swaygrab
- main.c
- json.c
-)
-
-target_link_libraries(swaygrab
- sway-common
- ${JSONC_LIBRARIES}
- rt
- m
-)
-
-install(
- TARGETS swaygrab
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
-)
-
-if (A2X_FOUND)
- add_manpage(swaygrab 1)
-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 @@
-include_directories(
- ${PROTOCOLS_INCLUDE_DIRS}
- ${WAYLAND_INCLUDE_DIR}
- ${CAIRO_INCLUDE_DIRS}
- ${PANGO_INCLUDE_DIRS}
- ${PAM_INCLUDE_DIRS}
- ${JSONC_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
-)
-
-add_executable(swaylock
- main.c
-)
-
-target_link_libraries(swaylock
- sway-common
- sway-wayland
- ${WAYLAND_CLIENT_LIBRARIES}
- ${WAYLAND_CURSOR_LIBRARIES}
- ${CAIRO_LIBRARIES}
- ${PANGO_LIBRARIES}
- ${PAM_LIBRARIES}
- ${JSONC_LIBRARIES}
- m
-)
-
-if (WITH_GDK_PIXBUF)
- include_directories(
- ${GDK_PIXBUF_INCLUDE_DIRS}
- )
- target_link_libraries(swaylock
- ${GDK_PIXBUF_LIBRARIES}
- )
-endif()
-
-install(
- TARGETS swaylock
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
-)
-
-install(
- FILES ${CMAKE_CURRENT_SOURCE_DIR}/pam/swaylock
- DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d/
- COMPONENT data
-)
-
-if (A2X_FOUND)
- add_manpage(swaylock 1)
-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 @@
-#define _XOPEN_SOURCE 500
-#include "wayland-swaylock-client-protocol.h"
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-names.h>
-#include <security/pam_appl.h>
-#include <json-c/json.h>
+#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 200112L
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdbool.h>
+#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
-#include "client/window.h"
-#include "client/registry.h"
-#include "client/cairo.h"
+#include <wayland-client.h>
+#include <wlr/util/log.h>
+#include "swaylock/seat.h"
#include "swaylock/swaylock.h"
-#include "ipc-client.h"
-#include "log.h"
+#include "background-image.h"
+#include "pool-buffer.h"
+#include "cairo.h"
#include "util.h"
+#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
-struct registry *registry;
-struct render_data render_data;
-struct lock_config *config;
-bool show_indicator = true;
-
-void wl_dispatch_events() {
- wl_display_flush(registry->display);
- if (wl_display_dispatch(registry->display) == -1) {
- sway_log(L_ERROR, "failed to run wl_display_dispatch");
+static void daemonize() {
+ int fds[2];
+ if (pipe(fds) != 0) {
+ wlr_log(L_ERROR, "Failed to pipe");
exit(1);
}
+ if (fork() == 0) {
+ close(fds[0]);
+ int devnull = open("/dev/null", O_RDWR);
+ dup2(STDOUT_FILENO, devnull);
+ dup2(STDERR_FILENO, devnull);
+ uint8_t success = 0;
+ if (chdir("/") != 0) {
+ write(fds[1], &success, 1);
+ exit(1);
+ }
+ success = 1;
+ if (write(fds[1], &success, 1) != 1) {
+ exit(1);
+ }
+ close(fds[1]);
+ } else {
+ close(fds[1]);
+ uint8_t success;
+ if (read(fds[0], &success, 1) != 1 || !success) {
+ wlr_log(L_ERROR, "Failed to daemonize");
+ exit(1);
+ }
+ close(fds[0]);
+ exit(0);
+ }
}
-void sigalarm_handler(int sig) {
- signal(SIGALRM, SIG_IGN);
- // Hide typing indicator
- render_data.auth_state = AUTH_STATE_IDLE;
- render(&render_data, config);
- wl_display_flush(registry->display);
- signal(SIGALRM, sigalarm_handler);
+static void layer_surface_configure(void *data,
+ struct zwlr_layer_surface_v1 *layer_surface,
+ uint32_t serial, uint32_t width, uint32_t height) {
+ struct swaylock_surface *surface = data;
+ surface->width = width;
+ surface->height = height;
+ zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
+ render_frame(surface);
}
-void sway_terminate(int exit_code) {
- int i;
- for (i = 0; i < render_data.surfaces->length; ++i) {
- struct window *window = render_data.surfaces->items[i];
- window_teardown(window);
- }
- list_free(render_data.surfaces);
- if (registry) {
- registry_teardown(registry);
- }
- exit(exit_code);
+static void layer_surface_closed(void *data,
+ struct zwlr_layer_surface_v1 *layer_surface) {
+ struct swaylock_surface *surface = data;
+ zwlr_layer_surface_v1_destroy(surface->layer_surface);
+ wl_surface_destroy(surface->surface);
+ surface->state->run_display = false;
}
-char *password;
-int password_size;
-enum line_source line_source = LINE_SOURCE_DEFAULT;
-
-struct lock_config *init_config() {
- struct lock_config *config = calloc(1, sizeof(struct lock_config));
-
- config->font = strdup("sans-serif");
- config->colors.text = 0x000000FF;
-
- config->colors.line = 0x000000FF;
- config->colors.separator = 0x000000FF;
-
- config->colors.input_cursor = 0x33DB00FF;
- config->colors.backspace_cursor = 0xDB3300FF;
-
- config->colors.normal.inner_ring = 0x000000BF;
- config->colors.normal.outer_ring = 0x337D00FF;
-
- config->colors.validating.inner_ring = 0x0072FFBF;
- config->colors.validating.outer_ring = 0x3300FAFF;
+static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = layer_surface_configure,
+ .closed = layer_surface_closed,
+};
- config->colors.invalid.inner_ring = 0xFA0000BF;
- config->colors.invalid.outer_ring = 0x7D3300FF;
-
- config->radius = 50;
- config->thickness = 10;
-
- return config;
+static void output_geometry(void *data, struct wl_output *output, int32_t x,
+ int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
+ const char *make, const char *model, int32_t transform) {
+ // Who cares
}
-void free_config(struct lock_config *config) {
- free(config->font);
- free(config);
+static void output_mode(void *data, struct wl_output *output, uint32_t flags,
+ int32_t width, int32_t height, int32_t refresh) {
+ // Who cares
}
-int function_conversation(int num_msg, const struct pam_message **msg,
- struct pam_response **resp, void *appdata_ptr) {
-
- const char* msg_style_names[] = {
- NULL,
- "PAM_PROMPT_ECHO_OFF",
- "PAM_PROMPT_ECHO_ON",
- "PAM_ERROR_MSG",
- "PAM_TEXT_INFO",
- };
-
- /* PAM expects an array of responses, one for each message */
- struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response));
- *resp = pam_reply;
-
- for(int i=0; i<num_msg; ++i) {
- sway_log(L_DEBUG, "msg[%d]: (%s) %s", i,
- msg_style_names[msg[i]->msg_style],
- msg[i]->msg);
-
- switch (msg[i]->msg_style) {
- case PAM_PROMPT_ECHO_OFF:
- case PAM_PROMPT_ECHO_ON:
- pam_reply[i].resp = password;
- break;
-
- case PAM_ERROR_MSG:
- case PAM_TEXT_INFO:
- break;
- }
- }
-
- return PAM_SUCCESS;
+static void output_done(void *data, struct wl_output *output) {
+ // Who cares
}
-/**
- * Note: PAM will free() 'password' during the process
- */
-bool verify_password() {
- struct passwd *passwd = getpwuid(getuid());
- char *username = passwd->pw_name;
-
- const struct pam_conv local_conversation = { function_conversation, NULL };
- pam_handle_t *local_auth_handle = NULL;
- int pam_err;
- if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
- sway_abort("PAM returned %d\n", pam_err);
+static void output_scale(void *data, struct wl_output *output, int32_t factor) {
+ struct swaylock_surface *surface = data;
+ surface->scale = factor;
+ if (surface->state->run_display) {
+ render_frames(surface->state);
}
- if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
- return false;
- }
- if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
- return false;
- }
- return true;
}
-void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) {
- int redraw_screen = 0;
- char *password_realloc;
- int i;
-
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- switch (sym) {
- case XKB_KEY_KP_Enter:
- case XKB_KEY_Return:
- render_data.auth_state = AUTH_STATE_VALIDATING;
-
- render(&render_data, config);
- // Make sure our render call will actually be displayed on the screen
- wl_dispatch_events();
-
- if (verify_password()) {
- exit(0);
- }
-
- render_data.auth_state = AUTH_STATE_INVALID;
- redraw_screen = 1;
-
- password_size = 1024;
- password = malloc(password_size);
- password[0] = '\0';
- break;
- case XKB_KEY_BackSpace:
- i = strlen(password);
- if (i > 0) {
- password[i - 1] = '\0';
- render_data.auth_state = AUTH_STATE_BACKSPACE;
- redraw_screen = 1;
- }
- break;
- case XKB_KEY_Control_L:
- case XKB_KEY_Control_R:
- case XKB_KEY_Shift_L:
- case XKB_KEY_Shift_R:
- case XKB_KEY_Caps_Lock:
- case XKB_KEY_Shift_Lock:
- case XKB_KEY_Meta_L:
- case XKB_KEY_Meta_R:
- case XKB_KEY_Alt_L:
- case XKB_KEY_Alt_R:
- case XKB_KEY_Super_L:
- case XKB_KEY_Super_R:
- case XKB_KEY_Hyper_L:
- case XKB_KEY_Hyper_R:
- break; // don't draw screen on modifier keys
- case XKB_KEY_Escape:
- case XKB_KEY_u:
- case XKB_KEY_U:
- // clear password buffer on ctrl-u (or escape for i3lock compatibility)
- if (sym == XKB_KEY_Escape || xkb_state_mod_name_is_active(registry->input->xkb.state,
- XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) {
- render_data.auth_state = AUTH_STATE_BACKSPACE;
- redraw_screen = 1;
-
- password_size = 1024;
- free(password);
- password = malloc(password_size);
- password[0] = '\0';
- break;
- }
- /* fallthrough */
- default:
- render_data.auth_state = AUTH_STATE_INPUT;
- redraw_screen = 1;
- i = strlen(password);
- if (i + 1 == password_size) {
- password_size += 1024;
- password_realloc = realloc(password, password_size);
- // reset password if realloc fails.
- if (password_realloc == NULL) {
- password_size = 1024;
- free(password);
- password = malloc(password_size);
- password[0] = '\0';
- break;
- } else {
- password = password_realloc;
- }
- }
- password[i] = (char)codepoint;
- password[i + 1] = '\0';
- break;
- }
- if (redraw_screen) {
- render(&render_data, config);
- wl_dispatch_events();
- // Hide the indicator after a couple of seconds
- alarm(5);
- }
+struct wl_output_listener output_listener = {
+ .geometry = output_geometry,
+ .mode = output_mode,
+ .done = output_done,
+ .scale = output_scale,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ struct swaylock_state *state = data;
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ state->compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 3);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ state->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ struct wl_seat *seat = wl_registry_bind(
+ registry, name, &wl_seat_interface, 1);
+ wl_seat_add_listener(seat, &seat_listener, state);
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ state->layer_shell = wl_registry_bind(
+ registry, name, &zwlr_layer_shell_v1_interface, 1);
+ } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) {
+ state->input_inhibit_manager = wl_registry_bind(
+ registry, name, &zwlr_input_inhibit_manager_v1_interface, 1);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ struct swaylock_surface *surface =
+ calloc(1, sizeof(struct swaylock_surface));
+ surface->state = state;
+ surface->output = wl_registry_bind(registry, name,
+ &wl_output_interface, 3);
+ wl_output_add_listener(surface->output, &output_listener, surface);
+ wl_list_insert(&state->surfaces, &surface->link);
}
}
-void render_color(struct window *window, uint32_t color) {
- cairo_set_source_u32(window->cairo, color);
- cairo_paint(window->cairo);
-}
-
-void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) {
- double width = cairo_image_surface_get_width(image);
- double height = cairo_image_surface_get_height(image);
- int wwidth = window->width * window->scale;
- int wheight = window->height * window->scale;
-
- switch (scaling_mode) {
- case SCALING_MODE_STRETCH:
- cairo_scale(window->cairo,
- (double) wwidth / width,
- (double) wheight / height);
- cairo_set_source_surface(window->cairo, image, 0, 0);
- break;
- case SCALING_MODE_FILL:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- } else {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- }
- break;
- }
- case SCALING_MODE_FIT:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- } else {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- }
- break;
- }
- case SCALING_MODE_CENTER:
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 - width/2,
- (double) wheight/2 - height/2);
- break;
- case SCALING_MODE_TILE:
- {
- cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
- cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
- cairo_set_source(window->cairo, pattern);
- break;
- }
- }
-
- cairo_paint(window->cairo);
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ // who cares
}
-cairo_surface_t *load_image(char *image_path) {
- cairo_surface_t *image = NULL;
-
-#ifdef WITH_GDK_PIXBUF
- GError *err = NULL;
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
- if (!pixbuf) {
- sway_abort("Failed to load background image: %s", err->message);
- }
- image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
-#else
- image = cairo_image_surface_create_from_png(image_path);
-#endif //WITH_GDK_PIXBUF
- if (!image) {
- sway_abort("Failed to read background image.");
- }
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
- return image;
-}
+static struct swaylock_state state;
int main(int argc, char **argv) {
- const char *scaling_mode_str = "fit", *socket_path = NULL;
- int i;
- void *images = NULL;
- config = init_config();
-
- render_data.num_images = 0;
- render_data.color_set = 0;
- render_data.color = 0xFFFFFFFF;
- render_data.auth_state = AUTH_STATE_IDLE;
-
- init_log(L_INFO);
- // Install SIGALARM handler (for hiding the typing indicator)
- signal(SIGALRM, sigalarm_handler);
-
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"color", required_argument, NULL, 'c'},
{"image", required_argument, NULL, 'i'},
- {"scaling", required_argument, NULL, 0},
+ {"scaling", required_argument, NULL, 's'},
{"tiling", no_argument, NULL, 't'},
{"version", no_argument, NULL, 'v'},
{"socket", required_argument, NULL, 'p'},
{"no-unlock-indicator", no_argument, NULL, 'u'},
{"daemonize", no_argument, NULL, 'f'},
- {"font", required_argument, NULL, 0},
- {"line-uses-ring", no_argument, NULL, 'r'},
- {"line-uses-inside", no_argument, NULL, 's'},
- {"textcolor", required_argument, NULL, 0},
- {"insidevercolor", required_argument, NULL, 0},
- {"insidewrongcolor", required_argument, NULL, 0},
- {"insidecolor", required_argument, NULL, 0},
- {"ringvercolor", required_argument, NULL, 0},
- {"ringwrongcolor", required_argument, NULL, 0},
- {"ringcolor", required_argument, NULL, 0},
- {"linecolor", required_argument, NULL, 0},
- {"separatorcolor", required_argument, NULL, 0},
- {"keyhlcolor", required_argument, NULL, 0},
- {"bshlcolor", required_argument, NULL, 0},
- {"indicator-radius", required_argument, NULL, 0},
- {"indicator-thickness", required_argument, NULL, 0},
{0, 0, 0, 0}
};
@@ -390,415 +169,124 @@ int main(int argc, char **argv) {
"\n"
" -h, --help Show help message and quit.\n"
" -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\n"
- " --scaling Scaling mode: stretch, fill, fit, center, tile.\n"
+ " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n"
" -t, --tiling Same as --scaling=tile.\n"
" -v, --version Show the version number and quit.\n"
" -i, --image [<output>:]<path> Display the given image.\n"
" -u, --no-unlock-indicator Disable the unlock indicator.\n"
- " -f, --daemonize Detach from the controlling terminal.\n"
- " --socket <socket> Use the specified socket.\n"
- " For more information see `man swaylock`\n";
+ " -f, --daemonize Detach from the controlling terminal.\n"
+ " --socket <socket> Use the specified socket.\n";
-
- registry = registry_poll();
+ struct swaylock_args args = {
+ .mode = BACKGROUND_MODE_SOLID_COLOR,
+ .color = 0xFFFFFFFF,
+ .show_indicator = true,
+ };
+ cairo_surface_t *background_image = NULL;
+ state.args = args;
+ wlr_log_init(L_DEBUG, NULL);
int c;
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hc:i:srtvuf", long_options, &option_index);
+ c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
- case 'c':
- {
- render_data.color = parse_color(optarg);
- render_data.color_set = 1;
+ case 'c': {
+ state.args.color = parse_color(optarg);
+ state.args.mode = BACKGROUND_MODE_SOLID_COLOR;
break;
}
case 'i':
- {
- char *image_path = strchr(optarg, ':');
- if (image_path == NULL) {
- if (render_data.num_images == 0) {
- // Provided image without output
- render_data.image = load_image(optarg);
- render_data.num_images = -1;
- } else {
- sway_log(L_ERROR, "output must be defined for all --images or no --images");
- exit(EXIT_FAILURE);
- }
- } else {
- // Provided image for all outputs
- if (render_data.num_images == 0) {
- images = calloc(registry->outputs->length, sizeof(char*) * 2);
- } else if (render_data.num_images == -1) {
- sway_log(L_ERROR, "output must be defined for all --images or no --images");
- exit(EXIT_FAILURE);
- }
-
- image_path[0] = '\0';
- ((char**) images)[render_data.num_images * 2] = optarg;
- ((char**) images)[render_data.num_images++ * 2 + 1] = ++image_path;
+ // TODO: Multiple background images (bleh)
+ background_image = load_background_image(optarg);
+ if (!background_image) {
+ return 1;
}
+ state.args.mode = BACKGROUND_MODE_FILL;
break;
- }
- case 't':
- scaling_mode_str = "tile";
+ case 's':
+ state.args.mode = parse_background_mode(optarg);
+ if (state.args.mode == BACKGROUND_MODE_INVALID) {
+ return 1;
+ }
break;
- case 'p':
- socket_path = optarg;
+ case 't':
+ state.args.mode = BACKGROUND_MODE_TILE;
break;
case 'v':
- fprintf(stdout, "swaylock version " SWAY_VERSION "\n");
- exit(EXIT_SUCCESS);
- break;
+#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE
+ fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n",
+ SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH);
+#else
+ fprintf(stdout, "version unknown\n");
+#endif
+ return 0;
case 'u':
- show_indicator = false;
- break;
- case 'f': {
- pid_t t = fork();
- if (t == -1) {
- sway_log(L_ERROR, "daemon call failed");
- exit(EXIT_FAILURE);
- } else if (t > 0) {
- exit(0);
- }
+ state.args.show_indicator = false;
break;
- }
- case 'r':
- if (line_source != LINE_SOURCE_DEFAULT) {
- sway_log(L_ERROR, "line source options conflict");
- exit(EXIT_FAILURE);
- }
- line_source = LINE_SOURCE_RING;
- break;
- case 's':
- if (line_source != LINE_SOURCE_DEFAULT) {
- sway_log(L_ERROR, "line source options conflict");
- exit(EXIT_FAILURE);
- }
- line_source = LINE_SOURCE_INSIDE;
- break;
- case 0:
- if (strcmp(long_options[option_index].name, "font") == 0) {
- free(config->font);
- config->font = strdup(optarg);
- } else if (strcmp(long_options[option_index].name, "scaling") == 0) {
- scaling_mode_str = optarg;
- } else if (strcmp(long_options[option_index].name, "textcolor") == 0) {
- config->colors.text = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "insidevercolor") == 0) {
- config->colors.validating.inner_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "insidewrongcolor") == 0) {
- config->colors.invalid.inner_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "insidecolor") == 0) {
- config->colors.normal.inner_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "ringvercolor") == 0) {
- config->colors.validating.outer_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "ringwrongcolor") == 0) {
- config->colors.invalid.outer_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "ringcolor") == 0) {
- config->colors.normal.outer_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "linecolor") == 0) {
- config->colors.line = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "separatorcolor") == 0) {
- config->colors.separator = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "keyhlcolor") == 0) {
- config->colors.input_cursor = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "bshlcolor") == 0) {
- config->colors.backspace_cursor = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "indicator-radius") == 0) {
- config->radius = atoi(optarg);
- } else if (strcmp(long_options[option_index].name, "indicator-thickness") == 0) {
- config->thickness = atoi(optarg);
- }
+ case 'f':
+ daemonize();
break;
default:
fprintf(stderr, "%s", usage);
- exit(EXIT_FAILURE);
- }
- }
-
- render_data.scaling_mode = SCALING_MODE_STRETCH;
- if (strcmp(scaling_mode_str, "stretch") == 0) {
- render_data.scaling_mode = SCALING_MODE_STRETCH;
- } else if (strcmp(scaling_mode_str, "fill") == 0) {
- render_data.scaling_mode = SCALING_MODE_FILL;
- } else if (strcmp(scaling_mode_str, "fit") == 0) {
- render_data.scaling_mode = SCALING_MODE_FIT;
- } else if (strcmp(scaling_mode_str, "center") == 0) {
- render_data.scaling_mode = SCALING_MODE_CENTER;
- } else if (strcmp(scaling_mode_str, "tile") == 0) {
- render_data.scaling_mode = SCALING_MODE_TILE;
- } else {
- sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
- }
-
- password_size = 1024;
- password = malloc(password_size);
- password[0] = '\0';
- render_data.surfaces = create_list();
- if (!socket_path) {
- socket_path = get_socketpath();
- if (!socket_path) {
- sway_abort("Unable to retrieve socket path");
+ return 1;
}
}
- if (!registry) {
- sway_abort("Unable to connect to wayland compositor");
- }
-
- if (!registry->swaylock) {
- sway_abort("swaylock requires the compositor to support the swaylock extension.");
- }
-
- if (registry->pointer) {
- // We don't want swaylock to have a pointer
- wl_pointer_destroy(registry->pointer);
- registry->pointer = NULL;
+ wl_list_init(&state.surfaces);
+ state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ state.display = wl_display_connect(NULL);
+ assert(state.display);
+
+ struct wl_registry *registry = wl_display_get_registry(state.display);
+ wl_registry_add_listener(registry, &registry_listener, &state);
+ wl_display_roundtrip(state.display);
+ assert(state.compositor && state.layer_shell && state.shm);
+ if (!state.input_inhibit_manager) {
+ wlr_log(L_ERROR, "Compositor does not support the input inhibitor "
+ "protocol, refusing to run insecurely");
}
- for (i = 0; i < registry->outputs->length; ++i) {
- struct output_state *output = registry->outputs->items[i];
- struct window *window = window_setup(registry,
- output->width, output->height, output->scale, true);
- if (!window) {
- sway_abort("Failed to create surfaces.");
- }
- list_add(render_data.surfaces, window);
- }
-
- registry->input->notify = notify_key;
-
- // Different background for the output
- if (render_data.num_images >= 1) {
- char **displays_paths = images;
- render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*));
-
- int socketfd = ipc_open_socket(socket_path);
- uint32_t len = 0;
- char *outputs = ipc_single_command(socketfd, IPC_GET_OUTPUTS, "", &len);
- struct json_object *json_outputs = json_tokener_parse(outputs);
-
- for (i = 0; i < registry->outputs->length; ++i) {
- if (displays_paths[i * 2] != NULL) {
- for (int j = 0;; ++j) {
- if (j >= json_object_array_length(json_outputs)) {
- sway_log(L_ERROR, "%s is not an extant output", displays_paths[i * 2]);
- exit(EXIT_FAILURE);
- }
-
- struct json_object *dsp_name, *at_j = json_object_array_get_idx(json_outputs, j);
- if (!json_object_object_get_ex(at_j, "name", &dsp_name)) {
- sway_abort("output doesn't have a name field");
- }
- if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) {
- render_data.images[j] = load_image(displays_paths[i * 2 + 1]);
- break;
- }
- }
- }
- }
-
- json_object_put(json_outputs);
- close(socketfd);
- free(displays_paths);
+ if (wl_list_empty(&state.surfaces)) {
+ wlr_log(L_DEBUG, "Exiting - no outputs to show on.");
+ return 0;
}
- render(&render_data, config);
- bool locked = false;
- while (wl_display_dispatch(registry->display) != -1) {
- if (!locked) {
- for (i = 0; i < registry->outputs->length; ++i) {
- struct output_state *output = registry->outputs->items[i];
- struct window *window = render_data.surfaces->items[i];
- lock_set_lock_surface(registry->swaylock, output->output, window->surface);
- }
- locked = true;
- }
+ struct swaylock_surface *surface;
+ wl_list_for_each(surface, &state.surfaces, link) {
+ surface->image = background_image;
+
+ surface->surface = wl_compositor_create_surface(state.compositor);
+ assert(surface->surface);
+
+ surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
+ state.layer_shell, surface->surface, surface->output,
+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen");
+ assert(surface->layer_surface);
+
+ zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0);
+ zwlr_layer_surface_v1_set_anchor(surface->layer_surface,
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
+ zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1);
+ zwlr_layer_surface_v1_set_keyboard_interactivity(
+ surface->layer_surface, true);
+ zwlr_layer_surface_v1_add_listener(surface->layer_surface,
+ &layer_surface_listener, surface);
+ wl_surface_commit(surface->surface);
+ wl_display_roundtrip(state.display);
}
- // Free surfaces
- if (render_data.num_images == -1) {
- cairo_surface_destroy(render_data.image);
- } else if (render_data.num_images >= 1) {
- for (i = 0; i < registry->outputs->length; ++i) {
- if (render_data.images[i] != NULL) {
- cairo_surface_destroy(render_data.images[i]);
- }
- }
- free(render_data.images);
- }
+ zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
- for (i = 0; i < render_data.surfaces->length; ++i) {
- struct window *window = render_data.surfaces->items[i];
- window_teardown(window);
+ state.run_display = true;
+ while (wl_display_dispatch(state.display) != -1 && state.run_display) {
+ // This space intentionally left blank
}
- list_free(render_data.surfaces);
- registry_teardown(registry);
-
- free_config(config);
-
return 0;
}
-
-void render(struct render_data *render_data, struct lock_config *config) {
- int i;
- for (i = 0; i < render_data->surfaces->length; ++i) {
- sway_log(L_DEBUG, "Render surface %d of %d", i, render_data->surfaces->length);
- struct window *window = render_data->surfaces->items[i];
- if (!window_prerender(window) || !window->cairo) {
- continue;
- }
- int wwidth = window->width * window->scale;
- int wheight = window->height * window->scale;
-
- cairo_save(window->cairo);
- cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR);
- cairo_paint(window->cairo);
- cairo_restore(window->cairo);
-
- // Reset the transformation matrix
- cairo_identity_matrix(window->cairo);
-
- if (render_data->num_images == 0 || render_data->color_set) {
- render_color(window, render_data->color);
- }
-
- if (render_data->num_images == -1) {
- // One background for all
- render_image(window, render_data->image, render_data->scaling_mode);
- } else if (render_data->num_images >= 1) {
- // Different backgrounds
- if (render_data->images[i] != NULL) {
- render_image(window, render_data->images[i], render_data->scaling_mode);
- }
- }
-
- // Reset the transformation matrix again
- cairo_identity_matrix(window->cairo);
-
- // Draw specific values (copied from i3)
- const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
- const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
-
- // Add visual indicator
- if (show_indicator && render_data->auth_state != AUTH_STATE_IDLE) {
- // Draw circle
- cairo_set_line_width(window->cairo, config->thickness);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, 0, 2 * M_PI);
- switch (render_data->auth_state) {
- case AUTH_STATE_INPUT:
- case AUTH_STATE_BACKSPACE: {
- cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring);
- cairo_fill_preserve(window->cairo);
- cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring);
- cairo_stroke(window->cairo);
- } break;
- case AUTH_STATE_VALIDATING: {
- cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring);
- cairo_fill_preserve(window->cairo);
- cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring);
- cairo_stroke(window->cairo);
- } break;
- case AUTH_STATE_INVALID: {
- cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring);
- cairo_fill_preserve(window->cairo);
- cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring);
- cairo_stroke(window->cairo);
- } break;
- default: break;
- }
-
- // Draw a message
- char *text = NULL;
- cairo_set_source_u32(window->cairo, config->colors.text);
- cairo_select_font_face(window->cairo, config->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
- cairo_set_font_size(window->cairo, config->radius/3.0f);
- switch (render_data->auth_state) {
- case AUTH_STATE_VALIDATING:
- text = "verifying";
- break;
- case AUTH_STATE_INVALID:
- text = "wrong";
- break;
- default: break;
- }
-
- if (text) {
- cairo_text_extents_t extents;
- double x, y;
-
- cairo_text_extents(window->cairo, text, &extents);
- x = wwidth/2 - ((extents.width/2) + extents.x_bearing);
- y = wheight/2 - ((extents.height/2) + extents.y_bearing);
-
- cairo_move_to(window->cairo, x, y);
- cairo_show_text(window->cairo, text);
- cairo_close_path(window->cairo);
- cairo_new_sub_path(window->cairo);
- }
-
- // Typing indicator: Highlight random part on keypress
- if (render_data->auth_state == AUTH_STATE_INPUT || render_data->auth_state == AUTH_STATE_BACKSPACE) {
- static double highlight_start = 0;
- highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5;
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE);
- if (render_data->auth_state == AUTH_STATE_INPUT) {
- cairo_set_source_u32(window->cairo, config->colors.input_cursor);
- } else {
- cairo_set_source_u32(window->cairo, config->colors.backspace_cursor);
- }
- cairo_stroke(window->cairo);
-
- // Draw borders
- cairo_set_source_u32(window->cairo, config->colors.separator);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS);
- cairo_stroke(window->cairo);
-
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start + TYPE_INDICATOR_RANGE, (highlight_start + TYPE_INDICATOR_RANGE) + TYPE_INDICATOR_BORDER_THICKNESS);
- cairo_stroke(window->cairo);
- }
-
- switch(line_source) {
- case LINE_SOURCE_RING:
- switch(render_data->auth_state) {
- case AUTH_STATE_VALIDATING:
- cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring);
- break;
- case AUTH_STATE_INVALID:
- cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring);
- break;
- default:
- cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring);
- }
- break;
- case LINE_SOURCE_INSIDE:
- switch(render_data->auth_state) {
- case AUTH_STATE_VALIDATING:
- cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring);
- break;
- case AUTH_STATE_INVALID:
- cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring);
- break;
- default:
- cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring);
- break;
- }
- break;
- default:
- cairo_set_source_u32(window->cairo, config->colors.line);
- break;
- }
- // Draw inner + outer border of the circle
- cairo_set_line_width(window->cairo, 2.0);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius - config->thickness/2, 0, 2*M_PI);
- cairo_stroke(window->cairo);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius + config->thickness/2, 0, 2*M_PI);
- cairo_stroke(window->cairo);
- }
- window_render(window);
- }
-}
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 @@
+executable(
+ 'swaylock', [
+ 'main.c',
+ 'password.c',
+ 'render.c',
+ 'seat.c'
+ ],
+ include_directories: [sway_inc],
+ dependencies: [
+ cairo,
+ client_protos,
+ gdk_pixbuf,
+ libpam,
+ math,
+ pango,
+ pangocairo,
+ xkbcommon,
+ wayland_client,
+ wlroots,
+ ],
+ link_with: [lib_sway_common, lib_sway_client],
+ install: true
+)
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 @@
+#include <assert.h>
+#include <pwd.h>
+#include <security/pam_appl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#include "swaylock/swaylock.h"
+#include "swaylock/seat.h"
+#include "unicode.h"
+
+static int function_conversation(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *data) {
+ struct swaylock_password *pw = data;
+ /* PAM expects an array of responses, one for each message */
+ struct pam_response *pam_reply = calloc(
+ num_msg, sizeof(struct pam_response));
+ *resp = pam_reply;
+ for (int i = 0; i < num_msg; ++i) {
+ switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ case PAM_PROMPT_ECHO_ON:
+ pam_reply[i].resp = pw->buffer;
+ break;
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ break;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+static bool attempt_password(struct swaylock_password *pw) {
+ struct passwd *passwd = getpwuid(getuid());
+ char *username = passwd->pw_name;
+ const struct pam_conv local_conversation = {
+ function_conversation, pw
+ };
+ pam_handle_t *local_auth_handle = NULL;
+ int pam_err;
+ if ((pam_err = pam_start("swaylock", username,
+ &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
+ wlr_log(L_ERROR, "PAM returned error %d", pam_err);
+ }
+ if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
+ wlr_log(L_ERROR, "pam_authenticate failed");
+ goto fail;
+ }
+ if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
+ wlr_log(L_ERROR, "pam_end failed");
+ goto fail;
+ }
+ // PAM frees this
+ pw->buffer = NULL;
+ pw->len = pw->size = 0;
+ return true;
+fail:
+ // PAM frees this
+ pw->buffer = NULL;
+ pw->len = pw->size = 0;
+ return false;
+}
+
+static bool backspace(struct swaylock_password *pw) {
+ if (pw->len != 0) {
+ pw->buffer[--pw->len] = 0;
+ return true;
+ }
+ return false;
+}
+
+static void append_ch(struct swaylock_password *pw, uint32_t codepoint) {
+ if (!pw->buffer) {
+ pw->size = 8;
+ if (!(pw->buffer = malloc(pw->size))) {
+ // TODO: Display error
+ return;
+ }
+ pw->buffer[0] = 0;
+ }
+ size_t utf8_size = utf8_chsize(codepoint);
+ if (pw->len + utf8_size + 1 >= pw->size) {
+ size_t size = pw->size * 2;
+ char *buffer = realloc(pw->buffer, size);
+ if (!buffer) {
+ // TODO: Display error
+ return;
+ }
+ pw->size = size;
+ pw->buffer = buffer;
+ }
+ utf8_encode(&pw->buffer[pw->len], codepoint);
+ pw->buffer[pw->len + utf8_size] = 0;
+ pw->len += utf8_size;
+}
+
+void swaylock_handle_key(struct swaylock_state *state,
+ xkb_keysym_t keysym, uint32_t codepoint) {
+ switch (keysym) {
+ case XKB_KEY_KP_Enter: /* fallthrough */
+ case XKB_KEY_Return:
+ state->auth_state = AUTH_STATE_VALIDATING;
+ render_frames(state);
+ wl_display_roundtrip(state->display);
+ if (attempt_password(&state->password)) {
+ state->run_display = false;
+ break;
+ }
+ state->auth_state = AUTH_STATE_INVALID;
+ render_frames(state);
+ break;
+ case XKB_KEY_BackSpace:
+ if (backspace(&state->password)) {
+ state->auth_state = AUTH_STATE_BACKSPACE;
+ render_frames(state);
+ }
+ break;
+ default:
+ if (codepoint) {
+ append_ch(&state->password, codepoint);
+ state->auth_state = AUTH_STATE_INPUT;
+ render_frames(state);
+ }
+ break;
+ }
+}
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 @@
+#define _POSIX_C_SOURCE 199506L
+#include <math.h>
+#include <stdlib.h>
+#include <wayland-client.h>
+#include "cairo.h"
+#include "background-image.h"
+#include "swaylock/swaylock.h"
+
+#define M_PI 3.14159265358979323846
+const int ARC_RADIUS = 50;
+const int ARC_THICKNESS = 10;
+const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
+const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
+
+void render_frame(struct swaylock_surface *surface) {
+ struct swaylock_state *state = surface->state;
+
+ int buffer_width = surface->width * surface->scale;
+ int buffer_height = surface->height * surface->scale;
+
+ surface->current_buffer = get_next_buffer(state->shm,
+ surface->buffers, buffer_width, buffer_height);
+ cairo_t *cairo = surface->current_buffer->cairo;
+ cairo_identity_matrix(cairo);
+
+ if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) {
+ cairo_set_source_u32(cairo, state->args.color);
+ cairo_paint(cairo);
+ } else {
+ render_background_image(cairo, surface->image,
+ state->args.mode, buffer_width, buffer_height);
+ }
+ cairo_identity_matrix(cairo);
+
+ int arc_radius = ARC_RADIUS * surface->scale;
+ int arc_thickness = ARC_THICKNESS * surface->scale;
+ float type_indicator_border_thickness =
+ TYPE_INDICATOR_BORDER_THICKNESS * surface->scale;
+
+ if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) {
+ // Draw circle
+ cairo_set_line_width(cairo, arc_thickness);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI);
+ switch (state->auth_state) {
+ case AUTH_STATE_INPUT:
+ case AUTH_STATE_BACKSPACE: {
+ cairo_set_source_rgba(cairo, 0, 0, 0, 0.75);
+ cairo_fill_preserve(cairo);
+ cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0);
+ cairo_stroke(cairo);
+ } break;
+ case AUTH_STATE_VALIDATING: {
+ cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75);
+ cairo_fill_preserve(cairo);
+ cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255);
+ cairo_stroke(cairo);
+ } break;
+ case AUTH_STATE_INVALID: {
+ cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75);
+ cairo_fill_preserve(cairo);
+ cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0);
+ cairo_stroke(cairo);
+ } break;
+ default: break;
+ }
+
+ // Draw a message
+ char *text = NULL;
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ cairo_select_font_face(cairo, "sans-serif",
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cairo, arc_radius / 3.0f);
+ switch (state->auth_state) {
+ case AUTH_STATE_VALIDATING:
+ text = "verifying";
+ break;
+ case AUTH_STATE_INVALID:
+ text = "wrong";
+ break;
+ default: break;
+ }
+
+ if (text) {
+ cairo_text_extents_t extents;
+ double x, y;
+ cairo_text_extents(cairo, text, &extents);
+ x = (buffer_width / 2) -
+ (extents.width / 2 + extents.x_bearing);
+ y = (buffer_height / 2) -
+ (extents.height / 2 + extents.y_bearing);
+
+ cairo_move_to(cairo, x, y);
+ cairo_show_text(cairo, text);
+ cairo_close_path(cairo);
+ cairo_new_sub_path(cairo);
+ }
+
+ // Typing indicator: Highlight random part on keypress
+ if (state->auth_state == AUTH_STATE_INPUT
+ || state->auth_state == AUTH_STATE_BACKSPACE) {
+ static double highlight_start = 0;
+ highlight_start +=
+ (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5;
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius, highlight_start,
+ highlight_start + TYPE_INDICATOR_RANGE);
+ if (state->auth_state == AUTH_STATE_INPUT) {
+ cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0);
+ } else {
+ cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0);
+ }
+ cairo_stroke(cairo);
+
+ // Draw borders
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius, highlight_start,
+ highlight_start + type_indicator_border_thickness);
+ cairo_stroke(cairo);
+
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius, highlight_start + TYPE_INDICATOR_RANGE,
+ highlight_start + TYPE_INDICATOR_RANGE +
+ type_indicator_border_thickness);
+ cairo_stroke(cairo);
+ }
+
+ // Draw inner + outer border of the circle
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ cairo_set_line_width(cairo, 2.0 * surface->scale);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius - arc_thickness / 2, 0, 2 * M_PI);
+ cairo_stroke(cairo);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius + arc_thickness / 2, 0, 2 * M_PI);
+ cairo_stroke(cairo);
+ }
+
+ wl_surface_set_buffer_scale(surface->surface, surface->scale);
+ wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0);
+ wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height);
+ wl_surface_commit(surface->surface);
+}
+
+void render_frames(struct swaylock_state *state) {
+ struct swaylock_surface *surface;
+ wl_list_for_each(surface, &state->surfaces, link) {
+ render_frame(surface);
+ }
+}
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 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#include "swaylock/swaylock.h"
+#include "swaylock/seat.h"
+
+const char *XKB_MASK_NAMES[MASK_LAST] = {
+ XKB_MOD_NAME_SHIFT,
+ XKB_MOD_NAME_CAPS,
+ XKB_MOD_NAME_CTRL,
+ XKB_MOD_NAME_ALT,
+ "Mod2",
+ "Mod3",
+ XKB_MOD_NAME_LOGO,
+ "Mod5",
+};
+
+const enum mod_bit XKB_MODS[MASK_LAST] = {
+ MOD_SHIFT,
+ MOD_CAPS,
+ MOD_CTRL,
+ MOD_ALT,
+ MOD_MOD2,
+ MOD_MOD3,
+ MOD_LOGO,
+ MOD_MOD5
+};
+
+static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t format, int32_t fd, uint32_t size) {
+ struct swaylock_state *state = data;
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format);
+ exit(1);
+ }
+ char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_shm == MAP_FAILED) {
+ close(fd);
+ wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting");
+ exit(1);
+ }
+ struct xkb_keymap *keymap = xkb_keymap_new_from_string(
+ state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
+ munmap(map_shm, size);
+ close(fd);
+ assert(keymap);
+ struct xkb_state *xkb_state = xkb_state_new(keymap);
+ assert(xkb_state);
+ xkb_keymap_unref(state->xkb.keymap);
+ xkb_state_unref(state->xkb.state);
+ state->xkb.keymap = keymap;
+ state->xkb.state = xkb_state;
+}
+
+static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
+ // Who cares
+}
+
+static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface) {
+ // Who cares
+}
+
+static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) {
+ struct swaylock_state *state = data;
+ enum wl_keyboard_key_state key_state = _key_state;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8);
+ uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ?
+ key + 8 : 0;
+ uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode);
+ if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ swaylock_handle_key(state, sym, codepoint);
+ }
+}
+
+static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group) {
+ struct swaylock_state *state = data;
+ xkb_state_update_mask(state->xkb.state,
+ mods_depressed, mods_latched, mods_locked, 0, 0, group);
+ xkb_mod_mask_t mask = xkb_state_serialize_mods(state->xkb.state,
+ XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
+ state->xkb.modifiers = 0;
+ for (uint32_t i = 0; i < MASK_LAST; ++i) {
+ if (mask & state->xkb.masks[i]) {
+ state->xkb.modifiers |= XKB_MODS[i];
+ }
+ }
+}
+
+static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
+ int32_t rate, int32_t delay) {
+ // TODO
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ .keymap = keyboard_keymap,
+ .enter = keyboard_enter,
+ .leave = keyboard_leave,
+ .key = keyboard_key,
+ .modifiers = keyboard_modifiers,
+ .repeat_info = keyboard_repeat_info,
+};
+
+static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0);
+}
+
+static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface) {
+ // Who cares
+}
+
+static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ // Who cares
+}
+
+static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
+ // Who cares
+}
+
+static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value) {
+ // Who cares
+}
+
+static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
+ // Who cares
+}
+
+static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis_source) {
+ // Who cares
+}
+
+static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis) {
+ // Who cares
+}
+
+static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis, int32_t discrete) {
+ // Who cares
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ .enter = wl_pointer_enter,
+ .leave = wl_pointer_leave,
+ .motion = wl_pointer_motion,
+ .button = wl_pointer_button,
+ .axis = wl_pointer_axis,
+ .frame = wl_pointer_frame,
+ .axis_source = wl_pointer_axis_source,
+ .axis_stop = wl_pointer_axis_stop,
+ .axis_discrete = wl_pointer_axis_discrete,
+};
+
+static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
+ enum wl_seat_capability caps) {
+ struct swaylock_state *state = data;
+ if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
+ struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat);
+ wl_pointer_add_listener(pointer, &pointer_listener, NULL);
+ }
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
+ struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat);
+ wl_keyboard_add_listener(keyboard, &keyboard_listener, state);
+ }
+}
+
+static void seat_handle_name(void *data, struct wl_seat *wl_seat,
+ const char *name) {
+ // Who cares
+}
+
+const struct wl_seat_listener seat_listener = {
+ .capabilities = seat_handle_capabilities,
+ .name = seat_handle_name,
+};
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 @@
-add_executable(swaymsg
- main.c
-)
-
-include_directories(
- ${JSONC_INCLUDE_DIRS}
-)
-
-target_link_libraries(swaymsg
- sway-common
- ${JSONC_LIBRARIES}
-)
-
-install(
- TARGETS swaymsg
- RUNTIME
- DESTINATION bin
- COMPONENT runtime
-)
-
-if (A2X_FOUND)
- add_manpage(swaymsg 1)
-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) {
);
}
-static void pretty_print_input(json_object *i) {
- json_object *id, *name, *size, *caps;
- json_object_object_get_ex(i, "identifier", &id);
- json_object_object_get_ex(i, "name", &name);
- json_object_object_get_ex(i, "size", &size);
- json_object_object_get_ex(i, "capabilities", &caps);
-
- printf( "Input device %s\n Type: ", json_object_get_string(name));
-
+static const char *pretty_type_name(const char *name) {
+ // TODO these constants probably belong in the common lib
struct {
const char *a;
const char *b;
- } cap_names[] = {
+ } type_names[] = {
{ "keyboard", "Keyboard" },
{ "pointer", "Mouse" },
- { "touch", "Touch" },
- { "tablet_tool", "Tablet tool" },
{ "tablet_pad", "Tablet pad" },
- { "gesture", "Gesture" },
- { "switch", "Switch" },
+ { "tablet_tool", "Tablet tool" },
+ { "touch", "Touch" },
};
- size_t len = json_object_array_length(caps);
- if (len == 0) {
- printf("Unknown");
- }
-
- json_object *cap;
- for (size_t i = 0; i < len; ++i) {
- cap = json_object_array_get_idx(caps, i);
- const char *cap_s = json_object_get_string(cap);
- const char *_name = NULL;
- for (size_t j = 0; j < sizeof(cap_names) / sizeof(cap_names[0]); ++i) {
- if (strcmp(cap_names[i].a, cap_s) == 0) {
- _name = cap_names[i].b;
- break;
- }
+ for (size_t i = 0; i < sizeof(type_names) / sizeof(type_names[0]); ++i) {
+ if (strcmp(type_names[i].a, name) == 0) {
+ return type_names[i].b;
}
- printf("%s%s", _name ? _name : cap_s, len > 1 && i != len - 1 ? ", " : "");
}
- printf("\n Sway ID: %s\n", json_object_get_string(id));
- if (size) {
- json_object *width, *height;
- json_object_object_get_ex(size, "width", &width);
- json_object_object_get_ex(size, "height", &height);
- printf(" Size: %lfmm x %lfmm\n",
- json_object_get_double(width), json_object_get_double(height));
- }
- printf("\n");
+
+ return name;
+}
+
+static void pretty_print_input(json_object *i) {
+ json_object *id, *name, *type, *product, *vendor;
+ json_object_object_get_ex(i, "identifier", &id);
+ json_object_object_get_ex(i, "name", &name);
+ json_object_object_get_ex(i, "type", &type);
+ json_object_object_get_ex(i, "product", &product);
+ json_object_object_get_ex(i, "vendor", &vendor);
+
+ const char *fmt =
+ "Input device: %s\n"
+ " Type: %s\n"
+ " Identifier: %s\n"
+ " Product ID: %d\n"
+ " Vendor ID: %d\n\n";
+
+
+ printf(fmt, json_object_get_string(name),
+ pretty_type_name(json_object_get_string(type)),
+ json_object_get_string(id),
+ json_object_get_int(product),
+ json_object_get_int(vendor));
}
static void pretty_print_output(json_object *o) {
- json_object *name, *rect, *focused, *active, *ws, *scale;
+ json_object *name, *rect, *focused, *active, *ws;
json_object_object_get_ex(o, "name", &name);
json_object_object_get_ex(o, "rect", &rect);
json_object_object_get_ex(o, "focused", &focused);
json_object_object_get_ex(o, "active", &active);
json_object_object_get_ex(o, "current_workspace", &ws);
+ json_object *make, *model, *serial, *scale, *refresh, *transform;
+ json_object_object_get_ex(o, "make", &make);
+ json_object_object_get_ex(o, "model", &model);
+ json_object_object_get_ex(o, "serial", &serial);
json_object_object_get_ex(o, "scale", &scale);
+ json_object_object_get_ex(o, "refresh", &refresh);
+ json_object_object_get_ex(o, "transform", &transform);
json_object *x, *y, *width, *height;
json_object_object_get_ex(rect, "x", &x);
json_object_object_get_ex(rect, "y", &y);
json_object_object_get_ex(rect, "width", &width);
json_object_object_get_ex(rect, "height", &height);
+ json_object *modes;
+ json_object_object_get_ex(o, "modes", &modes);
+
printf(
- "Output %s%s%s\n"
- " Geometry: %dx%d @ %d,%d\n"
+ "Output %s '%s %s %s'%s%s\n"
+ " Current mode: %dx%d @ %f Hz\n"
+ " Position: %d,%d\n"
" Scale factor: %dx\n"
- " Workspace: %s\n\n",
+ " Transform: %s\n"
+ " Workspace: %s\n",
json_object_get_string(name),
+ json_object_get_string(make),
+ json_object_get_string(model),
+ json_object_get_string(serial),
json_object_get_boolean(focused) ? " (focused)" : "",
!json_object_get_boolean(active) ? " (inactive)" : "",
json_object_get_int(width), json_object_get_int(height),
+ (float)json_object_get_int(refresh) / 1000,
json_object_get_int(x), json_object_get_int(y),
json_object_get_int(scale),
+ json_object_get_string(transform),
json_object_get_string(ws)
);
+
+ size_t modes_len = json_object_array_length(modes);
+ if (modes_len > 0) {
+ printf(" Available modes:\n");
+ for (size_t i = 0; i < modes_len; ++i) {
+ json_object *mode = json_object_array_get_idx(modes, i);
+
+ json_object *mode_width, *mode_height, *mode_refresh;
+ json_object_object_get_ex(mode, "width", &mode_width);
+ json_object_object_get_ex(mode, "height", &mode_height);
+ json_object_object_get_ex(mode, "refresh", &mode_refresh);
+
+ printf(" %dx%d @ %f Hz\n", json_object_get_int(mode_width),
+ json_object_get_int(mode_height),
+ (float)json_object_get_int(mode_refresh) / 1000);
+ }
+ }
+
+ printf("\n");
}
static void pretty_print_version(json_object *v) {
@@ -149,7 +178,7 @@ static void pretty_print_version(json_object *v) {
static void pretty_print_clipboard(json_object *v) {
if (success(v, true)) {
if (json_object_is_type(v, json_type_array)) {
- for (int i = 0; i < json_object_array_length(v); ++i) {
+ for (size_t i = 0; i < json_object_array_length(v); ++i) {
json_object *o = json_object_array_get_idx(v, i);
printf("%s\n", json_object_get_string(o));
}
@@ -225,7 +254,7 @@ int main(int argc, char **argv) {
char *socket_path = NULL;
char *cmdtype = NULL;
- init_log(L_INFO);
+ wlr_log_init(L_INFO, NULL);
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
@@ -314,9 +343,11 @@ int main(int argc, char **argv) {
}
free(cmdtype);
- char *command = strdup("");
+ char *command = NULL;
if (optind < argc) {
command = join_args(argv + optind, argc - optind);
+ } else {
+ command = strdup("");
}
int ret = 0;
@@ -341,7 +372,7 @@ int main(int argc, char **argv) {
} else {
pretty_print(type, obj);
}
- free(obj);
+ json_object_put(obj);
}
}
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 @@
+executable(
+ 'swaymsg',
+ 'main.c',
+ include_directories: [sway_inc],
+ dependencies: [jsonc, wlroots],
+ link_with: [lib_sway_common],
+ install: true
+)
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 @@
-include_directories(
- ${PROTOCOLS_INCLUDE_DIRS}
- ${PANGO_INCLUDE_DIRS}
- ${XKBCOMMON_INCLUDE_DIRS}
- ${WAYLAND_INCLUDE_DIR}
- ${EPOLLSHIM_INCLUDE_DIRS}
-)
-
-add_library(sway-wayland STATIC
- buffers.c
- pango.c
- registry.c
- window.c
- cairo.c
- )
-
-target_link_libraries(sway-wayland
- sway-common
- sway-protocols
- ${PANGO_LIBRARIES}
- ${XKBCOMMON_LIBRARIES}
- ${EPOLLSHIM_LIBRARIES}
- )
-
-if (WITH_GDK_PIXBUF)
- include_directories(
- ${GDK_PIXBUF_INCLUDE_DIRS}
- )
- target_link_libraries(sway-wayland
- ${GDK_PIXBUF_LIBRARIES}
- )
-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 @@
-#include <wayland-client.h>
-#include <xkbcommon/xkbcommon.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/timerfd.h>
-#include "wayland-desktop-shell-client-protocol.h"
-#include "wayland-swaylock-client-protocol.h"
-#include "client/registry.h"
-#include "stringop.h"
-#include "log.h"
-
-static void display_handle_mode(void *data, struct wl_output *wl_output,
- uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
- struct output_state *state = data;
- if (flags & WL_OUTPUT_MODE_CURRENT) {
- state->flags = flags;
- state->width = width;
- state->height = height;
- sway_log(L_DEBUG, "Got mode %dx%d:0x%X for output %p",
- width, height, flags, data);
- }
-}
-
-static void display_handle_geometry(void *data, struct wl_output *wl_output,
- int32_t x, int32_t y, int32_t physical_width, int32_t physical_height,
- int32_t subpixel, const char *make, const char *model, int32_t transform) {
- // this space intentionally left blank
-}
-
-static void display_handle_done(void *data, struct wl_output *wl_output) {
- // this space intentionally left blank
-}
-
-static void display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) {
- struct output_state *state = data;
- state->scale = factor;
- sway_log(L_DEBUG, "Got scale factor %d for output %p", factor, data);
-}
-
-static const struct wl_output_listener output_listener = {
- .mode = display_handle_mode,
- .geometry = display_handle_geometry,
- .done = display_handle_done,
- .scale = display_handle_scale
-};
-
-const char *XKB_MASK_NAMES[MASK_LAST] = {
- XKB_MOD_NAME_SHIFT,
- XKB_MOD_NAME_CAPS,
- XKB_MOD_NAME_CTRL,
- XKB_MOD_NAME_ALT,
- "Mod2",
- "Mod3",
- XKB_MOD_NAME_LOGO,
- "Mod5",
-};
-
-const enum mod_bit XKB_MODS[MASK_LAST] = {
- MOD_SHIFT,
- MOD_CAPS,
- MOD_CTRL,
- MOD_ALT,
- MOD_MOD2,
- MOD_MOD3,
- MOD_LOGO,
- MOD_MOD5
-};
-
-static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
- uint32_t format, int fd, uint32_t size) {
- // Keyboard errors are abort-worthy because you wouldn't be able to unlock your screen otherwise.
-
- struct registry *registry = data;
- if (!data) {
- close(fd);
- return;
- }
-
- if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
- close(fd);
- sway_abort("Unknown keymap format %d, aborting", format);
- }
-
- char *map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
- if (map_str == MAP_FAILED) {
- close(fd);
- sway_abort("Unable to initialized shared keyboard memory, aborting");
- }
-
- struct xkb_keymap *keymap = xkb_keymap_new_from_string(registry->input->xkb.context,
- map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
- munmap(map_str, size);
- close(fd);
-
- if (!keymap) {
- sway_abort("Failed to compile keymap, aborting");
- }
-
- struct xkb_state *state = xkb_state_new(keymap);
- if (!state) {
- xkb_keymap_unref(keymap);
- sway_abort("Failed to create xkb state, aborting");
- }
-
- xkb_keymap_unref(registry->input->xkb.keymap);
- xkb_state_unref(registry->input->xkb.state);
- registry->input->xkb.keymap = keymap;
- registry->input->xkb.state = state;
-
- int i;
- for (i = 0; i < MASK_LAST; ++i) {
- registry->input->xkb.masks[i] = 1 << xkb_keymap_mod_get_index(registry->input->xkb.keymap, XKB_MASK_NAMES[i]);
- }
-}
-
-static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
- // this space intentionally left blank
-}
-
-static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, struct wl_surface *surface) {
- // this space intentionally left blank
-}
-
-static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) {
- struct registry *registry = data;
- enum wl_keyboard_key_state state = state_w;
-
- if (!registry->input->xkb.state) {
- return;
- }
-
- xkb_keysym_t sym = xkb_state_key_get_one_sym(registry->input->xkb.state, key + 8);
- registry->input->sym = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? sym : XKB_KEY_NoSymbol);
- registry->input->code = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? key + 8 : 0);
- uint32_t codepoint = xkb_state_key_get_utf32(registry->input->xkb.state, registry->input->code);
- if (registry->input->notify) {
- registry->input->notify(state, sym, key, codepoint);
- }
-}
-
-static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group) {
- struct registry *registry = data;
-
- if (!registry->input->xkb.keymap) {
- return;
- }
-
- xkb_state_update_mask(registry->input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
- xkb_mod_mask_t mask = xkb_state_serialize_mods(registry->input->xkb.state,
- XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
-
- registry->input->modifiers = 0;
- for (uint32_t i = 0; i < MASK_LAST; ++i) {
- if (mask & registry->input->xkb.masks[i]) {
- registry->input->modifiers |= XKB_MODS[i];
- }
- }
-}
-
-static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
- int32_t rate, int32_t delay) {
- // this space intentionally left blank
-}
-
-static const struct wl_keyboard_listener keyboard_listener = {
- .keymap = keyboard_handle_keymap,
- .enter = keyboard_handle_enter,
- .leave = keyboard_handle_leave,
- .key = keyboard_handle_key,
- .modifiers = keyboard_handle_modifiers,
- .repeat_info = keyboard_handle_repeat_info
-};
-
-static void seat_handle_capabilities(void *data, struct wl_seat *seat,
- enum wl_seat_capability caps) {
- struct registry *reg = data;
-
- if ((caps & WL_SEAT_CAPABILITY_POINTER) && !reg->pointer) {
- reg->pointer = wl_seat_get_pointer(reg->seat);
- } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && reg->pointer) {
- wl_pointer_destroy(reg->pointer);
- reg->pointer = NULL;
- }
-
- if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !reg->keyboard) {
- reg->keyboard = wl_seat_get_keyboard(reg->seat);
- wl_keyboard_add_listener(reg->keyboard, &keyboard_listener, reg);
- } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && reg->keyboard) {
- wl_keyboard_destroy(reg->keyboard);
- reg->keyboard = NULL;
- }
-}
-
-static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) {
- // this space intentionally left blank
-}
-
-static const struct wl_seat_listener seat_listener = {
- .capabilities = seat_handle_capabilities,
- .name = seat_handle_name,
-};
-
-static void registry_global(void *data, struct wl_registry *registry,
- uint32_t name, const char *interface, uint32_t version) {
- struct registry *reg = data;
-
- if (strcmp(interface, wl_compositor_interface.name) == 0) {
- reg->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version);
- } else if (strcmp(interface, wl_shm_interface.name) == 0) {
- reg->shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
- } else if (strcmp(interface, wl_shell_interface.name) == 0) {
- reg->shell = wl_registry_bind(registry, name, &wl_shell_interface, version);
- } else if (strcmp(interface, wl_seat_interface.name) == 0) {
- reg->seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
- wl_seat_add_listener(reg->seat, &seat_listener, reg);
- } else if (strcmp(interface, wl_output_interface.name) == 0) {
- struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, version);
- struct output_state *ostate = malloc(sizeof(struct output_state));
- ostate->output = output;
- ostate->scale = 1;
- wl_output_add_listener(output, &output_listener, ostate);
- list_add(reg->outputs, ostate);
- } else if (strcmp(interface, desktop_shell_interface.name) == 0) {
- reg->desktop_shell = wl_registry_bind(registry, name, &desktop_shell_interface, version);
- } else if (strcmp(interface, lock_interface.name) == 0) {
- reg->swaylock = wl_registry_bind(registry, name, &lock_interface, version);
- }
-}
-
-static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
- // this space intentionally left blank
-}
-
-static const struct wl_registry_listener registry_listener = {
- .global = registry_global,
- .global_remove = registry_global_remove
-};
-
-struct registry *registry_poll(void) {
- struct registry *registry = malloc(sizeof(struct registry));
- memset(registry, 0, sizeof(struct registry));
- registry->outputs = create_list();
- registry->input = calloc(sizeof(struct input), 1);
- registry->input->xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
-
- registry->display = wl_display_connect(NULL);
- if (!registry->display) {
- sway_log(L_ERROR, "Error opening display");
- registry_teardown(registry);
- return NULL;
- }
-
- struct wl_registry *reg = wl_display_get_registry(registry->display);
- wl_registry_add_listener(reg, &registry_listener, registry);
- wl_display_dispatch(registry->display);
- wl_display_roundtrip(registry->display);
- wl_registry_destroy(reg);
-
- return registry;
-}
-
-void registry_teardown(struct registry *registry) {
- if (registry->pointer) {
- wl_pointer_destroy(registry->pointer);
- }
- if (registry->seat) {
- wl_seat_destroy(registry->seat);
- }
- if (registry->shell) {
- wl_shell_destroy(registry->shell);
- }
- if (registry->shm) {
- wl_shm_destroy(registry->shm);
- }
- if (registry->compositor) {
- wl_compositor_destroy(registry->compositor);
- }
- if (registry->display) {
- wl_display_disconnect(registry->display);
- }
- if (registry->outputs) {
- free_flat_list(registry->outputs);
- }
- free(registry);
-}
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 @@
-#include <wayland-client.h>
-#include <wayland-cursor.h>
-#include "wayland-xdg-shell-client-protocol.h"
-#include "wayland-desktop-shell-client-protocol.h"
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/mman.h>
-#include "client/window.h"
-#include "client/buffer.h"
-#include "list.h"
-#include "log.h"
-
-static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w) {
- struct window *window = data;
- if (window->registry->pointer) {
- struct wl_cursor_image *image = window->cursor.cursor->images[0];
- wl_pointer_set_cursor(pointer, serial, window->cursor.surface, image->hotspot_x, image->hotspot_y);
- }
-}
-
-static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface) {
-}
-
-static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
- uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) {
- struct window *window = data;
-
- window->pointer_input.last_x = wl_fixed_to_int(sx_w);
- window->pointer_input.last_y = wl_fixed_to_int(sy_w);
-}
-
-static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
- uint32_t time, uint32_t button, uint32_t state_w) {
- struct window *window = data;
- struct pointer_input *input = &window->pointer_input;
-
- if (window->pointer_input.notify_button) {
- window->pointer_input.notify_button(window, input->last_x, input->last_y, button, state_w);
- }
-}
-
-static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
- uint32_t time, uint32_t axis, wl_fixed_t value) {
- struct window *window = data;
- enum scroll_direction direction;
-
- switch (axis) {
- case 0:
- direction = wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN;
- break;
- case 1:
- direction = wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT;
- break;
- default:
- sway_log(L_DEBUG, "Unexpected axis value on mouse scroll");
- return;
- }
-
- if (window->pointer_input.notify_scroll) {
- window->pointer_input.notify_scroll(window, direction);
- }
-}
-
-static const struct wl_pointer_listener pointer_listener = {
- .enter = pointer_handle_enter,
- .leave = pointer_handle_leave,
- .motion = pointer_handle_motion,
- .button = pointer_handle_button,
- .axis = pointer_handle_axis
-};
-
-void shell_surface_configure(void *data, struct wl_shell_surface *wl_shell_surface,
- uint32_t edges, int32_t width, int32_t height) {
- struct window *window = data;
- window->width = width;
- window->height = height;
-}
-
-static const struct wl_shell_surface_listener surface_listener = {
- .configure = shell_surface_configure
-};
-
-void window_make_shell(struct window *window) {
- window->shell_surface = wl_shell_get_shell_surface(window->registry->shell, window->surface);
- wl_shell_surface_add_listener(window->shell_surface, &surface_listener, window);
- wl_shell_surface_set_toplevel(window->shell_surface);
-}
-
-struct window *window_setup(struct registry *registry, uint32_t width, uint32_t height,
- int32_t scale, bool shell_surface) {
- struct window *window = malloc(sizeof(struct window));
- memset(window, 0, sizeof(struct window));
- window->width = width;
- window->height = height;
- window->scale = scale;
- window->registry = registry;
- window->font = "monospace 10";
-
- window->surface = wl_compositor_create_surface(registry->compositor);
- if (shell_surface) {
- window_make_shell(window);
- }
- if (registry->pointer) {
- wl_pointer_add_listener(registry->pointer, &pointer_listener, window);
- }
-
- get_next_buffer(window);
-
- if (registry->pointer) {
- char *cursor_theme = getenv("SWAY_CURSOR_THEME");
- if (!cursor_theme) {
- cursor_theme = "default";
- }
- char *cursor_size = getenv("SWAY_CURSOR_SIZE");
- if (!cursor_size) {
- cursor_size = "16";
- }
-
- sway_log(L_DEBUG, "Cursor scale: %d", scale);
- window->cursor.cursor_theme = wl_cursor_theme_load(cursor_theme,
- atoi(cursor_size) * scale, registry->shm);
- window->cursor.cursor = wl_cursor_theme_get_cursor(window->cursor.cursor_theme, "left_ptr");
- window->cursor.surface = wl_compositor_create_surface(registry->compositor);
-
- struct wl_cursor_image *image = window->cursor.cursor->images[0];
- struct wl_buffer *cursor_buf = wl_cursor_image_get_buffer(image);
- wl_surface_attach(window->cursor.surface, cursor_buf, 0, 0);
- wl_surface_set_buffer_scale(window->cursor.surface, scale);
- wl_surface_damage(window->cursor.surface, 0, 0,
- image->width, image->height);
- wl_surface_commit(window->cursor.surface);
- }
-
- return window;
-}
-
-static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) {
- struct window *window = data;
- wl_callback_destroy(callback);
- window->frame_cb = NULL;
-}
-
-static const struct wl_callback_listener listener = {
- frame_callback
-};
-
-int window_prerender(struct window *window) {
- if (window->frame_cb) {
- return 0;
- }
-
- get_next_buffer(window);
- return 1;
-}
-
-int window_render(struct window *window) {
- window->frame_cb = wl_surface_frame(window->surface);
- wl_callback_add_listener(window->frame_cb, &listener, window);
-
- wl_surface_attach(window->surface, window->buffer->buffer, 0, 0);
- wl_surface_set_buffer_scale(window->surface, window->scale);
- wl_surface_damage(window->surface, 0, 0, window->width, window->height);
- wl_surface_commit(window->surface);
-
- return 1;
-}
-
-void window_teardown(struct window *window) {
- // TODO
-}