From 477d38d92851d9093ff7db8e6e158fff1380b3dd Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Sat, 30 Aug 2025 10:43:33 -0400 Subject: [PATCH 1/4] Wallpaper: shaders improvements with more parameters and new Stripes shader --- Commons/Settings.qml | 1 + Modules/Background/Background.qml | 56 ++++++--- Modules/SettingsPanel/Tabs/WallpaperTab.qml | 99 +++++++++------ Services/WallpaperService.qml | 6 +- Shaders/frag/wp_disc.frag | 11 +- Shaders/frag/wp_fade.frag | 9 +- Shaders/frag/wp_stripes.frag | 126 ++++++++++++++++++++ Shaders/frag/wp_wipe.frag | 18 ++- Shaders/qsb/wp_disc.frag.qsb | Bin 2398 -> 2532 bytes Shaders/qsb/wp_fade.frag.qsb | Bin 1548 -> 1470 bytes Shaders/qsb/wp_stripes.frag.qsb | Bin 0 -> 5031 bytes Shaders/qsb/wp_wipe.frag.qsb | Bin 2629 -> 2677 bytes 12 files changed, 260 insertions(+), 66 deletions(-) create mode 100644 Shaders/frag/wp_stripes.frag create mode 100644 Shaders/qsb/wp_stripes.frag.qsb diff --git a/Commons/Settings.qml b/Commons/Settings.qml index df60967..59f78e0 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -170,6 +170,7 @@ Singleton { property int randomIntervalSec: 300 // 5 min property int transitionDuration: 1500 // 1500 ms property string transitionType: "random" + property real transitionEdgeSmoothness: 0.05 property list monitors: [] } diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml index 780e17b..df13fc6 100644 --- a/Modules/Background/Background.qml +++ b/Modules/Background/Background.qml @@ -21,17 +21,20 @@ Variants { property bool firstWallpaper: true property string transitionType: 'fade' property bool transitioning: false - property real transitionProgress: 0.0 + property real transitionProgress: 0 + property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness readonly property var allTransitions: WallpaperService.allTransitions // Wipe direction: 0=left, 1=right, 2=up, 3=down property real wipeDirection: 0 - property real wipeSmoothness: 0.05 // Disc property real discCenterX: 0.5 property real discCenterY: 0.5 - property real discSmoothness: 0.05 + + // Stripe + property real stripesCount: 16 + property real stripesAngle: 0 // External state management property string servicedWallpaper: WallpaperService.getWallpaper(modelData.name) @@ -58,7 +61,8 @@ Variants { transitionType = 'fade' } - //Logger.log("Background", "Using transition:", transitionType) + Logger.log("Background", "New wallpaper: ", servicedWallpaper, "On:", modelData.name, "Transition:", transitionType) + switch (transitionType) { case "none": setWallpaperImmediate(servicedWallpaper) @@ -84,6 +88,11 @@ Variants { discCenterY = Math.random() setWallpaperWithTransition(servicedWallpaper) break + case "stripes": + stripesCount = Math.round(Math.random() * 24 + 2) + stripesAngle = Math.random() * 360 + setWallpaperWithTransition(servicedWallpaper) + break default: setWallpaperWithTransition(servicedWallpaper) break @@ -134,7 +143,7 @@ Variants { property variant source1: currentWallpaper property variant source2: nextWallpaper - property real fade: transitionProgress + property real progress: root.transitionProgress fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_fade.frag.qsb") } @@ -146,9 +155,10 @@ Variants { property variant source1: currentWallpaper property variant source2: nextWallpaper - property real progress: transitionProgress - property real direction: wipeDirection - property real smoothness: wipeSmoothness + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real direction: root.wipeDirection + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_wipe.frag.qsb") } @@ -160,14 +170,32 @@ Variants { property variant source1: currentWallpaper property variant source2: nextWallpaper - property real progress: transitionProgress - property real centerX: discCenterX - property real centerY: discCenterY - property real smoothness: discSmoothness - property real aspectRatio: width / height + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real aspectRatio: root.width / root.height + property real centerX: root.discCenterX + property real centerY: root.discCenterY + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_disc.frag.qsb") } + // Diagonal stripes transition shader + ShaderEffect { + id: stripesShader + anchors.fill: parent + visible: transitionType === 'stripes' + + property variant source1: currentWallpaper + property variant source2: nextWallpaper + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real aspectRatio: root.width / root.height + property real stripeCount: root.stripesCount + property real angle: root.stripesAngle + + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_stripes.frag.qsb") + } + // Animation for the transition progress NumberAnimation { id: transitionAnimation @@ -176,7 +204,7 @@ Variants { from: 0.0 to: 1.0 duration: Settings.data.wallpaper.transitionDuration ?? 1000 - easing.type: transitionType.startsWith('wipe_') ? Easing.InOutCubic : Easing.OutQuad + easing.type: Easing.InOutCubic //transitionType.startsWith('wipe_') ? Easing.InOutCubic : Easing.OutQuad onFinished: { // Swap images after transition completes currentWallpaper.source = nextWallpaper.source diff --git a/Modules/SettingsPanel/Tabs/WallpaperTab.qml b/Modules/SettingsPanel/Tabs/WallpaperTab.qml index 546a4ca..71dd82e 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperTab.qml @@ -88,47 +88,14 @@ ColumnLayout { // Random Wallpaper NToggle { label: "Random Wallpaper" - description: "Automatically select random wallpapers from the folder." + description: "Schedule random wallpaper changes at regular intervals." checked: Settings.data.wallpaper.randomEnabled onToggled: checked => Settings.data.wallpaper.randomEnabled = checked } - // Transition Type - NComboBox { - label: "Transition Type" - description: "Animation type when switching between wallpapers." - model: WallpaperService.transitionsModel - currentKey: Settings.data.wallpaper.transitionType - onSelected: key => Settings.data.wallpaper.transitionType = key - } - - // Transition Duration - ColumnLayout { - NLabel { - label: "Transition Duration" - description: "Duration of transition animations in seconds." - } - - RowLayout { - spacing: Style.marginL * scaling - NSlider { - Layout.fillWidth: true - from: 100 - to: 5000 - stepSize: 100 - value: Settings.data.wallpaper.transitionDuration - onMoved: Settings.data.wallpaper.transitionDuration = value - cutoutColor: Color.mSurface - } - NText { - text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(2) + "s" - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - } - } - } - - // Interval (slider + H:M inputs) + // Interval ColumnLayout { + visible: Settings.data.wallpaper.randomEnabled RowLayout { NLabel { label: "Wallpaper Interval" @@ -224,6 +191,66 @@ ColumnLayout { } } } + + // Transition Type + NComboBox { + label: "Transition Type" + description: "Animation type when switching between wallpapers." + model: WallpaperService.transitionsModel + currentKey: Settings.data.wallpaper.transitionType + onSelected: key => Settings.data.wallpaper.transitionType = key + } + + // Transition Duration + ColumnLayout { + NLabel { + label: "Transition Duration" + description: "Duration of transition animations in seconds." + } + + RowLayout { + spacing: Style.marginL * scaling + NSlider { + Layout.fillWidth: true + from: 100 + to: 5000 + stepSize: 100 + value: Settings.data.wallpaper.transitionDuration + onMoved: Settings.data.wallpaper.transitionDuration = value + cutoutColor: Color.mSurface + } + NText { + text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(2) + "s" + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + } + } + } + + // Edge Smoothness + ColumnLayout { + NLabel { + label: "Transition Edge Smoothness" + description: "Duration of transition animations in seconds." + } + + RowLayout { + spacing: Style.marginL * scaling + NSlider { + Layout.fillWidth: true + from: 0.0 + to: 1.0 + value: Settings.data.wallpaper.transitionEdgeSmoothness + onMoved: Settings.data.wallpaper.transitionEdgeSmoothness = value + cutoutColor: Color.mSurface + } + NText { + text: Math.round(Settings.data.wallpaper.transitionEdgeSmoothness * 100) + "%" + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + } + } + } + + } // Reusable component for interval preset chips diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index 472e317..403153a 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -31,6 +31,10 @@ Singleton { key: "disc" name: "Disc" } + ListElement { + key: "stripes" + name: "Stripes" + } ListElement { key: "wipe_left" name: "Wipe Left" @@ -149,7 +153,7 @@ Singleton { return } - Logger.log("Wallpaper", "setWallpaper on", screenName, ": ", path) + //Logger.log("Wallpaper", "setWallpaper on", screenName, ": ", path) var wallpaperChanged = false diff --git a/Shaders/frag/wp_disc.frag b/Shaders/frag/wp_disc.frag index 292ce59..4bde549 100644 --- a/Shaders/frag/wp_disc.frag +++ b/Shaders/frag/wp_disc.frag @@ -1,3 +1,4 @@ +// ===== wp_disc.frag ===== #version 450 layout(location = 0) in vec2 qt_TexCoord0; @@ -12,7 +13,7 @@ layout(std140, binding = 0) uniform buf { float progress; // Transition progress (0.0 to 1.0) float centerX; // X coordinate of disc center (0.0 to 1.0) float centerY; // Y coordinate of disc center (0.0 to 1.0) - float smoothness; // Edge smoothness (0.01 to 0.5, default 0.05) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) float aspectRatio; // Width / Height of the screen } ubuf; @@ -21,6 +22,10 @@ void main() { vec4 color1 = texture(source1, uv); // Current (old) wallpaper vec4 color2 = texture(source2, uv); // Next (new) wallpaper + // Map smoothness from 0.0-1.0 to 0.001-0.5 range + // Using a non-linear mapping for better control + float mappedSmoothness = mix(0.001, 0.5, ubuf.smoothness * ubuf.smoothness); + // Adjust UV coordinates to compensate for aspect ratio // This makes distances circular instead of elliptical vec2 adjustedUV = vec2(uv.x * ubuf.aspectRatio, uv.y); @@ -39,7 +44,7 @@ void main() { // Scale progress to cover the maximum distance // Add extra range for smoothness to ensure complete coverage // Adjust smoothness for aspect ratio to maintain consistent visual appearance - float adjustedSmoothness = ubuf.smoothness * max(1.0, ubuf.aspectRatio); + float adjustedSmoothness = mappedSmoothness * max(1.0, ubuf.aspectRatio); float radius = ubuf.progress * (maxDist + adjustedSmoothness); // Use smoothstep for a smooth edge transition @@ -49,4 +54,4 @@ void main() { fragColor = mix(color2, color1, factor); fragColor *= ubuf.qt_Opacity; -} \ No newline at end of file +} diff --git a/Shaders/frag/wp_fade.frag b/Shaders/frag/wp_fade.frag index 10c3a63..24917a5 100644 --- a/Shaders/frag/wp_fade.frag +++ b/Shaders/frag/wp_fade.frag @@ -7,16 +7,13 @@ layout(binding = 2) uniform sampler2D source2; layout(std140, binding = 0) uniform buf { mat4 qt_Matrix; float qt_Opacity; - float fade; + float progress; }; void main() { vec4 color1 = texture(source1, qt_TexCoord0); vec4 color2 = texture(source2, qt_TexCoord0); - // Smooth cross-fade using smoothstep for better visual quality - float smoothFade = smoothstep(0.0, 1.0, fade); - - // Mix the two textures based on fade value - fragColor = mix(color1, color2, smoothFade) * qt_Opacity; + // Mix the two textures based on progress value + fragColor = mix(color1, color2, progress) * qt_Opacity; } \ No newline at end of file diff --git a/Shaders/frag/wp_stripes.frag b/Shaders/frag/wp_stripes.frag new file mode 100644 index 0000000..7d12b30 --- /dev/null +++ b/Shaders/frag/wp_stripes.frag @@ -0,0 +1,126 @@ +// ===== wp_stripes.frag ===== +#version 450 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source1; // Current wallpaper +layout(binding = 2) uniform sampler2D source2; // Next wallpaper + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float progress; // Transition progress (0.0 to 1.0) + float stripeCount; // Number of stripes (default 12.0) + float angle; // Angle of stripes in degrees (default 30.0) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) + float aspectRatio; // Width / Height of the screen +} ubuf; + +void main() { + vec2 uv = qt_TexCoord0; + vec4 color1 = texture(source1, uv); // Current (old) wallpaper + vec4 color2 = texture(source2, uv); // Next (new) wallpaper + + // Map smoothness from 0.0-1.0 to 0.001-0.1 range + // Using a non-linear mapping for better control at low values + float mappedSmoothness = mix(0.001, 0.1, ubuf.smoothness * ubuf.smoothness); + + // Use values directly without forcing defaults + float stripes = (ubuf.stripeCount > 0.0) ? ubuf.stripeCount : 12.0; + float angleRad = radians(ubuf.angle); + float edgeSmooth = mappedSmoothness; + + // Create a coordinate system for stripes based on angle + // At 0°: vertical stripes (divide by x) + // At 45°: diagonal stripes + // At 90°: horizontal stripes (divide by y) + + // Transform coordinates based on angle + float cosA = cos(angleRad); + float sinA = sin(angleRad); + + // Project the UV position onto the stripe direction + // This gives us the position along the stripe direction + float stripeCoord = uv.x * cosA + uv.y * sinA; + + // Perpendicular coordinate (for edge movement) + float perpCoord = -uv.x * sinA + uv.y * cosA; + + // Calculate the range of perpCoord based on angle + // This determines how far edges need to travel to fully cover the screen + float minPerp = min(min(0.0 * -sinA + 0.0 * cosA, 1.0 * -sinA + 0.0 * cosA), + min(0.0 * -sinA + 1.0 * cosA, 1.0 * -sinA + 1.0 * cosA)); + float maxPerp = max(max(0.0 * -sinA + 0.0 * cosA, 1.0 * -sinA + 0.0 * cosA), + max(0.0 * -sinA + 1.0 * cosA, 1.0 * -sinA + 1.0 * cosA)); + + // Determine which stripe we're in + float stripePos = stripeCoord * stripes; + int stripeIndex = int(floor(stripePos)); + + // Determine if this is an odd or even stripe + bool isOddStripe = (stripeIndex % 2) == 1; + + // Calculate the progress for this specific stripe with wave delay + // Use absolute stripe position for consistent delay across all stripes + float normalizedStripePos = clamp(stripePos / stripes, 0.0, 1.0); + + // Reduced delay factor and better scaling to match other shaders' timing + float maxDelay = 0.15; // Maximum delay for the last stripe + float stripeDelay = normalizedStripePos * maxDelay; + + // Ensure all stripes complete when progress reaches 1.0 + // without making the overall animation appear faster + float stripeProgress = clamp((ubuf.progress - stripeDelay) / (1.0 - maxDelay), 0.0, 1.0); + + // Apply smooth easing + stripeProgress = smoothstep(0.0, 1.0, stripeProgress); + + // Use the perpendicular coordinate for edge comparison + float yPos = perpCoord; + + // Calculate edge position for this stripe + // Use the actual perpendicular coordinate range for this angle + float perpRange = maxPerp - minPerp; + float margin = edgeSmooth * 2.0; // Simplified margin calculation + float edgePosition; + if (isOddStripe) { + // Odd stripes: edge moves from max to min + edgePosition = maxPerp + margin - stripeProgress * (perpRange + margin * 2.0); + } else { + // Even stripes: edge moves from min to max + edgePosition = minPerp - margin + stripeProgress * (perpRange + margin * 2.0); + } + + // Determine which wallpaper to show based on rotated position + float mask; + if (isOddStripe) { + // Odd stripes reveal new wallpaper from bottom + mask = smoothstep(edgePosition - edgeSmooth, edgePosition + edgeSmooth, yPos); + } else { + // Even stripes reveal new wallpaper from top + mask = 1.0 - smoothstep(edgePosition - edgeSmooth, edgePosition + edgeSmooth, yPos); + } + + // Mix the wallpapers + fragColor = mix(color1, color2, mask); + + // Force exact values at start and end to prevent any bleed-through + if (ubuf.progress <= 0.0) { + fragColor = color1; // Only show old wallpaper at start + } else if (ubuf.progress >= 1.0) { + fragColor = color2; // Only show new wallpaper at end + } else { + // Add manga-style edge shadow only during transition + float edgeDist = abs(yPos - edgePosition); + float shadowStrength = 1.0 - smoothstep(0.0, edgeSmooth * 2.5, edgeDist); + shadowStrength *= 0.2 * (1.0 - abs(stripeProgress - 0.5) * 2.0); + fragColor.rgb *= (1.0 - shadowStrength); + + // Add slight vignette during transition for dramatic effect + float vignette = 1.0 - ubuf.progress * 0.1 * (1.0 - abs(stripeProgress - 0.5) * 2.0); + fragColor.rgb *= vignette; + } + + fragColor *= ubuf.qt_Opacity; +} \ No newline at end of file diff --git a/Shaders/frag/wp_wipe.frag b/Shaders/frag/wp_wipe.frag index f6db80e..10b948a 100644 --- a/Shaders/frag/wp_wipe.frag +++ b/Shaders/frag/wp_wipe.frag @@ -1,3 +1,5 @@ + +// ===== wp_wipe.frag ===== #version 450 layout(location = 0) in vec2 qt_TexCoord0; @@ -11,7 +13,7 @@ layout(std140, binding = 0) uniform buf { float qt_Opacity; float progress; // Transition progress (0.0 to 1.0) float direction; // 0=left, 1=right, 2=up, 3=down - float smoothness; // Edge smoothness (0.01 to 0.5, default 0.05) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) } ubuf; void main() { @@ -19,37 +21,41 @@ void main() { vec4 color1 = texture(source1, uv); // Current (old) wallpaper vec4 color2 = texture(source2, uv); // Next (new) wallpaper + // Map smoothness from 0.0-1.0 to 0.001-0.5 range + // Using a non-linear mapping for better control + float mappedSmoothness = mix(0.001, 0.5, ubuf.smoothness * ubuf.smoothness); + float edge = 0.0; float factor = 0.0; // Extend the progress range to account for smoothness // This ensures the transition completes fully at the edges - float extendedProgress = ubuf.progress * (1.0 + 2.0 * ubuf.smoothness) - ubuf.smoothness; + float extendedProgress = ubuf.progress * (1.0 + 2.0 * mappedSmoothness) - mappedSmoothness; // Calculate edge position based on direction // As progress goes from 0 to 1, we reveal source2 (new wallpaper) if (ubuf.direction < 0.5) { // Wipe from right to left (new image enters from right) edge = 1.0 - extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.x); fragColor = mix(color1, color2, factor); } else if (ubuf.direction < 1.5) { // Wipe from left to right (new image enters from left) edge = extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.x); fragColor = mix(color2, color1, factor); } else if (ubuf.direction < 2.5) { // Wipe from bottom to top (new image enters from bottom) edge = 1.0 - extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.y); fragColor = mix(color1, color2, factor); } else { // Wipe from top to bottom (new image enters from top) edge = extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.y); fragColor = mix(color2, color1, factor); } diff --git a/Shaders/qsb/wp_disc.frag.qsb b/Shaders/qsb/wp_disc.frag.qsb index 485186eb3b9d20a42aabf0e0bd11c919b0f55384..9c66bfb94229f7df7cd1831b23c20033749d497a 100644 GIT binary patch literal 2532 zcmV}sVPyep=d%yIwXJ&sRjYcbQ8`>nN-g9K_z4N<|xpVj4 z*^!B89}%U9Xb%ycq$k9wO3UO>hvvv9p8~2;o`^<>i2jL-5-y?O2-PVdiyBh6DaB*; zuM{1pb&9A?M0+Xf-mhq=327kq@E%#jDIk|RM6@4>>l*qEb#oLV3)CiyY;uXyGZ?+D zyfd#is7(R+)S!SI3MquZ{4jw-WRpW4adIe-ZVUMBoxxWDzdH0hIUY|>g z!-S5LMIp7xA)7eeml1QxhuHEkF?ERrKSUw<6j31EEf7%#Yq%$QRyw?>!DaS**J~W# zwH|B#pGQYdPY8GR(_Z=mxNp*kbZ|e7%kNuvm+t05UdxrHMJm%F8kIOPc9^7z3y|~6CJ=Ucs zkjpynG`hmVA>tdyG4?^wr@=c$Df$pP#GKB6{s`+S9QYjY1iYZnX|#|p zYI73f7YzMnQ#>_N;&&|8MS7qEAM#-yC8SpxHS8j*7= zBVYDu`61T-utA^DXt7?WG+M}?HRu@wf777v81y59{@kE#gLV!23Fuio4+oIrS7}tv z?EuC^P98-*D(o@L{}GMJcpeAl7~~Iwr{v#8{wn!*4Ec9~c?a?*z`H?XGLL?S-1`VR z#9C*-SD48J%oOs)VhC)yjS!s0FD z7*qOJ65_uKzQX(}0aF2A`TlG8UeSD5(|lM3M)>duV@g*Qbx6^_K^;=-Zvi9t2WVVk z)}i|oj0?S=gRk_6P1Y%G9!^3m>) z6)iSvLeC4>@PY9(yKU(4Vb=vu=@2g{u11H@sn&)A6Ka^qkl8f+cnZv>h7s>6H?Lhn-abM!W)`?u&Fi!>p}zXnhL2)knJ8?Cl}1zlgm?iR+? zn*0uH@%L8oJKe4lT4hO8B~9h={`xa*hG&&%m_w$pRy>i=YPFkAP@HF>9|g8kD8B+wET_|(R`AUA8f??Ci(+QWPG`>dgIZpU z`EHGgP-i9)6p4y9*{lJ@Sc%zw(+>)4mT{-UqrfT7XW&ehMVpf*vY3D@YREVgEvwyj zYD?WPv02t~JDGegpD*O&t8}q+`C@6ha6UhM{?g@AzEr$?;nKzFEX(Jnvoc({UYOaG z$>}*(WLE8Q6mqBb;31opoJ_Qt>nIs*S%jatXGx-Sa*?~@LJp~rtfmK8?VEVb4LO^Y zUzTS(nVxG|!Taw|CIFLctPa{(&9VV~t4XqKmNgx( z!8bB8Ov>wQSNCL}l$vmV9+;^yCEY3H^4XXBA6PXv5*j2*J-lp+DL>em1dDwt>XyxY zvC45Ch1_Xpp!RHnY*yJ!Qk<2G4E2p>_0zy=%v;>53Ptq%UJR|lv6&+4G- zC2a-$h{wCOdSEr?LVQoX@DAj{ah= zNArX)1L}(nhsg9oYi*O+5}67Md@BvufJE~ z0A5vfT8}puVOxsJ^%A$Z!>T}4QN(q0DA2?77W?!ThUhIO>J{j8$Sv;L@ifgop^x=9 z3-r*X#XI*OR_6jg46Ap%cEpiJ3EJ2UTfVt6x3F-3J|BlFLB~SG4JDdcHp7-4R+p`y z;c%#sJxc(peT@s|hqNx}?KUrjJ{JcrV=jexexbE91lN%4274zbA5AA{@8syCF3&#d z*@3K`GC8_MCkeMb$V3ss0vTGuVePJJw7bFb5pQA#N)rpY z)o`k=xL1XuuRzV}h+IP5=+&8+kzhL3;7cL~>71M%0Ii(L&g<|f@C*gd^=_WRFTS|O zw@&o>2wr|qhZf#Ibqwf9VVrc4$Hf} z|A+Ka1TRG}@*+UPxPkZ1K##<-a&8elZqYw{(e!|SyRn@ zho<^{{jyNELOwI|zxaJ2(b9kLE5mDUuRXVZ+`l&XMw@9RyjI4~n(<=UFPQXlskJFT u?oOgTjmbiRw@u@X6aQCi literal 2398 zcmV-k38D4?04eu)ob6iccN51Izu2-3!aNe5d6Z7#CXpSX$g0W1LO@~@Ac2}-hv2C0 zcD-833(~IGoi!rFJ?ZJ`*M96@(m$?WdfIbmeJialAg&nb$z`6S7uP`S-&Rm0)BHG{3J!xQOhA|hx{RzZjTc> zO*Tc;C6^qMbVo(ZqX1$X{*%ciUC*I^SsYAz8eG9 z;n~TFxfyonFdd{nfcpwfC%R87G8MHa~$cWVm zno$0{MU%=0{(YJzl}L{83<;{~8@q zF`UjwTur}0lk^H=&GN7Ib1AXMG_NliG_NN=rzl6iz+CuwAGDr7>-#1AVIR&?PQ~g7 z{5?m9RBYeIc-Dt?{K{C@D(H8>V;|my4+r1_)9)Gb1z@KQAB!nGZ}Lfl{s=xY&2m@O zjDEPR$G=6sXnNaN!w|ZTz+a}Llzv@`Kcp!or)v^p{!SB0cNO_?$XI_~-(xBIj6pO0 z%@kcWXkL$MioTqpZ>8vs6up_E?G){St|7l?k+YX*Qq5%+W1M3rkzX2n3iJJjrd0e+ z19J-U$H3F_uOT0`{Oc+CH-LE^@@K&Nl%|xg0{C;#F^&B#fZ^PkN4~vhU>3mtGfg3{ z61fFK_j%|p8geD@Z_||W_XEh5;NP3zY1?iQA44#hnMdX{NYsj}*no;^LAv^t1ZpD!MJVW<2@c#t4>)7Mh47nQ__PzjL+xrr-Ul?<~i8&YG&zG3I|K6-_}ce7@ck>phjqh;HDK6>TNu;2zQ+F7^j++Kz5d?-!~9vARv3NE`wZi( zcLRLwcO8CzYv}$h_!_g75#Qf|ujgSI^SFv}9sA!yrv)E2!PEL3=-)Q(vJ4fr06yWc-vL0WiLS>7tg;(m_Q( zM64EJBLIed^f1n{To)z!hNffm6|H+0b^JEXsPRYOnIE>xV?Ed{L2nE^+l?@;*W^2_ z$z#LT6Y#Y!-=kmt1vPvRyeEcE=KX^X(8tM1CWkZZ0W1xUH@Uyf?Yd#)1-@7;m-F4w zbv#Awc&(kTXto1eR!7*LFRS@{B*WN|Vmof;pX7xQqGQV?@laZ~Y#DmJs^TdP%v|f* zjwc_R+-?}OLN|&`p5yw`4eyPzHceL42?Dv}r)2G@>pJp|Exn+c->c^HvF|m5u){8j zcsp(ytYjjQ-RZX7u(B$mAPyaOvHA=^rJB$0+Tml*Z;4&ksqoA;+}=tMgpD$f1ztmN zs0;H5ibBP^qLzYUY)LplI|vs=O-Q#Vl zttaJkUE0zW7El(7s)>#mJx*_BNblkpy_HP8jLt{WmY$PLGhMgMvEky4p0?wuMxU(ZvKl#>fW!>7F2<^VE?beIid)jW; z9cey^y7I@R;u@m?&ai!H&Q1=B<+@KdHh@N+M#MuGZCeXO+4822_wUFq9IU3 z0x~%zGXO?yQ*F?dapm#Zj@+=&4jg|V&Z;}EZ0>_NsAa@qvl>h($ diff --git a/Shaders/qsb/wp_fade.frag.qsb b/Shaders/qsb/wp_fade.frag.qsb index e06417c6f07afa3020f1dea72d0ef13311f0fd73..89641d8b0148d85f48da59479b767e133b282537 100644 GIT binary patch literal 1470 zcmV;v1wr}%024NNob6a`Z`)K9eww7s=8e6-c5inpjAW}q>U9V;Yc42hjxUzb!<{{SBlpZG!i75)TY5a?VVCpT`|Zh#OUu$0E<_&Lu#_c`a-P5~ST zPy#Ro;0oSA!NDec46up_5hPlu11JE%AADfKK?LW}hJ=fb4({skl>W%j8Ehj#Tc0_2 z02{y)15?!WX@1Y)LO~+H0Kf@GJT}m0vXcuOvW`AncnF}d%S%4aa?2lQ(1%2X4kUcU zh&f>Gpn&Wkkxn5-gai^Ed@KN{@V<#+-!Tri4Y*eP?;bP!-5R+{FN%e^GNp3@$1u;{ zvnc2UPhduWfAwtRSuIwr+TuehKThciq?vM-A#y}|gaJa=WjPWCMOuAqb)e2w;VlQp}+Yo_}y z;TErdlR0j&9^|`-lGamj4)@u8Fi!|#|>+*VOdES zv?JLgKH)sD4Ntmg?DqOyU)pOTjuPql3ymWHM6u(_-5~6U9pAGli%oy98bz{Ir)yEr z5+qBd${X16y+z?g-AFEoWug3mN~CYERd~VqF@|a*7h_L|u_wlKy3Uum#(n9BQFZP5zgV$DYW07Wbd`QK zs`S&SN)P|TRSLpMV-zJSoq@J=JFC>Y?><`ZKUjy3)i8s9aH8n(MK68X6YN)SJ88Fl zspVV}((eSZ@?~Yap6ygd-#b0`+eoI>SR5?U63(!GVoCH}@4Kkoj(t_}hRN&FF_M$X zK^ONyvyJ3|wcTo3{LSos^e9bcB`dX=yP_O6(u!{=SNXzWl*4^sqGLiI#A}c7E*!vX zPvj-E6)RT-Ub@A6tC`ov`;A9t?ev#GJ#DjiOny*SzTX$xmHi5 zn!vH1s2qv0tXL8oubfR+c6`MYy6p+zjP*W84!fVlt)o!0#D zMXHtFU`F??W;1&f9164z;DQEbw;U$g==Hm#1-qGT9AxJ~#FX{%XtVTL7H{_&hfaIS zkv;M>^SlVc+ItN97=?W(MU?CONzcP{nWLYG#;8Q;gwTjgYXSHXnR`s~|04L`O1WRm Yge99kx%qnMxa?PVK3*Sx0@5c_2-dd!CjbBd literal 1548 zcmV+n2J`s<02HBkob6cKZrer>9ZPcTFlp}hq{$|3Q#w%@iE)a+iroZu(*#K1AU4th zfzK$9~(b9VO3&XN`Y z90q_2089YD1$YetJXiw`eOQ775=fy1H2^390Q^A*j5(0t6oin14{aITk>QH|z|cdm z0Xc;7&V#$)0>BdkQLjQh7Cr<1i(4z_aT|v5E(HdN&GGCkM!6={9{ z`30!JhlI!ZT_sJ?hd8A}1+Gy(S82>^q!0SeLPhG$D5u+G-!SGdL-(5R6WSA|h!1U> zd^+ACzRzG%#_AqM%x_SV>!(5WIBamCea5g+k4t1PKt<|)j5J3Zbv;Fzeg%^fcbYKh zUxc#cKW)UHBh7XE`9k~!!knl0Gvs>&lTwS1NUs~jfx3K5{-ZE0?VB|JI@RwAVUX_< zJ;zuaZgW} zzBc)OhY7f^I-^2+&;;#s)Giah&TR2Ci;|etYc;Ev@*tAdX4Ky7v9Oc)qOr>z#iC(Z znMm_Mu#G&l-dKz=*7e04do8@@zDT2fL;55GI#+sr5Q%Nm8~QEYuyz`jmB&$-q+R4< z-d)%5r~xy-+w1VuU1nL5rva}w4ges2~rH%Y|iW531O0vVa$J-g=A zs;rKGyt}T00#R7I<2_FO_OdVhW^J71!-r4sK`-2cn^{$>5qDC^I|vflfTmNkmUHENOz)n#Gox0i64zkTz*d+Yua zjXOyEZ|hlnJL@k}be{`0@-XD7tie1>c{|DkPwkDGmM2ZV^Sb^wNvb9>*Pp{7yg|X) zJnQ+vw@DagT-d?rj0s7}6rgjvpg8p+$l0D%t7T1Zkq=ZNi+XLCc?%qWRV|TK;S0_@ zLV2`sOmv3my?EUb-ufQA?pR(#Tbb}h6sYMH*QWVwbb(TTt-g5r@|CxgCRye^jeEJE za*SbP^K9*lua{O#> zE{eT(GZ+m-LKZj%!oFWag|ef<>R?@sX{ioRd%JpS$m#U^{XVsnS7@o-(yC2mSJ_x5 z{5JO@yw@U*6mP*qIHRjXCrmX^jxj?khC~_)WO^KP&c;%c-OWXw#sz{MbH~7MdHsJ7 ze$wBciQ=G>w>W#y<-+fHnP_#QjYVtd&!TSY4*qdA7cDE#@Ixy0yFBap0cSF5+$Z2* ze|fK$3DIHJHJr&_OTF2Ym9}fM>+548qj;Z2Yc}(Yr*$uk9O!%>!{7qb5tl3oiuuQ8u_#AAhJ!!brHJkc%voFv!fOQGfyA%2q|hwm-nlBf ygWkGEZzZawu%{-aYV;Z|yo%}PANmEa@FN9c(Z;jTkZ&EQf9NR2%i~XD`kmn)s~n5~ diff --git a/Shaders/qsb/wp_stripes.frag.qsb b/Shaders/qsb/wp_stripes.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..14cfb3e9be1ea1c8f60a73d309ab9835601d9570 GIT binary patch literal 5031 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 z zga@q9@g`y?CStLg>y$iyhMY8OhifH;CAtz=Gu4Xir+1rySMv*YYGMIEdLkA(YWZ`H zTQ-l{g*5eT%AVcjd44fTZJtv!DcJFTgoUGON6nlDMO(%!cool2nK?7GXTzFrr+3HU z%%E92+OH$iEs$viDMB%0RjYRKU_DG`&YW>(ykMOQvcebli+%L)FkT?y=#LaB5jsRGqmMKP#*qs*%VR?*CvzEyNAHxMpz zexw_Av22TIQY5wDs=$I5>@;)qD=xK16a|h;?SbRAX-AJepS+6KjwWW!0j{;*+{EcQ zbAW4!h*fRBS{I5BkyyXPDRNv-WS%(gr0rMv^l;lW=(M#;Q2SZ3qdM3KeUn+OS}5 zmTu9#o*Xn&iDZlJ?6xb`oSAE>b71~OB65!vZ>t5NU7W;n&u2B=WODsC2h5aImL49< zq{gzN_QB&w)!;t8Byvc z8|T;PNGdy)%A}KH<71g&u^L7q)MP`fdqHSd;}SZkp(4SZ6Jt`pVfDNCfaR8Hu;sKL zlJ{uzp76_#OQBR2rU5gZNb-r=(6himHLY=zUf_g|=Qh@CWeVyGwJJcqNS8OkKN>wb z?HYrvc(;jUcu$1ya_+ZX<(n*G`5LNQ+;lP)WpIPr7uA0PMrT08zaizRF&K4yQ>2Ne z#6mrXEn0$xPJ_jAC4a4Nm3LdAHJv|~vtth1AKcCt=fovgEyb#}7O)17tRuVkJ5 zjjS{KyvsTrx7BM-uNI2>M9H_xyQo}tYF>RV_QhxCwWplvo!6e7*Pfl%o}Jg8o!6e7 z*Pfl%p3nNVXMO+jT)**D4L_GJJDcow-g7?d_ngY6_Y}S2tZQuhcC&$K_hPewI6K~H z&L73az0TC**8C<@k6ZIgOg(PxGs(uivv3kg=YQ$D2=dROTwFL@E0t`Yr(|19-!3~r zX#4TQNtrD9-m(0Qb(`mlamvhQxQ%>0voyDuRjY8jS1JW|7%xPRY)L5G(Xg3jY<;*j zWMZxvtDe}@;m4cdJZo(N9&4euafmdE&bCn6$Z>TrcbU@ODS0e*s|evXnd7Pv4=Hd5 zMXGx+v_jj=1C>VttfB)#x6_-B=uNfJn{KI>(6J!2LZ=|Qspr^gTQt!|1>)f3-UBz~ zclllre%Dtf(sFwW>mMjwar+Hsyb25L#tB@6czNjYYD+oQg=aNX{C`oM5ytEi`zX zkdOEVC!sD6+GzN8So2*?!3ni!(t|(dh-=M58>+rlp0Q|e;+D;#W0hSmpsd&_yPj_s z%`1m)o^gV}amzzh-wVBPu4)HY#$p>Bw@|4SZFB349a@!q5Ed)W;cW^(aAt~|>7T@r zZLwI5RG6+cV+U2MV4GZOA{N`c+1yL%`CMQc}TG?N`pkEgR#AUM(DxIw)- zbY>knfS)vyNgCaDb%g0@{?v#k`wtyzA?djDR57NfYk}>@D_+5Eh?8m>o`z}hDJ~V7 zj%!X&%b9=Z5IvpUAhcW_)vNeDXHHLx`W*Lbu>J+>qNw;Z3Q5z`@(9Abz_bEPaiFXL zLD9jPYK4LUHnc@SX&pqGPohK%m$}f(aSbPOW7?CG1xambyUm{Y%;m&u_ zJBKn(vglH>f^f?gs+Kf9o%EN!=T&tIzMT&J)~s~;RD4r?URD2JzppMj{k;9wy4wBD zch@`LUGIE%ef}zpr{x{34swY7%V{~vp__j$k8`xN{Ufi)oE=|XkCtL0k$e{An1!k_ z5xs3IGEr2!s!SAh<{tBi7G)L_*~JxRTE()tIMXVY%>^2-MJ(GFX`=U8tK%PpLN4!# x!Uk0~dU=|L^B~%p5d5!7_z%Cd|7(Y^M9aU&r<6q&Ehqn8o-WU?9{}KJR>8^@N67#H literal 0 HcmV?d00001 diff --git a/Shaders/qsb/wp_wipe.frag.qsb b/Shaders/qsb/wp_wipe.frag.qsb index e1977cede70717110957613f25d9f80aba5a19a1..478c27d43bcfa1ee9a77e6e4e95506c9ef77faed 100644 GIT binary patch literal 2677 zcmV-*3X1gr05*eoob6j}cN51IzSy!20zyI{kfcc_rAcLDgd}WUYz!uL2qaJgc1Rk< zQP$E*-XLkk?#c*=6H-E&G%ZP+x3(Yp3;F~4u|KB&p)cq3v}b0Yk*-E7V@OIJ;yp+9 z&YkDpJNISw-W}OQw26pfM6`j3cGG1FC`WT-Q-h|+C6_!ZP>P6ph=~4!iy|&5U^^Ab zBa2Fkxvbbj@pokHrv<80k%%@@y?K9*q5719aD`{dqJTVds6j-VftY0IhxN@4u*^`6 zEXtEZ0bPaCNxeGrdYEeDkxM1=$R?kBFidx2*iHp<6gW=-Ipk6mET_7$>>!^i%EtjM zQngLbnQlz_lHquWd~&Hy9_7iVG!Z4>VJV{HYKLbST*da=Uj6u8Xpy$odVBT^$mMLN zjr2!QAE6$#z|GXJe!p~P_Ds?b3dxaCnP~J7^(vg4+eQP*5Asv79O+XP?ei)|y^1~{ zWl&7fpN0-Oew2uwg&aNpF`M6w`5rcI&G$CvPEkzV>l{2qea-OzGL6&r;J2o20Bw@d zdO(|Iv|i9oFD z5jG8A9LM_QcpT%+ph^C%;Ew_S5U`T|IBinC>5oWAJ${mU=@Ix)@^5eENT^SaW!#93 zWt<53A3>kgv6o`%x3u5gesfHZp9$f|cT-H+y%p zWPWTj#=F>9#`7s-yw4c#H^wQ9Cy>uukfX<_SLxn@Ihj*CkWU)B6YG18`jmej17;`q zw}GblA4mRa{wECnCxLka{JTIqL47J8eu^A=4l<;#6QFC%U<76e^jp-2d~i9PTPaB>|_VP4+v14iEO$Gz5_f$lWZeE@VlK8P6UwH!j8O8N(&9Y!u6Vz?ua zb(qN-gRGmti~~1@^`$`5>zly(jxrg?K>rK%sXZ|XnzaA#)JrcQH~&R3HFrYAE6mdq zOwJT?Nv~xZHcT@$>h9+Sn9aF#pZHG4Bj^4W&!gy z!z>s)c_VHGVDc<(c0}BYpzFAmSllegDYCegSezDFoaESHag?&&G2(U!H2%?XE3>#Q zLe?b~w=&{358qZ;+^P|Ab3xZ}y9`l^!K-gy4K!1mGC*hgQnwS5D+UigC z|KIj3*`DvXRgq4m;x*6CJ8HD(loo5ESaz*oyo2migYkIW54?Il5DWEU{CZpnAu3id zA}$BHvsU0ajd4X&5+re^X62pWDyP;wx8&Kr&uIn6v-5%DRyonHxNfjmRnX<}c)VV9 zimq3YszrUFUSy!rnn2<%2s_w3Ac0*e_I^`$`*nTbGV7&7!k z#j4fp!fewYVnS4$Mk19=rP8VJ8aXs__W*F6C#yNaca@Z2d>viw;O?7E!c%~h^v?o2_;{>#xcIqDv zW$ulbs4pcO@R+=BFdgZ@Hk&4V~six+BBg(aCccIq{u4=&IqMZ<8aPj_PpMipH;5 zv+tZ6?PiB};ZH;A&_wt+ve3sj;+_R^*Xku z9_Z_J?EciTuUQ@YQ>%`RJ|K0>sYbnux%D7?YbttHX-cNsck_C-+LyESSF`Wut64K$ zesFJPk(RH&kge>W2fBvcV@7l@UoC-mu3SC%;tL@|WCV(YJ#{KGk-HrJ-1kfgS0@HczVzWYd)l z+j;<);A@>=cI;-NuMCFOb55TC9t}-Brb{LFOdt)M4J+nsU_oBab^p9~$BFhgM; z;j)eb&NVoj&R)&oZ`t;0t2zBGs^j0{a7Q8znUpT)zr}6`z{S;^0OyPo;3AHIi}!d2 zoHH7YV)ss?`2TG+RDjHi5^7C08jZ$kTa7l0(@l%la<9=cot2hO)9?E5x6KuqHdk~q z-Ayvw_u9M4$DFu}ZX;)O8=W;Fd^zl>H^L9BlAUwpy?*+)RhESiD8|ol0T9{E=Kd7LV6``ITqYs@Q(b%G*M5MSZ#3Y`yNsAp*2ZBU94w zZY4dNRXLPM4bIO;a5~i$8j5VT?%Q6X?B=U2b}71ISDEVYE};bqrz)~pofPx)GK;Hz zU{#f`o>%E8ve~euRqGmVeyv*wo3B<+m(A)!g%yDr223lUZpR>Kwo)rgXMhZ8?VyYa zl9F`Jx2%<)32}XSyrUCxs>#(iL!lCVrlM4@q7iE23KzWo4fyx#n8h`oXi+cJVfY`+ z!Q%NYW&ekWd+nkcCN8jKS3At-&?LE`g|y zx|UY*f}|C@DIkI=2 zx%bZ8``n#7JF3=#DZ5&Z`j#azMWBWt!?_ zQGpx^=sE@+*RwOPC#X&yxl|^PZ1Ty6!elpvZB!&j2^T0Jhg_;b<#acd?c`Hc#W%N$OD(+(`ZE_o=fpXVZRAOphFriAGzfSINn~tu&zGAU{>hkugGJ_3uJ|4{Nvkd)2+{J57leE_3iS^|jgq$TW`a#&3jie2VF_SoF~?UhXJT#c(-e!rQ`Dozd5LTMIiTTU+DB2r=N&U^xPe6VPWF`I#ZBVi4k4Z?~evW$SNyJddE}nFe-Zb`sZZra3c0igwq?u*foqxlkQrh!Y2Y7HpK2dK4oUod zU>W4u0VX#L`x$0G8)N?<@Y~QEf$l;0A%JNcuffJ4X5)Ln|4Mx-M-Ky&x%+qOr6b6d ze^Em9jS|re@@ACT83WgPE=S?l7>n;wCVvdMqvtjro~NgpAIaH=rwX?MHZabbA`lj&%X4Jkvg&X^(O4Gx@;asu9E0B5-Z<8uS*KOw+J)J;u%r;F|NB z(7VAnUjpavFuiwyYtG+eoL_^TcNypJF^+E;&-DAoGyQXe^V`7qM|1uGvBho7>mG_1yn}z1?TnpZpQn*K7_F`!}tl*TY?R0`F1lury&S-hXBHqV4&P zTNA^XOtS9T1xK|Oobp0llq#+jj7DIm7K|p7e&96_XtUH4ge!*9{hGN_1&7nQYXYlIvCF(4sNl zC^0CUpl?;{72C^B3g2yb1$%gO1%d2nGP!7Z*PU8fEZT*vobjyPoN!&Qn2~+1Qxr0U zsX;iaq#BE2%n(K2h$y%f*BcgNBCwl5!?UxKDNJTaG!_R<$!v^dmPv+@u#06|jEPJ- z!_kse2wX3U+s(kP7470V1X_%Vl#EweM_%j|so``+>=UVMIwSU~Xr|lY9vmDL`%N$$ z!70I3i!pITWYUKQRc{z0d11zKl>Lzjg{IVmwF}`meqh&AN;NY4P-t>r6mKl1n~0XY z%9Z>{&niz^ftAb5i7`=inyHYLA;f%05s(T8AB66b`e|2u+i)e;ftlmA~ixy#DQ ztL<`f=o?Z_oLbzAiQ5RmcZiZ_l_zApAI{6lYF|~>UQ`~!e!a!rx9lAy*6+2~ll#w# z=&IV0CGi=s>MVG~ZzlJd4c)6q+u+ItqzB)K;adNf!hEAtvOTqM9~PcncKpEhQu7(T zqWal96G#VV!ooEZSb;6_kjkT+acN&lce9)A zushtvZZ_7glur7A6*z^^OS59~zRt3VUBb+{Gv_blCp_2p^Ji-HMgYQNtck;7_Qb`B z>FM*6nJ`o_Hl`baVT27U4vU!!`B}>=+W}0d>MaHG9VHyYcPZkq+bZJ-eL4(W%A~?P zS7e19!8J5n!CvM>vefNJvb35r$-HqUS;C=Y38#|zxKqh|w^PZyaVlBrek@sfkaNj= zmjy4s%5q<^y?SJ2564V5o6Y8G%YBEx!!3UwO})P#r@K4OmiVaS_k0}L5r}L@Afih5 zk_ZnuHY-)YRVGF>Mv*uz>-J;l?~3_w4;4ReRQ%|=9KI(-)_mayR@u%w@?NjN9V`DB zmq^Df$n6C@1Y(8`Ex4)?3UMUO!^7&7l=JMM;nfTSOMF9W!3 zQndrClJ|pR#hE{vY~#LDEgq0R(hEnE$%Ze#3#(aG+pk*%TPUr#Z~a=^pZ~@CG)9+B zW~AfY%4#mBawwGTH%D(z8Zaiw{O>2UgF*_ zLoU9F=MG)H=cOmk`)|>gqxYR5(Ktn7wXE-tr`M~=4AFI8(rGsCG(t+DsJme;m&LqK nEx$OPFpenj?-KYQ8{?1drL1hrkFgV2wVu4<^wsrW)>D-%9z;2$ From 3c7d03ada907584ed0103983861c6b73be7d2742 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Sat, 30 Aug 2025 11:22:09 -0400 Subject: [PATCH 2/4] Wallpaper: added a bash script to compile all shaders + code cleanup --- Bin/shaders-compile.sh | 36 ++++++++++++++++ Modules/Background/Background.qml | 44 ++++++++------------ Modules/SettingsPanel/Tabs/WallpaperTab.qml | 2 - Services/WallpaperService.qml | 19 ++------- Shaders/frag/wp_stripes.frag | 26 ++++++++---- Shaders/qsb/wp_stripes.frag.qsb | Bin 5031 -> 5147 bytes 6 files changed, 75 insertions(+), 52 deletions(-) create mode 100755 Bin/shaders-compile.sh diff --git a/Bin/shaders-compile.sh b/Bin/shaders-compile.sh new file mode 100755 index 0000000..b9f76a8 --- /dev/null +++ b/Bin/shaders-compile.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Directory containing the source shaders. +SOURCE_DIR="Shaders/frag/" + +# Directory where the compiled shaders will be saved. +DEST_DIR="Shaders/qsb/" + +# Check if the source directory exists. +if [ ! -d "$SOURCE_DIR" ]; then + echo "Source directory $SOURCE_DIR not found!" + exit 1 +fi + +# Create the destination directory if it doesn't exist. +mkdir -p "$DEST_DIR" + +# Loop through all files in the source directory ending with .frag +for shader in "$SOURCE_DIR"*.frag; do + # Check if a file was found (to handle the case of no .frag files). + if [ -f "$shader" ]; then + # Get the base name of the file (e.g., wp_fade). + shader_name=$(basename "$shader" .frag) + + # Construct the output path for the compiled shader. + output_path="$DEST_DIR$shader_name.frag.qsb" + + # Construct and run the qsb command. + qsb --qt6 -o "$output_path" "$shader" + + # Print a message to confirm compilation. + echo "Compiled $shader to $output_path" + fi +done + +echo "Shader compilation complete." \ No newline at end of file diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml index df13fc6..4915386 100644 --- a/Modules/Background/Background.qml +++ b/Modules/Background/Background.qml @@ -12,14 +12,14 @@ Variants { required property ShellScreen modelData - active: Settings.isLoaded && WallpaperService.getWallpaper(modelData.name) + active: Settings.isLoaded sourceComponent: PanelWindow { id: root // Internal state management property bool firstWallpaper: true - property string transitionType: 'fade' + property string transitionType: "fade" property bool transitioning: false property real transitionProgress: 0 property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness @@ -40,7 +40,6 @@ Variants { property string servicedWallpaper: WallpaperService.getWallpaper(modelData.name) onServicedWallpaperChanged: { if (servicedWallpaper && servicedWallpaper !== currentWallpaper.source) { - // Set wallpaper immediately on startup if (firstWallpaper) { firstWallpaper = false @@ -51,36 +50,25 @@ Variants { // Get the transitionType from the settings transitionType = Settings.data.wallpaper.transitionType - if (transitionType == 'random') { + if (transitionType == "random") { var index = Math.floor(Math.random() * allTransitions.length) transitionType = allTransitions[index] } // Ensure the transition type really exists if (transitionType !== "none" && !allTransitions.includes(transitionType)) { - transitionType = 'fade' + transitionType = "fade" } - Logger.log("Background", "New wallpaper: ", servicedWallpaper, "On:", modelData.name, "Transition:", transitionType) + Logger.log("Background", "New wallpaper: ", servicedWallpaper, "On:", modelData.name, "Transition:", + transitionType) switch (transitionType) { case "none": setWallpaperImmediate(servicedWallpaper) break - case "wipe_left": - wipeDirection = 0 - setWallpaperWithTransition(servicedWallpaper) - break - case "wipe_right": - wipeDirection = 1 - setWallpaperWithTransition(servicedWallpaper) - break - case "wipe_up": - wipeDirection = 2 - setWallpaperWithTransition(servicedWallpaper) - break - case "wipe_down": - wipeDirection = 3 + case "wipe": + wipeDirection = Math.random() * 4 setWallpaperWithTransition(servicedWallpaper) break case "disc": @@ -89,7 +77,7 @@ Variants { setWallpaperWithTransition(servicedWallpaper) break case "stripes": - stripesCount = Math.round(Math.random() * 24 + 2) + stripesCount = Math.round(Math.random() * 24 + 6) stripesAngle = Math.random() * 360 setWallpaperWithTransition(servicedWallpaper) break @@ -139,7 +127,7 @@ Variants { ShaderEffect { id: fadeShader anchors.fill: parent - visible: transitionType === 'fade' || transitionType === 'none' + visible: transitionType === "fade" || transitionType === "none" property variant source1: currentWallpaper property variant source2: nextWallpaper @@ -151,7 +139,7 @@ Variants { ShaderEffect { id: wipeShader anchors.fill: parent - visible: transitionType.startsWith('wipe_') + visible: transitionType === "wipe" property variant source1: currentWallpaper property variant source2: nextWallpaper @@ -166,7 +154,7 @@ Variants { ShaderEffect { id: discShader anchors.fill: parent - visible: transitionType === 'disc' + visible: transitionType === "disc" property variant source1: currentWallpaper property variant source2: nextWallpaper @@ -183,7 +171,7 @@ Variants { ShaderEffect { id: stripesShader anchors.fill: parent - visible: transitionType === 'stripes' + visible: transitionType === "stripes" property variant source1: currentWallpaper property variant source2: nextWallpaper @@ -203,8 +191,10 @@ Variants { property: "transitionProgress" from: 0.0 to: 1.0 - duration: Settings.data.wallpaper.transitionDuration ?? 1000 - easing.type: Easing.InOutCubic //transitionType.startsWith('wipe_') ? Easing.InOutCubic : Easing.OutQuad + // The stripes shader feels faster visually, we make it a bit slower here. + duration: transitionType == "stripes" ? Settings.data.wallpaper.transitionDuration + * 1.4 : Settings.data.wallpaper.transitionDuration + easing.type: Easing.InOutCubic onFinished: { // Swap images after transition completes currentWallpaper.source = nextWallpaper.source diff --git a/Modules/SettingsPanel/Tabs/WallpaperTab.qml b/Modules/SettingsPanel/Tabs/WallpaperTab.qml index 71dd82e..8212b4a 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperTab.qml @@ -249,8 +249,6 @@ ColumnLayout { } } } - - } // Reusable component for interval preset chips diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index 403153a..5e7bed6 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -36,24 +36,12 @@ Singleton { name: "Stripes" } ListElement { - key: "wipe_left" - name: "Wipe Left" - } - ListElement { - key: "wipe_right" - name: "Wipe Right" - } - ListElement { - key: "wipe_up" - name: "Wipe Up" - } - ListElement { - key: "wipe_down" - name: "Wipe Down" + key: "wipe" + name: "Wipe" } } - // All transition keys but filter out "random" + // All transition keys but filter out "none" and "random" so we are left with the real transitions readonly property var allTransitions: Array.from({ "length": transitionsModel.count }, (_, i) => transitionsModel.get(i).key).filter( @@ -154,7 +142,6 @@ Singleton { } //Logger.log("Wallpaper", "setWallpaper on", screenName, ": ", path) - var wallpaperChanged = false var monitor = getMonitorConfig(screenName) diff --git a/Shaders/frag/wp_stripes.frag b/Shaders/frag/wp_stripes.frag index 7d12b30..61a20f7 100644 --- a/Shaders/frag/wp_stripes.frag +++ b/Shaders/frag/wp_stripes.frag @@ -65,16 +65,28 @@ void main() { // Use absolute stripe position for consistent delay across all stripes float normalizedStripePos = clamp(stripePos / stripes, 0.0, 1.0); - // Reduced delay factor and better scaling to match other shaders' timing - float maxDelay = 0.15; // Maximum delay for the last stripe + // Increased delay and better distribution + float maxDelay = 0.15; float stripeDelay = normalizedStripePos * maxDelay; - // Ensure all stripes complete when progress reaches 1.0 - // without making the overall animation appear faster - float stripeProgress = clamp((ubuf.progress - stripeDelay) / (1.0 - maxDelay), 0.0, 1.0); + // Better progress mapping that uses the full 0.0-1.0 range + // Map progress so that: + // - First stripe starts at progress = 0.0 + // - Last stripe finishes at progress = 1.0 + float stripeProgress; + if (ubuf.progress <= stripeDelay) { + stripeProgress = 0.0; + } else if (ubuf.progress >= (stripeDelay + (1.0 - maxDelay))) { + stripeProgress = 1.0; + } else { + // Scale the progress within the active window for this stripe + float activeStart = stripeDelay; + float activeEnd = stripeDelay + (1.0 - maxDelay); + stripeProgress = (ubuf.progress - activeStart) / (activeEnd - activeStart); + } - // Apply smooth easing - stripeProgress = smoothstep(0.0, 1.0, stripeProgress); + // Use gentler easing curve + stripeProgress = stripeProgress * stripeProgress * (3.0 - 2.0 * stripeProgress); // Smootherstep instead of smoothstep // Use the perpendicular coordinate for edge comparison float yPos = perpCoord; diff --git a/Shaders/qsb/wp_stripes.frag.qsb b/Shaders/qsb/wp_stripes.frag.qsb index 14cfb3e9be1ea1c8f60a73d309ab9835601d9570..0d8d94e99c00cee0789221ded7cbba6a616f09fb 100644 GIT binary patch literal 5147 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 z zga@q9@g`y?CStLg>y$iyhMY8OhifH;CAtz=Gu4Xir+1rySMv*YYGMIEdLkA(YWZ`H zTQ-l{g*5eT%AVcjd44fTZJtv!DcJFTgoUGON6nlDMO(%!cool2nK?7GXTzFrr+3HU z%%E92+OH$iEs$viDMB%0RjYRKU_DG`&YW>(ykMOQvcebli+%L)FkT?y=#LaB5jsRGqmMKP#*qs*%VR?*CvzEyNAHxMpz zexw_Av22TIQY5wDs=$I5>@;)qD=xK16a|h;?SbRAX-AJepS+6KjwWW!0j{;*+{EcQ zbAW4!h*fRBS{I5BkyyXPDRNv-WS%(gr0rMv^l;lW=(M#;Q2SZ3qdM3KeUn+OS}5 zmTu9#o*Xn&iDZlJ?6xb`oSAE>b71~OB65!vZ>t5NU7W;n&u2B=WODsC2h5aImL49< zq{gzN_QB&w)!;t8Byvc z8|T;PNGdy)%A}KH<71g&u^L7q)MP`fdqHSd;}SZkp(4SZ6Jt`pVfDNCfaR8Hu;sKL zlJ{uzp76_#OQBR2rU5gZNb-r=(6himHLY=zUf_g|=Qh@CWeVyGwJJcqNS8OkKN>wb z?HYrvc(;jUcu$1ya_+ZX<(n*G`5LNQ+;lP)WpIPr7uA0PMrT08zaizRF&K4yQ>2Ne z#6mrXEn0$xPJ_jAC4a4Nm3LdAHJv|~vtth1AKcCt=fovgEyb#}7O)17tRuVkJ5 zjjS{KyvsTrx7BM-uNI2>M9H_xyQo}tYF>RV_QhxCwWplvo!6e7*Pfl%o}Jg8o!6e7 z*Pfl%p3nNVXMO+jT)**D4L_GJJDcow-g7?d_ngY6_Y}S2tZQuhcC&$K_hPewI6K~H z&L73az0TC**8C<@k6ZIgOg(PxGs(uivv3kg=YQ$D2=dROTwFL@E0t`Yr(|19-!3~r zX#4TQNtrD9-m(0Qb(`mlamvhQxQ%>0voyDuRjY8jS1JW|7%xPRY)L5G(Xg3jY<;*j zWMZxvtDe}@;m4cdJZo(N9&4euafmdE&bCn6$Z>TrcbU@ODS0e*s|evXnd7Pv4=Hd5 zMXGx+v_jj=1C>VttfB)#x6_-B=uNfJn{KI>(6J!2LZ=|Qspr^gTQt!|1>)f3-UBz~ zclllre%Dtf(sFwW>mMjwar+Hsyb25L#tB@6czNjYYD+oQg=aNX{C`oM5ytEi`zX zkdOEVC!sD6+GzN8So2*?!3ni!(t|(dh-=M58>+rlp0Q|e;+D;#W0hSmpsd&_yPj_s z%`1m)o^gV}amzzh-wVBPu4)HY#$p>Bw@|4SZFB349a@!q5Ed)W;cW^(aAt~|>7T@r zZLwI5RG6+cV+U2MV4GZOA{N`c+1yL%`CMQc}TG?N`pkEgR#AUM(DxIw)- zbY>knfS)vyNgCaDb%g0@{?v#k`wtyzA?djDR57NfYk}>@D_+5Eh?8m>o`z}hDJ~V7 zj%!X&%b9=Z5IvpUAhcW_)vNeDXHHLx`W*Lbu>J+>qNw;Z3Q5z`@(9Abz_bEPaiFXL zLD9jPYK4LUHnc@SX&pqGPohK%m$}f(aSbPOW7?CG1xambyUm{Y%;m&u_ zJBKn(vglH>f^f?gs+Kf9o%EN!=T&tIzMT&J)~s~;RD4r?URD2JzppMj{k;9wy4wBD zch@`LUGIE%ef}zpr{x{34swY7%V{~vp__j$k8`xN{Ufi)oE=|XkCtL0k$e{An1!k_ z5xs3IGEr2!s!SAh<{tBi7G)L_*~JxRTE()tIMXVY%>^2-MJ(GFX`=U8tK%PpLN4!# x!Uk0~dU=|L^B~%p5d5!7_z%Cd|7(Y^M9aU&r<6q&Ehqn8o-WU?9{}KJR>8^@N67#H From 125a3ace08c5c7924b0dc72ee2373735e0c93137 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Sat, 30 Aug 2025 12:19:38 -0400 Subject: [PATCH 3/4] Wallpaper: made the selection more responsive to clicks + code cleanup --- Modules/Background/Background.qml | 123 ++++++++++-------- .../Tabs/WallpaperSelectorTab.qml | 8 +- Services/WallpaperService.qml | 6 +- Shaders/frag/wp_stripes.frag | 6 +- Shaders/qsb/wp_stripes.frag.qsb | Bin 5147 -> 5180 bytes 5 files changed, 78 insertions(+), 65 deletions(-) diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml index 4915386..fdca504 100644 --- a/Modules/Background/Background.qml +++ b/Modules/Background/Background.qml @@ -20,10 +20,11 @@ Variants { // Internal state management property bool firstWallpaper: true property string transitionType: "fade" - property bool transitioning: false property real transitionProgress: 0 - property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness + + readonly property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness readonly property var allTransitions: WallpaperService.allTransitions + readonly property bool transitioning: transitionAnimation.running // Wipe direction: 0=left, 1=right, 2=up, 3=down property real wipeDirection: 0 @@ -38,53 +39,15 @@ Variants { // External state management property string servicedWallpaper: WallpaperService.getWallpaper(modelData.name) + property string futureWallpaper: "" onServicedWallpaperChanged: { - if (servicedWallpaper && servicedWallpaper !== currentWallpaper.source) { - // Set wallpaper immediately on startup - if (firstWallpaper) { - firstWallpaper = false - setWallpaperImmediate(servicedWallpaper) - return - } - - // Get the transitionType from the settings - transitionType = Settings.data.wallpaper.transitionType - - if (transitionType == "random") { - var index = Math.floor(Math.random() * allTransitions.length) - transitionType = allTransitions[index] - } - - // Ensure the transition type really exists - if (transitionType !== "none" && !allTransitions.includes(transitionType)) { - transitionType = "fade" - } - - Logger.log("Background", "New wallpaper: ", servicedWallpaper, "On:", modelData.name, "Transition:", - transitionType) - - switch (transitionType) { - case "none": - setWallpaperImmediate(servicedWallpaper) - break - case "wipe": - wipeDirection = Math.random() * 4 - setWallpaperWithTransition(servicedWallpaper) - break - case "disc": - discCenterX = Math.random() - discCenterY = Math.random() - setWallpaperWithTransition(servicedWallpaper) - break - case "stripes": - stripesCount = Math.round(Math.random() * 24 + 6) - stripesAngle = Math.random() * 360 - setWallpaperWithTransition(servicedWallpaper) - break - default: - setWallpaperWithTransition(servicedWallpaper) - break - } + // Set wallpaper immediately on startup + if (firstWallpaper) { + firstWallpaper = false + setWallpaperImmediate(servicedWallpaper) + } else { + futureWallpaper = servicedWallpaper + debounceTimer.restart() } } @@ -101,6 +64,16 @@ Variants { left: true } + Timer { + id: debounceTimer + interval: 333 + running: false + repeat: false + onTriggered: { + changeWallpaper() + } + } + Image { id: currentWallpaper anchors.fill: parent @@ -110,6 +83,8 @@ Variants { mipmap: false visible: false cache: false + // currentWallpaper should not be asynchronous to avoid flickering when swapping next to current. + asynchronous: false } Image { @@ -121,6 +96,7 @@ Variants { mipmap: false visible: false cache: false + asynchronous: true } // Fade or None transition shader @@ -193,29 +169,27 @@ Variants { to: 1.0 // The stripes shader feels faster visually, we make it a bit slower here. duration: transitionType == "stripes" ? Settings.data.wallpaper.transitionDuration - * 1.4 : Settings.data.wallpaper.transitionDuration + * 1.6 : Settings.data.wallpaper.transitionDuration easing.type: Easing.InOutCubic onFinished: { // Swap images after transition completes currentWallpaper.source = nextWallpaper.source nextWallpaper.source = "" transitionProgress = 0.0 - transitioning = false } } function startTransition() { if (!transitioning && nextWallpaper.source != currentWallpaper.source) { - transitioning = true transitionAnimation.start() } } function setWallpaperImmediate(source) { + transitionAnimation.stop() + transitionProgress = 0.0 currentWallpaper.source = source nextWallpaper.source = "" - transitionProgress = 0.0 - transitioning = false } function setWallpaperWithTransition(source) { @@ -224,16 +198,55 @@ Variants { if (transitioning) { // We are interrupting a transition transitionAnimation.stop() + transitionProgress = 0 currentWallpaper.source = nextWallpaper.source nextWallpaper.source = "" - transitionProgress = 0 - transitioning = false } nextWallpaper.source = source startTransition() } } + + // Main method that actually trigger the wallpaper change + function changeWallpaper() { + // Get the transitionType from the settings + transitionType = Settings.data.wallpaper.transitionType + + if (transitionType == "random") { + var index = Math.floor(Math.random() * allTransitions.length) + transitionType = allTransitions[index] + } + + // Ensure the transition type really exists + if (transitionType !== "none" && !allTransitions.includes(transitionType)) { + transitionType = "fade" + } + + //Logger.log("Background", "New wallpaper: ", futureWallpaper, "On:", modelData.name, "Transition:", transitionType) + switch (transitionType) { + case "none": + setWallpaperImmediate(futureWallpaper) + break + case "wipe": + wipeDirection = Math.random() * 4 + setWallpaperWithTransition(futureWallpaper) + break + case "disc": + discCenterX = Math.random() + discCenterY = Math.random() + setWallpaperWithTransition(futureWallpaper) + break + case "stripes": + stripesCount = Math.round(Math.random() * 20 + 4) + stripesAngle = Math.random() * 360 + setWallpaperWithTransition(futureWallpaper) + break + default: + setWallpaperWithTransition(futureWallpaper) + break + } + } } } } diff --git a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml index b708f9d..fcd9e50 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml @@ -8,6 +8,7 @@ import qs.Widgets ColumnLayout { id: root + width: parent.width spacing: Style.marginL * scaling @@ -94,12 +95,10 @@ ColumnLayout { GridView { id: wallpaperGridView anchors.fill: parent - clip: true model: wallpapersList - boundsBehavior: Flickable.StopAtBounds - flickableDirection: Flickable.VerticalFlick interactive: false + clip: true property int columns: 5 property int itemSize: Math.floor((width - leftMargin - rightMargin - (4 * Style.marginS * scaling)) / columns) @@ -180,7 +179,8 @@ ColumnLayout { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: { + // Use on pressed instead of clicked to better register clicks + onPressed: { if (Settings.data.wallpaper.setWallpaperOnAllMonitors) { WallpaperService.changeWallpaper(undefined, wallpaperPath) } else { diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index 5e7bed6..b66ce2a 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -121,17 +121,17 @@ Singleton { // ------------------------------------------------------------------- function changeWallpaper(screenName, path) { if (screenName !== undefined) { - setWallpaper(screenName, path) + _setWallpaper(screenName, path) } else { // If no screenName specified change for all screens for (var i = 0; i < Quickshell.screens.length; i++) { - setWallpaper(Quickshell.screens[i].name, path) + _setWallpaper(Quickshell.screens[i].name, path) } } } // ------------------------------------------------------------------- - function setWallpaper(screenName, path) { + function _setWallpaper(screenName, path) { if (path === "" || path === undefined) { return } diff --git a/Shaders/frag/wp_stripes.frag b/Shaders/frag/wp_stripes.frag index 61a20f7..34df964 100644 --- a/Shaders/frag/wp_stripes.frag +++ b/Shaders/frag/wp_stripes.frag @@ -22,9 +22,9 @@ void main() { vec4 color1 = texture(source1, uv); // Current (old) wallpaper vec4 color2 = texture(source2, uv); // Next (new) wallpaper - // Map smoothness from 0.0-1.0 to 0.001-0.1 range + // Map smoothness from 0.0-1.0 to 0.001-0.3 range // Using a non-linear mapping for better control at low values - float mappedSmoothness = mix(0.001, 0.1, ubuf.smoothness * ubuf.smoothness); + float mappedSmoothness = mix(0.001, 0.3, ubuf.smoothness * ubuf.smoothness); // Use values directly without forcing defaults float stripes = (ubuf.stripeCount > 0.0) ? ubuf.stripeCount : 12.0; @@ -66,7 +66,7 @@ void main() { float normalizedStripePos = clamp(stripePos / stripes, 0.0, 1.0); // Increased delay and better distribution - float maxDelay = 0.15; + float maxDelay = 0.1; float stripeDelay = normalizedStripePos * maxDelay; // Better progress mapping that uses the full 0.0-1.0 range diff --git a/Shaders/qsb/wp_stripes.frag.qsb b/Shaders/qsb/wp_stripes.frag.qsb index 0d8d94e99c00cee0789221ded7cbba6a616f09fb..4466bce27f0aa81f9befd685f4dd31d76dd86474 100644 GIT binary patch literal 5180 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 literal 5147 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 Date: Sat, 30 Aug 2025 12:44:30 -0400 Subject: [PATCH 4/4] BTService: add percent symbol (%) after battery level --- Services/BluetoothService.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index 368534c..2e88f5b 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -116,7 +116,7 @@ Singleton { } function getBattery(device) { - return `Battery: ${Math.round(device.battery * 100)}` + return `Battery: ${Math.round(device.battery * 100)}%` } function getSignalIcon(device) {