-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwallpaper.lua
More file actions
213 lines (186 loc) · 6.68 KB
/
Copy pathwallpaper.lua
File metadata and controls
213 lines (186 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
-- plugins/wallpaper.lua
-- Native wallpaper plugin - renders directly in compositor scene graph
-- For color wallpapers: uses wlr_scene_rect (native)
-- For image wallpapers: starts the wallpaper client (Wayland client)
local log = require("shared.log")
local wl = require("shared.wayland.server")
local color = require("shared.color")
local process = require("compositor.process")
local ffi = wl._ffi
local plugin = {
name = "wallpaper",
}
-- State
local background_nodes = {} -- Output object -> { type = "rect"|"client", node = scene_node, process = Process }
local config_cache = nil
local wallpaper_processes = {} -- track client processes
-- Parse color to RGBA float array (0-1 range) for wlroots
local function parse_color(value)
if type(value) == "string" and value:sub(1, 1) == "#" then
local c = color.hex(value)
if c then
return ffi.new("float[4]", c)
end
elseif type(value) == "table" then
return ffi.new("float[4]", value) -- Already a float array
end
-- Default dark gray
return ffi.new("float[4]", { 0.2, 0.2, 0.2, 1.0 })
end
-- Start wallpaper client for an output with image config
local function start_wallpaper_client(output_name, image_path, scale_mode)
local p = process.new({ program = "clients.wallpaper" })
-- Args parser expects --key=value format
p:set_args({
string.format("--output=%s", output_name),
string.format("--image=%s", image_path),
string.format("--scale=%s", scale_mode or "fill"),
})
if p:start_local() then
table.insert(wallpaper_processes, p)
return p
else
log.error("Failed to start wallpaper client for %s", output_name)
return nil
end
end
-- Cleanup existing wallpaper for an output
local function cleanup_wallpaper(output)
local existing = background_nodes[output]
if existing then
if existing.node then
wl.roots.scene_node_destroy(existing.node)
end
if existing.process and existing.process:is_running() then
existing.process:stop()
end
background_nodes[output] = nil
end
end
-- Create or update wallpaper for an output
local function update_wallpaper_for_output(output)
local surface_service = require("compositor.services.surface")
local output_service = require("compositor.services.output")
local output_name = output.name
-- Get output dimensions
local width, height = output_service:get_output_dimensions(output)
if not width or width <= 0 then
width = 1920
height = 1080
end
-- Find config for this output
local wallpaper_value = nil
if config_cache then
local wildcard_value = nil
for key, value in pairs(config_cache) do
local key_name = nil
if type(key) == "table" and key.name then
key_name = key.name
elseif type(key) == "table" and key.get_name then
key_name = key:get_name()
elseif type(key) == "string" then
key_name = key
end
if key_name == output_name then
wallpaper_value = value
break
elseif key_name == "*" then
wildcard_value = value
end
end
if wallpaper_value == nil then
wallpaper_value = wildcard_value
end
end
-- Remove existing wallpaper
cleanup_wallpaper(output)
-- Get output position
local ox, oy = output_service:get_output_position(output)
ox = ox or 0
oy = oy or 0
-- Handle image wallpaper -> use client
if type(wallpaper_value) == "table" and wallpaper_value.image then
local scale_mode = wallpaper_value.scale or "fill"
local p = start_wallpaper_client(output_name, wallpaper_value.image, scale_mode)
if p then
background_nodes[output] = { type = "client", process = p }
end
return
end
-- Handle color wallpaper -> use native scene rect
if type(wallpaper_value) == "string" and wallpaper_value:sub(1, 1) == "#" then
local rgba = parse_color(wallpaper_value)
local rect = wl.roots.scene_rect_create(
surface_service.layers.background,
width, height, rgba
)
if rect and rect ~= ffi.NULL then
wl.roots.scene_node_set_position(rect.node, ox, oy)
background_nodes[output] = { type = "rect", node = rect.node }
end
return
end
-- Default dark background (native)
local rgba = parse_color("#1a1a2e")
local rect = wl.roots.scene_rect_create(
surface_service.layers.background,
width, height, rgba
)
if rect and rect ~= ffi.NULL then
wl.roots.scene_node_set_position(rect.node, ox, oy)
background_nodes[output] = { type = "rect", node = rect.node }
end
end
function plugin:mount(config)
config_cache = config
local output_service = require("compositor.services.output")
local core_service = require("compositor.services.core")
-- Subscribe to compositor:ready to create wallpapers after outputs exist
self._ready_listener = core_service.events:on("compositor:ready", function()
-- Create wallpapers for existing outputs
local outputs = output_service:get_outputs()
for _, output in ipairs(outputs) do
update_wallpaper_for_output(output)
end
end)
-- Listen for new outputs
self._output_add_listener = output_service.events:on("output:add", function(output)
update_wallpaper_for_output(output)
end)
-- Listen for output removal
self._output_remove_listener = output_service.events:on("output:remove", function(output)
cleanup_wallpaper(output)
end)
return {}
end
function plugin:unmount()
-- Clean up listeners
if self._ready_listener and self._ready_listener.off then
self._ready_listener:off()
end
if self._output_add_listener and self._output_add_listener.off then
self._output_add_listener:off()
end
if self._output_remove_listener and self._output_remove_listener.off then
self._output_remove_listener:off()
end
-- Destroy background nodes and stop client processes
for _, entry in pairs(background_nodes) do
if entry.node then
wl.roots.scene_node_destroy(entry.node)
end
if entry.process and entry.process:is_running() then
entry.process:stop()
end
end
background_nodes = {}
-- Stop any remaining wallpaper processes
for _, p in ipairs(wallpaper_processes) do
if p:is_running() then
p:stop()
end
end
wallpaper_processes = {}
config_cache = nil
end
return plugin