feat!: add W Engine plugin (#81)
* feat!: add W Engine plugin * feat!: add en translation * fix: rm useless line * feat!: add preview window in a pannel * fix: rm no dependencies tools * feat!: update thumbnail with new image * feat: add screen choose with selector and add id check for warn string command folder * docs: add dependencies on toml and read me * fix: rm pkill and use kill * feat!: add multiple path check, default output, select where set wallpaper * feat!: rm debug notif * docs: add new action documentation * feat!: add new service for good restart (pkill all instance linux-wallpaper and rm all tmp file created by w engine) * feat!: add check if pid is a linux-wallpaper instance * docs: add depedencie and new entries * feat!: support personnal path with json load * docs: add docs for custom path * feat!: use new api function for show all wallpaper * fix: rm useless notify
This commit is contained in:
@@ -0,0 +1,83 @@
|
|||||||
|
# W Engine
|
||||||
|
|
||||||
|
A [Noctalia](https://github.com/noctalia-dev/noctalia) v5 plugin for applying Wallpaper Engine wallpapers from Steam Workshop.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Before installing this plugin, make sure you already have Wallpaper Engine installed from Steam. Without it, the plugin will not work.
|
||||||
|
|
||||||
|
You need Steam itself to download wallpapers from the Workshop, plus the `linux-wallpaperengine` tool.
|
||||||
|
|
||||||
|
You also need `kill` to stop duplicate wallpaper processes when changing wallpapers, and `setsid` to start the wallpaper process correctly.
|
||||||
|
|
||||||
|
For more details, see the install section below.
|
||||||
|
|
||||||
|
## Plugin
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| ID | `tadomika_ari/w-engine` |
|
||||||
|
| Entries | Bar widget: `w-engine-widget`; panel: `w-engine-panel`; Service: `start`|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Add the `W Engine` widget from Noctalia's widget picker, then click it to open the panel. You can also open the panel directly or bind it in your compositor:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
noctalia msg panel-toggle tadomika_ari/w-engine:w-engine-panel
|
||||||
|
```
|
||||||
|
|
||||||
|
| Action | Effect |
|
||||||
|
| --- | --- |
|
||||||
|
| Left click on the bar glyph | Open or close the W Engine panel |
|
||||||
|
| Click a wallpaper entry in the panel | Apply that wallpaper |
|
||||||
|
| Select an output | Change the monitor where the wallpaper is displayed |
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
Custom path:
|
||||||
|
|
||||||
|
To define a custom path for Steam or the Workshop, you can add a specific path for W Engine.
|
||||||
|
You can create a `data.json` file in `$NOCTALIA_STATE_HOME` based on this example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"personnalPath": ["path1"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `path1` with your personal path or add other paths. The path is loaded after a reload or if you reopen the widget.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- noctalia ≥ 5.0.0
|
||||||
|
- `linux-wallpaperengine`
|
||||||
|
- `kill`
|
||||||
|
- `pkill`
|
||||||
|
- `setsid`
|
||||||
|
- Wallpaper Engine
|
||||||
|
- Steam for Workshop access
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Install **W Engine** from Noctalia's plugin store (*Settings → Plugins*), then add the widget to a bar from *Settings → Bar*. Plugin options live in *Settings → Plugins*.
|
||||||
|
|
||||||
|
Next, install Wallpaper Engine from Steam: https://store.steampowered.com/app/431960/Wallpaper_Engine/
|
||||||
|
|
||||||
|
Then install `linux-wallpaperengine` from its project page: https://github.com/Almamu/linux-wallpaperengine
|
||||||
|
|
||||||
|
Note that some Wallpaper Engine wallpapers may not work correctly on Linux, especially ones with advanced visual effects.
|
||||||
|
|
||||||
|
If you use NixOS, be aware that the package version in nixpkgs may lag behind upstream.
|
||||||
|
|
||||||
|
For local development, add your working copy as a path source instead
|
||||||
|
(`.luau` edits hot-reload):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
noctalia msg plugins source add dev path /path/to/plugins
|
||||||
|
noctalia msg plugins enable tadomika_ari/w-engine
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
id = "tadomika_ari/w-engine"
|
||||||
|
name = "W Engine"
|
||||||
|
version = "1.0.0"
|
||||||
|
plugin_api = 3
|
||||||
|
author = "TadomiKa-Ari"
|
||||||
|
license = "MIT"
|
||||||
|
dependencies = ["linux-wallpaperengine", "kill", "setsid"]
|
||||||
|
icon = "movie"
|
||||||
|
description = "A Noctalia widget for applying Wallpaper Engine wallpapers from Steam Workshop with one click"
|
||||||
|
tags = ["wallpaper", "utility"]
|
||||||
|
|
||||||
|
[[widget]]
|
||||||
|
id = "w-engine-widget"
|
||||||
|
entry = "w-engine.luau"
|
||||||
|
|
||||||
|
[[panel]]
|
||||||
|
id = "w-engine-panel"
|
||||||
|
entry = "w-engine-panel.luau"
|
||||||
|
width = 800
|
||||||
|
height = 600
|
||||||
|
|
||||||
|
[[service]]
|
||||||
|
id = "start"
|
||||||
|
entry = "start.luau"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- reset section
|
||||||
|
|
||||||
|
noctalia.runAsync("pkill linux-wallpaper")
|
||||||
|
local entries = noctalia.listDir("/tmp")
|
||||||
|
for _, entrieName in entries do
|
||||||
|
if entrieName:match("^w%-engine") then -- match for w-engine tmp file
|
||||||
|
noctalia.removeFile("/tmp/" .. entrieName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- realod section
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"plugin_name": "W Engine",
|
||||||
|
"plugin_description": "A widget for Wallpaper Engine. Set wallpaper from Wallpaper Engine with simple click.",
|
||||||
|
"panel": {
|
||||||
|
"apply": "Apply wallpaper {id}",
|
||||||
|
"list_failed": "Could not list Wallpaper Engine wallpapers",
|
||||||
|
"missing_project_json": "No project.json for {name}",
|
||||||
|
"no_wallpapers": "No Wallpaper Engine wallpapers found",
|
||||||
|
"title": "W Engine"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
--!nonstrict
|
||||||
|
-- W-Engine panel — picks a Wallpaper Engine workshop item and applies it via
|
||||||
|
-- linux-wallpaperengine.
|
||||||
|
|
||||||
|
-- Local variable
|
||||||
|
|
||||||
|
wallpaperPath = {"~/.steam/steam/steamapps/workshop/content/431960/",
|
||||||
|
"~/.local/share/Steam/steamapps/workshop/content/431960/",
|
||||||
|
"~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/workshop/content/431960/",
|
||||||
|
"~/snap/steam/common/.local/share/Steam/steamapps/workshop/content/431960/",
|
||||||
|
"~/snap/steam/common/.local/share/steam/steamapps/workshop/content/431960/"}
|
||||||
|
wallpaperPathSelected = nil
|
||||||
|
|
||||||
|
local pickSlots = {}
|
||||||
|
local columns = 4
|
||||||
|
local previewWidth = 180
|
||||||
|
local previewHeight = 100
|
||||||
|
|
||||||
|
local outputFinalList = {}
|
||||||
|
local outputSelectedName = nil
|
||||||
|
local lastOutputUse = nil
|
||||||
|
|
||||||
|
local pidList: { killSystem } = {}
|
||||||
|
|
||||||
|
-- load personnal path
|
||||||
|
|
||||||
|
local dir = noctalia.pluginDataDir()
|
||||||
|
local path = dir .. "/data.json"
|
||||||
|
local raw = noctalia.readFile(path)
|
||||||
|
if raw then
|
||||||
|
local data = noctalia.json.decode(raw)
|
||||||
|
if data and data.personnalPath then
|
||||||
|
for _, p in ipairs(data.personnalPath) do
|
||||||
|
table.insert(wallpaperPath, p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
type Wallpaper = {
|
||||||
|
nb: string,
|
||||||
|
name: string,
|
||||||
|
preview: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type killSystem = {
|
||||||
|
pid: number,
|
||||||
|
output: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
function tr(key, subst)
|
||||||
|
if subst then
|
||||||
|
return noctalia.tr(key, subst)
|
||||||
|
end
|
||||||
|
return noctalia.tr(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function takeWallpaper()
|
||||||
|
local wallpaper: { Wallpaper } = {}
|
||||||
|
|
||||||
|
for count = 1, #wallpaperPath, 1 do
|
||||||
|
if noctalia.listDir(wallpaperPath[count]) then
|
||||||
|
wallpaperPathSelected = wallpaperPath[count]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local entries, err = noctalia.listDir(wallpaperPathSelected)
|
||||||
|
if not entries then
|
||||||
|
warn(err or "impossible de lister")
|
||||||
|
noctalia.notify(tr("panel.title"), tr("panel.list_failed"))
|
||||||
|
return wallpaper
|
||||||
|
end
|
||||||
|
for _, entryName in entries do
|
||||||
|
|
||||||
|
if not tonumber(entryName) then -- test if is a id
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
local content, readErr = noctalia.readFile(wallpaperPathSelected .. entryName .. "/project.json")
|
||||||
|
if content then
|
||||||
|
local value = noctalia.json.decode(content)
|
||||||
|
local name = value.title or entryName
|
||||||
|
local preview = value.preview or "no preview"
|
||||||
|
table.insert(wallpaper, {
|
||||||
|
nb = entryName,
|
||||||
|
name = name,
|
||||||
|
preview = preview,
|
||||||
|
})
|
||||||
|
else
|
||||||
|
noctalia.notify(tr("panel.title"), tr("panel.missing_project_json", { name = entryName }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return wallpaper
|
||||||
|
end
|
||||||
|
|
||||||
|
function trim(s) -- personnal trim
|
||||||
|
return (s:gsub("^%s+", ""):gsub("%s+$", ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
function isValidPid(screen, pid)
|
||||||
|
local path = "/tmp/w-engine-" .. screen .. ".pid"
|
||||||
|
if noctalia.fileExists(path) then
|
||||||
|
local fileTmp = noctalia.readFile(path)
|
||||||
|
if not fileTmp then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local file = trim(fileTmp)
|
||||||
|
if file == pid then
|
||||||
|
|
||||||
|
local check = "/proc/" .. pid .. "/cmdline"
|
||||||
|
local checkContentTmp = noctalia.readFile(check)
|
||||||
|
if not checkContentTmp then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local checkContent = trim(checkContentTmp)
|
||||||
|
local match = "^linux%-wallpaperengine"
|
||||||
|
if checkContent:match(match) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function getStoredPid(screen)
|
||||||
|
for _, entry in ipairs(pidList) do
|
||||||
|
if entry.output == screen then
|
||||||
|
if isValidPid(screen, entry.pid) then
|
||||||
|
return entry.pid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function setStoredPid(screen, pid)
|
||||||
|
for _, entry in ipairs(pidList) do
|
||||||
|
if entry.output == screen then
|
||||||
|
entry.pid = pid
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(pidList, { pid = pid, output = screen })
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function launchWallpaper(screen, id, pidFile)
|
||||||
|
local cmd = "setsid linux-wallpaperengine --screen-root " .. screen .. " " .. id
|
||||||
|
.. " > /dev/null 2>&1 & echo $! > " .. pidFile
|
||||||
|
|
||||||
|
noctalia.runAsync(cmd, function(result)
|
||||||
|
local pidContent = noctalia.readFile(pidFile)
|
||||||
|
if pidContent then
|
||||||
|
local pid = trim(pidContent)
|
||||||
|
setStoredPid(screen, pid)
|
||||||
|
end
|
||||||
|
end, 5000)
|
||||||
|
end
|
||||||
|
|
||||||
|
function loadWallpaper(id)
|
||||||
|
lastOutputUse = outputSelectedName or takeOutput()
|
||||||
|
local pidFile = "/tmp/w-engine-" .. lastOutputUse .. ".pid"
|
||||||
|
local oldPid = getStoredPid(lastOutputUse) -- here
|
||||||
|
if oldPid then
|
||||||
|
noctalia.runAsync("kill " .. oldPid, function() -- here
|
||||||
|
launchWallpaper(lastOutputUse, id, pidFile)
|
||||||
|
end, 3000)
|
||||||
|
else
|
||||||
|
launchWallpaper(lastOutputUse, id, pidFile)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function onPickAt(slot)
|
||||||
|
local id = pickSlots[slot]
|
||||||
|
if id ~= nil then
|
||||||
|
loadWallpaper(id)
|
||||||
|
noctalia.notify(tr("panel.title"), tr("panel.apply", { id = id }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function render()
|
||||||
|
pickSlots = {}
|
||||||
|
local tiles = {}
|
||||||
|
local list = takeWallpaper()
|
||||||
|
for count = 1, #list, 1 do
|
||||||
|
local data = list[count]
|
||||||
|
local slotIndex = count - 1
|
||||||
|
pickSlots[slotIndex] = data.nb
|
||||||
|
table.insert(tiles, ui.column({ gap = 4, key = data.nb, align = "center" }, {
|
||||||
|
ui.image({
|
||||||
|
path = wallpaperPathSelected .. data.nb .. "/" .. data.preview,
|
||||||
|
width = previewWidth,
|
||||||
|
height = previewHeight,
|
||||||
|
fit = "cover",
|
||||||
|
radius = 8,
|
||||||
|
onClick = function()
|
||||||
|
loadWallpaper(pickSlots[slotIndex])
|
||||||
|
noctalia.notify(tr("panel.title"), tr("panel.apply", { id = pickSlots[slotIndex] }))
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
ui.label({
|
||||||
|
text = data.name,
|
||||||
|
fontSize = 11,
|
||||||
|
maxLines = 1,
|
||||||
|
maxWidth = 120,
|
||||||
|
textAlign = "center",
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
local rows = {}
|
||||||
|
local row = {}
|
||||||
|
for i, tile in ipairs(tiles) do
|
||||||
|
table.insert(row, tile)
|
||||||
|
if #row == columns or i == #tiles then
|
||||||
|
table.insert(rows, ui.row({ gap = 12, align = "start" }, row))
|
||||||
|
row = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
panel.render(ui.column({ flexGrow = 1, gap = 16 }, {
|
||||||
|
ui.row({ align = "center", justify = "space_between", gap = 8 }, {
|
||||||
|
ui.label({ text = tr("panel.title"), align = "center", fontSize = 16, fontWeight = "bold", color = "on_surface", flexGrow = 1 }),
|
||||||
|
ui.select({ options = getOutput(), onChange = "outputSelected" }),
|
||||||
|
ui.button({ glyph = "close", onClick = "onCloseClicked" }),
|
||||||
|
}),
|
||||||
|
ui.scroll({ gap = 12, align = "stretch", flexGrow = 1 }, rows),
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
function takeOutput()
|
||||||
|
local output = noctalia.outputs()
|
||||||
|
for _, outputF in ipairs(output) do
|
||||||
|
return outputF.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function outputSelected(index, label)
|
||||||
|
if type(label) == "string" and label ~= "" then
|
||||||
|
outputSelectedName = label
|
||||||
|
noctalia.notify("Output change :", outputSelectedName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function getOutput()
|
||||||
|
outputFinalList = {}
|
||||||
|
local outputsList = noctalia.outputs()
|
||||||
|
if not outputsList then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, output in ipairs(outputsList) do
|
||||||
|
noctalia.log(output.name)
|
||||||
|
table.insert(outputFinalList, output.name)
|
||||||
|
end
|
||||||
|
return outputFinalList
|
||||||
|
end
|
||||||
|
|
||||||
|
function onOpen(_context)
|
||||||
|
outputSelectedName = takeOutput()
|
||||||
|
render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function onCloseClicked()
|
||||||
|
panel.close()
|
||||||
|
end
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
function update()
|
||||||
|
noctalia.setUpdateInterval(1000)
|
||||||
|
barWidget.setGlyph("movie")
|
||||||
|
end
|
||||||
|
|
||||||
|
function onClick()
|
||||||
|
noctalia.togglePanel("tadomika_ari/w-engine:w-engine-panel")
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user