commit 41f30261047f42b328fd2068b2486b496d1eb56d
parent 23f214fb7d705c3fdd2cd6f19c789bbcdee3ea65
Author: deurzen <m.deurzen@tum.de>
Date: Fri, 20 May 2022 00:03:48 +0200
initial server->server/model overhaul
Diffstat:
21 files changed, 3825 insertions(+), 158 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -12,6 +12,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_compile_options(
+ -g
-fdiagnostics-show-option
# -Weffc++
-Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic
@@ -31,6 +32,7 @@ add_compile_options(
-Wuseless-cast
-Wdouble-promotion
-Wformat=2
+ -DXWAYLAND
)
find_program(CCACHE ccache)
@@ -117,6 +119,12 @@ add_custom_command(
)
add_custom_command(
+ OUTPUT ${CMAKE_SOURCE_DIR}/include/protocols/wlr-layer-shell-unstable-v1-protocol.h
+ COMMAND wayland-scanner server-header ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml ${CMAKE_SOURCE_DIR}/include/protocols/wlr-layer-shell-unstable-v1-protocol.h
+ COMMENT "Generating layer-shell-protocol.h"
+)
+
+add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/include/protocols/pointer-constraints-unstable-v1-protocol.h
COMMAND wayland-scanner server-header ${WAYLAND_PROTOCOLS}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml ${CMAKE_SOURCE_DIR}/include/protocols/pointer-constraints-unstable-v1-protocol.h
COMMENT "Generating pointer-constraints-unstable-v1.h"
@@ -124,6 +132,7 @@ add_custom_command(
add_custom_target(run ALL
DEPENDS ${CMAKE_SOURCE_DIR}/include/protocols/xdg-shell-protocol.h
+ DEPENDS ${CMAKE_SOURCE_DIR}/include/protocols/wlr-layer-shell-unstable-v1-protocol.h
DEPENDS ${CMAKE_SOURCE_DIR}/include/protocols/pointer-constraints-unstable-v1-protocol.h
)
@@ -144,6 +153,7 @@ pkg_check_modules(wlroots REQUIRED IMPORTED_TARGET wlroots)
pkg_check_modules(pixman_1 REQUIRED IMPORTED_TARGET pixman-1)
pkg_check_modules(libinput REQUIRED IMPORTED_TARGET libinput)
pkg_check_modules(xkbcommon REQUIRED IMPORTED_TARGET xkbcommon)
+pkg_check_modules(xcb REQUIRED IMPORTED_TARGET xcb)
target_compile_features(kranewl PRIVATE cxx_std_20)
target_compile_features(kranec PRIVATE cxx_std_20)
@@ -163,6 +173,7 @@ target_link_libraries(kranewl
PkgConfig::pixman_1
PkgConfig::libinput
PkgConfig::xkbcommon
+ PkgConfig::xcb
)
target_include_directories(kranewl PRIVATE
diff --git a/include/kranewl/client.hh b/include/kranewl/client.hh
@@ -14,6 +14,7 @@ extern "C" {
#include <string>
#include <vector>
+typedef class Server* Server_ptr;
typedef class Partition* Partition_ptr;
typedef class Context* Context_ptr;
typedef class Workspace* Workspace_ptr;
@@ -40,6 +41,7 @@ typedef struct Client final
|| client->disowned;
}
+ Client();
Client(
Surface surface,
Partition_ptr partition,
@@ -56,6 +58,8 @@ typedef struct Client final
OutsideState get_outside_state() const noexcept;
+ struct wlr_surface* get_surface() noexcept;
+
void focus() noexcept;
void unfocus() noexcept;
@@ -77,6 +81,9 @@ typedef struct Client final
void set_tile_decoration(Decoration const&) noexcept;
void set_free_decoration(Decoration const&) noexcept;
+ struct wl_list link;
+ Server_ptr server;
+
SurfaceType surface_type;
Surface surface;
@@ -132,9 +139,11 @@ typedef struct Client final
struct wl_listener l_destroy;
struct wl_listener l_set_title;
struct wl_listener l_fullscreen;
+ struct wl_listener l_request_move;
+ struct wl_listener l_request_resize;
#ifdef XWAYLAND
- struct wl_listener l_activate;
- struct wl_listener l_configure;
+ struct wl_listener l_request_activate;
+ struct wl_listener l_request_configure;
struct wl_listener l_set_hints;
#else
struct wl_listener l_new_xdg_popup;
diff --git a/include/kranewl/common.hh b/include/kranewl/common.hh
@@ -1,10 +1,33 @@
#pragma once
+#include <cstddef>
+
typedef unsigned Pid;
+typedef std::size_t Ident;
+typedef std::size_t Index;
+
+enum class Direction {
+ Forward,
+ Backward,
+};
+
+enum class Edge {
+ Left,
+ Right,
+ Top,
+ Bottom,
+};
+
+enum class Corner {
+ TopLeft,
+ TopRight,
+ BottomLeft,
+ BottomRight,
+};
enum class Toggle
{
On,
Off,
- Reverse
+ Reverse,
};
diff --git a/include/kranewl/cycle.hh b/include/kranewl/cycle.hh
@@ -0,0 +1,199 @@
+#pragma once
+
+#include <kranewl/common.hh>
+
+#include <cstdlib>
+#include <deque>
+#include <vector>
+#include <optional>
+#include <unordered_map>
+#include <variant>
+
+enum class StackAction
+{
+ Insert,
+ Remove
+};
+
+template <typename T>
+class HistoryStack final
+{
+ static_assert(std::is_pointer<T>::value,
+ "Only pointer types may be stored in a history stack.");
+
+public:
+ HistoryStack();
+ ~HistoryStack();
+
+ void clear();
+ void push_back(T);
+ void replace(T, T);
+ std::optional<T> peek_back() const;
+ std::optional<T> pop_back();
+ void remove(T);
+
+ std::vector<T> const& as_vector() const;
+
+private:
+ std::vector<T> m_stack;
+
+};
+
+template <typename T>
+class Cycle final
+{
+ static_assert(std::is_pointer<T>::value,
+ "Only pointer types may be stored in a cycle.");
+
+public:
+ Cycle(std::vector<T>, bool);
+ Cycle(std::initializer_list<T>, bool);
+ ~Cycle();
+
+ bool next_will_wrap(Direction) const;
+ bool empty() const;
+ bool contains(T) const;
+ bool is_active_element(T) const;
+ bool is_active_index(Index) const;
+
+ std::size_t size() const;
+ std::size_t length() const;
+
+ std::optional<Index> index() const;
+ Index active_index() const;
+ Index last_index() const;
+ Index next_index(Direction) const;
+ Index next_index_from(Index, Direction) const;
+
+ std::optional<Index> index_of_element(const T) const;
+
+ std::optional<T> next_element(Direction) const;
+ std::optional<T> active_element() const;
+ std::optional<T> prev_active_element() const;
+ std::optional<T> element_at_index(Index) const;
+ std::optional<T> element_at_front(T) const;
+ std::optional<T> element_at_back(T) const;
+
+ void activate_first();
+ void activate_last();
+ void activate_at_index(Index);
+ void activate_element(T);
+
+ template<typename UnaryPredicate>
+ void activate_for_condition(UnaryPredicate predicate)
+ {
+ auto it = std::find_if(
+ m_elements.begin(),
+ m_elements.end(),
+ predicate
+ );
+
+ if (it != m_elements.end())
+ activate_element(*it);
+ }
+
+ bool remove_first();
+ bool remove_last();
+ bool remove_at_index(Index);
+ bool remove_element(T);
+
+ template<typename UnaryPredicate>
+ bool remove_for_condition(UnaryPredicate predicate)
+ {
+ auto it = std::find_if(
+ m_elements.begin(),
+ m_elements.end(),
+ predicate
+ );
+
+ if (it != m_elements.end())
+ return remove_element(*it);
+
+ return false;
+ }
+
+ std::optional<T> pop_back();
+
+ void replace_element(T, T);
+ void swap_elements(T, T);
+ void swap_indices(Index, Index);
+
+ void reverse();
+ void rotate(Direction);
+ void rotate_range(Direction, Index, Index);
+ std::optional<T> cycle_active(Direction);
+ std::optional<T> drag_active(Direction);
+
+ void insert_at_front(T);
+ void insert_at_back(T);
+ void insert_before_index(Index, T);
+ void insert_after_index(Index, T);
+ void insert_before_element(T, T);
+ void insert_after_element(T, T);
+
+ void clear();
+
+ std::deque<T> const& as_deque() const;
+ std::vector<T> const& stack() const;
+
+ typename std::deque<T>::iterator
+ begin()
+ {
+ return m_elements.begin();
+ }
+
+ typename std::deque<T>::const_iterator
+ begin() const
+ {
+ return m_elements.begin();
+ }
+
+ typename std::deque<T>::const_iterator
+ cbegin() const
+ {
+ return m_elements.cbegin();
+ }
+
+ typename std::deque<T>::iterator
+ end()
+ {
+ return m_elements.end();
+ }
+
+ typename std::deque<T>::const_iterator
+ end() const
+ {
+ return m_elements.end();
+ }
+
+ typename std::deque<T>::const_iterator
+ cend() const
+ {
+ return m_elements.cend();
+ }
+
+ T operator[](std::size_t index)
+ {
+ return m_elements[index];
+ }
+
+ const T operator[](std::size_t index) const
+ {
+ return m_elements[index];
+ }
+
+private:
+ Index m_index;
+
+ std::deque<T> m_elements;
+
+ bool m_unwindable;
+ HistoryStack<T> m_stack;
+
+ void sync_active();
+
+ void push_index_to_stack(std::optional<Index>);
+ void push_active_to_stack();
+ std::optional<T> get_active_from_stack();
+
+};
diff --git a/include/kranewl/cycle.t.hh b/include/kranewl/cycle.t.hh
@@ -0,0 +1,651 @@
+#pragma once
+
+#include <kranewl/cycle.hh>
+#include <kranewl/util.hh>
+
+template <typename T>
+HistoryStack<T>::HistoryStack()
+ : m_stack({})
+{}
+
+template <typename T>
+HistoryStack<T>::~HistoryStack()
+{}
+
+template <typename T>
+void
+HistoryStack<T>::clear()
+{
+ m_stack.clear();
+}
+
+template <typename T>
+void
+HistoryStack<T>::push_back(T element)
+{
+ m_stack.push_back(element);
+}
+
+template <typename T>
+void
+HistoryStack<T>::replace(T element, T replacement)
+{
+ std::optional<Index> index = Util::index_of(m_stack, element);
+
+ if (index)
+ m_stack[*index] = replacement;
+}
+
+template <typename T>
+std::optional<T>
+HistoryStack<T>::peek_back() const
+{
+ if (!m_stack.empty())
+ return m_stack.back();
+
+ return std::nullopt;
+}
+
+template <typename T>
+std::optional<T>
+HistoryStack<T>::pop_back()
+{
+ if (!m_stack.empty()) {
+ T element = m_stack.back();
+ m_stack.pop_back();
+ return element;
+ }
+
+ return std::nullopt;
+}
+
+template <typename T>
+void
+HistoryStack<T>::remove(T element)
+{
+ Util::erase_remove(m_stack, element);
+}
+
+template <typename T>
+std::vector<T> const&
+HistoryStack<T>::as_vector() const
+{
+ return m_stack;
+}
+
+
+template <typename T>
+Cycle<T>::Cycle(std::vector<T> elements, bool unwindable)
+ : m_index(Util::last_index(elements)),
+ m_elements({}),
+ m_unwindable(unwindable),
+ m_stack(HistoryStack<T>())
+{
+ m_elements.resize(elements.size());
+ std::copy(elements.begin(), elements.end(), m_elements.begin());
+}
+
+template <typename T>
+Cycle<T>::Cycle(std::initializer_list<T> elements, bool unwindable)
+ : m_index(0),
+ m_elements({}),
+ m_unwindable(unwindable),
+ m_stack(HistoryStack<T>())
+{
+ std::copy(elements.begin(), elements.end(), m_elements.begin());
+ m_index = Util::last_index(m_elements);
+}
+
+template <typename T>
+Cycle<T>::~Cycle()
+{}
+
+template <typename T>
+bool
+Cycle<T>::next_will_wrap(Direction direction) const
+{
+ switch (direction) {
+ case Direction::Backward: return m_index == 0;
+ case Direction::Forward: return m_index == Util::last_index(m_elements);
+ }
+}
+
+template <typename T>
+bool
+Cycle<T>::empty() const
+{
+ return m_elements.empty();
+}
+
+template <typename T>
+bool
+Cycle<T>::contains(T element) const
+{
+ return Util::contains(m_elements, element);
+}
+
+template <typename T>
+bool
+Cycle<T>::is_active_element(T element) const
+{
+ std::optional<Index> current = this->index();
+ return current && *current == index_of_element(element);
+}
+
+template <typename T>
+bool
+Cycle<T>::is_active_index(Index index) const
+{
+ std::optional<Index> current = this->index();
+ return current && *current == index;
+}
+
+
+template <typename T>
+std::size_t
+Cycle<T>::size() const
+{
+ return m_elements.size();
+}
+
+template <typename T>
+std::size_t
+Cycle<T>::length() const
+{
+ return m_elements.size();
+}
+
+
+template <typename T>
+std::optional<Index>
+Cycle<T>::index() const
+{
+ if (m_index < m_elements.size())
+ return m_index;
+
+ return std::nullopt;
+}
+
+template <typename T>
+Index
+Cycle<T>::active_index() const
+{
+ return m_index;
+}
+
+template <typename T>
+Index
+Cycle<T>::last_index() const
+{
+ return Util::last_index(m_elements);
+}
+
+template <typename T>
+Index
+Cycle<T>::next_index(Direction direction) const
+{
+ return next_index_from(m_index, direction);
+}
+
+template <typename T>
+Index
+Cycle<T>::next_index_from(Index index, Direction direction) const
+{
+ Index end = Util::last_index(m_elements);
+
+ switch (direction) {
+ case Direction::Backward: return index == 0 ? end : index - 1;
+ case Direction::Forward: return index == end ? 0 : index + 1;
+ default: return index;
+ }
+}
+
+
+template <typename T>
+std::optional<Index>
+Cycle<T>::index_of_element(const T element) const
+{
+ return Util::index_of(m_elements, element);
+}
+
+
+template <typename T>
+std::optional<T>
+Cycle<T>::next_element(Direction direction) const
+{
+ std::optional<Index> index = next_index(direction);
+ if (index && *index < m_elements.size())
+ return m_elements[*index];
+
+ return std::nullopt;
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::active_element() const
+{
+ if (m_index < m_elements.size())
+ return m_elements[m_index];
+
+ return std::nullopt;
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::prev_active_element() const
+{
+ return m_stack.peek_back();
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::element_at_index(Index index) const
+{
+ if (index < m_elements.size())
+ return m_elements[index];
+
+ return std::nullopt;
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::element_at_front(T) const
+{
+ if (!m_elements.empty())
+ return m_elements[0];
+
+ return std::nullopt;
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::element_at_back(T) const
+{
+ if (!m_elements.empty())
+ return m_elements[Util::last_index(m_elements)];
+
+ return std::nullopt;
+}
+
+
+template <typename T>
+void
+Cycle<T>::activate_first()
+{
+ activate_at_index(0);
+}
+
+template <typename T>
+void
+Cycle<T>::activate_last()
+{
+ activate_at_index(Util::last_index(m_elements));
+}
+
+template <typename T>
+void
+Cycle<T>::activate_at_index(Index index)
+{
+ if (index != m_index) {
+ push_active_to_stack();
+ m_index = index;
+ }
+}
+
+template <typename T>
+void
+Cycle<T>::activate_element(T element)
+{
+ std::optional<Index> index = Util::index_of(m_elements, element);
+ if (index)
+ activate_at_index(*index);
+}
+
+
+template <typename T>
+bool
+Cycle<T>::remove_first()
+{
+ bool must_resync = is_active_index(0);
+
+ std::size_t size_before = m_elements.size();
+ Util::erase_at_index(m_elements, 0);
+
+ if (must_resync)
+ sync_active();
+
+ return size_before != m_elements.size();
+}
+
+template <typename T>
+bool
+Cycle<T>::remove_last()
+{
+ Index end = Util::last_index(m_elements);
+ bool must_resync = is_active_index(end);
+
+ std::size_t size_before = m_elements.size();
+ Util::erase_at_index(m_elements, end);
+
+ if (must_resync)
+ sync_active();
+
+ return size_before != m_elements.size();
+}
+
+template <typename T>
+bool
+Cycle<T>::remove_at_index(Index index)
+{
+ bool must_resync = is_active_index(index);
+
+ std::size_t size_before = m_elements.size();
+ Util::erase_at_index(m_elements, index);
+
+ if (must_resync)
+ sync_active();
+
+ return size_before != m_elements.size();
+}
+
+template <typename T>
+bool
+Cycle<T>::remove_element(T element)
+{
+ std::optional<Index> index = index_of_element(element);
+ bool must_resync = is_active_element(element);
+
+ std::size_t size_before = m_elements.size();
+
+ Util::erase_remove(m_elements, element);
+
+ m_stack.remove(element);
+
+ if (m_index != 0 && index && m_index >= *index)
+ --m_index;
+
+ if (must_resync)
+ sync_active();
+
+ return size_before != m_elements.size();
+}
+
+
+template <typename T>
+std::optional<T>
+Cycle<T>::pop_back()
+{
+ std::optional<T> value = std::nullopt;
+
+ if (!m_elements.empty()) {
+ bool must_resync = is_active_element(m_elements.back());
+
+ value = std::optional(m_elements.back());
+ m_elements.pop_back();
+
+ if (must_resync)
+ sync_active();
+ }
+
+ return value;
+}
+
+
+template <typename T>
+void
+Cycle<T>::replace_element(T element, T replacement)
+{
+ if (contains(replacement))
+ return;
+
+ std::optional<Index> index = index_of_element(element);
+
+ if (index) {
+ m_elements[*index] = replacement;
+ m_stack.replace(element, replacement);
+ }
+}
+
+template <typename T>
+void
+Cycle<T>::swap_elements(T element1, T element2)
+{
+ std::optional<Index> index1 = index_of_element(element1);
+ std::optional<Index> index2 = index_of_element(element2);
+
+ if (index1 && index2)
+ std::iter_swap(m_elements.begin() + *index1, m_elements.begin() + *index2);
+}
+
+template <typename T>
+void
+Cycle<T>::swap_indices(Index index1, Index index2)
+{
+ if (index1 < m_elements.size() && index2 < m_elements.size())
+ std::iter_swap(m_elements.begin() + index1, m_elements.begin() + index2);
+}
+
+
+template <typename T>
+void
+Cycle<T>::reverse()
+{
+ std::reverse(m_elements.begin(), m_elements.end());
+}
+
+template <typename T>
+void
+Cycle<T>::rotate(Direction direction)
+{
+ switch (direction) {
+ case Direction::Backward:
+ {
+ std::rotate(
+ m_elements.rbegin(),
+ std::next(m_elements.rbegin()),
+ m_elements.rend()
+ );
+
+ return;
+ }
+ case Direction::Forward:
+ {
+ std::rotate(
+ m_elements.begin(),
+ std::next(m_elements.begin()),
+ m_elements.end()
+ );
+
+ return;
+ }
+ default: return;
+ }
+}
+
+template <typename T>
+void
+Cycle<T>::rotate_range(Direction direction, Index begin, Index end)
+{
+ if (begin >= end || begin >= m_elements.size() || end > m_elements.size())
+ return;
+
+ switch (direction) {
+ case Direction::Backward:
+ {
+ std::rotate(
+ m_elements.begin() + begin,
+ std::next(m_elements.begin() + begin),
+ m_elements.begin() + end
+ );
+
+ return;
+ }
+ case Direction::Forward:
+ {
+ std::rotate(
+ m_elements.rend() - end,
+ std::next(m_elements.rend() - end),
+ m_elements.rend() - begin
+ );
+
+ return;
+ }
+ default: return;
+ }
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::cycle_active(Direction direction)
+{
+ push_active_to_stack();
+ m_index = next_index(direction);
+ return active_element();
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::drag_active(Direction direction)
+{
+ Index index = next_index(direction);
+
+ if (m_index != index && m_index < m_elements.size() && index < m_elements.size())
+ std::iter_swap(m_elements.begin() + m_index, m_elements.begin() + index);
+
+ return cycle_active(direction);
+}
+
+
+template <typename T>
+void
+Cycle<T>::insert_at_front(T element)
+{
+ push_active_to_stack();
+ m_elements.push_front(element);
+ m_index = 0;
+}
+
+template <typename T>
+void
+Cycle<T>::insert_at_back(T element)
+{
+ push_active_to_stack();
+ m_elements.push_back(element);
+ m_index = Util::last_index(m_elements);
+}
+
+template <typename T>
+void
+Cycle<T>::insert_before_index(Index index, T element)
+{
+ if (index >= m_elements.size())
+ index = Util::last_index(m_elements);
+
+ push_active_to_stack();
+ m_elements.insert(m_elements.begin() + index, element);
+}
+
+template <typename T>
+void
+Cycle<T>::insert_after_index(Index index, T element)
+{
+ if (m_elements.empty() || index >= m_elements.size() - 1) {
+ insert_at_back(element);
+ return;
+ }
+
+ push_active_to_stack();
+ m_elements.insert(m_elements.begin() + index + 1, element);
+}
+
+template <typename T>
+void
+Cycle<T>::insert_before_element(T other, T element)
+{
+ std::optional<Index> index = index_of_element(other);
+
+ if (index)
+ insert_before_index(*index, element);
+ else
+ insert_at_back(element);
+}
+
+template <typename T>
+void
+Cycle<T>::insert_after_element(T other, T element)
+{
+ std::optional<Index> index = index_of_element(other);
+
+ if (index)
+ insert_after_index(*index, element);
+ else
+ insert_at_back(element);
+}
+
+
+template <typename T>
+void
+Cycle<T>::clear()
+{
+ m_elements.clear();
+ m_stack.clear();
+}
+
+
+template <typename T>
+void
+Cycle<T>::sync_active()
+{
+ std::optional<T> element = get_active_from_stack();
+ for (; element && !contains(*element); element = get_active_from_stack());
+ if (element)
+ m_index = *index_of_element(*element);
+}
+
+
+template <typename T>
+void
+Cycle<T>::push_index_to_stack(std::optional<Index> index)
+{
+ if (!m_unwindable || !index)
+ return;
+
+ std::optional<T> element = element_at_index(*index);
+ if (element) {
+ m_stack.remove(*element);
+ m_stack.push_back(*element);
+ }
+}
+
+template <typename T>
+void
+Cycle<T>::push_active_to_stack()
+{
+ if (!m_unwindable)
+ return;
+
+ push_index_to_stack(index());
+}
+
+template <typename T>
+std::optional<T>
+Cycle<T>::get_active_from_stack()
+{
+ return m_stack.pop_back();
+}
+
+
+template <typename T>
+std::deque<T> const&
+Cycle<T>::as_deque() const
+{
+ return m_elements;
+}
+
+template <typename T>
+std::vector<T> const&
+Cycle<T>::stack() const
+{
+ return m_stack.as_vector();
+}
+
diff --git a/include/kranewl/geometry.hh b/include/kranewl/geometry.hh
@@ -2,18 +2,6 @@
#include <ostream>
-enum class Edge {
- Left, Right, Top, Bottom
-};
-
-enum class Corner {
- TopLeft, TopRight, BottomLeft, BottomRight
-};
-
-enum class Direction {
- Forward, Backward
-};
-
struct Dim final {
int w;
int h;
diff --git a/include/kranewl/layout.hh b/include/kranewl/layout.hh
@@ -0,0 +1,173 @@
+#pragma once
+
+#include <kranewl/cycle.hh>
+#include <kranewl/placement.hh>
+#include <kranewl/decoration.hh>
+#include <kranewl/util.hh>
+
+#include <vector>
+#include <deque>
+#include <unordered_map>
+
+class LayoutHandler final
+{
+ typedef std::deque<Client_ptr>::const_iterator client_iter;
+ typedef std::vector<Placement>& placement_vector;
+
+public:
+ enum class LayoutKind
+ {
+ /// free layouts
+ Float,
+ FramelessFloat,
+ SingleFloat,
+ FramelessSingleFloat,
+
+ // overlapping tiled layouts
+ Center,
+ Monocle,
+ MainDeck,
+ StackDeck,
+ DoubleDeck,
+
+ // non-overlapping tiled layouts
+ Paper,
+ CompactPaper,
+ DoubleStack,
+ CompactDoubleStack,
+ HorizontalStack,
+ CompactHorizontalStack,
+ VerticalStack,
+ CompactVerticalStack,
+ };
+
+private:
+ typedef struct Layout final
+ {
+ typedef struct LayoutData final
+ {
+ constexpr static std::size_t MAX_MAIN_COUNT = 16;
+ constexpr static std::size_t MAX_GAP_SIZE = 300;
+ constexpr static Extents MAX_MARGIN
+ = Extents { 700, 700, 400, 400 };
+
+ LayoutData(
+ Extents margin,
+ std::size_t gap_size,
+ std::size_t main_count,
+ float main_factor
+ )
+ : margin(margin),
+ gap_size(gap_size),
+ main_count(main_count),
+ main_factor(main_factor)
+ {}
+
+ // generic layout data
+ Extents margin;
+ std::size_t gap_size;
+
+ // tiled layout data
+ std::size_t main_count;
+ float main_factor;
+ }* LayoutData_ptr;
+
+ private:
+ struct LayoutConfig final
+ {
+ Placement::PlacementMethod method;
+ Decoration decoration;
+ bool margin;
+ bool gap;
+ bool persistent;
+ bool single;
+ bool wraps;
+ };
+
+ public:
+ Layout(LayoutKind);
+ ~Layout();
+
+ inline bool
+ operator==(const Layout& other) const
+ {
+ return other.kind == kind;
+ }
+
+ const LayoutKind kind;
+ const LayoutConfig config;
+
+ const LayoutData default_data;
+ Cycle<LayoutData_ptr> data;
+
+ static LayoutConfig kind_to_config(LayoutKind kind);
+ static LayoutData kind_to_default_data(LayoutKind kind);
+
+ }* Layout_ptr;
+
+public:
+ LayoutHandler();
+ ~LayoutHandler();
+
+ void arrange(Region, placement_vector, client_iter, client_iter) const;
+
+ LayoutKind kind() const;
+ void set_kind(LayoutKind);
+ void set_prev_kind();
+
+ bool layout_is_free() const;
+ bool layout_has_margin() const;
+ bool layout_has_gap() const;
+ bool layout_is_persistent() const;
+ bool layout_is_single() const;
+ bool layout_wraps() const;
+
+ std::size_t gap_size() const;
+ std::size_t main_count() const;
+ float main_factor() const;
+ Extents margin() const;
+
+ void copy_data_from_prev_layout();
+ void set_prev_layout_data();
+
+ void change_gap_size(Util::Change<int>);
+ void change_main_count(Util::Change<int>);
+ void change_main_factor(Util::Change<float>);
+ void change_margin(Util::Change<int>);
+ void change_margin(Edge, Util::Change<int>);
+ void reset_gap_size();
+ void reset_margin();
+ void reset_layout_data();
+ void cycle_layout_data(Direction);
+
+ void save_layout(std::size_t) const;
+ void load_layout(std::size_t);
+
+private:
+ LayoutKind m_kind;
+ LayoutKind m_prev_kind;
+
+ std::unordered_map<LayoutKind, Layout_ptr> m_layouts;
+
+ Layout_ptr mp_layout;
+ Layout_ptr mp_prev_layout;
+
+ void arrange_float(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_frameless_float(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_single_float(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_frameless_single_float(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_center(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_monocle(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_main_deck(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_stack_deck(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_double_deck(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_paper(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_compact_paper(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_double_stack(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_compact_double_stack(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_horizontal_stack(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_compact_horizontal_stack(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_vertical_stack(Region, placement_vector, client_iter, client_iter) const;
+ void arrange_compact_vertical_stack(Region, placement_vector, client_iter, client_iter) const;
+
+};
diff --git a/include/kranewl/model.hh b/include/kranewl/model.hh
@@ -1,5 +1,7 @@
#pragma once
+#include <kranewl/cycle.hh>
+
#include <optional>
#include <string>
@@ -12,9 +14,16 @@ public:
~Model();
void run();
+ void exit();
+
+ void spawn_external(std::string&&) const;
private:
Server& m_server;
Config const& m_config;
+ /* Cycle<Partition_ptr> m_partitions; */
+ /* Cycle<Context_ptr> m_contexts; */
+ /* Cycle<Workspace_ptr> m_workspaces; */
+
};
diff --git a/include/kranewl/placement.hh b/include/kranewl/placement.hh
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <kranewl/geometry.hh>
+#include <kranewl/decoration.hh>
+
+#include <cstdlib>
+#include <optional>
+
+typedef class Client* Client_ptr;
+struct PlacementTarget final {
+ enum class TargetType
+ {
+ Client,
+ Tab,
+ Layout
+ };
+
+ TargetType type;
+ union
+ {
+ Client_ptr client;
+ std::size_t tab;
+ };
+};
+
+struct Placement final {
+ enum class PlacementMethod
+ {
+ Free,
+ Tile,
+ };
+
+ PlacementMethod method;
+ Client_ptr client;
+ Decoration decoration;
+ std::optional<Region> region;
+};
diff --git a/include/kranewl/server.hh b/include/kranewl/server.hh
@@ -11,9 +11,9 @@ extern "C" {
#include <cstdint>
#include <string>
-struct View;
-class Server final
-{
+typedef struct Client* Client_ptr;
+typedef class Server* Server_ptr;
+typedef class Server final {
enum class CursorMode {
Passthrough,
Move,
@@ -28,15 +28,21 @@ public:
private:
static void new_output(struct wl_listener*, void*);
+ static void output_layout_change(struct wl_listener*, void*);
static void output_manager_apply(struct wl_listener*, void*);
static void output_manager_test(struct wl_listener*, void*);
static void new_xdg_surface(struct wl_listener*, void*);
static void new_layer_shell_surface(struct wl_listener*, void*);
+ static void xdg_activation(struct wl_listener*, void*);
static void new_input(struct wl_listener*, void*);
static void new_pointer(Server*, struct wlr_input_device*);
static void new_keyboard(Server*, struct wlr_input_device*);
+ static void inhibit_activate(struct wl_listener*, void*);
+ static void inhibit_deactivate(struct wl_listener*, void*);
+ static void idle_inhibitor_create(struct wl_listener*, void*);
+ static void idle_inhibitor_destroy(struct wl_listener*, void*);
static void cursor_motion(struct wl_listener*, void*);
static void cursor_motion_absolute(struct wl_listener*, void*);
@@ -57,17 +63,26 @@ private:
static void request_set_primary_selection(struct wl_listener*, void*);
static void output_frame(struct wl_listener*, void*);
- static View* desktop_view_at(Server*, double, double, struct wlr_surface**, double*, double*);
- static void focus_view(View*, struct wlr_surface*);
+ static Client_ptr desktop_client_at(Server_ptr, double, double, struct wlr_surface**, double*, double*);
+ static void focus_client(Client_ptr, struct wlr_surface*);
static void xdg_toplevel_map(struct wl_listener*, void*);
static void xdg_toplevel_unmap(struct wl_listener*, void*);
static void xdg_toplevel_destroy(struct wl_listener*, void*);
static void xdg_toplevel_request_move(struct wl_listener*, void*);
static void xdg_toplevel_request_resize(struct wl_listener*, void*);
- static void xdg_toplevel_handle_moveresize(View*, CursorMode, uint32_t);
+ static void xdg_toplevel_handle_moveresize(Client_ptr, CursorMode, uint32_t);
+
+#ifdef XWAYLAND
+ static void xwayland_ready(struct wl_listener*, void*);
+ static void new_xwayland_surface(struct wl_listener*, void*);
+ static void xwayland_request_activate(struct wl_listener*, void*);
+ static void xwayland_request_configure(struct wl_listener*, void*);
+ static void xwayland_set_hints(struct wl_listener*, void*);
+#endif
struct wl_display* m_display;
+ struct wl_event_loop* m_event_loop;
struct wlr_backend* m_backend;
struct wlr_renderer* m_renderer;
@@ -75,30 +90,45 @@ private:
struct wlr_compositor* m_compositor;
struct wlr_data_device_manager* m_data_device_manager;
+#ifdef XWAYLAND
+ struct wlr_xwayland* m_xwayland;
+#endif
+
+ struct wlr_output_layout* m_output_layout;
struct wlr_scene* m_scene;
struct wlr_xdg_shell* m_xdg_shell;
- struct wl_listener ml_new_xdg_surface;
- struct wl_listener ml_new_layer_shell_surface;
- struct wl_list m_views;
+ struct wlr_layer_shell_v1* m_layer_shell;
+ struct wlr_xdg_activation_v1* m_xdg_activation;
- struct wlr_seat* m_seat;
+ struct wlr_output_manager_v1* m_output_manager;
struct wlr_presentation* m_presentation;
struct wlr_idle* m_idle;
struct wlr_server_decoration_manager* m_server_decoration_manager;
struct wlr_xdg_decoration_manager_v1* m_xdg_decoration_manager;
- struct wlr_output_manager_v1* m_output_manager;
- struct wlr_input_inhibit_manager* m_input_inhibit_manager;
- struct wlr_keyboard_shortcuts_inhibit_manager_v1* m_keyboard_shortcuts_inhibit_manager;
-
+ struct wlr_seat* m_seat;
+ struct wlr_cursor* m_cursor;
+ struct wlr_xcursor_manager* m_cursor_manager;
struct wlr_pointer_constraints_v1* m_pointer_constraints;
struct wlr_relative_pointer_manager_v1* m_relative_pointer_manager;
struct wlr_virtual_pointer_manager_v1* m_virtual_pointer_manager;
struct wlr_virtual_keyboard_manager_v1* m_virtual_keyboard_manager;
+ struct wlr_input_inhibit_manager* m_input_inhibit_manager;
+ struct wlr_idle_inhibit_manager_v1* m_idle_inhibit_manager;
+ struct wlr_keyboard_shortcuts_inhibit_manager_v1* m_keyboard_shortcuts_inhibit_manager;
- struct wlr_cursor* m_cursor;
- struct wlr_xcursor_manager* m_cursor_mgr;
+ struct wl_list m_outputs;
+ struct wl_list m_clients;
+ struct wl_list m_keyboards;
+
+ struct wl_listener ml_new_output;
+ struct wl_listener ml_output_layout_change;
+ struct wl_listener ml_output_manager_apply;
+ struct wl_listener ml_output_manager_test;
+ struct wl_listener ml_new_xdg_surface;
+ struct wl_listener ml_new_layer_shell_surface;
+ struct wl_listener ml_xdg_activation;
struct wl_listener ml_cursor_motion;
struct wl_listener ml_cursor_motion_absolute;
struct wl_listener ml_cursor_button;
@@ -107,24 +137,24 @@ private:
struct wl_listener ml_request_set_cursor;
struct wl_listener ml_request_start_drag;
struct wl_listener ml_start_drag;
-
- struct wl_list m_keyboards;
struct wl_listener ml_new_input;
struct wl_listener ml_request_set_selection;
struct wl_listener ml_request_set_primary_selection;
+ struct wl_listener ml_inhibit_activate;
+ struct wl_listener ml_inhibit_deactivate;
+ struct wl_listener ml_idle_inhibitor_create;
+ struct wl_listener ml_idle_inhibitor_destroy;
+#ifdef XWAYLAND
+ struct wl_listener ml_xwayland_ready;
+ struct wl_listener ml_new_xwayland_surface;
+#endif
CursorMode m_cursor_mode;
- View* m_grabbed_view;
+ Client_ptr m_grabbed_client;
double m_grab_x, m_grab_y;
struct wlr_box m_grab_geobox;
uint32_t m_resize_edges;
- struct wlr_output_layout* m_output_layout;
- struct wl_list m_outputs;
- struct wl_listener ml_new_output;
- struct wl_listener ml_output_manager_apply;
- struct wl_listener ml_output_manager_test;
-
const std::string m_socket;
-};
+}* Server_ptr;
diff --git a/include/kranewl/util.hh b/include/kranewl/util.hh
@@ -0,0 +1,139 @@
+#pragma once
+
+#include <cstdlib>
+#include <iostream>
+#include <optional>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+namespace Util
+{
+
+ void die(const std::string&&);
+ void warn(const std::string&&);
+ void assert(bool, const std::string&&);
+
+ template <
+ typename T,
+ typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type
+ >
+ struct Change final
+ {
+ Change(T value)
+ : value(value)
+ {}
+
+ operator T() const { return value; }
+
+ T value;
+ };
+
+ template<typename T>
+ struct is_iterable final
+ {
+ private:
+ template<typename Container> static char test(typename Container::iterator*);
+ template<typename Container> static int test(...);
+ public:
+ enum { value = sizeof(test<T>(0)) == sizeof(char) };
+ };
+
+ template <typename Container>
+ typename std::enable_if<is_iterable<Container>::value, void>::type
+ erase_remove(Container& c, typename Container::value_type const& t)
+ {
+ auto iter = std::remove(c.begin(), c.end(), t);
+
+ if (iter == c.end())
+ return;
+
+ c.erase(iter, c.end());
+ }
+
+ template <typename Container, typename UnaryPredicate>
+ typename std::enable_if<is_iterable<Container>::value, void>::type
+ erase_remove_if(Container& c, UnaryPredicate p)
+ {
+ auto iter = std::remove_if(c.begin(), c.end(), p);
+
+ if (iter == c.end())
+ return;
+
+ c.erase(iter, c.end());
+ }
+
+ template <typename Container>
+ typename std::enable_if<is_iterable<Container>::value, void>::type
+ erase_at_index(Container& c, const std::size_t index)
+ {
+ if (index < c.size())
+ c.erase(c.begin() + index);
+ }
+
+ template <typename Container>
+ typename std::enable_if<is_iterable<Container>::value, void>::type
+ append(Container& c1, Container const& c2)
+ {
+ c1.reserve(c1.size() + c2.size());
+ c1.insert(c1.end(), c2.begin(), c2.end());
+ }
+
+ template <typename Container>
+ typename std::enable_if<is_iterable<Container>::value, const bool>::type
+ contains(Container const& c, typename Container::value_type const& t)
+ {
+ return std::find(c.begin(), c.end(), t) != c.end();
+ }
+
+ template <typename K, typename V>
+ std::optional<V>
+ retrieve(std::unordered_map<K, V>& c, K& key)
+ {
+ typename std::unordered_map<K, V>::iterator iter = c.find(key);
+
+ if (iter == c.end())
+ return std::nullopt;
+
+ return iter->second;
+ }
+
+ template <typename K, typename V>
+ V&
+ at(std::unordered_map<K, V>& c, K& key)
+ {
+ return c.at(key);
+ }
+
+ template <typename K, typename V>
+ std::optional<const V>
+ const_retrieve(std::unordered_map<K, V> const& c, K const& key)
+ {
+ typename std::unordered_map<K, V>::const_iterator iter = c.find(key);
+
+ if (iter == c.end())
+ return std::nullopt;
+
+ return iter->second;
+ }
+
+ template <typename Container>
+ typename std::enable_if<is_iterable<Container>::value, const std::optional<const std::size_t>>::type
+ index_of(Container const& c, typename Container::value_type const& t)
+ {
+ auto iter = std::find(c.begin(), c.end(), t);
+ if (iter != c.end())
+ return iter - c.begin();
+
+ return std::nullopt;
+ }
+
+ template <typename Container>
+ typename std::enable_if<is_iterable<Container>::value, const std::size_t>::type
+ last_index(Container const& c)
+ {
+ return c.empty() ? 0 : c.size() - 1;
+ }
+
+}
diff --git a/include/kranewl/workspace.hh b/include/kranewl/workspace.hh
@@ -0,0 +1,162 @@
+#pragma once
+
+#include <kranewl/common.hh>
+#include <kranewl/geometry.hh>
+#include <kranewl/placement.hh>
+#include <kranewl/cycle.hh>
+#include <kranewl/util.hh>
+
+typedef class Client* Client_ptr;
+typedef class Context* Context_ptr;
+typedef class Workspace final {
+public:
+ Workspace(Index index, std::string name, Context_ptr context)
+ : m_index(index),
+ m_name(name),
+ m_layout_handler({}),
+ mp_context(context),
+ mp_active(nullptr),
+ m_clients({}, true),
+ m_icons({}, true),
+ m_disowned({}, true),
+ m_focus_follows_mouse(false)
+ {}
+
+ bool empty() const;
+ bool contains(Client_ptr) const;
+
+ bool focus_follows_mouse() const;
+ void set_focus_follows_mouse(bool);
+
+ bool layout_is_free() const;
+ bool layout_has_margin() const;
+ bool layout_has_gap() const;
+ bool layout_is_persistent() const;
+ bool layout_is_single() const;
+ bool layout_wraps() const;
+
+ Index size() const;
+ Index length() const;
+ Index main_count() const;
+
+ Context_ptr context() const;
+
+ Index index() const;
+ std::string const& name() const;
+ std::string identifier() const;
+ Client_ptr active() const;
+
+ Cycle<Client_ptr> const& clients() const;
+ std::vector<Client_ptr> stack_after_focus() const;
+
+ Client_ptr next_client() const;
+ Client_ptr prev_client() const;
+
+ void cycle(Direction);
+ void drag(Direction);
+ void reverse();
+ void rotate(Direction);
+ void shuffle_main(Direction);
+ void shuffle_stack(Direction);
+
+ void activate_client(Client_ptr);
+
+ void add_client(Client_ptr);
+ void remove_client(Client_ptr);
+ void replace_client(Client_ptr, Client_ptr);
+
+ void client_to_icon(Client_ptr);
+ void icon_to_client(Client_ptr);
+ void add_icon(Client_ptr);
+ void remove_icon(Client_ptr);
+ std::optional<Client_ptr> pop_icon();
+
+ void client_to_disowned(Client_ptr);
+ void disowned_to_client(Client_ptr);
+ void add_disowned(Client_ptr);
+ void remove_disowned(Client_ptr);
+
+ void toggle_layout_data();
+ void cycle_layout_data(Direction);
+ void copy_data_from_prev_layout();
+
+ void change_gap_size(Util::Change<int>);
+ void change_main_count(Util::Change<int>);
+ void change_main_factor(Util::Change<float>);
+ void change_margin(Util::Change<int>);
+ void change_margin(Edge, Util::Change<int>);
+ void reset_gap_size();
+ void reset_margin();
+ void reset_layout_data();
+
+ void save_layout(Index) const;
+ void load_layout(Index);
+
+ void toggle_layout();
+ void set_layout(LayoutHandler::LayoutKind);
+ std::vector<Placement> arrange(Region) const;
+
+ std::deque<Client_ptr>::iterator
+ begin()
+ {
+ return m_clients.begin();
+ }
+
+ std::deque<Client_ptr>::const_iterator
+ begin() const
+ {
+ return m_clients.begin();
+ }
+
+ std::deque<Client_ptr>::const_iterator
+ cbegin() const
+ {
+ return m_clients.cbegin();
+ }
+
+ std::deque<Client_ptr>::iterator
+ end()
+ {
+ return m_clients.end();
+ }
+
+ std::deque<Client_ptr>::const_iterator
+ end() const
+ {
+ return m_clients.end();
+ }
+
+ std::deque<Client_ptr>::const_iterator
+ cend() const
+ {
+ return m_clients.cend();
+ }
+
+ Client_ptr
+ operator[](Index i)
+ {
+ return m_clients[i];
+ }
+
+ Client_ptr
+ operator[](Index i) const
+ {
+ return m_clients[i];
+ }
+
+private:
+ Index m_index;
+ std::string m_name;
+
+ LayoutHandler m_layout_handler;
+
+ Context_ptr mp_context;
+
+ Client_ptr mp_active;
+ Cycle<Client_ptr> m_clients;
+ Cycle<Client_ptr> m_icons;
+ Cycle<Client_ptr> m_disowned;
+
+ bool m_focus_follows_mouse;
+
+}* Workspace_ptr;
diff --git a/include/version.hh b/include/version.hh
@@ -1 +1 @@
-#define VERSION "master/79fe788+"
-\ No newline at end of file
+#define VERSION "master/23f214f+"
+\ No newline at end of file
diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,390 @@
+<?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="4">
+ <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.
+
+ After creating a layer_surface object and setting it up, the client
+ must perform an initial commit without any buffer attached.
+ The compositor will reply with a layer_surface.configure event.
+ The client must acknowledge it and is then allowed to attach a buffer
+ to map the surface.
+
+ You may pass NULL for output to allow the compositor to decide which
+ output to use. Generally this will be the one that the user most
+ recently interacted with.
+
+ 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" allow-null="true"/>
+ <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>
+
+ <!-- Version 3 additions -->
+
+ <request name="destroy" type="destructor" since="3">
+ <description summary="destroy the layer_shell object">
+ This request indicates that the client will not use the layer_shell
+ object any more. Objects that have been created through this instance
+ are not affected.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwlr_layer_surface_v1" version="4">
+ <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 (layer, 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.
+
+ Attaching a null buffer to a layer surface unmaps it.
+
+ Unmapping a layer_surface means that the surface cannot be shown by the
+ compositor until it is explicitly mapped again. The layer_surface
+ returns to the state it had right after layer_shell.get_layer_surface.
+ The client can re-map the surface by performing a commit without any
+ buffer attached, waiting for a configure event and handling it as usual.
+ </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 orthogonal 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 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 one
+ edge or an edge and both perpendicular edges. If the surface is not
+ anchored, anchored to only two perpendicular edges (a corner), anchored
+ to only two parallel edges or anchored to all edges, a positive value
+ will be treated the same as zero.
+
+ A positive zone is the distance from the edge in surface-local
+ coordinates to consider 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 exclusive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accommodate 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>
+
+ <enum name="keyboard_interactivity">
+ <description summary="types of keyboard interaction possible for a layer shell surface">
+ Types of keyboard interaction possible for layer shell surfaces. The
+ rationale for this is twofold: (1) some applications are not interested
+ in keyboard events and not allowing them to be focused can improve the
+ desktop experience; (2) some applications will want to take exclusive
+ keyboard focus.
+ </description>
+
+ <entry name="none" value="0">
+ <description summary="no keyboard focus is possible">
+ This value indicates that this surface is not interested in keyboard
+ events and the compositor should never assign it the keyboard focus.
+
+ This is the default value, set for newly created layer shell surfaces.
+
+ This is useful for e.g. desktop widgets that display information or
+ only have interaction with non-keyboard input devices.
+ </description>
+ </entry>
+ <entry name="exclusive" value="1">
+ <description summary="request exclusive keyboard focus">
+ Request exclusive keyboard focus if this surface is above the shell surface layer.
+
+ For the top and overlay layers, the seat will always give
+ exclusive keyboard focus to the top-most layer which has keyboard
+ interactivity set to exclusive. If this layer contains multiple
+ surfaces with keyboard interactivity set to exclusive, the compositor
+ determines the one receiving keyboard events in an implementation-
+ defined manner. In this case, no guarantee is made when this surface
+ will receive keyboard focus (if ever).
+
+ For the bottom and background layers, the compositor is allowed to use
+ normal focus semantics.
+
+ This setting is mainly intended for applications that need to ensure
+ they receive all keyboard events, such as a lock screen or a password
+ prompt.
+ </description>
+ </entry>
+ <entry name="on_demand" value="2" since="4">
+ <description summary="request regular keyboard focus semantics">
+ This requests the compositor to allow this surface to be focused and
+ unfocused by the user in an implementation-defined manner. The user
+ should be able to unfocus this surface even regardless of the layer
+ it is on.
+
+ Typically, the compositor will want to use its normal mechanism to
+ manage keyboard focus between layer shell surfaces with this setting
+ and regular toplevels on the desktop layer (e.g. click to focus).
+ Nevertheless, it is possible for a compositor to require a special
+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
+ a click even if focus follows the mouse normally, or providing a
+ keybinding to switch focus between layers).
+
+ This setting is mainly intended for desktop shell components (e.g.
+ panels) that allow keyboard interaction. Using this option can allow
+ implementing a desktop shell that can be fully usable without the
+ mouse.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_keyboard_interactivity">
+ <description summary="requests keyboard events">
+ Set how keyboard events are delivered to this surface. By default,
+ layer shell surfaces do not receive keyboard events; this request can
+ be used to change this.
+
+ This setting is inherited by child surfaces set by the get_popup
+ request.
+
+ 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.
+
+ Keyboard interactivity is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
+ </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"/>
+ <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity 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>
+
+ <!-- Version 2 additions -->
+
+ <request name="set_layer" since="2">
+ <description summary="change the layer of the surface">
+ Change the layer that the surface is rendered on.
+
+ Layer is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/src/kranewl/client.cc b/src/kranewl/client.cc
@@ -1,5 +1,25 @@
#include <kranewl/client.hh>
+// https://github.com/swaywm/wlroots/issues/682
+#include <pthread.h>
+#define class class_
+#define namespace namespace_
+#define static
+extern "C" {
+#include <wlr/types/wlr_xdg_shell.h>
+#ifdef XWAYLAND
+#include <wlr/xwayland.h>
+#endif
+}
+#undef static
+#undef class
+#undef namespace
+
+Client::Client()
+{
+ wl_list_init(&link);
+}
+
Client::Client(
Surface surface,
Partition_ptr partition,
@@ -43,7 +63,9 @@ Client::Client(
last_focused(std::chrono::steady_clock::now()),
managed_since(std::chrono::steady_clock::now()),
m_outside_state(OutsideState::Unfocused)
-{}
+{
+ wl_list_init(&link);
+}
Client::~Client()
{}
@@ -57,6 +79,19 @@ Client::get_outside_state() const noexcept
return m_outside_state;
}
+struct wlr_surface*
+Client::get_surface() noexcept
+{
+ switch (surface_type) {
+ case SurfaceType::XDGShell: //fallthrough
+ case SurfaceType::LayerShell: return surface.xdg->surface;
+ case SurfaceType::X11Managed: //fallthrough
+ case SurfaceType::X11Unmanaged: return surface.xwayland->surface;
+ }
+
+ return nullptr;
+}
+
void
Client::focus() noexcept
{
diff --git a/src/kranewl/conf/options.cc b/src/kranewl/conf/options.cc
@@ -3,11 +3,13 @@
#include <kranewl/conf/options.hh>
#include <cassert>
-#include <optional>
-#include <iostream>
#include <cstdlib>
+#include <iostream>
+#include <optional>
+extern "C" {
#include <sys/stat.h>
#include <unistd.h>
+}
static const std::string CONFIG_FILE = "kranewlrc.lua";
static const std::string DEFAULT_CONFIG = "/etc/kranewl/" + CONFIG_FILE;
diff --git a/src/kranewl/exec.cc b/src/kranewl/exec.cc
@@ -1,6 +1,8 @@
-#include <unistd.h>
#include <cstdlib>
#include <string>
+extern "C" {
+#include <unistd.h>
+}
void
exec_external(std::string& command) {
diff --git a/src/kranewl/layout.cc b/src/kranewl/layout.cc
@@ -0,0 +1,1548 @@
+#include <kranewl/layout.hh>
+
+#include <kranewl/util.hh>
+#include <kranewl/client.hh>
+#include <kranewl/cycle.t.hh>
+
+#include <algorithm>
+#include <cmath>
+#include <sstream>
+#include <fstream>
+
+LayoutHandler::Layout::Layout(LayoutKind kind)
+ : kind(kind),
+ config(kind_to_config(kind)),
+ default_data(kind_to_default_data(kind)),
+ data({}, true)
+{
+ data.insert_at_back(new LayoutData(default_data));
+ data.insert_at_back(new LayoutData(default_data));
+ data.insert_at_back(new LayoutData(default_data));
+}
+
+LayoutHandler::Layout::~Layout()
+{
+ for (LayoutData_ptr data : data)
+ delete data;
+}
+
+
+LayoutHandler::LayoutHandler()
+ : m_kind(LayoutKind::Float),
+ m_prev_kind(LayoutKind::Float),
+ m_layouts({
+#define NEW_LAYOUT(layout) { layout, new Layout(layout) }
+ NEW_LAYOUT(LayoutKind::Float),
+ NEW_LAYOUT(LayoutKind::FramelessFloat),
+ NEW_LAYOUT(LayoutKind::SingleFloat),
+ NEW_LAYOUT(LayoutKind::FramelessSingleFloat),
+ NEW_LAYOUT(LayoutKind::Center),
+ NEW_LAYOUT(LayoutKind::Monocle),
+ NEW_LAYOUT(LayoutKind::MainDeck),
+ NEW_LAYOUT(LayoutKind::StackDeck),
+ NEW_LAYOUT(LayoutKind::DoubleDeck),
+ NEW_LAYOUT(LayoutKind::Paper),
+ NEW_LAYOUT(LayoutKind::CompactPaper),
+ NEW_LAYOUT(LayoutKind::DoubleStack),
+ NEW_LAYOUT(LayoutKind::CompactDoubleStack),
+ NEW_LAYOUT(LayoutKind::HorizontalStack),
+ NEW_LAYOUT(LayoutKind::CompactHorizontalStack),
+ NEW_LAYOUT(LayoutKind::VerticalStack),
+ NEW_LAYOUT(LayoutKind::CompactVerticalStack),
+#undef NEW_LAYOUT
+ }),
+ mp_layout(m_layouts.at(m_kind)),
+ mp_prev_layout(m_layouts.at(m_kind))
+{}
+
+LayoutHandler::~LayoutHandler()
+{
+ for (auto [_,layout] : m_layouts)
+ delete layout;
+}
+
+
+void
+LayoutHandler::arrange(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ if (mp_layout->config.margin) {
+ Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+
+ screen_region.pos.x += data->margin.left;
+ screen_region.pos.y += data->margin.top;
+
+ screen_region.dim.w -= data->margin.left + data->margin.right;
+ screen_region.dim.h -= data->margin.top + data->margin.bottom;
+ }
+
+ switch (m_kind) {
+ case LayoutKind::Float:
+ {
+ arrange_float(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::FramelessFloat:
+ {
+ arrange_frameless_float(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::SingleFloat:
+ {
+ arrange_single_float(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::FramelessSingleFloat:
+ {
+ arrange_frameless_single_float(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::Center:
+ {
+ arrange_center(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::Monocle:
+ {
+ arrange_monocle(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::MainDeck:
+ {
+ arrange_main_deck(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::StackDeck:
+ {
+ arrange_stack_deck(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::DoubleDeck:
+ {
+ arrange_double_deck(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::Paper:
+ {
+ arrange_paper(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::CompactPaper:
+ {
+ arrange_compact_paper(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::DoubleStack:
+ {
+ arrange_double_stack(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::CompactDoubleStack:
+ {
+ arrange_compact_double_stack(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::HorizontalStack:
+ {
+ arrange_horizontal_stack(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::CompactHorizontalStack:
+ {
+ arrange_compact_horizontal_stack(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::VerticalStack:
+ {
+ arrange_vertical_stack(screen_region, placements, begin, end);
+ break;
+ }
+ case LayoutKind::CompactVerticalStack:
+ {
+ arrange_compact_vertical_stack(screen_region, placements, begin, end);
+ break;
+ }
+ }
+
+ if (mp_layout->config.gap) {
+ Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+
+ std::for_each(
+ placements.begin(),
+ placements.end(),
+ [data](Placement& placement) {
+ if (placement.region && !Client::is_free(placement.client)) {
+ placement.region->pos.x += data->gap_size;
+ placement.region->pos.y += data->gap_size;
+ placement.region->dim.w -= 2 * data->gap_size;
+ placement.region->dim.h -= 2 * data->gap_size;
+
+ placement.region->apply_minimum_dim(Client::MIN_CLIENT_DIM);
+ }
+ }
+ );
+ }
+}
+
+
+LayoutHandler::LayoutKind
+LayoutHandler::kind() const
+{
+ return m_kind;
+}
+
+void
+LayoutHandler::set_kind(LayoutKind kind)
+{
+ if (kind == m_kind)
+ return;
+
+ m_prev_kind = m_kind;
+ m_kind = kind;
+
+ mp_prev_layout = mp_layout;
+ mp_layout = *Util::const_retrieve(m_layouts, m_kind);
+}
+
+void
+LayoutHandler::set_prev_kind()
+{
+ if (m_prev_kind == m_kind)
+ return;
+
+ std::swap(m_kind, m_prev_kind);
+ std::swap(mp_layout, mp_prev_layout);
+}
+
+
+bool
+LayoutHandler::layout_is_free() const
+{
+ return mp_layout->config.method == Placement::PlacementMethod::Free;
+}
+
+bool
+LayoutHandler::layout_has_margin() const
+{
+ return mp_layout->config.margin;
+}
+
+bool
+LayoutHandler::layout_has_gap() const
+{
+ return mp_layout->config.gap;
+}
+
+bool
+LayoutHandler::layout_is_persistent() const
+{
+ return mp_layout->config.persistent;
+}
+
+bool
+LayoutHandler::layout_is_single() const
+{
+ return mp_layout->config.single;
+}
+
+bool
+LayoutHandler::layout_wraps() const
+{
+ return mp_layout->config.wraps;
+}
+
+
+std::size_t
+LayoutHandler::gap_size() const
+{
+ return (*mp_layout->data.active_element())->gap_size;
+}
+
+std::size_t
+LayoutHandler::main_count() const
+{
+ return (*mp_layout->data.active_element())->main_count;
+}
+
+float
+LayoutHandler::main_factor() const
+{
+ return (*mp_layout->data.active_element())->main_factor;
+}
+
+Extents
+LayoutHandler::margin() const
+{
+ return (*mp_layout->data.active_element())->margin;
+}
+
+
+void
+LayoutHandler::copy_data_from_prev_layout()
+{
+ *(*mp_layout->data.active_element())
+ = *(*mp_prev_layout->data.active_element());
+}
+
+void
+LayoutHandler::set_prev_layout_data()
+{
+ std::optional<Layout::LayoutData_ptr> prev_data
+ = mp_layout->data.prev_active_element();
+
+ if (prev_data)
+ mp_layout->data.activate_element(*prev_data);
+}
+
+
+void
+LayoutHandler::change_gap_size(Util::Change<int> change)
+{
+ Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ int value = static_cast<int>(data->gap_size) + change;
+
+ if (value <= 0)
+ data->gap_size = 0;
+ else if (static_cast<std::size_t>(value) >= Layout::LayoutData::MAX_GAP_SIZE)
+ data->gap_size = Layout::LayoutData::MAX_GAP_SIZE;
+ else
+ data->gap_size = static_cast<std::size_t>(value);
+}
+
+void
+LayoutHandler::change_main_count(Util::Change<int> change)
+{
+ Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ int value = static_cast<int>(data->main_count) + change;
+
+ if (value <= 0)
+ data->main_count = 0;
+ else if (static_cast<std::size_t>(value) >= Layout::LayoutData::MAX_MAIN_COUNT)
+ data->main_count = Layout::LayoutData::MAX_MAIN_COUNT;
+ else
+ data->main_count = static_cast<std::size_t>(value);
+}
+
+void
+LayoutHandler::change_main_factor(Util::Change<float> change)
+{
+ Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ float value = data->main_factor + change;
+
+ if (value <= 0.05f)
+ data->main_factor = 0.05f;
+ else if (value >= 0.95f)
+ data->main_factor = 0.95f;
+ else
+ data->main_factor = value;
+}
+
+void
+LayoutHandler::change_margin(Util::Change<int> change)
+{
+ change_margin(Edge::Left, change);
+ change_margin(Edge::Top, change);
+ change_margin(Edge::Right, change);
+ change_margin(Edge::Bottom, change);
+}
+
+void
+LayoutHandler::change_margin(Edge edge, Util::Change<int> change)
+{
+ Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ int* margin;
+ const int* max_value;
+
+ switch (edge) {
+ case Edge::Left:
+ {
+ margin = &data->margin.left;
+ max_value = &Layout::LayoutData::MAX_MARGIN.left;
+ break;
+ }
+ case Edge::Top:
+ {
+ margin = &data->margin.top;
+ max_value = &Layout::LayoutData::MAX_MARGIN.top;
+ break;
+ }
+ case Edge::Right:
+ {
+ margin = &data->margin.right;
+ max_value = &Layout::LayoutData::MAX_MARGIN.right;
+ break;
+ }
+ case Edge::Bottom:
+ {
+ margin = &data->margin.bottom;
+ max_value = &Layout::LayoutData::MAX_MARGIN.bottom;
+ break;
+ }
+ }
+
+ int value = *margin + change;
+
+ if (value <= 0)
+ *margin = 0;
+ else if (value >= *max_value)
+ *margin = *max_value;
+ else
+ *margin = value;
+}
+
+void
+LayoutHandler::reset_gap_size()
+{
+ (*mp_layout->data.active_element())->gap_size
+ = mp_layout->default_data.gap_size;
+}
+
+void
+LayoutHandler::reset_margin()
+{
+ (*mp_layout->data.active_element())->margin
+ = mp_layout->default_data.margin;
+}
+
+void
+LayoutHandler::reset_layout_data()
+{
+ *(*mp_layout->data.active_element())
+ = mp_layout->default_data;
+}
+
+void
+LayoutHandler::cycle_layout_data(Direction direction)
+{
+ mp_layout->data.cycle_active(direction);
+}
+
+
+void
+LayoutHandler::save_layout(std::size_t number) const
+{
+ std::stringstream datadir_ss;
+ std::string home_path = std::getenv("HOME");
+
+ if (const char* env_xdgdata = std::getenv("XDG_DATA_HOME"))
+ datadir_ss << env_xdgdata << "/" << WM_NAME << "/";
+ else
+ datadir_ss << home_path << "/.local/share/" << WM_NAME << "/"
+ << "layout_" << number;
+
+ std::string file_path = datadir_ss.str();
+
+ std::vector<Layout::LayoutData> data;
+ data.reserve(mp_layout->data.size());
+ for (Layout::LayoutData_ptr data_ptr : mp_layout->data.as_deque())
+ data.push_back(*data_ptr);
+
+ typename std::vector<Layout::LayoutData>::size_type size
+ = data.size();
+
+ std::ofstream out(file_path, std::ios::out | std::ios::binary);
+ out.write(reinterpret_cast<const char*>(&m_kind), sizeof(LayoutKind));
+ out.write(reinterpret_cast<const char*>(&size), sizeof(size));
+ out.write(reinterpret_cast<const char*>(&data[0]),
+ data.size() * sizeof(Layout::LayoutData));
+ out.close();
+}
+
+void
+LayoutHandler::load_layout(std::size_t number)
+{
+ std::stringstream datadir_ss;
+ std::string home_path = std::getenv("HOME");
+
+ if (const char* env_xdgdata = std::getenv("XDG_DATA_HOME"))
+ datadir_ss << env_xdgdata << "/" << WM_NAME << "/";
+ else
+ datadir_ss << home_path << "/.local/share/" << WM_NAME << "/"
+ << "layout_" << number;
+
+ std::string file_path = datadir_ss.str();
+
+ LayoutKind kind;
+ std::vector<Layout::LayoutData> data;
+ typename std::vector<Layout::LayoutData>::size_type size = 0;
+
+ std::ifstream in(file_path, std::ios::in | std::ios::binary);
+ if (in.good()) {
+ in.read(reinterpret_cast<char*>(&kind), sizeof(LayoutKind));
+ in.read(reinterpret_cast<char*>(&size), sizeof(size));
+
+ data.resize(size, mp_layout->default_data);
+ in.read(reinterpret_cast<char*>(&data[0]),
+ data.size() * sizeof(Layout::LayoutData));
+
+ set_kind(kind);
+ for (Layout::LayoutData_ptr data : mp_layout->data)
+ delete data;
+
+ mp_layout->data.clear();
+ for (auto data_ : data)
+ mp_layout->data.insert_at_back(new Layout::LayoutData(data_));
+ }
+ in.close();
+}
+
+
+void
+LayoutHandler::arrange_float(
+ Region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [this](Client_ptr client) -> Placement {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ client->free_region
+ };
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_frameless_float(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ arrange_float(screen_region, placements, begin, end);
+}
+
+void
+LayoutHandler::arrange_single_float(
+ Region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [this](Client_ptr client) -> Placement {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ client->focused ? std::optional(client->free_region) : std::nullopt
+ };
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_frameless_single_float(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ arrange_single_float(screen_region, placements, begin, end);
+}
+
+void
+LayoutHandler::arrange_center(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+
+ std::size_t h_comp = Layout::LayoutData::MAX_MAIN_COUNT;
+ float w_ratio = data->main_factor / 0.95;
+ float h_ratio = static_cast<float>((h_comp - data->main_count))
+ / static_cast<float>(h_comp);
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this](Client_ptr client) -> Placement {
+ Region region = screen_region;
+
+ int w = region.dim.w * w_ratio;
+ int h = region.dim.h * h_ratio;
+
+ if (w <= region.dim.w)
+ region.pos.x += (region.dim.w - w) / 2.f;
+
+ if (h <= region.dim.h)
+ region.pos.y += (region.dim.h - h) / 2.f;
+
+ region.dim = { w, h };
+
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ region
+ };
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_monocle(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this](Client_ptr client) {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ screen_region
+ };
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_main_deck(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ std::size_t n = end - begin;
+
+ if (n == 1) {
+ placements.emplace_back(Placement {
+ mp_layout->config.method,
+ *begin,
+ Decoration::NO_DECORATION,
+ screen_region
+ });
+
+ return;
+ }
+
+ std::size_t n_main;
+ std::size_t n_stack;
+
+ if (n <= data->main_count) {
+ n_main = n;
+ n_stack = 0;
+ } else {
+ n_main = data->main_count;
+ n_stack = n - n_main;
+ }
+
+ int w_main
+ = data->main_count > 0
+ ? static_cast<float>(screen_region.dim.w) * data->main_factor
+ : 0;
+
+ int x_stack = screen_region.pos.x + w_main;
+ int w_stack = screen_region.dim.w - w_main;
+ int h_main = n_main > 0 ? screen_region.dim.h : 0;
+ int h_stack = n_stack > 0 ? screen_region.dim.h / n_stack : 0;
+
+ std::size_t i = 0;
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this,&i](Client_ptr client) -> Placement {
+ if (i < data->main_count) {
+ ++i;
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ screen_region.pos.x,
+ screen_region.pos.y
+ },
+ Dim {
+ n_stack == 0 ? screen_region.dim.w : w_main,
+ h_main
+ }
+ }
+ };
+ } else {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ x_stack,
+ screen_region.pos.y
+ + static_cast<int>((i++ - data->main_count) * h_stack)
+ },
+ Dim {
+ w_stack,
+ h_stack
+ }
+ }
+ };
+ }
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_stack_deck(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ std::size_t n = end - begin;
+
+ if (n == 1) {
+ placements.emplace_back(Placement {
+ mp_layout->config.method,
+ *begin,
+ Decoration::NO_DECORATION,
+ screen_region
+ });
+
+ return;
+ }
+
+ std::size_t n_main;
+ std::size_t n_stack;
+
+ if (n <= data->main_count) {
+ n_main = n;
+ n_stack = 0;
+ } else {
+ n_main = data->main_count;
+ n_stack = n - n_main;
+ }
+
+ int w_main
+ = data->main_count > 0
+ ? static_cast<float>(screen_region.dim.w) * data->main_factor
+ : 0;
+
+ int x_stack = screen_region.pos.x + w_main;
+ int w_stack = screen_region.dim.w - w_main;
+ int h_main = n_main > 0 ? screen_region.dim.h / n_main : 0;
+ int h_stack = n_stack > 0 ? screen_region.dim.h : 0;
+
+ std::size_t i = 0;
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this,&i](Client_ptr client) -> Placement {
+ if (i < data->main_count) {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ screen_region.pos.x,
+ screen_region.pos.y
+ + static_cast<int>(i++) * h_main
+ },
+ Dim {
+ n_stack == 0 ? screen_region.dim.w : w_main,
+ h_main
+ }
+ }
+ };
+ } else {
+ ++i;
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ x_stack,
+ screen_region.pos.y
+ },
+ Dim {
+ w_stack,
+ h_stack
+ }
+ }
+ };
+ }
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_double_deck(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ std::size_t n = end - begin;
+
+ if (n == 1) {
+ placements.emplace_back(Placement {
+ mp_layout->config.method,
+ *begin,
+ Decoration::NO_DECORATION,
+ screen_region
+ });
+
+ return;
+ }
+
+ std::size_t n_main;
+ std::size_t n_stack;
+
+ if (n <= data->main_count) {
+ n_main = n;
+ n_stack = 0;
+ } else {
+ n_main = data->main_count;
+ n_stack = n - n_main;
+ }
+
+ int w_main
+ = data->main_count > 0
+ ? static_cast<float>(screen_region.dim.w) * data->main_factor
+ : 0;
+
+ int x_stack = screen_region.pos.x + w_main;
+ int w_stack = screen_region.dim.w - w_main;
+ int h_main = n_main > 0 ? screen_region.dim.h : 0;
+ int h_stack = n_stack > 0 ? screen_region.dim.h : 0;
+
+ std::size_t i = 0;
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this,&i](Client_ptr client) -> Placement {
+ if (i++ < data->main_count) {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ screen_region.pos.x,
+ screen_region.pos.y
+ },
+ Dim {
+ n_stack == 0 ? screen_region.dim.w : w_main,
+ h_main
+ }
+ }
+ };
+ } else {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ x_stack,
+ screen_region.pos.y
+ },
+ Dim {
+ w_stack,
+ h_stack
+ }
+ }
+ };
+ }
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_paper(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ static const float MIN_W_RATIO = 0.5;
+
+ const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ std::size_t n = end - begin;
+
+ if (n == 1) {
+ placements.emplace_back(Placement {
+ mp_layout->config.method,
+ *begin,
+ Decoration::NO_DECORATION,
+ screen_region
+ });
+
+ return;
+ }
+
+ int cw;
+ if (data->main_factor > MIN_W_RATIO) {
+ cw = screen_region.dim.w * data->main_factor;
+ } else {
+ cw = screen_region.dim.w * MIN_W_RATIO;
+ }
+
+ int w = static_cast<float>(screen_region.dim.w - cw)
+ / static_cast<float>(n - 1);
+
+ bool contains_active = false;
+ const auto last_active = std::max_element(
+ begin,
+ end,
+ [&contains_active](const Client_ptr lhs, const Client_ptr rhs) {
+ if (lhs->focused) {
+ contains_active = true;
+ return false;
+ } else if (rhs->focused) {
+ contains_active = true;
+ return true;
+ }
+
+ return lhs->last_focused < rhs->last_focused;
+ }
+ );
+
+ bool after_active = false;
+ std::size_t i = 0;
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this,&after_active,&i](Client_ptr client) -> Placement {
+ int x = screen_region.pos.x + static_cast<int>(i++ * w);
+
+ if ((!contains_active && *last_active == client) || client->focused) {
+ after_active = true;
+
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ x,
+ screen_region.pos.y
+ },
+ Dim {
+ cw,
+ screen_region.dim.h
+ }
+ }
+ };
+ } else {
+ if (after_active)
+ x += cw - w;
+
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ x,
+ screen_region.pos.y
+ },
+ Dim {
+ w,
+ screen_region.dim.h
+ }
+ }
+ };
+ }
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_compact_paper(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ arrange_paper(screen_region, placements, begin, end);
+}
+
+void
+LayoutHandler::arrange_double_stack(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
+ std::size_t n = end - begin;
+
+ if (n == 1) {
+ placements.emplace_back(Placement {
+ mp_layout->config.method,
+ *begin,
+ Decoration::NO_DECORATION,
+ screen_region
+ });
+
+ return;
+ }
+
+ std::size_t n_main;
+ std::size_t n_stack;
+
+ if (n <= data->main_count) {
+ n_main = n;
+ n_stack = 0;
+ } else {
+ n_main = data->main_count;
+ n_stack = n - n_main;
+ }
+
+ int w_main
+ = data->main_count > 0
+ ? static_cast<float>(screen_region.dim.w) * data->main_factor
+ : 0;
+
+ int x_stack = screen_region.pos.x + w_main;
+ int w_stack = screen_region.dim.w - w_main;
+ int h_main = n_main > 0 ? screen_region.dim.h / n_main : 0;
+ int h_stack = n_stack > 0 ? screen_region.dim.h / n_stack : 0;
+
+ std::size_t i = 0;
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this,&i](Client_ptr client) -> Placement {
+ if (i < data->main_count) {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ screen_region.pos.x,
+ screen_region.pos.y
+ + static_cast<int>(i++) * h_main
+ },
+ Dim {
+ n_stack == 0 ? screen_region.dim.w : w_main,
+ h_main
+ }
+ }
+ };
+ } else {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ x_stack,
+ screen_region.pos.y
+ + static_cast<int>((i++ - data->main_count) * h_stack)
+ },
+ Dim {
+ w_stack,
+ h_stack
+ }
+ }
+ };
+ }
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_compact_double_stack(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ arrange_double_stack(screen_region, placements, begin, end);
+}
+
+void
+LayoutHandler::arrange_horizontal_stack(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ std::size_t n = end - begin;
+
+ if (n == 1) {
+ placements.emplace_back(Placement {
+ mp_layout->config.method,
+ *begin,
+ Decoration::NO_DECORATION,
+ screen_region
+ });
+
+ return;
+ }
+
+ int w = std::lround(static_cast<float>(screen_region.dim.w) / n);
+ std::size_t i = 0;
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this,&i](Client_ptr client) -> Placement {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ screen_region.pos.x + static_cast<int>(i++ * w),
+ screen_region.pos.y
+ },
+ Dim {
+ w,
+ screen_region.dim.h
+ }
+ }
+ };
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_compact_horizontal_stack(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ arrange_horizontal_stack(screen_region, placements, begin, end);
+}
+
+void
+LayoutHandler::arrange_vertical_stack(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ std::size_t n = end - begin;
+
+ if (n == 1) {
+ placements.emplace_back(Placement {
+ mp_layout->config.method,
+ *begin,
+ Decoration::NO_DECORATION,
+ screen_region
+ });
+
+ return;
+ }
+
+ int h = std::lround(static_cast<float>(screen_region.dim.h) / n);
+ std::size_t i = 0;
+
+ std::transform(
+ begin,
+ end,
+ std::back_inserter(placements),
+ [=,this,&i](Client_ptr client) -> Placement {
+ return Placement {
+ mp_layout->config.method,
+ client,
+ mp_layout->config.decoration,
+ Region {
+ Pos {
+ screen_region.pos.x,
+ screen_region.pos.y + static_cast<int>(i++ * h)
+ },
+ Dim {
+ screen_region.dim.w,
+ h
+ }
+ }
+ };
+ }
+ );
+}
+
+void
+LayoutHandler::arrange_compact_vertical_stack(
+ Region screen_region,
+ placement_vector placements,
+ client_iter begin,
+ client_iter end
+) const
+{
+ arrange_vertical_stack(screen_region, placements, begin, end);
+}
+
+
+LayoutHandler::Layout::LayoutConfig
+LayoutHandler::Layout::kind_to_config(LayoutKind kind)
+{
+ switch (kind) {
+ case LayoutKind::Float:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Free,
+ Decoration::FREE_DECORATION,
+ false,
+ false,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::FramelessFloat:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Free,
+ Decoration::NO_DECORATION,
+ false,
+ false,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::SingleFloat:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Free,
+ Decoration::FREE_DECORATION,
+ false,
+ false,
+ true,
+ true,
+ true
+ };
+ }
+ case LayoutKind::FramelessSingleFloat:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Free,
+ Decoration::NO_DECORATION,
+ false,
+ false,
+ true,
+ true,
+ true
+ };
+ }
+ case LayoutKind::Center:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration::NO_DECORATION,
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::Monocle:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration::NO_DECORATION,
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::MainDeck:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 0, 0, 3, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::StackDeck:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 0, 0, 3, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::DoubleDeck:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 0, 0, 3, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::Paper:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 1, 1, 0, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ true,
+ true,
+ false,
+ false
+ };
+ }
+ case LayoutKind::CompactPaper:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 1, 1, 0, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ false,
+ true,
+ false,
+ false
+ };
+ }
+ case LayoutKind::DoubleStack:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 0, 0, 3, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::CompactDoubleStack:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 0, 0, 3, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ false,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::HorizontalStack:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 0, 0, 3, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::CompactHorizontalStack:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 0, 0, 3, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ false,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::VerticalStack:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 3, 0, 0, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ true,
+ false,
+ false,
+ true
+ };
+ }
+ case LayoutKind::CompactVerticalStack:
+ {
+ return LayoutConfig {
+ Placement::PlacementMethod::Tile,
+ Decoration {
+ std::nullopt,
+ Frame {
+ Extents { 3, 0, 0, 0 },
+ ColorScheme::DEFAULT_COLOR_SCHEME
+ }
+ },
+ true,
+ false,
+ false,
+ false,
+ true
+ };
+ }
+ default: Util::die("no associated configuration defined");
+ }
+
+ return kind_to_config(LayoutKind::Float);
+}
+
+LayoutHandler::Layout::LayoutData
+LayoutHandler::Layout::kind_to_default_data(LayoutKind kind)
+{
+ switch (kind) {
+ case LayoutKind::Center:
+ {
+ return Layout::LayoutData {
+ Extents { 0, 0, 0, 0 },
+ 0,
+ 5,
+ .40f
+ };
+ }
+ case LayoutKind::Float: // fallthrough
+ case LayoutKind::FramelessFloat: // fallthrough
+ case LayoutKind::SingleFloat: // fallthrough
+ case LayoutKind::FramelessSingleFloat: // fallthrough
+ case LayoutKind::Monocle: // fallthrough
+ case LayoutKind::MainDeck: // fallthrough
+ case LayoutKind::StackDeck: // fallthrough
+ case LayoutKind::DoubleDeck: // fallthrough
+ case LayoutKind::Paper: // fallthrough
+ case LayoutKind::CompactPaper: // fallthrough
+ case LayoutKind::DoubleStack: // fallthrough
+ case LayoutKind::CompactDoubleStack: // fallthrough
+ case LayoutKind::HorizontalStack: // fallthrough
+ case LayoutKind::CompactHorizontalStack: // fallthrough
+ case LayoutKind::VerticalStack: // fallthrough
+ case LayoutKind::CompactVerticalStack:
+ {
+ return Layout::LayoutData {
+ Extents { 0, 0, 0, 0 },
+ 0,
+ 1,
+ .50f
+ };
+ }
+ default: Util::die("no associated default data defined");
+ }
+
+ return kind_to_default_data(LayoutKind::Float);
+}
+
diff --git a/src/kranewl/main.cc b/src/kranewl/main.cc
@@ -22,7 +22,7 @@ int
main(int argc, char** argv)
{
#ifndef NDEBUG
- wlr_log_init(WLR_DEBUG, NULL);
+ /* wlr_log_init(WLR_DEBUG, NULL); */
#endif
const Options options = parse_options(argc, argv);
diff --git a/src/kranewl/server.cc b/src/kranewl/server.cc
@@ -1,9 +1,9 @@
#include <kranewl/server.hh>
+#include <kranewl/client.hh>
+#include <kranewl/exec.hh>
#include <kranewl/input/keyboard.hh>
#include <kranewl/tree/output.hh>
-#include <kranewl/tree/view.hh>
-#include <kranewl/exec.hh>
#include <spdlog/spdlog.h>
@@ -23,16 +23,20 @@ extern "C" {
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_idle.h>
+#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_input_inhibitor.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
+#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_presentation_time.h>
+#include <wlr/types/wlr_primary_selection.h>
+#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_scene.h>
@@ -41,11 +45,13 @@ extern "C" {
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/box.h>
-#ifdef WLR_HAS_XWAYLAND
+#ifdef XWAYLAND
+#include <X11/Xlib.h>
#include <wlr/xwayland.h>
#endif
}
@@ -61,6 +67,7 @@ extern "C" {
Server::Server()
: m_display(wl_display_create()),
+ m_event_loop(wl_display_get_event_loop(m_display)),
m_backend(wlr_backend_autocreate(m_display)),
m_renderer([](struct wl_display* display, struct wlr_backend* backend) {
struct wlr_renderer* renderer = wlr_renderer_autocreate(backend);
@@ -69,8 +76,13 @@ Server::Server()
return renderer;
}(m_display, m_backend)),
m_allocator(wlr_allocator_autocreate(m_backend, m_renderer)),
+ ml_new_output({ .notify = Server::new_output }),
+ ml_output_layout_change({ .notify = Server::output_layout_change }),
+ ml_output_manager_apply({ .notify = Server::output_manager_apply }),
+ ml_output_manager_test({ .notify = Server::output_manager_test }),
ml_new_xdg_surface({ .notify = Server::new_xdg_surface }),
ml_new_layer_shell_surface({ .notify = Server::new_layer_shell_surface }),
+ ml_xdg_activation({ .notify = Server::xdg_activation }),
ml_cursor_motion({ .notify = Server::cursor_motion }),
ml_cursor_motion_absolute({ .notify = Server::cursor_motion_absolute }),
ml_cursor_button({ .notify = Server::cursor_button }),
@@ -81,9 +93,15 @@ Server::Server()
ml_start_drag({ .notify = Server::start_drag }),
ml_new_input({ .notify = Server::new_input }),
ml_request_set_selection({ .notify = Server::request_set_selection }),
- ml_new_output({ .notify = Server::new_output }),
- ml_output_manager_apply({ .notify = Server::output_manager_apply }),
- ml_output_manager_test({ .notify = Server::output_manager_test }),
+ ml_request_set_primary_selection({ .notify = Server::request_set_primary_selection }),
+ ml_inhibit_activate({ .notify = Server::inhibit_activate }),
+ ml_inhibit_deactivate({ .notify = Server::inhibit_deactivate }),
+ ml_idle_inhibitor_create({ .notify = Server::idle_inhibitor_create }),
+ ml_idle_inhibitor_destroy({ .notify = Server::idle_inhibitor_destroy }),
+#ifdef XWAYLAND
+ ml_xwayland_ready({ .notify = Server::xwayland_ready }),
+ ml_new_xwayland_surface({ .notify = Server::new_xwayland_surface }),
+#endif
m_socket(wl_display_add_socket_auto(m_display))
{
m_compositor = wlr_compositor_create(m_display, m_renderer);
@@ -97,20 +115,27 @@ Server::Server()
m_scene = wlr_scene_create();
wlr_scene_attach_output_layout(m_scene, m_output_layout);
- wl_list_init(&m_views);
+ wl_list_init(&m_clients);
+
+ m_layer_shell = wlr_layer_shell_v1_create(m_display);
+ wl_signal_add(&m_layer_shell->events.new_surface, &ml_new_layer_shell_surface);
+
m_xdg_shell = wlr_xdg_shell_create(m_display);
wl_signal_add(&m_xdg_shell->events.new_surface, &ml_new_xdg_surface);
m_cursor = wlr_cursor_create();
wlr_cursor_attach_output_layout(m_cursor, m_output_layout);
- m_cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
- wlr_xcursor_manager_load(m_cursor_mgr, 1);
+ m_cursor_manager = wlr_xcursor_manager_create(NULL, 24);
+ wlr_xcursor_manager_load(m_cursor_manager, 1);
m_seat = wlr_seat_create(m_display, "seat0");
m_presentation = wlr_presentation_create(m_display, m_backend);
m_idle = wlr_idle_create(m_display);
+ m_idle_inhibit_manager = wlr_idle_inhibit_v1_create(m_display);
+ wl_signal_add(&m_idle_inhibit_manager->events.new_inhibitor, &ml_idle_inhibitor_create);
+
{ // set up cursor handling
wl_signal_add(&m_cursor->events.motion, &ml_cursor_motion);
wl_signal_add(&m_cursor->events.motion_absolute, &ml_cursor_motion_absolute);
@@ -136,17 +161,45 @@ Server::Server()
);
wlr_xdg_output_manager_v1_create(m_display, m_output_layout);
+ wl_signal_add(&m_output_layout->events.change, &ml_output_layout_change);
+
m_output_manager = wlr_output_manager_v1_create(m_display);
- wl_signal_add(&m_output_manager->events.apply, &ml_output_manager_apply);
- wl_signal_add(&m_output_manager->events.test, &ml_output_manager_test);
+ wl_signal_add(&m_output_manager->events.apply, &ml_output_manager_apply);
+ wl_signal_add(&m_output_manager->events.test, &ml_output_manager_test);
+
+ wlr_scene_set_presentation(m_scene, wlr_presentation_create(m_display, m_backend));
+
+#ifdef XWAYLAND
+ m_xwayland = wlr_xwayland_create(m_display, m_compositor, 1);
+
+ if (m_xwayland) {
+ wl_signal_add(&m_xwayland->events.ready, &ml_xwayland_ready);
+ wl_signal_add(&m_xwayland->events.new_surface, &ml_new_xwayland_surface);
+
+ setenv("DISPLAY", m_xwayland->display_name, 1);
+ } else
+ spdlog::error("Failed to initiate XWayland");
+ spdlog::warn("Continuing without XWayland functionality");
+#endif
m_input_inhibit_manager = wlr_input_inhibit_manager_create(m_display);
+ wl_signal_add(&m_input_inhibit_manager->events.activate, &ml_inhibit_activate);
+ wl_signal_add(&m_input_inhibit_manager->events.deactivate, &ml_inhibit_deactivate);
+
m_keyboard_shortcuts_inhibit_manager = wlr_keyboard_shortcuts_inhibit_v1_create(m_display);
+ // TODO: m_keyboard_shortcuts_inhibit_manager signals
m_pointer_constraints = wlr_pointer_constraints_v1_create(m_display);
+ // TODO: m_pointer_constraints signals
+
m_relative_pointer_manager = wlr_relative_pointer_manager_v1_create(m_display);
+ // TODO: m_relative_pointer_manager signals
+
m_virtual_pointer_manager = wlr_virtual_pointer_manager_v1_create(m_display);
+ // TODO: m_virtual_pointer_manager signals
+
m_virtual_keyboard_manager = wlr_virtual_keyboard_manager_v1_create(m_display);
+ // TODO: m_virtual_keyboard_manager signals
if (m_socket.empty()) {
wlr_backend_destroy(m_backend);
@@ -185,7 +238,7 @@ Server::start() noexcept
void
Server::new_output(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_new_output);
+ Server_ptr server = wl_container_of(listener, server, ml_new_output);
struct wlr_output* wlr_output = reinterpret_cast<struct wlr_output*>(data);
@@ -211,6 +264,12 @@ Server::new_output(struct wl_listener* listener, void* data)
}
void
+Server::output_layout_change(struct wl_listener* listener, void* data)
+{
+ // TODO
+}
+
+void
Server::output_manager_apply(struct wl_listener* listener, void* data)
{
// TODO
@@ -225,7 +284,7 @@ Server::output_manager_test(struct wl_listener* listener, void* data)
void
Server::new_xdg_surface(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_new_xdg_surface);
+ Server_ptr server = wl_container_of(listener, server, ml_new_xdg_surface);
struct wlr_xdg_surface* xdg_surface = reinterpret_cast<struct wlr_xdg_surface*>(data);
@@ -241,28 +300,32 @@ Server::new_xdg_surface(struct wl_listener* listener, void* data)
}
assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
- View* view = reinterpret_cast<View*>(calloc(1, sizeof(View)));
- view->server = server;
- view->xdg_surface = xdg_surface;
- view->scene_node = wlr_scene_xdg_surface_create(
- &view->server->m_scene->node,
- view->xdg_surface
+ Client_ptr client = new Client;
+
+ client->server = server;
+ xdg_surface->data = client->scene;
+
+ client->surface = Surface{ .xdg = xdg_surface };
+ client->surface_type = SurfaceType::XDGShell;
+
+ client->scene = wlr_scene_xdg_surface_create(
+ &client->server->m_scene->node,
+ client->surface.xdg
);
- view->scene_node->data = view;
- xdg_surface->data = view->scene_node;
+ client->scene->data = client;
- view->l_map.notify = xdg_toplevel_map;
- wl_signal_add(&xdg_surface->events.map, &view->l_map);
- view->l_unmap.notify = xdg_toplevel_unmap;
- wl_signal_add(&xdg_surface->events.unmap, &view->l_unmap);
- view->l_destroy.notify = xdg_toplevel_destroy;
- wl_signal_add(&xdg_surface->events.destroy, &view->l_destroy);
+ client->l_map.notify = xdg_toplevel_map;
+ wl_signal_add(&xdg_surface->events.map, &client->l_map);
+ client->l_unmap.notify = xdg_toplevel_unmap;
+ wl_signal_add(&xdg_surface->events.unmap, &client->l_unmap);
+ client->l_destroy.notify = xdg_toplevel_destroy;
+ wl_signal_add(&xdg_surface->events.destroy, &client->l_destroy);
struct wlr_xdg_toplevel* toplevel = xdg_surface->toplevel;
- view->l_request_move.notify = xdg_toplevel_request_move;
- wl_signal_add(&toplevel->events.request_move, &view->l_request_move);
- view->l_request_resize.notify = xdg_toplevel_request_resize;
- wl_signal_add(&toplevel->events.request_resize, &view->l_request_resize);
+ client->l_request_move.notify = xdg_toplevel_request_move;
+ wl_signal_add(&toplevel->events.request_move, &client->l_request_move);
+ client->l_request_resize.notify = xdg_toplevel_request_resize;
+ wl_signal_add(&toplevel->events.request_resize, &client->l_request_resize);
}
void
@@ -272,9 +335,15 @@ Server::new_layer_shell_surface(struct wl_listener* listener, void* data)
}
void
+Server::xdg_activation(struct wl_listener* listener, void* data)
+{
+ // TODO
+}
+
+void
Server::new_input(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_new_input);
+ Server_ptr server = wl_container_of(listener, server, ml_new_input);
struct wlr_input_device* device = reinterpret_cast<struct wlr_input_device*>(data);
switch (device->type) {
@@ -296,13 +365,13 @@ Server::new_input(struct wl_listener* listener, void* data)
}
void
-Server::new_pointer(Server* server, struct wlr_input_device* device)
+Server::new_pointer(Server_ptr server, struct wlr_input_device* device)
{
wlr_cursor_attach_input_device(server->m_cursor, device);
}
void
-Server::new_keyboard(Server* server, struct wlr_input_device* device)
+Server::new_keyboard(Server_ptr server, struct wlr_input_device* device)
{
Keyboard* keyboard = reinterpret_cast<Keyboard*>(calloc(1, sizeof(Keyboard)));
keyboard->server = server;
@@ -330,9 +399,33 @@ Server::new_keyboard(Server* server, struct wlr_input_device* device)
}
void
+Server::inhibit_activate(struct wl_listener* listener, void* data)
+{
+ // TODO
+}
+
+void
+Server::inhibit_deactivate(struct wl_listener* listener, void* data)
+{
+ // TODO
+}
+
+void
+Server::idle_inhibitor_create(struct wl_listener* listener, void* data)
+{
+ // TODO
+}
+
+void
+Server::idle_inhibitor_destroy(struct wl_listener* listener, void* data)
+{
+ // TODO
+}
+
+void
Server::cursor_motion(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_cursor_motion);
+ Server_ptr server = wl_container_of(listener, server, ml_cursor_motion);
struct wlr_event_pointer_motion* event
= reinterpret_cast<wlr_event_pointer_motion*>(data);
@@ -343,7 +436,7 @@ Server::cursor_motion(struct wl_listener* listener, void* data)
void
Server::cursor_motion_absolute(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_cursor_motion_absolute);
+ Server_ptr server = wl_container_of(listener, server, ml_cursor_motion_absolute);
struct wlr_event_pointer_motion_absolute* event
= reinterpret_cast<wlr_event_pointer_motion_absolute*>(data);
@@ -354,7 +447,7 @@ Server::cursor_motion_absolute(struct wl_listener* listener, void* data)
void
Server::cursor_button(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_cursor_button);
+ Server_ptr server = wl_container_of(listener, server, ml_cursor_button);
struct wlr_event_pointer_button* event
= reinterpret_cast<wlr_event_pointer_button*>(data);
@@ -369,7 +462,7 @@ Server::cursor_button(struct wl_listener* listener, void* data)
struct wlr_surface* surface = NULL;
double sx, sy;
- View* view = desktop_view_at(
+ Client_ptr client = desktop_client_at(
server,
server->m_cursor->x,
server->m_cursor->y,
@@ -381,13 +474,13 @@ Server::cursor_button(struct wl_listener* listener, void* data)
if (event->state == WLR_BUTTON_RELEASED)
server->m_cursor_mode = Server::CursorMode::Passthrough;
else
- focus_view(view, surface);
+ focus_client(client, surface);
}
void
Server::cursor_axis(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_cursor_axis);
+ Server_ptr server = wl_container_of(listener, server, ml_cursor_axis);
struct wlr_event_pointer_axis* event
= reinterpret_cast<wlr_event_pointer_axis*>(data);
@@ -405,7 +498,7 @@ Server::cursor_axis(struct wl_listener* listener, void* data)
void
Server::cursor_frame(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_cursor_frame);
+ Server_ptr server = wl_container_of(listener, server, ml_cursor_frame);
wlr_seat_pointer_notify_frame(server->m_seat);
}
@@ -413,7 +506,7 @@ Server::cursor_frame(struct wl_listener* listener, void* data)
void
Server::request_set_cursor(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_request_set_cursor);
+ Server_ptr server = wl_container_of(listener, server, ml_request_set_cursor);
struct wlr_seat_pointer_request_set_cursor_event* event
= reinterpret_cast<struct wlr_seat_pointer_request_set_cursor_event*>(data);
@@ -429,7 +522,7 @@ Server::request_set_cursor(struct wl_listener* listener, void* data)
}
void
-Server::cursor_process_motion(Server* server, uint32_t time)
+Server::cursor_process_motion(Server_ptr server, uint32_t time)
{
switch (server->m_cursor_mode) {
case Server::CursorMode::Move: cursor_process_move(server, time); return;
@@ -441,7 +534,7 @@ Server::cursor_process_motion(Server* server, uint32_t time)
struct wlr_surface* surface = NULL;
double sx, sy;
- View* view = desktop_view_at(
+ Client_ptr client = desktop_client_at(
server,
server->m_cursor->x,
server->m_cursor->y,
@@ -450,9 +543,9 @@ Server::cursor_process_motion(Server* server, uint32_t time)
&sy
);
- if (!view)
+ if (!client)
wlr_xcursor_manager_set_cursor_image(
- server->m_cursor_mgr,
+ server->m_cursor_manager,
"left_ptr",
server->m_cursor
);
@@ -465,20 +558,24 @@ Server::cursor_process_motion(Server* server, uint32_t time)
}
void
-Server::cursor_process_move(Server* server, uint32_t time)
+Server::cursor_process_move(Server_ptr server, uint32_t time)
{
- View* view = server->m_grabbed_view;
+ Client_ptr client = server->m_grabbed_client;
- view->x = server->m_cursor->x - server->m_grab_x;
- view->y = server->m_cursor->y - server->m_grab_y;
+ client->active_region.pos.x = server->m_cursor->x - server->m_grab_x;
+ client->active_region.pos.y = server->m_cursor->y - server->m_grab_y;
- wlr_scene_node_set_position(view->scene_node, view->x, view->y);
+ wlr_scene_node_set_position(
+ client->scene,
+ client->active_region.pos.x,
+ client->active_region.pos.y
+ );
}
void
-Server::cursor_process_resize(Server* server, uint32_t time)
+Server::cursor_process_resize(Server_ptr server, uint32_t time)
{
- View* view = server->m_grabbed_view;
+ Client_ptr client = server->m_grabbed_client;
double border_x = server->m_cursor->x - server->m_grab_x;
double border_y = server->m_cursor->y - server->m_grab_y;
@@ -513,14 +610,25 @@ Server::cursor_process_resize(Server* server, uint32_t time)
}
struct wlr_box geo_box;
- wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box);
- view->x = new_left - geo_box.x;
- view->y = new_top - geo_box.y;
- wlr_scene_node_set_position(view->scene_node, view->x, view->y);
+ wlr_xdg_surface_get_geometry(client->surface.xdg, &geo_box);
+
+ client->active_region.pos.x = new_left - geo_box.x;
+ client->active_region.pos.y = new_top - geo_box.y;
+
+ wlr_scene_node_set_position(
+ client->scene,
+ client->active_region.pos.x,
+ client->active_region.pos.y
+ );
int new_width = new_right - new_left;
int new_height = new_bottom - new_top;
- wlr_xdg_toplevel_set_size(view->xdg_surface, new_width, new_height);
+
+ wlr_xdg_toplevel_set_size(
+ client->surface.xdg,
+ new_width,
+ new_height
+ );
}
void
@@ -551,7 +659,7 @@ void
Server::keyboard_handle_key(struct wl_listener* listener, void* data)
{
Keyboard* keyboard = wl_container_of(listener, keyboard, l_key);
- Server* server = keyboard->server;
+ Server_ptr server = keyboard->server;
struct wlr_event_keyboard_key* event
= reinterpret_cast<struct wlr_event_keyboard_key*>(data);
@@ -586,7 +694,7 @@ Server::keyboard_handle_key(struct wl_listener* listener, void* data)
}
bool
-Server::keyboard_handle_keybinding(Server* server, xkb_keysym_t sym)
+Server::keyboard_handle_keybinding(Server_ptr server, xkb_keysym_t sym)
{
switch (sym) {
case XKB_KEY_Escape:
@@ -594,11 +702,19 @@ Server::keyboard_handle_keybinding(Server* server, xkb_keysym_t sym)
break;
case XKB_KEY_j:
{
- if (wl_list_length(&server->m_views) < 2)
+ if (wl_list_length(&server->m_clients) < 2)
break;
- View* prev_view = wl_container_of(server->m_views.prev, prev_view, link);
- focus_view(prev_view, prev_view->xdg_surface->surface);
+ Client_ptr prev_client = wl_container_of(
+ server->m_clients.prev,
+ prev_client,
+ link
+ );
+
+ focus_client(
+ prev_client,
+ prev_client->get_surface()
+ );
}
break;
case XKB_KEY_Return:
@@ -607,6 +723,12 @@ Server::keyboard_handle_keybinding(Server* server, xkb_keysym_t sym)
exec_external(foot);
}
break;
+ case XKB_KEY_semicolon:
+ {
+ std::string st = "st";
+ exec_external(st);
+ }
+ break;
default: return false;
}
@@ -616,7 +738,7 @@ Server::keyboard_handle_keybinding(Server* server, xkb_keysym_t sym)
void
Server::request_set_selection(struct wl_listener* listener, void* data)
{
- Server* server = wl_container_of(listener, server, ml_request_set_selection);
+ Server_ptr server = wl_container_of(listener, server, ml_request_set_selection);
struct wlr_seat_request_set_selection_event* event
= reinterpret_cast<struct wlr_seat_request_set_selection_event*>(data);
@@ -625,6 +747,17 @@ Server::request_set_selection(struct wl_listener* listener, void* data)
}
void
+Server::request_set_primary_selection(struct wl_listener* listener, void* data)
+{
+ Server_ptr server = wl_container_of(listener, server, ml_request_set_primary_selection);
+
+ struct wlr_seat_request_set_primary_selection_event* event
+ = reinterpret_cast<struct wlr_seat_request_set_primary_selection_event*>(data);
+
+ wlr_seat_set_primary_selection(server->m_seat, event->source, event->serial);
+}
+
+void
Server::output_frame(struct wl_listener* listener, void* data)
{
Output* output = wl_container_of(listener, output, l_frame);
@@ -640,9 +773,9 @@ Server::output_frame(struct wl_listener* listener, void* data)
wlr_scene_output_send_frame_done(scene_output, &now);
}
-View*
-Server::desktop_view_at(
- Server* server,
+Client_ptr
+Server::desktop_client_at(
+ Server_ptr server,
double lx,
double ly,
struct wlr_surface** surface,
@@ -664,39 +797,44 @@ Server::desktop_view_at(
while (node && !node->data)
node = node->parent;
- return reinterpret_cast<View*>(node->data);
+ return reinterpret_cast<Client_ptr>(node->data);
}
void
-Server::focus_view(View* view, struct wlr_surface* surface)
+Server::focus_client(Client_ptr client, struct wlr_surface* surface)
{
- if (!view)
+ if (!client)
return;
- Server* server = view->server;
+ Server_ptr server = client->server;
struct wlr_seat* seat = server->m_seat;
struct wlr_surface* prev_surface = seat->keyboard_state.focused_surface;
if (prev_surface == surface)
return;
- if (prev_surface)
- wlr_xdg_toplevel_set_activated(
- wlr_xdg_surface_from_wlr_surface(
- seat->keyboard_state.focused_surface
- ),
- false
- );
+ /* if (prev_surface) */
+ /* wlr_xdg_toplevel_set_activated( */
+ /* wlr_xdg_surface_from_wlr_surface( */
+ /* seat->keyboard_state.focused_surface */
+ /* ), */
+ /* false */
+ /* ); */
struct wlr_keyboard* keyboard = wlr_seat_get_keyboard(seat);
- wlr_scene_node_raise_to_top(view->scene_node);
- wl_list_remove(&view->link);
- wl_list_insert(&server->m_views, &view->link);
- wlr_xdg_toplevel_set_activated(view->xdg_surface, true);
+ if (client->scene)
+ wlr_scene_node_raise_to_top(client->scene);
+
+ wl_list_remove(&client->link);
+ wl_list_insert(&server->m_clients, &client->link);
+
+ if (client->surface_type == SurfaceType::XDGShell || client->surface_type == SurfaceType::LayerShell)
+ wlr_xdg_toplevel_set_activated(client->surface.xdg, true);
+
wlr_seat_keyboard_notify_enter(
seat,
- view->xdg_surface->surface,
+ client->get_surface(),
keyboard->keycodes,
keyboard->num_keycodes,
&keyboard->modifiers
@@ -706,38 +844,40 @@ Server::focus_view(View* view, struct wlr_surface* surface)
void
Server::xdg_toplevel_map(struct wl_listener* listener, void* data)
{
- View* view = wl_container_of(listener, view, l_map);
+ Client_ptr client = wl_container_of(listener, client, l_map);
+
+ printf("Address of x is %p\n", (void *)client);
- wl_list_insert(&view->server->m_views, &view->link);
- focus_view(view, view->xdg_surface->surface);
+ wl_list_insert(&client->server->m_clients, &client->link);
+ focus_client(client, client->get_surface());
}
void
Server::xdg_toplevel_unmap(struct wl_listener* listener, void* data)
{
- View* view = wl_container_of(listener, view, l_unmap);
- wl_list_remove(&view->link);
+ Client_ptr client = wl_container_of(listener, client, l_unmap);
+ wl_list_remove(&client->link);
}
void
Server::xdg_toplevel_destroy(struct wl_listener* listener, void* data)
{
- View* view = wl_container_of(listener, view, l_destroy);
+ Client_ptr client = wl_container_of(listener, client, l_destroy);
- wl_list_remove(&view->l_map.link);
- wl_list_remove(&view->l_unmap.link);
- wl_list_remove(&view->l_destroy.link);
- wl_list_remove(&view->l_request_move.link);
- wl_list_remove(&view->l_request_resize.link);
+ wl_list_remove(&client->l_map.link);
+ wl_list_remove(&client->l_unmap.link);
+ wl_list_remove(&client->l_destroy.link);
+ wl_list_remove(&client->l_request_move.link);
+ wl_list_remove(&client->l_request_resize.link);
- free(view);
+ delete client;
}
void
Server::xdg_toplevel_request_move(struct wl_listener* listener, void* data)
{
- View* view = wl_container_of(listener, view, l_request_move);
- xdg_toplevel_handle_moveresize(view, Server::CursorMode::Move, 0);
+ Client_ptr client = wl_container_of(listener, client, l_request_move);
+ xdg_toplevel_handle_moveresize(client, Server::CursorMode::Move, 0);
}
void
@@ -746,46 +886,50 @@ Server::xdg_toplevel_request_resize(struct wl_listener* listener, void* data)
struct wlr_xdg_toplevel_resize_event* event
= reinterpret_cast<struct wlr_xdg_toplevel_resize_event*>(data);
- View* view = wl_container_of(listener, view, l_request_resize);
- xdg_toplevel_handle_moveresize(view, Server::CursorMode::Resize, event->edges);
+ Client_ptr client = wl_container_of(listener, client, l_request_resize);
+ xdg_toplevel_handle_moveresize(client, Server::CursorMode::Resize, event->edges);
}
void
-Server::xdg_toplevel_handle_moveresize(View* view, Server::CursorMode mode, uint32_t edges)
+Server::xdg_toplevel_handle_moveresize(
+ Client_ptr client,
+ Server::CursorMode mode,
+ uint32_t edges
+)
{
- Server* server = view->server;
+ Server_ptr server = client->server;
- if (view->xdg_surface->surface
+ if (client->get_surface()
!= wlr_surface_get_root_surface(server->m_seat->pointer_state.focused_surface))
{
return;
}
- server->m_grabbed_view = view;
+ server->m_grabbed_client = client;
server->m_cursor_mode = mode;
switch (mode) {
case Server::CursorMode::Move:
- server->m_grab_x = server->m_cursor->x - view->x;
- server->m_grab_y = server->m_cursor->y - view->y;
+ server->m_grab_x = server->m_cursor->x - client->active_region.pos.x;
+ server->m_grab_y = server->m_cursor->y - client->active_region.pos.y;
return;
case Server::CursorMode::Resize:
{
struct wlr_box geo_box;
- wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box);
+ wlr_xdg_surface_get_geometry(client->surface.xdg, &geo_box);
- double border_x = (view->x + geo_box.x) +
+ double border_x = (client->active_region.pos.x + geo_box.x) +
((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
- double border_y = (view->y + geo_box.y) +
+ double border_y = (client->active_region.pos.y + geo_box.y) +
((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
server->m_grab_x = server->m_cursor->x - border_x;
server->m_grab_y = server->m_cursor->y - border_y;
server->m_grab_geobox = geo_box;
- server->m_grab_geobox.x += view->x;
- server->m_grab_geobox.y += view->y;
+ server->m_grab_geobox.x += client->active_region.pos.x;
+ server->m_grab_geobox.y += client->active_region.pos.y;
server->m_resize_edges = edges;
}
@@ -793,3 +937,88 @@ Server::xdg_toplevel_handle_moveresize(View* view, Server::CursorMode mode, uint
default: return;
}
}
+
+#ifdef XWAYLAND
+void
+Server::xwayland_ready(struct wl_listener* listener, void*)
+{
+ Server_ptr server = wl_container_of(listener, server, ml_xwayland_ready);
+
+ xcb_connection_t* xconn = xcb_connect(server->m_xwayland->display_name, NULL);
+ if (xcb_connection_has_error(xconn)) {
+ spdlog::error("Could not establish a connection with the X server");
+ spdlog::warn("Continuing with limited XWayland functionality");
+ return;
+ }
+
+ /* netatom[NetWMWindowTypeDialog] = getatom(xconn, "_NET_WM_WINDOW_TYPE_DIALOG"); */
+ /* netatom[NetWMWindowTypeSplash] = getatom(xconn, "_NET_WM_WINDOW_TYPE_SPLASH"); */
+ /* netatom[NetWMWindowTypeToolbar] = getatom(xconn, "_NET_WM_WINDOW_TYPE_TOOLBAR"); */
+ /* netatom[NetWMWindowTypeUtility] = getatom(xconn, "_NET_WM_WINDOW_TYPE_UTILITY"); */
+
+ wlr_xwayland_set_seat(server->m_xwayland, server->m_seat);
+
+ struct wlr_xcursor* xcursor;
+ if ((xcursor = wlr_xcursor_manager_get_xcursor(server->m_cursor_manager, "left_ptr", 1)))
+ wlr_xwayland_set_cursor(server->m_xwayland,
+ xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
+ xcursor->images[0]->width, xcursor->images[0]->height,
+ xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
+
+ xcb_disconnect(xconn);
+}
+
+void
+Server::new_xwayland_surface(struct wl_listener* listener, void* data)
+{
+ Server_ptr server = wl_container_of(listener, server, ml_new_xwayland_surface);
+
+ struct wlr_xwayland_surface* xwayland_surface
+ = reinterpret_cast<wlr_xwayland_surface*>(data);
+
+ Client_ptr client = new Client;
+
+ client->server = server;
+ xwayland_surface->data = client;
+
+ client->surface = Surface{ .xwayland = xwayland_surface };
+ client->surface_type = xwayland_surface->override_redirect
+ ? SurfaceType::X11Unmanaged
+ : SurfaceType::X11Managed;
+
+ client->l_map = { .notify = Server::xdg_toplevel_map };
+ client->l_unmap = { .notify = Server::xdg_toplevel_unmap };
+ client->l_destroy = { .notify = Server::xdg_toplevel_destroy };
+ // TODO: client->l_set_title = { .notify = Server::... };
+ // TODO: client->l_fullscreen = { .notify = Server::... };
+ client->l_request_activate = { .notify = Server::xwayland_request_activate };
+ client->l_request_configure = { .notify = Server::xwayland_request_configure };
+ client->l_set_hints = { .notify = Server::xwayland_set_hints };
+ // TODO: client->l_new_xdg_popup = { .notify = Server::... };
+
+ wl_signal_add(&xwayland_surface->events.map, &client->l_map);
+ wl_signal_add(&xwayland_surface->events.unmap, &client->l_unmap);
+ wl_signal_add(&xwayland_surface->events.destroy, &client->l_destroy);
+ wl_signal_add(&xwayland_surface->events.request_activate, &client->l_request_activate);
+ wl_signal_add(&xwayland_surface->events.request_configure, &client->l_request_configure);
+ wl_signal_add(&xwayland_surface->events.set_hints, &client->l_set_hints);
+}
+
+void
+Server::xwayland_request_activate(struct wl_listener* listener, void*)
+{
+
+}
+
+void
+Server::xwayland_request_configure(struct wl_listener* listener, void*)
+{
+
+}
+
+void
+Server::xwayland_set_hints(struct wl_listener* listener, void*)
+{
+
+}
+#endif
diff --git a/src/kranewl/util.cc b/src/kranewl/util.cc
@@ -0,0 +1,30 @@
+#include <kranewl/util.hh>
+
+#define assert assert_
+#include <spdlog/spdlog.h>
+#undef assert
+
+#include <utility>
+extern "C" {
+#include <unistd.h>
+}
+
+void
+Util::die(const std::string&& msg)
+{
+ spdlog::critical(msg);
+ exit(1);
+}
+
+void
+Util::warn(const std::string&& msg)
+{
+ spdlog::warn(msg);
+}
+
+void
+Util::assert(bool condition, std::string const&& msg)
+{
+ if (!condition)
+ Util::die(std::forward<const std::string&&>(msg));
+}