1 module compositor;
2 
3 import backend;
4 import seat;
5 import shell;
6 import output;
7 import wayland.server;
8 import wayland.server.shm;
9 import wayland.native.server;
10 
11 import std.algorithm;
12 import std.typecons : Flag;
13 import std.stdio;
14 import std.process;
15 import std.format;
16 import std.exception;
17 import core.time;
18 
19 
20 class Compositor : WlCompositor
21 {
22 	private {
23 		WlDisplay _display;
24 
25 		Backend _backend;
26 		Seat _seat;
27 		Shell _shell;
28 
29 		WlClient[] _clients;
30 		Output[] _outputs;
31 		uint _outputMaskShift;
32 
33 		MonoTime _startTime;
34 	}
35 
36 	this(WlDisplay display)
37 	{
38 		this._display = display;
39 		super(display, ver);
40 		_seat = new Seat(this);
41 		_shell = new Shell(this);
42 		_startTime = MonoTime.currTime;
43 	}
44 
45 	@property Duration time()
46 	{
47 		return MonoTime.currTime - _startTime;
48 	}
49 
50 	@property WlDisplay display()
51 	{
52 		return _display;
53 	}
54 
55 	@property Output[] outputs()
56 	{
57 		return _outputs;
58 	}
59 
60 	@property Shell shell()
61 	{
62 		return _shell;
63 	}
64 
65 	@property Seat seat()
66 	{
67 		return _seat;
68 	}
69 
70 	void addOutput(Output output)
71 	{
72 		_outputMaskShift += 1;
73 		output.mask = 1 << _outputMaskShift;
74 		_outputs ~= output;
75 	}
76 
77 	void exit()
78 	{
79 		_display.terminate();
80 	}
81 
82     void eventExpose()
83 	{}
84 
85     void eventMouseMove(int x, int y)
86 	{}
87 
88     void eventMouseButton(int x, int y, int button, WlPointer.ButtonState state)
89 	{
90 		_shell.mouseButton(x, y, button, state);
91 	}
92 
93     void eventKey(int key, Flag!"down" down)
94 	{}
95 
96 	void scheduleRepaint()
97 	{
98 		foreach (o; _outputs) {
99 			o.scheduleRepaint();
100 		}
101 	}
102 
103 	// WlCompositor
104 
105 	override WlSurface createSurface(WlClient cl, Resource, uint id)
106 	{
107 		return new Surface(this, cl, id);
108 	}
109 
110 	override WlRegion createRegion(WlClient cl, Resource, uint id)
111 	{
112 		return new Region(cl, id);
113 	}
114 
115 private:
116 
117 	int run()
118 	{
119 		_display.initShm();
120 
121 		_backend = Backend.create();
122 		_backend.initialize(new BackendConfig(false, 640, 480), this);
123 		scope(exit) _backend.terminate();
124 
125 		addOutput(_backend.createOutput());
126 
127 		auto timer = _display.eventLoop.addTimer({
128 			spawnProcess([
129 				"wayland-tracker", "simple",
130 				"-x", "protocol/wayland.xml",
131 				"--", "examples/hello/wayland_hello"
132 			]);
133 			return 1;
134 		});
135 		timer.update(1000);
136 
137 		_display.addClientCreatedListener(&addClient);
138 
139 		scheduleRepaint();
140 
141 		_display.run();
142 
143 		return 0;
144 	}
145 
146 	void addClient(WlClient cl)
147 	{
148 		_clients ~= cl;
149 		cl.addDestroyListener(&removeClient);
150 		// Some interfaces are implemented by libwayland-server (i.e. wl_shm, wl_shm_pool).
151 		// Therefore, some resources are not created in the D code stack.
152 		// Here we listen for the creation of buffer and wrap them with a D object.
153 		cl.addNativeResourceCreatedListener((wl_resource* natRes) {
154 			import core.stdc.string : strcmp;
155 			if (strcmp(wl_resource_get_class(natRes), "wl_buffer") == 0) {
156 				new Buffer(natRes);
157 			}
158 		});
159 	}
160 
161 	void removeClient(WlClient cl)
162 	{
163 		_clients = _clients.remove!(c => c is cl);
164 	}
165 }
166 
167 struct Rect {
168 	int x; int y; int width; int height;
169 }
170 
171 // dumbest possible region implementation
172 class Region : WlRegion
173 {
174 	Rect[] rects;
175 
176 	this(WlClient cl, int id)
177     {
178         super(cl, WlRegion.ver, id);
179     }
180 
181 	override protected void destroy(WlClient cl)
182 	{}
183 
184     override protected void add(WlClient cl,
185                                 int x,
186                                 int y,
187                                 int width,
188                                 int height)
189 	{
190 		// add without checking for interference
191 		rects ~= Rect(x, y, width, height);
192 	}
193 
194     override protected void subtract(WlClient cl,
195                                      int x,
196                                      int y,
197                                      int width,
198                                      int height)
199 	{
200 		// naive tentative. no complex algo to refont the rect list
201 		immutable rs = Rect(x, y, width, height);
202 		foreach (i, r; rects) {
203 			if (r == rs) {
204 				rects = rects.remove(i);
205 				return;
206 			}
207 		}
208 	}
209 }
210 
211 
212 class Buffer : WlBuffer
213 {
214 	WlShmBuffer shmBuffer;
215 	int width;
216 	int height;
217 	size_t stride;
218 	WlShm.Format format;
219 
220 	this(wl_resource* natRes) {
221 		super(natRes);
222 	}
223 
224 	void fetch()
225 	{
226 		shmBuffer = enforce(WlShmBuffer.get(this));
227 		width = shmBuffer.width;
228 		height = shmBuffer.height;
229 		stride = shmBuffer.stride;
230 		format = shmBuffer.format;
231 	}
232 
233 	void[] beginAccess()
234 	{
235 		shmBuffer.beginAccess();
236 		return shmBuffer.data();
237 	}
238 
239 	void endAccess()
240 	{
241 		shmBuffer.endAccess();
242 	}
243 
244 	// WlBuffer
245 
246 	override protected void destroy(WlClient cl)
247 	{}
248 }
249 
250 class SurfaceState
251 {
252 	// attach
253 	bool newlyAttached;
254 	int x; int y;
255 	Buffer buffer;
256 
257 	// damage
258 	Rect[] damageReg;
259 	// damageBuffer
260 	Rect[] damageBufferReg;
261 	// setOpaqueRegion
262 	Rect[] opaqueReg;
263 	// setInputRegion
264 	Rect[] inputReg;
265 
266 	void flushTo(SurfaceState state)
267 	{
268 		state.newlyAttached = newlyAttached;
269 		state.x = x;
270 		state.y = y;
271 		state.buffer = buffer;
272 		state.damageReg = damageReg;
273 		state.damageBufferReg = damageBufferReg;
274 		state.opaqueReg = opaqueReg;
275 		state.inputReg = inputReg;
276 	}
277 }
278 
279 class AlreadyAssignedRoleException : Exception
280 {
281 	this(string oldRole, string newRole)
282 	{
283 		super(format("Surface role already assigned: was '%s', tentative to assign '%s'.", oldRole, newRole));
284 	}
285 }
286 
287 class Surface : WlSurface
288 {
289 	private Compositor _comp;
290 	private SurfaceState _pending;
291 	private SurfaceState _state;
292 	private string _role;
293 	private uint _outputMask;
294 
295 	this(Compositor comp, WlClient cl, uint id) {
296 		_comp = comp;
297 		_pending = new SurfaceState;
298 		_state = new SurfaceState;
299 		super(cl, WlSurface.ver, id);
300 	}
301 
302 	@property SurfaceState state()
303 	{
304 		return _state;
305 	}
306 
307 	@property string role()
308 	{
309 		return _role;
310 	}
311 
312 	void assignRole(string role)
313 	{
314 		if (_role.length && _role != role)
315 		{
316 			throw new AlreadyAssignedRoleException(_role, role);
317 		}
318 	}
319 
320 	@property uint outputMask()
321 	{
322 		return _outputMask;
323 	}
324 
325 	@property void outputMask(uint mask)
326 	{
327 		_outputMask = mask;
328 	}
329 
330 	void scheduleRepaint()
331 	{
332 		foreach (o; _comp.outputs)
333 		{
334 			if (_outputMask & o.mask) {
335 				o.scheduleRepaint();
336 			}
337 		}
338 	}
339 
340 	// WlSurface
341 
342 	override protected void destroy(WlClient cl)
343 	{
344 
345 	}
346 
347     override protected void attach(WlClient cl,
348                                    WlBuffer buffer,
349                                    int x,
350                                    int y)
351 	{
352 		auto b = cast(Buffer)buffer;
353 		_pending.buffer = b;
354 		_pending.newlyAttached = true;
355 		_pending.x = x;
356 		_pending.y = y;
357 		if (b) b.fetch();
358 	}
359 
360     override protected void damage(WlClient cl,
361                                    int x,
362                                    int y,
363                                    int width,
364                                    int height)
365 	{
366 		_pending.damageReg ~= Rect(x, y, width, height);
367 	}
368 
369     override protected WlCallback frame(WlClient cl,
370                                   	   	uint callback)
371 	{
372 		return null;
373 	}
374 
375     override protected void setOpaqueRegion(WlClient cl,
376                                             WlRegion region)
377 	{
378 		_pending.opaqueReg = (cast(Region)region).rects;
379 	}
380 
381     override protected void setInputRegion(WlClient cl,
382                                            WlRegion region)
383 	{
384 		_pending.inputReg = (cast(Region)region).rects;
385 	}
386 
387     override protected void commit(WlClient cl)
388 	{
389 		_pending.flushTo(_state);
390 		scheduleRepaint();
391 	}
392 
393     override protected void setBufferTransform(WlClient cl,
394                                                int transform)
395 	{}
396 
397     override protected void setBufferScale(WlClient cl,
398                                            int scale)
399 	{}
400 
401     override protected void damageBuffer(WlClient cl,
402                                          int x,
403                                          int y,
404                                          int width,
405                                          int height)
406 	{
407 		_pending.damageBufferReg ~= Rect(x, y, width, height);
408 	}
409 }
410 
411 
412 int main()
413 {
414 	auto display = WlDisplay.create();
415 	scope(exit) display.destroy();
416 
417 	environment["WAYLAND_DISPLAY"] = display.addSocketAuto();
418 
419 	auto comp = new Compositor(display);
420 	scope(exit) comp.destroy();
421 
422 	return comp.run();
423 }