Skip to content

Commit 47a2a15

Browse files
committed
wayland/screencopy: add screencopy
1 parent 918dd23 commit 47a2a15

37 files changed

+3146
-3
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343
qt6-shadertools \
4444
wayland-protocols \
4545
wayland \
46+
libdrm \
4647
libxcb \
4748
libpipewire \
4849
cli11 \

BUILD.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,24 @@ which allows quickshell to be used as a session lock under compatible wayland co
130130

131131
To disable: `-DWAYLAND_TOPLEVEL_MANAGEMENT=OFF`
132132

133+
#### Screencopy
134+
Enables streaming video from monitors and toplevel windows through various protocols.
135+
136+
To disable: `-DSCREENCOPY=OFF`
137+
138+
Dependencies:
139+
- `libdrm`
140+
- `libgbm`
141+
142+
Specific protocols can also be disabled:
143+
- `DSCREENCOPY_ICC=OFF` - Disable screencopy via [ext-image-copy-capture-v1]
144+
- `DSCREENCOPY_WLR=OFF` - Disable screencopy via [zwlr-screencopy-v1]
145+
- `DSCREENCOPY_HYPRLAND_TOPLEVEL=OFF` - Disable screencopy via [hyprland-toplevel-export-v1]
146+
147+
[ext-image-copy-capture-v1]:https://wayland.app/protocols/ext-image-copy-capture-v1
148+
[zwlr-screencopy-v1]: https://wayland.app/protocols/wlr-screencopy-unstable-v1
149+
[hyprland-toplevel-export-v1]: https://wayland.app/protocols/hyprland-toplevel-export-v1
150+
133151
### X11
134152
This feature enables x11 support. Currently this implements panel windows for X11 similarly
135153
to the wlroots layershell above.

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ boption(HYPRLAND_IPC " Hyprland IPC" ON REQUIRES HYPRLAND)
5656
boption(HYPRLAND_GLOBAL_SHORTCUTS " Hyprland Global Shortcuts" ON REQUIRES HYPRLAND)
5757
boption(HYPRLAND_FOCUS_GRAB " Hyprland Focus Grabbing" ON REQUIRES HYPRLAND)
5858
boption(HYPRLAND_SURFACE_EXTENSIONS " Hyprland Surface Extensions" ON REQUIRES HYPRLAND)
59+
boption(SCREENCOPY " Screencopy" ON REQUIRES WAYLAND)
60+
boption(SCREENCOPY_ICC " Image Copy Capture" ON REQUIRES WAYLAND)
61+
boption(SCREENCOPY_WLR " Wlroots Screencopy" ON REQUIRES WAYLAND)
62+
boption(SCREENCOPY_HYPRLAND_TOPLEVEL " Hyprland Toplevel Export" ON REQUIRES WAYLAND)
5963
boption(X11 "X11" ON)
6064
boption(I3 "I3/Sway" ON)
6165
boption(I3_IPC " I3/Sway IPC" ON REQUIRES I3)
@@ -70,7 +74,7 @@ boption(SERVICE_NOTIFICATIONS "Notifications" ON)
7074
include(cmake/install-qml-module.cmake)
7175
include(cmake/util.cmake)
7276

73-
add_compile_options(-Wall -Wextra)
77+
add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension)
7478

7579
# pipewire defines this, breaking PCH
7680
add_compile_definitions(_REENTRANT)

default.nix

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
jemalloc,
1515
wayland,
1616
wayland-protocols,
17+
libdrm,
18+
libgbm ? null,
1719
xorg,
1820
pipewire,
1921
pam,
@@ -64,7 +66,7 @@
6466
++ lib.optional withCrashReporter breakpad
6567
++ lib.optional withJemalloc jemalloc
6668
++ lib.optional withQtSvg qt6.qtsvg
67-
++ lib.optionals withWayland [ qt6.qtwayland wayland ]
69+
++ lib.optionals withWayland ([ qt6.qtwayland wayland ] ++ (if libgbm != null then [ libdrm libgbm ] else []))
6870
++ lib.optional withX11 xorg.libxcb
6971
++ lib.optional withPam pam
7072
++ lib.optional withPipewire pipewire;
@@ -79,6 +81,7 @@
7981
(lib.cmakeBool "CRASH_REPORTER" withCrashReporter)
8082
(lib.cmakeBool "USE_JEMALLOC" withJemalloc)
8183
(lib.cmakeBool "WAYLAND" withWayland)
84+
(lib.cmakeBool "SCREENCOPY" (libgbm != null))
8285
(lib.cmakeBool "SERVICE_PIPEWIRE" withPipewire)
8386
(lib.cmakeBool "SERVICE_PAM" withPam)
8487
(lib.cmakeBool "HYPRLAND" withHyprland)

src/core/stacklist.hpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#pragma once
2+
3+
#include <algorithm>
4+
#include <array>
5+
#include <cstddef>
6+
#include <cstdint>
7+
#include <iterator>
8+
#include <vector>
9+
10+
#include <qtypes.h>
11+
12+
template <class T, size_t N>
13+
class StackList {
14+
public:
15+
T& operator[](size_t i) {
16+
if (i < N) {
17+
return this->array[i];
18+
} else {
19+
return this->vec[i - N];
20+
}
21+
}
22+
23+
const T& operator[](size_t i) const {
24+
return const_cast<StackList<T, N>*>(this)->operator[](i); // NOLINT
25+
}
26+
27+
void push(const T& value) {
28+
if (this->size < N) {
29+
this->array[this->size] = value;
30+
} else {
31+
this->vec.push_back(value);
32+
}
33+
34+
++this->size;
35+
}
36+
37+
[[nodiscard]] size_t length() const { return this->size; }
38+
[[nodiscard]] bool isEmpty() const { return this->size == 0; }
39+
40+
[[nodiscard]] bool operator==(const StackList<T, N>& other) const {
41+
if (other.size != this->size) return false;
42+
43+
for (size_t i = 0; i < this->size; ++i) {
44+
if (this->operator[](i) != other[i]) return false;
45+
}
46+
47+
return true;
48+
}
49+
50+
template <typename Self, typename ListPtr, typename IT>
51+
struct BaseIterator {
52+
using iterator_category = std::bidirectional_iterator_tag;
53+
using difference_type = int64_t;
54+
using value_type = IT;
55+
using pointer = IT*;
56+
using reference = IT&;
57+
58+
BaseIterator() = default;
59+
explicit BaseIterator(ListPtr list, size_t i): list(list), i(i) {}
60+
61+
reference operator*() const { return this->list->operator[](this->i); }
62+
pointer operator->() const { return &**this; }
63+
64+
Self& operator++() {
65+
++this->i;
66+
return *static_cast<Self*>(this);
67+
}
68+
Self& operator--() {
69+
--this->i;
70+
return *static_cast<Self*>(this);
71+
}
72+
73+
Self operator++(int) {
74+
auto v = *this;
75+
this->operator++();
76+
return v;
77+
}
78+
Self operator--(int) {
79+
auto v = *this;
80+
this->operator--();
81+
return v;
82+
}
83+
84+
difference_type operator-(const Self& other) {
85+
return static_cast<int64_t>(this->i) - static_cast<int64_t>(other.i);
86+
}
87+
88+
Self& operator+(difference_type offset) {
89+
return Self(this->list, static_cast<int64_t>(this->i) + offset);
90+
}
91+
92+
[[nodiscard]] bool operator==(const Self& other) const {
93+
return this->list == other.list && this->i == other.i;
94+
}
95+
96+
[[nodiscard]] bool operator!=(const Self& other) const { return !(*this == other); }
97+
98+
private:
99+
ListPtr list = nullptr;
100+
size_t i = 0;
101+
};
102+
103+
struct Iterator: public BaseIterator<Iterator, StackList<T, N>*, T> {
104+
Iterator() = default;
105+
Iterator(StackList<T, N>* list, size_t i)
106+
: BaseIterator<Iterator, StackList<T, N>*, T>(list, i) {}
107+
};
108+
109+
struct ConstIterator: public BaseIterator<ConstIterator, const StackList<T, N>*, const T> {
110+
ConstIterator() = default;
111+
ConstIterator(const StackList<T, N>* list, size_t i)
112+
: BaseIterator<ConstIterator, const StackList<T, N>*, const T>(list, i) {}
113+
};
114+
115+
[[nodiscard]] Iterator begin() { return Iterator(this, 0); }
116+
[[nodiscard]] Iterator end() { return Iterator(this, this->size); }
117+
118+
[[nodiscard]] ConstIterator begin() const { return ConstIterator(this, 0); }
119+
[[nodiscard]] ConstIterator end() const { return ConstIterator(this, this->size); }
120+
121+
[[nodiscard]] bool isContiguous() const { return this->vec.empty(); }
122+
[[nodiscard]] const T* pArray() const { return this->array.data(); }
123+
[[nodiscard]] size_t dataLength() const { return this->size * sizeof(T); }
124+
125+
const T* populateAlloc(void* alloc) const {
126+
auto arraylen = std::min(this->size, N) * sizeof(T);
127+
memcpy(alloc, this->array.data(), arraylen);
128+
129+
if (!this->vec.empty()) {
130+
memcpy(
131+
static_cast<char*>(alloc) + arraylen, // NOLINT
132+
this->vec.data(),
133+
this->vec.size() * sizeof(T)
134+
);
135+
}
136+
137+
return static_cast<T*>(alloc);
138+
}
139+
140+
private:
141+
std::array<T, N> array {};
142+
std::vector<T> vec;
143+
size_t size = 0;
144+
};
145+
146+
// might be incorrectly aligned depending on type
147+
// #define STACKLIST_ALLOCA_VIEW(list) ((list).isContiguous() ? (list).pArray() : (list).populateAlloc(alloca((list).dataLength())))
148+
149+
// NOLINTBEGIN
150+
#define STACKLIST_VLA_VIEW(type, list, var) \
151+
const type* var; \
152+
type var##Data[(list).length()]; \
153+
if ((list).isContiguous()) { \
154+
(var) = (list).pArray(); \
155+
} else { \
156+
(list).populateAlloc(var##Data); \
157+
(var) = var##Data; \
158+
}
159+
// NOLINTEND

src/wayland/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ if (WAYLAND_TOPLEVEL_MANAGEMENT)
103103
list(APPEND WAYLAND_MODULES Quickshell.Wayland._ToplevelManagement)
104104
endif()
105105

106+
if (SCREENCOPY)
107+
add_subdirectory(buffer)
108+
add_subdirectory(screencopy)
109+
list(APPEND WAYLAND_MODULES Quickshell.Wayland._Screencopy)
110+
endif()
111+
112+
106113
if (HYPRLAND)
107114
add_subdirectory(hyprland)
108115
endif()

src/wayland/buffer/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
find_package(PkgConfig REQUIRED)
2+
pkg_check_modules(dmabuf-deps REQUIRED IMPORTED_TARGET libdrm gbm egl)
3+
4+
qt_add_library(quickshell-wayland-buffer STATIC
5+
manager.cpp
6+
dmabuf.cpp
7+
shm.cpp
8+
)
9+
10+
wl_proto(wlp-linux-dmabuf linux-dmabuf-v1 "${WAYLAND_PROTOCOLS}/stable/linux-dmabuf")
11+
12+
target_link_libraries(quickshell-wayland-buffer PRIVATE
13+
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
14+
PkgConfig::dmabuf-deps
15+
wlp-linux-dmabuf
16+
)
17+
18+
qs_pch(quickshell-wayland-buffer SET large)

0 commit comments

Comments
 (0)