Initial release of Lumen Wallpaper
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<kcfg>
|
||||
<group name="Wallpaper">
|
||||
<entry name="shaderPath" type="String">
|
||||
<default></default>
|
||||
</entry>
|
||||
<entry name="targetFps" type="Int">
|
||||
<default>30</default>
|
||||
</entry>
|
||||
<entry name="speed" type="Double">
|
||||
<default>1.0</default>
|
||||
</entry>
|
||||
<entry name="running" type="Bool">
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="mouseEnabled" type="Bool">
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="watchSource" type="Bool">
|
||||
<default>true</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
||||
@@ -0,0 +1,111 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls as Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Item {
|
||||
id: configRoot
|
||||
|
||||
property alias cfg_shaderPath: shaderPath.text
|
||||
property alias cfg_targetFps: fpsSlider.value
|
||||
property alias cfg_speed: speedSlider.value
|
||||
property alias cfg_running: runningCheck.checked
|
||||
property alias cfg_mouseEnabled: mouseCheck.checked
|
||||
property alias cfg_watchSource: watchCheck.checked
|
||||
|
||||
implicitWidth: 560
|
||||
implicitHeight: form.implicitHeight + Kirigami.Units.gridUnit * 2
|
||||
|
||||
FileDialog {
|
||||
id: shaderDialog
|
||||
title: "Choose a fragment shader"
|
||||
nameFilters: ["Fragment shaders (*.frag *.glsl)", "All files (*)"]
|
||||
onAccepted: shaderPath.text = selectedFile.toString()
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: form
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
margins: Kirigami.Units.gridUnit
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: "Fragment shader:"
|
||||
Layout.fillWidth: true
|
||||
|
||||
Controls.TextField {
|
||||
id: shaderPath
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Built-in Lumen shader"
|
||||
}
|
||||
Controls.Button {
|
||||
text: "Browse…"
|
||||
onClicked: shaderDialog.open()
|
||||
}
|
||||
Controls.Button {
|
||||
text: "Reset"
|
||||
onClicked: shaderPath.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: "Shadertoy-style GLSL with mainImage(). Files reload automatically when enabled below."
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: "Frame rate:"
|
||||
Controls.Slider {
|
||||
id: fpsSlider
|
||||
from: 5
|
||||
to: 120
|
||||
stepSize: 1
|
||||
value: 30
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Controls.Label {
|
||||
text: Math.round(fpsSlider.value) + " FPS"
|
||||
Layout.minimumWidth: 64
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: "Animation speed:"
|
||||
Controls.Slider {
|
||||
id: speedSlider
|
||||
from: 0.1
|
||||
to: 3.0
|
||||
stepSize: 0.05
|
||||
value: 1.0
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Controls.Label {
|
||||
text: speedSlider.value.toFixed(2) + "×"
|
||||
Layout.minimumWidth: 64
|
||||
}
|
||||
}
|
||||
|
||||
Controls.CheckBox {
|
||||
id: runningCheck
|
||||
Kirigami.FormData.label: "Playback:"
|
||||
text: "Animate wallpaper"
|
||||
checked: true
|
||||
}
|
||||
Controls.CheckBox {
|
||||
id: mouseCheck
|
||||
text: "React to the pointer over the desktop"
|
||||
checked: true
|
||||
}
|
||||
Controls.CheckBox {
|
||||
id: watchCheck
|
||||
text: "Reload the shader when its file changes"
|
||||
checked: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module dev.emil.lumen
|
||||
plugin lumenplugin
|
||||
classname LumenPlugin
|
||||
@@ -0,0 +1,74 @@
|
||||
import QtQuick
|
||||
import org.kde.plasma.plasmoid
|
||||
import "lumen"
|
||||
|
||||
WallpaperItem {
|
||||
id: root
|
||||
|
||||
readonly property var activeConfiguration: {
|
||||
if (typeof wallpaper !== "undefined" && wallpaper && wallpaper.configuration)
|
||||
return wallpaper.configuration
|
||||
return typeof configuration !== "undefined" ? configuration : null
|
||||
}
|
||||
|
||||
function option(name, fallback) {
|
||||
if (!activeConfiguration)
|
||||
return fallback
|
||||
const value = activeConfiguration[name]
|
||||
return value === undefined ? fallback : value
|
||||
}
|
||||
|
||||
VulkanWallpaper {
|
||||
id: engine
|
||||
anchors.fill: parent
|
||||
shaderUrl: {
|
||||
const path = String(root.option("shaderPath", ""))
|
||||
return path.length > 0 ? path : Qt.resolvedUrl("shaders/Lumen.frag")
|
||||
}
|
||||
targetFps: Number(root.option("targetFps", 30))
|
||||
speed: Number(root.option("speed", 1.0))
|
||||
running: Boolean(root.option("running", true))
|
||||
mouseEnabled: Boolean(root.option("mouseEnabled", true))
|
||||
watchSource: Boolean(root.option("watchSource", true))
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#111522"
|
||||
visible: engine.errorMessage.length > 0 && !engine.fallbackActive
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width * 0.8, 640)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
color: "#f3f5fa"
|
||||
font.pixelSize: 16
|
||||
text: "Lumen Wallpaper\n\n" + engine.errorMessage
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 80
|
||||
height: warningText.implicitHeight + 16
|
||||
color: "#cc421f35"
|
||||
visible: engine.fallbackActive && engine.errorMessage.length > 0
|
||||
|
||||
Text {
|
||||
id: warningText
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: 12
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 3
|
||||
color: "#ffd9e2"
|
||||
text: "Lumen: using the last working shader\n" + engine.errorMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
float hash21(vec2 p) {
|
||||
p = fract(p * vec2(123.34, 456.21));
|
||||
p += dot(p, p + 45.32);
|
||||
return fract(p.x * p.y);
|
||||
}
|
||||
|
||||
float noise21(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
f = f * f * (3.0 - 2.0 * f);
|
||||
return mix(
|
||||
mix(hash21(i), hash21(i + vec2(1.0, 0.0)), f.x),
|
||||
mix(hash21(i + vec2(0.0, 1.0)), hash21(i + vec2(1.0, 1.0)), f.x),
|
||||
f.y
|
||||
);
|
||||
}
|
||||
|
||||
float fbm(vec2 p) {
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
mat2 turn = mat2(0.80, 0.60, -0.60, 0.80);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
value += amplitude * noise21(p);
|
||||
p = turn * p * 2.03 + vec2(3.1, 1.7);
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
||||
vec2 uv = fragCoord / iResolution.xy;
|
||||
float unit = min(iResolution.x, iResolution.y);
|
||||
vec2 p = (2.0 * fragCoord - iResolution.xy) / unit;
|
||||
vec2 mouse = (2.0 * iMouse.xy - iResolution.xy) / unit;
|
||||
float t = iTime * 0.12;
|
||||
|
||||
vec2 pointerDelta = p - mouse;
|
||||
float pointerGlow = exp(-dot(pointerDelta, pointerDelta) * 4.0);
|
||||
float pressed = iMouse.z > 0.0 ? 1.0 : 0.0;
|
||||
|
||||
vec2 q = vec2(
|
||||
fbm(p * 0.65 + vec2(t * 0.35, -t * 0.20)),
|
||||
fbm(p * 0.65 + vec2(4.7 - t * 0.24, 2.9 + t * 0.18))
|
||||
);
|
||||
vec2 r = vec2(
|
||||
fbm(p + q * 1.8 + vec2(1.7, 8.2) + t * 0.16),
|
||||
fbm(p + q * 1.8 + vec2(8.3, 2.8) - t * 0.13)
|
||||
);
|
||||
float field = fbm(p * 0.85 + r * 2.2 + pointerDelta * pointerGlow * 0.12);
|
||||
|
||||
float ribbon = 0.5 + 0.5 * sin(10.0 * field + 3.0 * r.x - 1.8 * p.y + t * 1.4);
|
||||
float silk = pow(ribbon, 4.0);
|
||||
float edge = pow(1.0 - abs(ribbon * 2.0 - 1.0), 10.0);
|
||||
|
||||
vec3 color = mix(vec3(0.006, 0.010, 0.032), vec3(0.075, 0.025, 0.16), field);
|
||||
color = mix(color, vec3(0.02, 0.65, 0.72), silk * 0.72);
|
||||
color += vec3(0.65, 0.08, 0.32) * pow(1.0 - field, 5.0) * 0.28;
|
||||
color += vec3(0.08, 0.55, 0.70) * edge * 0.16;
|
||||
color += mix(vec3(0.02, 0.55, 0.68), vec3(0.85, 0.12, 0.38), pressed)
|
||||
* pointerGlow * (0.14 + pressed * 0.18);
|
||||
|
||||
float vignette = 1.0 - smoothstep(0.25, 1.25, length((uv - 0.5) * vec2(1.1, 0.9)));
|
||||
color *= 0.62 + vignette * 0.38;
|
||||
color = 1.0 - exp(-color * 1.35);
|
||||
color += (hash21(fragCoord + fract(iTime) * 91.0) - 0.5) * 0.012;
|
||||
fragColor = vec4(color, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"KPackageStructure": "Plasma/Wallpaper",
|
||||
"KPlugin": {
|
||||
"Authors": [
|
||||
{
|
||||
"Name": "Emil"
|
||||
}
|
||||
],
|
||||
"Id": "dev.emil.lumen",
|
||||
"License": "GPL-3.0-or-later",
|
||||
"Name": "Lumen Wallpaper",
|
||||
"Version": "0.1.0",
|
||||
"Description": "Procedural GLSL wallpapers rendered natively with Rust and Vulkan",
|
||||
"Website": "https://git.shanaty.ru/Emil_Shanaty/Lumen-wallpaper"
|
||||
},
|
||||
"X-KDE-ParentApp": "org.kde.plasmashell",
|
||||
"X-Plasma-API-Minimum-Version": "6.0"
|
||||
}
|
||||
Reference in New Issue
Block a user