Commit e05a2e8
13
plików
+710
dodanych
-0
usuniętych
@@ -0,0 +1,50 @@
1
+# GENEROWANY PLIK — nie edytować ręcznie.
2
+# Źródło prawdy: paganDE/config/ i paganDE/session/.
3
+# Regeneracja: make tarball-meta && make recipe
4
+
5
+pkgname: pagande
6
+pkgver: '0.1.0'
7
+pkgrel: 2
8
+pkgdesc: 'PaganDE — nowoczesne, modułowe środowisko Wayland z pływającymi oknami (własny shell, bez GTK/Qt)'
9
+url: 'https://paganlinux.eu'
10
+arch: x86_64
11
+license:
12
+- MIT
13
+depends:
14
+- labwc-pagan
15
+- pagande-shell
16
+- pipewire
17
+- polkit-gnome
18
+- network-manager-applet
19
+- xdg-desktop-portal
20
+- xdg-desktop-portal-gtk
21
+- papirus-icon-theme
22
+- cantarell-fonts
23
+- xcursor-dmz
24
+- alacritty
25
+- thunar
26
+makedepends: []
27
+source:
28
+- pagande-${pkgver}.tar.gz
29
+sha256sums:
30
+- SKIP
31
+build: |
32
+ cd ${pkgname}
33
+
34
+ # ── Pliki sesji ─────────────────────────────────────────────
35
+ install -dm755 "${PKGDIR}/usr/bin"
36
+ install -m755 session/pagande-session "${PKGDIR}/usr/bin/pagande-session"
37
+
38
+ install -dm755 "${PKGDIR}/usr/share/wayland-sessions"
39
+ install -m644 session/pagande.desktop "${PKGDIR}/usr/share/wayland-sessions/pagande.desktop"
40
+
41
+ # ── Motyw okien (fork labwc) ────────────────────────────────
42
+ install -dm755 "${PKGDIR}/usr/share/themes"
43
+ cp -a config/theme/pagan "${PKGDIR}/usr/share/themes/pagan"
44
+
45
+ # ── Domyślne konfiguracje (sesja rozkłada je do ~/.config) ─
46
+ install -dm755 "${PKGDIR}/etc/xdg/pagande"
47
+ cp -a config/labwc "${PKGDIR}/etc/xdg/pagande/labwc"
48
+ cp -a config/shell "${PKGDIR}/etc/xdg/pagande/shell"
49
+package: |
50
+ true
0
51
new file mode 100644
1
52
Binary files /dev/null and b/de/pagande/pagande-0.1.0.tar.gz differ
2
53
new file mode 100644
@@ -0,0 +1,20 @@
1
+pkgname: hwdata
2
+pkgver: '0.410'
3
+pkgrel: 1
4
+pkgdesc: Hardware identification and configuration data (pci.ids, usb.ids, ...)
5
+url: https://github.com/vcrhonek/hwdata
6
+arch: x86_64
7
+license:
8
+- GPL-2.0-or-later
9
+depends: []
10
+makedepends: []
11
+source:
12
+- https://github.com/vcrhonek/hwdata/archive/refs/tags/v${pkgver}.tar.gz
13
+sha256sums:
14
+- SKIP
15
+build: |
16
+ cd ${pkgname}
17
+
18
+ make install PREFIX=/usr DESTDIR=${PKGDIR}
19
+package: |
20
+ true
0
21
new file mode 100644
@@ -0,0 +1,45 @@
1
+pkgname: labwc-pagan
2
+pkgver: '0.20.2'
3
+pkgrel: 1
4
+pkgdesc: Labwc PaganDE (fork) - lekki kompozytor Wayland z pływającymi oknami, branding PaganDE
5
+url: https://paganlinux.eu
6
+arch: x86_64
7
+license:
8
+- GPL-2.0-only
9
+depends:
10
+- wlroots
11
+- libxkbcommon
12
+- xorg-libdrm
13
+- libxml2
14
+- glib
15
+- cairo
16
+- pango
17
+- libinput
18
+- xorg-pixman
19
+- libpng
20
+- systemd
21
+makedepends:
22
+- meson
23
+- ninja
24
+- wayland
25
+- wayland-protocols
26
+- glib
27
+source:
28
+- labwc-pagan-${pkgver}.tar.gz
29
+sha256sums:
30
+- SKIP
31
+build: |
32
+ cd ${pkgname}
33
+
34
+ # Nasz fork: Xwayland, SVG (librsvg) i ikony z libsfdo wyłączone w v1.
35
+ # Rozjazdy względem upstream: pagan/FORK.md (wersja 0.20.2-pagan,
36
+ # XDG_CURRENT_DESKTOP=PaganDE:wlroots).
37
+ meson setup build --prefix=/usr \
38
+ -Dxwayland=disabled \
39
+ -Dsvg=disabled \
40
+ -Dicon=disabled
41
+
42
+ ninja -C build
43
+ DESTDIR=${PKGDIR} ninja -C build install
44
+package: |
45
+ true
0
46
new file mode 100644
1
47
Binary files /dev/null and b/gui/labwc-pagan/labwc-pagan-0.20.2.tar.gz differ
2
48
new file mode 100644
@@ -0,0 +1,25 @@
1
+pkgname: libdisplay-info
2
+pkgver: '0.4.0'
3
+pkgrel: 1
4
+pkgdesc: EDID and DisplayID parser library (wymagana przez wlroots)
5
+url: https://gitlab.freedesktop.org/emersion/libdisplay-info
6
+arch: x86_64
7
+license:
8
+- MIT
9
+depends:
10
+- xorg-libdrm
11
+makedepends:
12
+- meson
13
+- ninja
14
+source:
15
+- https://gitlab.freedesktop.org/emersion/libdisplay-info/-/archive/${pkgver}/${pkgname}-${pkgver}.tar.gz
16
+sha256sums:
17
+- SKIP
18
+build: |
19
+ cd ${pkgname}
20
+
21
+ meson setup build --prefix=/usr
22
+ ninja -C build
23
+ DESTDIR=${PKGDIR} ninja -C build install
24
+package: |
25
+ true
0
26
new file mode 100644
@@ -0,0 +1,27 @@
1
+pkgname: libliftoff
2
+pkgver: '0.5.0'
3
+pkgrel: 1
4
+pkgdesc: Lightweight KMS plane library (wymagana przez wlroots)
5
+url: https://gitlab.freedesktop.org/emersion/libliftoff
6
+arch: x86_64
7
+license:
8
+- MIT
9
+depends:
10
+- xorg-libdrm
11
+- systemd
12
+makedepends:
13
+- meson
14
+- ninja
15
+- systemd
16
+source:
17
+- https://gitlab.freedesktop.org/emersion/libliftoff/-/archive/v${pkgver}/${pkgname}-v${pkgver}.tar.gz
18
+sha256sums:
19
+- SKIP
20
+build: |
21
+ cd ${pkgname}
22
+
23
+ meson setup build --prefix=/usr
24
+ ninja -C build
25
+ DESTDIR=${PKGDIR} ninja -C build install
26
+package: |
27
+ true
0
28
new file mode 100644
@@ -0,0 +1,33 @@
1
+pkgname: pagande-shell
2
+pkgver: '0.1.1'
3
+pkgrel: 1
4
+pkgdesc: PaganDE shell - własne moduły środowiska Wayland (bar, dock, launcher, wallpaper, idle) - bez GTK/Qt
5
+url: https://paganlinux.eu
6
+arch: x86_64
7
+license:
8
+- MIT
9
+depends:
10
+- wayland
11
+- cairo
12
+- pango
13
+- libxkbcommon
14
+- glib
15
+makedepends:
16
+- meson
17
+- ninja
18
+- wayland
19
+- wayland-protocols
20
+- pango
21
+- gettext
22
+source:
23
+- pagande-shell-${pkgver}.tar.gz
24
+sha256sums:
25
+- SKIP
26
+build: |
27
+ cd ${pkgname}
28
+
29
+ meson setup build --prefix=/usr
30
+ ninja -C build
31
+ DESTDIR=${PKGDIR} ninja -C build install
32
+package: |
33
+ true
0
34
new file mode 100644
1
35
Binary files /dev/null and b/gui/pagande-shell/pagande-shell-0.1.1.tar.gz differ
2
36
new file mode 100644
@@ -0,0 +1,32 @@
1
+pkgname: seatd
2
+pkgver: '0.9.3'
3
+pkgrel: 1
4
+pkgdesc: Minimal seat management daemon and libseat (sesja DRM dla wlroots, backend logind)
5
+url: https://github.com/kennylevinsen/seatd
6
+arch: x86_64
7
+license:
8
+- MIT
9
+depends:
10
+- systemd
11
+makedepends:
12
+- meson
13
+- ninja
14
+- systemd
15
+source:
16
+- https://github.com/kennylevinsen/seatd/archive/refs/tags/${pkgver}.tar.gz
17
+sha256sums:
18
+- SKIP
19
+build: |
20
+ cd ${pkgname}
21
+
22
+ # Budujemy samą bibliotekę libseat (daemon seatd jest zbędny — PaganOS ma
23
+ # systemd-logind, którego libseat używa jako backendu w runtime).
24
+ meson setup build --prefix=/usr \
25
+ -Dserver=disabled \
26
+ -Dexamples=false \
27
+ -Dlibseat-logind=enabled
28
+
29
+ ninja -C build
30
+ DESTDIR=${PKGDIR} ninja -C build install
31
+package: |
32
+ true
0
33
new file mode 100644
@@ -0,0 +1,60 @@
1
+pkgname: wlroots
2
+pkgver: '0.20.2'
3
+pkgrel: 3
4
+pkgdesc: 'Modular Wayland compositor library (fundament labwc; patch PaganDE: zaokrąglone rogi okien + poprawka kanałów BGR888)'
5
+url: https://gitlab.freedesktop.org/wlroots/wlroots
6
+arch: x86_64
7
+license:
8
+- MIT
9
+depends:
10
+- wayland
11
+- xorg-libdrm
12
+- libinput
13
+- libxkbcommon
14
+- xorg-pixman
15
+- mesa
16
+- libdisplay-info
17
+- libliftoff
18
+- seatd
19
+- hwdata
20
+- systemd
21
+makedepends:
22
+- meson
23
+- ninja
24
+- wayland-protocols
25
+- systemd
26
+source:
27
+- https://gitlab.freedesktop.org/wlroots/wlroots/-/archive/${pkgver}/${pkgname}-${pkgver}.tar.gz
28
+- wlroots-0.20.2-rounded-clip.patch
29
+- wlroots-0.20.2-bgr888-read-pixels.patch
30
+sha256sums:
31
+- SKIP
32
+- SKIP
33
+- SKIP
34
+build: |
35
+ cd ${pkgname}
36
+
37
+ # PaganDE: wlr_scene_buffer_set_rounded_clip() — renderer gles2 przycina
38
+ # tekstury do prostokąta z zaokrąglonymi rogami (używane przez labwc-pagan
39
+ # do zaokrąglania treści okien; patrz paganDE/compositor/labwc/pagan/FORK.md).
40
+ patch -p1 -i ../wlroots-0.20.2-rounded-clip.patch
41
+
42
+ # PaganDE: glReadPixels(GL_RGB) dla DRM_FORMAT_BGR888 zwraca bajty R,G,B,
43
+ # a układ BGR888 oczekuje B,G,R — bez tego screencopy/zrzuty miały zamienione
44
+ # kanały czerwony i niebieski (widoczne na llvmpipe, gdzie preferred read
45
+ # format to BGR888).
46
+ patch -p1 -i ../wlroots-0.20.2-bgr888-read-pixels.patch
47
+
48
+ # Renderer gles2 (mesa), backendy DRM + libinput; Xwayland i zarządzanie
49
+ # kolorem wyłączone w v1 PaganDE (patrz paganDE/docs/ARCHITECTURE.md).
50
+ meson setup build --prefix=/usr \
51
+ -Dexamples=false \
52
+ -Drenderers=gles2 \
53
+ -Dbackends=drm,libinput \
54
+ -Dxwayland=disabled \
55
+ -Dcolor-management=disabled
56
+
57
+ ninja -C build
58
+ DESTDIR=${PKGDIR} ninja -C build install
59
+package: |
60
+ true
0
61
new file mode 100644
@@ -0,0 +1,50 @@
1
+From: PaganDE <dev@paganlinux.org>
2
+Subject: [PATCH] gles2: fix R/B swap when reading DRM_FORMAT_BGR888
3
+
4
+GLES2 has no GL_BGR, so wlroots maps DRM_FORMAT_BGR888 to
5
+glReadPixels(GL_RGB, GL_UNSIGNED_BYTE). GL_RGB returns bytes in
6
+R,G,B order while the BGR888 layout expects B,G,R - the resulting
7
+buffer ends up with red and blue swapped.
8
+
9
+On llvmpipe the implementation read format is GL_RGB, so
10
+wlr_output_preferred_read_format() reports BGR888 and screencopy
11
+clients (e.g. our pg-shot) receive a buffer with inverted channels.
12
+
13
+Fix: after reading, swap R and B in place for BGR888 destinations.
14
+
15
+Origin: PaganDE fork of wlroots 0.20.2
16
+---
17
+ render/gles2/texture.c | 16 ++++++++++++++++
18
+ 1 file changed, 16 insertions(+)
19
+
20
+diff --git a/render/gles2/texture.c b/render/gles2/texture.c
21
+index 0000000..0000000 100644
22
+--- a/render/gles2/texture.c
23
+@@ -216,6 +216,22 @@ static bool gles2_texture_read_pixels(struct wlr_texture *wlr_texture,
24
+ }
25
+ }
26
+
27
++ /*
28
++ * DRM_FORMAT_BGR888 has no GLES2 equivalent: we read it as GL_RGB,
29
++ * which returns bytes in R,G,B order, while the BGR888 layout
30
++ * expects B,G,R. Swap the channels so the buffer actually contains
31
++ * BGR data. Otherwise screencopy/screenshot clients see red and
32
++ * blue swapped (visible on llvmpipe, where the preferred read
33
++ * format is BGR888).
34
++ */
35
++ if (drm_fmt->drm_format == DRM_FORMAT_BGR888) {
36
++ for (int32_t i = 0; i < src.height; ++i) {
37
++ unsigned char *row = p + (size_t)i * options->stride;
38
++ for (int32_t x = 0; x < src.width; ++x) {
39
++ unsigned char tmp = row[x * 3];
40
++ row[x * 3] = row[x * 3 + 2];
41
++ row[x * 3 + 2] = tmp;
42
++ }
43
++ }
44
++ }
45
++
46
+ wlr_egl_restore_context(&prev_ctx);
47
+ pop_gles2_debug(texture->renderer);
48
+
49
+ return glGetError() == GL_NO_ERROR;
0
50
new file mode 100644
@@ -0,0 +1,368 @@
1
+diff -ruN a/include/render/gles2.h b/include/render/gles2.h
2
+--- a/include/render/gles2.h 2026-07-07 23:46:09.000000000 +0200
3
+@@ -35,6 +35,12 @@
4
+ GLint tex;
5
+ GLint alpha;
6
+ GLint pos_attrib;
7
++ /* PaganDE: rounded clip */
8
++ GLint rounded;
9
++ GLint box_pos;
10
++ GLint box_size;
11
++ GLint radius;
12
++ GLint corners;
13
+ };
14
+
15
+ struct wlr_gles2_renderer {
16
+diff -ruN a/include/wlr/render/pass.h b/include/wlr/render/pass.h
17
+--- a/include/wlr/render/pass.h 2026-07-07 23:46:09.000000000 +0200
18
+@@ -84,6 +84,14 @@
19
+ WLR_SCALE_FILTER_NEAREST,
20
+ };
21
+
22
++/* PaganDE: rounded corners bitmask (see corner_radius below) */
23
++enum wlr_rounded_corner {
24
++ WLR_ROUNDED_CORNER_TOP_LEFT = 1 << 0,
25
++ WLR_ROUNDED_CORNER_TOP_RIGHT = 1 << 1,
26
++ WLR_ROUNDED_CORNER_BOTTOM_RIGHT = 1 << 2,
27
++ WLR_ROUNDED_CORNER_BOTTOM_LEFT = 1 << 3,
28
++};
29
++
30
+ struct wlr_render_texture_options {
31
+ /* Source texture */
32
+ struct wlr_texture *texture;
33
+@@ -95,6 +103,12 @@
34
+ const float *alpha;
35
+ /* Clip region, leave NULL to disable clipping */
36
+ const pixman_region32_t *clip;
37
++ /* PaganDE: rounded corners. When corner_radius > 0 and rounded_corners != 0,
38
++ * the texture is clipped to a rectangle with rounded corners (bitmask of
39
++ * enum wlr_rounded_corner). Renderers that do not support this feature
40
++ * must ignore these fields. */
41
++ float corner_radius;
42
++ uint32_t rounded_corners;
43
+ /* Transform applied to the source texture */
44
+ enum wl_output_transform transform;
45
+ /* Filtering */
46
+diff -ruN a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h
47
+--- a/include/wlr/types/wlr_scene.h 2026-07-07 23:46:09.000000000 +0200
48
+@@ -215,6 +215,11 @@
49
+
50
+ // True if the underlying buffer is a wlr_single_pixel_buffer_v1
51
+ bool is_single_pixel_buffer;
52
++
53
++ // PaganDE: rounded clip applied when rendering this buffer
54
++ // (see wlr_scene_buffer_set_rounded_clip)
55
++ float clip_radius;
56
++ uint32_t clip_corners;
57
+ // If is_single_pixel_buffer is set, contains the color of the buffer
58
+ // as {R, G, B, A} where the max value of each component is UINT32_MAX
59
+ uint32_t single_pixel_buffer_color[4];
60
+@@ -549,6 +554,16 @@
61
+ enum wl_output_transform transform);
62
+
63
+ /**
64
++ * PaganDE: clip the buffer to a rectangle with rounded corners.
65
++ *
66
++ * radius is the corner radius in output pixels; corners is a bitmask of
67
++ * enum wlr_rounded_corner. Passing radius 0 (or corners 0) disables the clip.
68
++ * Renderers without support (e.g. pixman) ignore this.
69
++ */
70
++void wlr_scene_buffer_set_rounded_clip(struct wlr_scene_buffer *scene_buffer,
71
++ float radius, uint32_t corners);
72
++
73
++/**
74
+ * Sets the opacity of this buffer
75
+ */
76
+ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer,
77
+diff -ruN a/render/gles2/pass.c b/render/gles2/pass.c
78
+--- a/render/gles2/pass.c 2026-07-07 23:46:09.000000000 +0200
79
+@@ -247,6 +247,19 @@
80
+ set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
81
+ set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
82
+
83
++ bool rounded = options->corner_radius > 0 && options->rounded_corners != 0;
84
++ glUniform1f(shader->rounded, rounded ? 1.0f : 0.0f);
85
++ if (rounded) {
86
++ glUniform2f(shader->box_pos, dst_box.x, dst_box.y);
87
++ glUniform2f(shader->box_size, dst_box.width, dst_box.height);
88
++ glUniform1f(shader->radius, options->corner_radius);
89
++ glUniform4f(shader->corners,
90
++ (options->rounded_corners & WLR_ROUNDED_CORNER_TOP_LEFT) ? 1.0f : 0.0f,
91
++ (options->rounded_corners & WLR_ROUNDED_CORNER_TOP_RIGHT) ? 1.0f : 0.0f,
92
++ (options->rounded_corners & WLR_ROUNDED_CORNER_BOTTOM_RIGHT) ? 1.0f : 0.0f,
93
++ (options->rounded_corners & WLR_ROUNDED_CORNER_BOTTOM_LEFT) ? 1.0f : 0.0f);
94
++ }
95
++
96
+ render(&dst_box, options->clip, shader->pos_attrib);
97
+
98
+ glBindTexture(texture->target, 0);
99
+diff -ruN a/render/gles2/renderer.c b/render/gles2/renderer.c
100
+--- a/render/gles2/renderer.c 2026-07-07 23:46:09.000000000 +0200
101
+@@ -652,6 +652,11 @@
102
+ renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex");
103
+ renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha");
104
+ renderer->shaders.tex_rgba.pos_attrib = glGetAttribLocation(prog, "pos");
105
++ renderer->shaders.tex_rgba.rounded = glGetUniformLocation(prog, "u_rounded");
106
++ renderer->shaders.tex_rgba.box_pos = glGetUniformLocation(prog, "u_box_pos");
107
++ renderer->shaders.tex_rgba.box_size = glGetUniformLocation(prog, "u_box_size");
108
++ renderer->shaders.tex_rgba.radius = glGetUniformLocation(prog, "u_radius");
109
++ renderer->shaders.tex_rgba.corners = glGetUniformLocation(prog, "u_corners");
110
+
111
+ renderer->shaders.tex_rgbx.program = prog =
112
+ link_program(renderer, common_vert_src, tex_rgbx_frag_src);
113
+@@ -663,6 +668,11 @@
114
+ renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex");
115
+ renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha");
116
+ renderer->shaders.tex_rgbx.pos_attrib = glGetAttribLocation(prog, "pos");
117
++ renderer->shaders.tex_rgbx.rounded = glGetUniformLocation(prog, "u_rounded");
118
++ renderer->shaders.tex_rgbx.box_pos = glGetUniformLocation(prog, "u_box_pos");
119
++ renderer->shaders.tex_rgbx.box_size = glGetUniformLocation(prog, "u_box_size");
120
++ renderer->shaders.tex_rgbx.radius = glGetUniformLocation(prog, "u_radius");
121
++ renderer->shaders.tex_rgbx.corners = glGetUniformLocation(prog, "u_corners");
122
+
123
+ if (renderer->exts.OES_egl_image_external) {
124
+ renderer->shaders.tex_ext.program = prog =
125
+@@ -675,6 +685,11 @@
126
+ renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex");
127
+ renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha");
128
+ renderer->shaders.tex_ext.pos_attrib = glGetAttribLocation(prog, "pos");
129
++ renderer->shaders.tex_ext.rounded = glGetUniformLocation(prog, "u_rounded");
130
++ renderer->shaders.tex_ext.box_pos = glGetUniformLocation(prog, "u_box_pos");
131
++ renderer->shaders.tex_ext.box_size = glGetUniformLocation(prog, "u_box_size");
132
++ renderer->shaders.tex_ext.radius = glGetUniformLocation(prog, "u_radius");
133
++ renderer->shaders.tex_ext.corners = glGetUniformLocation(prog, "u_corners");
134
+ }
135
+
136
+ pop_gles2_debug(renderer);
137
+diff -ruN a/render/gles2/shaders/tex_external.frag b/render/gles2/shaders/tex_external.frag
138
+--- a/render/gles2/shaders/tex_external.frag 2026-07-07 23:46:09.000000000 +0200
139
+@@ -10,6 +10,51 @@
140
+ uniform samplerExternalOES texture0;
141
+ uniform float alpha;
142
+
143
++
144
++// PaganDE: rounded clip (discard fragments outside the rounded rect)
145
++// GLSL ES 1.00: tylko floaty (bez bitowych operacji); maska rogów w vec4.
146
++uniform float u_rounded;
147
++uniform vec2 u_box_pos;
148
++uniform vec2 u_box_size;
149
++uniform float u_radius;
150
++uniform vec4 u_corners; /* x=TL y=TR z=BR w=BL, 1.0 = zaokrąglony */
151
++
152
++void rounded_clip() {
153
++ if (u_rounded == 0.0) {
154
++ return;
155
++ }
156
++ vec2 p = gl_FragCoord.xy - u_box_pos;
157
++ float r = u_radius;
158
++ /* top-left */
159
++ if (u_corners.x > 0.5) {
160
++ if (p.x <= r && p.y <= r && distance(p, vec2(r, r)) > r) {
161
++ discard;
162
++ }
163
++ }
164
++ /* top-right */
165
++ if (u_corners.y > 0.5) {
166
++ if (p.x >= u_box_size.x - r && p.y <= r
167
++ && distance(p, vec2(u_box_size.x - r, r)) > r) {
168
++ discard;
169
++ }
170
++ }
171
++ /* bottom-right */
172
++ if (u_corners.z > 0.5) {
173
++ if (p.x >= u_box_size.x - r && p.y >= u_box_size.y - r
174
++ && distance(p, vec2(u_box_size.x - r, u_box_size.y - r)) > r) {
175
++ discard;
176
++ }
177
++ }
178
++ /* bottom-left */
179
++ if (u_corners.w > 0.5) {
180
++ if (p.x <= r && p.y >= u_box_size.y - r
181
++ && distance(p, vec2(r, u_box_size.y - r)) > r) {
182
++ discard;
183
++ }
184
++ }
185
++}
186
++
187
+ void main() {
188
++ rounded_clip();
189
+ gl_FragColor = texture2D(texture0, v_texcoord) * alpha;
190
+ }
191
+diff -ruN a/render/gles2/shaders/tex_rgba.frag b/render/gles2/shaders/tex_rgba.frag
192
+--- a/render/gles2/shaders/tex_rgba.frag 2026-07-07 23:46:09.000000000 +0200
193
+@@ -8,6 +8,51 @@
194
+ uniform sampler2D tex;
195
+ uniform float alpha;
196
+
197
++
198
++// PaganDE: rounded clip (discard fragments outside the rounded rect)
199
++// GLSL ES 1.00: tylko floaty (bez bitowych operacji); maska rogów w vec4.
200
++uniform float u_rounded;
201
++uniform vec2 u_box_pos;
202
++uniform vec2 u_box_size;
203
++uniform float u_radius;
204
++uniform vec4 u_corners; /* x=TL y=TR z=BR w=BL, 1.0 = zaokrąglony */
205
++
206
++void rounded_clip() {
207
++ if (u_rounded == 0.0) {
208
++ return;
209
++ }
210
++ vec2 p = gl_FragCoord.xy - u_box_pos;
211
++ float r = u_radius;
212
++ /* top-left */
213
++ if (u_corners.x > 0.5) {
214
++ if (p.x <= r && p.y <= r && distance(p, vec2(r, r)) > r) {
215
++ discard;
216
++ }
217
++ }
218
++ /* top-right */
219
++ if (u_corners.y > 0.5) {
220
++ if (p.x >= u_box_size.x - r && p.y <= r
221
++ && distance(p, vec2(u_box_size.x - r, r)) > r) {
222
++ discard;
223
++ }
224
++ }
225
++ /* bottom-right */
226
++ if (u_corners.z > 0.5) {
227
++ if (p.x >= u_box_size.x - r && p.y >= u_box_size.y - r
228
++ && distance(p, vec2(u_box_size.x - r, u_box_size.y - r)) > r) {
229
++ discard;
230
++ }
231
++ }
232
++ /* bottom-left */
233
++ if (u_corners.w > 0.5) {
234
++ if (p.x <= r && p.y >= u_box_size.y - r
235
++ && distance(p, vec2(r, u_box_size.y - r)) > r) {
236
++ discard;
237
++ }
238
++ }
239
++}
240
++
241
+ void main() {
242
++ rounded_clip();
243
+ gl_FragColor = texture2D(tex, v_texcoord) * alpha;
244
+ }
245
+diff -ruN a/render/gles2/shaders/tex_rgbx.frag b/render/gles2/shaders/tex_rgbx.frag
246
+--- a/render/gles2/shaders/tex_rgbx.frag 2026-07-07 23:46:09.000000000 +0200
247
+@@ -8,6 +8,51 @@
248
+ uniform sampler2D tex;
249
+ uniform float alpha;
250
+
251
++
252
++// PaganDE: rounded clip (discard fragments outside the rounded rect)
253
++// GLSL ES 1.00: tylko floaty (bez bitowych operacji); maska rogów w vec4.
254
++uniform float u_rounded;
255
++uniform vec2 u_box_pos;
256
++uniform vec2 u_box_size;
257
++uniform float u_radius;
258
++uniform vec4 u_corners; /* x=TL y=TR z=BR w=BL, 1.0 = zaokrąglony */
259
++
260
++void rounded_clip() {
261
++ if (u_rounded == 0.0) {
262
++ return;
263
++ }
264
++ vec2 p = gl_FragCoord.xy - u_box_pos;
265
++ float r = u_radius;
266
++ /* top-left */
267
++ if (u_corners.x > 0.5) {
268
++ if (p.x <= r && p.y <= r && distance(p, vec2(r, r)) > r) {
269
++ discard;
270
++ }
271
++ }
272
++ /* top-right */
273
++ if (u_corners.y > 0.5) {
274
++ if (p.x >= u_box_size.x - r && p.y <= r
275
++ && distance(p, vec2(u_box_size.x - r, r)) > r) {
276
++ discard;
277
++ }
278
++ }
279
++ /* bottom-right */
280
++ if (u_corners.z > 0.5) {
281
++ if (p.x >= u_box_size.x - r && p.y >= u_box_size.y - r
282
++ && distance(p, vec2(u_box_size.x - r, u_box_size.y - r)) > r) {
283
++ discard;
284
++ }
285
++ }
286
++ /* bottom-left */
287
++ if (u_corners.w > 0.5) {
288
++ if (p.x <= r && p.y >= u_box_size.y - r
289
++ && distance(p, vec2(r, u_box_size.y - r)) > r) {
290
++ discard;
291
++ }
292
++ }
293
++}
294
++
295
+ void main() {
296
++ rounded_clip();
297
+ gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha;
298
+ }
299
+diff -ruN a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c
300
+--- a/types/scene/wlr_scene.c 2026-07-07 23:46:09.000000000 +0200
301
+@@ -1124,6 +1124,18 @@
302
+ }
303
+ }
304
+
305
++void wlr_scene_buffer_set_rounded_clip(struct wlr_scene_buffer *scene_buffer,
306
++ float radius, uint32_t corners) {
307
++ if (scene_buffer->clip_radius == radius
308
++ && scene_buffer->clip_corners == corners) {
309
++ return;
310
++ }
311
++ assert(radius >= 0);
312
++ scene_buffer->clip_radius = radius;
313
++ scene_buffer->clip_corners = corners;
314
++ scene_node_update(&scene_buffer->node, NULL);
315
++}
316
++
317
+ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer,
318
+ float opacity) {
319
+ if (scene_buffer->opacity == opacity) {
320
+@@ -1499,6 +1511,26 @@
321
+ case WLR_SCENE_NODE_BUFFER:;
322
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
323
+
324
++ bool rounded = scene_buffer->clip_radius > 0
325
++ && scene_buffer->clip_corners != 0;
326
++ if (rounded) {
327
++ /* Corner areas are no longer opaque: exclude them from the
328
++ * opaque region so occlusion and blending stay correct. */
329
++ int r = (int)scene_buffer->clip_radius;
330
++ pixman_region32_t corners;
331
++ pixman_region32_init(&corners);
332
++ pixman_region32_union_rect(&corners, &corners,
333
++ x, y, r, r);
334
++ pixman_region32_union_rect(&corners, &corners,
335
++ x + dst_box.width - r, y, r, r);
336
++ pixman_region32_union_rect(&corners, &corners,
337
++ x + dst_box.width - r, y + dst_box.height - r, r, r);
338
++ pixman_region32_union_rect(&corners, &corners,
339
++ x, y + dst_box.height - r, r, r);
340
++ pixman_region32_subtract(&opaque, &opaque, &corners);
341
++ pixman_region32_fini(&corners);
342
++ }
343
++
344
+ if (scene_buffer->is_single_pixel_buffer) {
345
+ // Render the buffer as a rect, this is likely to be more efficient
346
+ wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){
347
+@@ -1544,9 +1576,11 @@
348
+ .dst_box = dst_box,
349
+ .transform = transform,
350
+ .clip = &render_region,
351
++ .corner_radius = scene_buffer->clip_radius,
352
++ .rounded_corners = scene_buffer->clip_corners,
353
+ .alpha = &scene_buffer->opacity,
354
+ .filter_mode = scene_buffer->filter_mode,
355
+- .blend_mode = !data->output->scene->calculate_visibility ||
356
++ .blend_mode = rounded || !data->output->scene->calculate_visibility ||
357
+ !pixman_region32_empty(&opaque) ?
358
+ WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE,
359
+ .transfer_function = scene_buffer->transfer_function,