Skip to content

Commit

Permalink
wayland/screencopy: add screencopy
Browse files Browse the repository at this point in the history
  • Loading branch information
outfoxxed committed Jan 14, 2025
1 parent 918dd23 commit 47a2a15
Show file tree
Hide file tree
Showing 37 changed files with 3,146 additions and 3 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
qt6-shadertools \
wayland-protocols \
wayland \
libdrm \
libxcb \
libpipewire \
cli11 \
Expand Down
18 changes: 18 additions & 0 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,24 @@ which allows quickshell to be used as a session lock under compatible wayland co

To disable: `-DWAYLAND_TOPLEVEL_MANAGEMENT=OFF`

#### Screencopy
Enables streaming video from monitors and toplevel windows through various protocols.

To disable: `-DSCREENCOPY=OFF`

Dependencies:
- `libdrm`
- `libgbm`

Specific protocols can also be disabled:
- `DSCREENCOPY_ICC=OFF` - Disable screencopy via [ext-image-copy-capture-v1]
- `DSCREENCOPY_WLR=OFF` - Disable screencopy via [zwlr-screencopy-v1]
- `DSCREENCOPY_HYPRLAND_TOPLEVEL=OFF` - Disable screencopy via [hyprland-toplevel-export-v1]

[ext-image-copy-capture-v1]:https://wayland.app/protocols/ext-image-copy-capture-v1
[zwlr-screencopy-v1]: https://wayland.app/protocols/wlr-screencopy-unstable-v1
[hyprland-toplevel-export-v1]: https://wayland.app/protocols/hyprland-toplevel-export-v1

### X11
This feature enables x11 support. Currently this implements panel windows for X11 similarly
to the wlroots layershell above.
Expand Down
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ boption(HYPRLAND_IPC " Hyprland IPC" ON REQUIRES HYPRLAND)
boption(HYPRLAND_GLOBAL_SHORTCUTS " Hyprland Global Shortcuts" ON REQUIRES HYPRLAND)
boption(HYPRLAND_FOCUS_GRAB " Hyprland Focus Grabbing" ON REQUIRES HYPRLAND)
boption(HYPRLAND_SURFACE_EXTENSIONS " Hyprland Surface Extensions" ON REQUIRES HYPRLAND)
boption(SCREENCOPY " Screencopy" ON REQUIRES WAYLAND)
boption(SCREENCOPY_ICC " Image Copy Capture" ON REQUIRES WAYLAND)
boption(SCREENCOPY_WLR " Wlroots Screencopy" ON REQUIRES WAYLAND)
boption(SCREENCOPY_HYPRLAND_TOPLEVEL " Hyprland Toplevel Export" ON REQUIRES WAYLAND)
boption(X11 "X11" ON)
boption(I3 "I3/Sway" ON)
boption(I3_IPC " I3/Sway IPC" ON REQUIRES I3)
Expand All @@ -70,7 +74,7 @@ boption(SERVICE_NOTIFICATIONS "Notifications" ON)
include(cmake/install-qml-module.cmake)
include(cmake/util.cmake)

add_compile_options(-Wall -Wextra)
add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension)

# pipewire defines this, breaking PCH
add_compile_definitions(_REENTRANT)
Expand Down
5 changes: 4 additions & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
jemalloc,
wayland,
wayland-protocols,
libdrm,
libgbm ? null,
xorg,
pipewire,
pam,
Expand Down Expand Up @@ -64,7 +66,7 @@
++ lib.optional withCrashReporter breakpad
++ lib.optional withJemalloc jemalloc
++ lib.optional withQtSvg qt6.qtsvg
++ lib.optionals withWayland [ qt6.qtwayland wayland ]
++ lib.optionals withWayland ([ qt6.qtwayland wayland ] ++ (if libgbm != null then [ libdrm libgbm ] else []))
++ lib.optional withX11 xorg.libxcb
++ lib.optional withPam pam
++ lib.optional withPipewire pipewire;
Expand All @@ -79,6 +81,7 @@
(lib.cmakeBool "CRASH_REPORTER" withCrashReporter)
(lib.cmakeBool "USE_JEMALLOC" withJemalloc)
(lib.cmakeBool "WAYLAND" withWayland)
(lib.cmakeBool "SCREENCOPY" (libgbm != null))
(lib.cmakeBool "SERVICE_PIPEWIRE" withPipewire)
(lib.cmakeBool "SERVICE_PAM" withPam)
(lib.cmakeBool "HYPRLAND" withHyprland)
Expand Down
159 changes: 159 additions & 0 deletions src/core/stacklist.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#pragma once

#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <vector>

#include <qtypes.h>

template <class T, size_t N>
class StackList {
public:
T& operator[](size_t i) {
if (i < N) {
return this->array[i];
} else {
return this->vec[i - N];
}
}

const T& operator[](size_t i) const {
return const_cast<StackList<T, N>*>(this)->operator[](i); // NOLINT
}

void push(const T& value) {
if (this->size < N) {
this->array[this->size] = value;
} else {
this->vec.push_back(value);
}

++this->size;
}

[[nodiscard]] size_t length() const { return this->size; }
[[nodiscard]] bool isEmpty() const { return this->size == 0; }

[[nodiscard]] bool operator==(const StackList<T, N>& other) const {
if (other.size != this->size) return false;

for (size_t i = 0; i < this->size; ++i) {
if (this->operator[](i) != other[i]) return false;
}

return true;
}

template <typename Self, typename ListPtr, typename IT>
struct BaseIterator {
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = int64_t;
using value_type = IT;
using pointer = IT*;
using reference = IT&;

BaseIterator() = default;
explicit BaseIterator(ListPtr list, size_t i): list(list), i(i) {}

reference operator*() const { return this->list->operator[](this->i); }
pointer operator->() const { return &**this; }

Self& operator++() {
++this->i;
return *static_cast<Self*>(this);
}
Self& operator--() {
--this->i;
return *static_cast<Self*>(this);
}

Self operator++(int) {
auto v = *this;
this->operator++();
return v;
}
Self operator--(int) {
auto v = *this;
this->operator--();
return v;
}

difference_type operator-(const Self& other) {
return static_cast<int64_t>(this->i) - static_cast<int64_t>(other.i);
}

Self& operator+(difference_type offset) {
return Self(this->list, static_cast<int64_t>(this->i) + offset);
}

[[nodiscard]] bool operator==(const Self& other) const {
return this->list == other.list && this->i == other.i;
}

[[nodiscard]] bool operator!=(const Self& other) const { return !(*this == other); }

private:
ListPtr list = nullptr;
size_t i = 0;
};

struct Iterator: public BaseIterator<Iterator, StackList<T, N>*, T> {
Iterator() = default;
Iterator(StackList<T, N>* list, size_t i)
: BaseIterator<Iterator, StackList<T, N>*, T>(list, i) {}
};

struct ConstIterator: public BaseIterator<ConstIterator, const StackList<T, N>*, const T> {
ConstIterator() = default;
ConstIterator(const StackList<T, N>* list, size_t i)
: BaseIterator<ConstIterator, const StackList<T, N>*, const T>(list, i) {}
};

[[nodiscard]] Iterator begin() { return Iterator(this, 0); }
[[nodiscard]] Iterator end() { return Iterator(this, this->size); }

[[nodiscard]] ConstIterator begin() const { return ConstIterator(this, 0); }
[[nodiscard]] ConstIterator end() const { return ConstIterator(this, this->size); }

[[nodiscard]] bool isContiguous() const { return this->vec.empty(); }
[[nodiscard]] const T* pArray() const { return this->array.data(); }
[[nodiscard]] size_t dataLength() const { return this->size * sizeof(T); }

const T* populateAlloc(void* alloc) const {
auto arraylen = std::min(this->size, N) * sizeof(T);
memcpy(alloc, this->array.data(), arraylen);

if (!this->vec.empty()) {
memcpy(
static_cast<char*>(alloc) + arraylen, // NOLINT
this->vec.data(),
this->vec.size() * sizeof(T)
);
}

return static_cast<T*>(alloc);
}

private:
std::array<T, N> array {};
std::vector<T> vec;
size_t size = 0;
};

// might be incorrectly aligned depending on type
// #define STACKLIST_ALLOCA_VIEW(list) ((list).isContiguous() ? (list).pArray() : (list).populateAlloc(alloca((list).dataLength())))

// NOLINTBEGIN
#define STACKLIST_VLA_VIEW(type, list, var) \
const type* var; \
type var##Data[(list).length()]; \
if ((list).isContiguous()) { \
(var) = (list).pArray(); \
} else { \
(list).populateAlloc(var##Data); \
(var) = var##Data; \
}
// NOLINTEND
7 changes: 7 additions & 0 deletions src/wayland/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ if (WAYLAND_TOPLEVEL_MANAGEMENT)
list(APPEND WAYLAND_MODULES Quickshell.Wayland._ToplevelManagement)
endif()

if (SCREENCOPY)
add_subdirectory(buffer)
add_subdirectory(screencopy)
list(APPEND WAYLAND_MODULES Quickshell.Wayland._Screencopy)
endif()


if (HYPRLAND)
add_subdirectory(hyprland)
endif()
Expand Down
18 changes: 18 additions & 0 deletions src/wayland/buffer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
find_package(PkgConfig REQUIRED)
pkg_check_modules(dmabuf-deps REQUIRED IMPORTED_TARGET libdrm gbm egl)

qt_add_library(quickshell-wayland-buffer STATIC
manager.cpp
dmabuf.cpp
shm.cpp
)

wl_proto(wlp-linux-dmabuf linux-dmabuf-v1 "${WAYLAND_PROTOCOLS}/stable/linux-dmabuf")

target_link_libraries(quickshell-wayland-buffer PRIVATE
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
PkgConfig::dmabuf-deps
wlp-linux-dmabuf
)

qs_pch(quickshell-wayland-buffer SET large)
Loading

0 comments on commit 47a2a15

Please sign in to comment.