1 // Copyright © 2017-2021 Rémi Thebault
2 module wayland.server.core;
3 
4 import wayland.server.protocol : WlDisplay, WlShm;
5 import wayland.server.eventloop;
6 import wayland.server.listener;
7 import wayland.native.server;
8 import wayland.native.util;
9 import wayland.util;
10 
11 import std..string;
12 import std.stdio;
13 import std.exception : enforce;
14 import core.sys.posix.sys.types;
15 
16 
17 enum : uint
18 {
19     WL_EVENT_READABLE = 0x01,
20     WL_EVENT_WRITABLE = 0x02,
21     WL_EVENT_HANGUP   = 0x04,
22     WL_EVENT_ERROR    = 0x08
23 }
24 
25 
26 class WlDisplayBase : Native!wl_display
27 {
28     mixin nativeImpl!(wl_display);
29 
30     alias DestroySig = Signal!();
31     alias ClientCreatedSig = Signal!(WlClient);
32 
33     private wl_listener _destroyListener;
34     private DestroySig _destroySig;
35 
36     private wl_listener _clientCreatedListener;
37     private ClientCreatedSig _clientCreatedSig;
38 
39     // one loop per display, so no need to use the object store
40     private WlEventLoop _loop;
41 
42     private WlClient[] _clients;
43 
44 
45     static WlDisplay create()
46     {
47         return new WlDisplay(wl_display_create());
48     }
49 
50     protected this (wl_display* native)
51     {
52         _native = native;
53         ObjectCache.set(native, this);
54 
55         wl_list_init(&_destroyListener.link);
56         _destroyListener.notify = &wl_d_display_destroy;
57         wl_display_add_destroy_listener(native, &_destroyListener);
58 
59         wl_list_init(&_clientCreatedListener.link);
60         _clientCreatedListener.notify = &wl_d_client_created;
61         wl_display_add_client_created_listener(native, &_clientCreatedListener);
62     }
63 
64     void destroy()
65     {
66         wl_display_destroy(native);
67     }
68 
69     private DestroySig destroySig()
70     {
71         if (!_destroySig) _destroySig = new DestroySig();
72         return _destroySig;
73     }
74 
75     private ClientCreatedSig clientCreatedSig()
76     {
77         if (!_clientCreatedSig) _clientCreatedSig = new ClientCreatedSig();
78         return _clientCreatedSig;
79     }
80 
81     void addDestroyListener(DestroySig.Listener listener)
82     {
83         destroySig.add(listener);
84     }
85 
86     void addClientCreatedListener(ClientCreatedSig.Listener listener)
87     {
88         clientCreatedSig.add(listener);
89     }
90 
91     @property WlEventLoop eventLoop()
92     {
93         if (!_loop) _loop = new WlEventLoop(wl_display_get_event_loop(native));
94         return _loop;
95     }
96 
97     int addSocket(string name)
98     {
99         return wl_display_add_socket(native, toStringz(name));
100     }
101 
102     string addSocketAuto()
103     {
104         return fromStringz(wl_display_add_socket_auto(native)).idup;
105     }
106 
107     int addSocketFd(int fd)
108     {
109         return wl_display_add_socket_fd(native, fd);
110     }
111 
112     void terminate()
113     {
114         wl_display_terminate(native);
115     }
116 
117     void run()
118     {
119         wl_display_run(native);
120     }
121 
122     void flushClients()
123     {
124         wl_display_flush_clients(native);
125     }
126 
127     @property uint serial()
128     {
129         return wl_display_get_serial(native);
130     }
131 
132     uint nextSerial()
133     {
134         return wl_display_next_serial(native);
135     }
136 
137     WlClient createClient(int fd)
138     {
139         auto natCl = wl_client_create(native, fd);
140         WlClient cl = cast(WlClient)ObjectCache.get(natCl);
141         assert(cl, "could not retrieve client from obj cache");
142         return cl;
143     }
144 
145     @property WlClient[] clients()
146     {
147         return _clients;
148     }
149 
150     void initShm()
151     {
152         wl_display_init_shm(native);
153     }
154 
155     void addShmFormat(WlShm.Format format)
156     {
157         wl_display_add_shm_format(native, cast(uint)format);
158     }
159 }
160 
161 
162 abstract class WlGlobal : Native!wl_global
163 {
164     mixin nativeImpl!wl_global;
165 
166     this (wl_global* native)
167     {
168         _native = native;
169         ObjectCache.set(native, this);
170     }
171 
172     void destroy()
173     {
174         wl_global_destroy(_native);
175     }
176 }
177 
178 
179 struct Credentials
180 {
181     pid_t pid;
182     uid_t uid;
183     gid_t gid;
184 }
185 
186 final class WlClient : Native!wl_client
187 {
188     mixin nativeImpl!wl_client;
189 
190     alias DestroySig = Signal!(WlClient);
191     alias NativeResourceCreatedSig = Signal!(wl_resource*);
192 
193     private wl_listener _destroyListener;
194     private DestroySig _destroySig;
195 
196     private wl_listener _resourceCreatedListener;
197     private NativeResourceCreatedSig _nativeResourceCreatedSig;
198 
199     this (wl_client* native)
200     {
201         _native = native;
202         ObjectCache.set(native, this);
203 
204         wl_list_init(&_destroyListener.link);
205         _destroyListener.notify = &wl_d_client_destroy;
206         wl_client_add_destroy_listener(native, &_destroyListener);
207 
208         wl_list_init(&_resourceCreatedListener.link);
209         _resourceCreatedListener.notify = &wl_d_client_resource_created;
210         wl_client_add_resource_created_listener(native, &_resourceCreatedListener);
211     }
212 
213     void destroy()
214     {
215         wl_client_destroy(native);
216     }
217 
218     private DestroySig destroySig()
219     {
220         if (!_destroySig) _destroySig = new DestroySig();
221         return _destroySig;
222     }
223 
224     private NativeResourceCreatedSig nativeResourceCreatedSig()
225     {
226         if (!_nativeResourceCreatedSig) _nativeResourceCreatedSig = new NativeResourceCreatedSig();
227         return _nativeResourceCreatedSig;
228     }
229 
230     void addDestroyListener(DestroySig.Listener listener)
231     {
232         destroySig.add(listener);
233     }
234 
235     void addNativeResourceCreatedListener(NativeResourceCreatedSig.Listener listener)
236     {
237         nativeResourceCreatedSig.add(listener);
238     }
239 
240     void flush()
241     {
242         wl_client_flush(native);
243     }
244 
245     @property Credentials credentials()
246     {
247         Credentials res;
248         wl_client_get_credentials(native, &res.pid, &res.uid, &res.gid);
249         return res;
250     }
251 
252     @property int fd()
253     {
254         return wl_client_get_fd(native);
255     }
256 
257     WlResource object(uint id)
258     {
259         auto natRes = wl_client_get_object(native, id);
260         if (!natRes) return null;
261         auto res = cast(WlResource)ObjectCache.get(natRes);
262         assert(res);
263         return res;
264     }
265 
266     void postNoMemory()
267     {
268         wl_client_post_no_memory(native);
269     }
270 
271     @property WlDisplay display()
272     {
273         auto natDpy = wl_client_get_display(native);
274         assert(natDpy);
275         auto dpy = cast(WlDisplay)ObjectCache.get(natDpy);
276         assert(dpy);
277         return dpy;
278     }
279 }
280 
281 abstract class WlResource : Native!wl_resource
282 {
283     mixin nativeImpl!wl_resource;
284 
285     alias DestroySig = Signal!(WlResource);
286 
287     private wl_listener _destroyListener;
288     private DestroySig _destroySig;
289 
290     this (wl_resource* native)
291     {
292         _native = native;
293         ObjectCache.set(native, this);
294 
295         wl_list_init(&_destroyListener.link);
296         _destroyListener.notify = &wl_d_resource_destroy;
297         wl_resource_add_destroy_listener(native, &_destroyListener);
298     }
299 
300     void destroy()
301     {
302         wl_resource_destroy(native);
303     }
304 
305     private DestroySig destroySig()
306     {
307         if (!_destroySig) _destroySig = new DestroySig();
308         return _destroySig;
309     }
310 
311     void addDestroyListener(DestroySig.Listener listener)
312     {
313         destroySig.add(listener);
314     }
315 
316     @property uint id()
317     {
318         return wl_resource_get_id(native);
319     }
320 
321     @property WlClient client()
322     {
323         auto natCl = wl_resource_get_client(native);
324         assert(natCl);
325         auto cl = cast(WlClient)ObjectCache.get(natCl);
326         assert(cl);
327         return cl;
328     }
329 
330     @property int ver()
331     {
332         return wl_resource_get_version(native);
333     }
334 
335     @property string cls()
336     {
337         return fromStringz(wl_resource_get_class(native)).idup;
338     }
339 
340     void postError(Args...)(uint code, string fmt, Args args)
341     {
342         wl_resource_post_error(native, code, toStringz(format(fmt, args)));
343     }
344 }
345 
346 private extern(C) nothrow
347 {
348     void wl_d_display_destroy(wl_listener*, void* data)
349     {
350         nothrowFnWrapper!({
351             auto dpy = cast(WlDisplayBase)ObjectCache.get(data);
352             assert(dpy, "wl_d_display_destroy: could not get display from cache");
353             if (dpy._destroySig) dpy._destroySig.emit();
354             ObjectCache.remove(data);
355         });
356     }
357 
358     void wl_d_client_created(wl_listener*, void* data)
359     {
360         nothrowFnWrapper!({
361             auto natCl = cast(wl_client*)data;
362             auto natDpy = wl_client_get_display(natCl);
363             auto dpy = cast(WlDisplayBase)ObjectCache.get(natDpy);
364             assert(dpy, "wl_d_client_created: could not get display from cache");
365 
366             auto cl = new WlClient(natCl);
367             dpy._clients ~= cl;
368             if (dpy._clientCreatedSig) dpy._clientCreatedSig.emit(cl);
369         });
370     }
371 
372     void wl_d_client_destroy(wl_listener*, void* data)
373     {
374         nothrowFnWrapper!({
375             auto natCl = cast(wl_client*)data;
376             auto natDpy = wl_client_get_display(natCl);
377             auto dpy = cast(WlDisplayBase)ObjectCache.get(natDpy);
378             assert(dpy, "wl_d_client_destroy: could not get display from cache");
379             WlClient cl = cast(WlClient)ObjectCache.get(natCl);
380             assert(cl, "wl_d_client_destroy: could not get client from cache");
381 
382             import std.algorithm : remove;
383             if (cl._destroySig) cl._destroySig.emit(cl);
384             dpy._clients = dpy._clients.remove!(c => c is cl);
385             ObjectCache.remove(natCl);
386         });
387     }
388 
389     void wl_d_client_resource_created(wl_listener*, void* data)
390     {
391         nothrowFnWrapper!({
392             auto natRes = cast(wl_resource*)data;
393             auto natCl = wl_resource_get_client(natRes);
394             auto cl = cast(WlClient)ObjectCache.get(natCl);
395             assert(cl);
396             if (cl._nativeResourceCreatedSig) cl._nativeResourceCreatedSig.emit(natRes);
397         });
398     }
399 
400     void wl_d_resource_destroy(wl_listener*, void* data)
401     {
402         nothrowFnWrapper!({
403             auto natRes = cast(wl_resource*)data;
404 
405             auto res = cast(WlResource)ObjectCache.get(natRes);
406             if (res && res._destroySig) res._destroySig.emit(res);
407 
408             ObjectCache.remove(natRes);
409         });
410     }
411 }