1 // Copyright © 2017-2021 Rémi Thebault
2 module wayland.client.core;
3 
4 import wayland.client.protocol : WlDisplay;
5 import wayland.native.client;
6 import wayland.native.util;
7 import wayland.util;
8 
9 /++
10  + A queue for WlProxy object events.
11  +
12  + Event queues allows the events on a display to be handled in a thread-safe
13  + manner. See WlDisplay for details.
14  +/
15 class WlEventQueue : Native!wl_event_queue
16 {
17     mixin nativeImpl!(wl_event_queue);
18     this(wl_event_queue* native)
19     {
20         _native = native;
21     }
22 }
23 
24 /++
25  + Represents a connection to the compositor and acts as a proxy to
26  + the wl_display singleton object.
27  +
28  + A WlDisplay object represents a client connection to a Wayland
29  + compositor. It is created with either WlDisplay.connect() or
30  + WlDisplay.connect_to_fd(). A connection is terminated using disconnect().
31  +
32  + A WlDisplay is also used as the WlProxy for the wl_display
33  + singleton object on the compositor side.
34  +
35  + A WlDisplay object handles all the data sent from and to the
36  + compositor. When a WlProxy marshals a request, it will write its wire
37  + representation to the display's write buffer. The data is sent to the
38  + compositor when the client calls WlDisplay.flush().
39  +
40  + Incoming data is handled in two steps: queueing and dispatching. In the
41  + queue step, the data coming from the display fd is interpreted and
42  + added to a queue. On the dispatch step, the handler for the incoming
43  + event set by the client on the corresponding WlProxy is called.
44  +
45  + A WlDisplay has at least one event queue, called the <em>default
46  + queue</em>. Clients can create additional event queues with
47  + WlDisplay.createQueue() and assign WlProxy's to it. Events
48  + occurring in a particular proxy are always queued in its assigned queue.
49  + A client can ensure that a certain assumption, such as holding a lock
50  + or running from a given thread, is true when a proxy event handler is
51  + called by assigning that proxy to an event queue and making sure that
52  + this queue is only dispatched when the assumption holds.
53  +
54  + The default queue is dispatched by calling WlDisplay.dispatch().
55  + This will dispatch any events queued on the default queue and attempt
56  + to read from the display fd if it's empty. Events read are then queued
57  + on the appropriate queues according to the proxy assignment.
58  +
59  + A user created queue is dispatched with WlDisplay.dispatchQueue().
60  + This function behaves exactly the same as WlDisplay.dispatch()
61  + but it dispatches given queue instead of the default queue.
62  +
63  + A real world example of event queue usage is Mesa's implementation of
64  + eglSwapBuffers() for the Wayland platform. This function might need
65  + to block until a frame callback is received, but dispatching the default
66  + queue could cause an event handler on the client to start drawing
67  + again. This problem is solved using another event queue, so that only
68  + the events handled by the EGL code are dispatched during the block.
69  +
70  + This creates a problem where a thread dispatches a non-default
71  + queue, reading all the data from the display fd. If the application
72  + would call \em poll(2) after that it would block, even though there
73  + might be events queued on the default queue. Those events should be
74  + dispatched with WlDisplay.dispatchPending() or
75  + WlDisplay.dispatchQueuePending() before flushing and blocking.
76  +/
77 abstract class WlDisplayBase : WlProxy, Native!wl_display
78 {
79     protected this(wl_display* native)
80     {
81         super(cast(wl_proxy*)native);
82     }
83 
84     static WlDisplay connect(in string name = null)
85     {
86         import std.exception : enforce, ErrnoException;
87         import std..string : toStringz;
88 
89         const(char)* displayName = name.length ? toStringz(name) : null;
90         auto nativeDpy = enforce!ErrnoException(
91             wl_display_connect(displayName),
92             "Could not get a display handle from Wayland"
93         );
94         return new WlDisplay(nativeDpy);
95     }
96 
97     static WlDisplay connectToFd(in int fd)
98     {
99         auto nativeDpy = wl_display_connect_to_fd(fd);
100         return nativeDpy ? new WlDisplay(nativeDpy) : null;
101     }
102 
103     final override @property inout(wl_display)* native() inout
104     {
105         return cast(inout(wl_display)*)(proxy);
106     }
107 
108     final void disconnect()
109     {
110         wl_display_disconnect(native);
111         WlProxy.destroyNotify();
112     }
113 
114     final int getFd()
115     {
116         return wl_display_get_fd(native);
117     }
118 
119     final int dispatch()
120     {
121         return wl_display_dispatch(native);
122     }
123 
124     final int dispatchPending()
125     {
126         return wl_display_dispatch_pending(native);
127     }
128 
129     final int dispatchQueue(WlEventQueue queue)
130     {
131         return wl_display_dispatch_queue(native, queue.native);
132     }
133 
134     final int dispatchQueuePending(WlEventQueue queue)
135     {
136         return wl_display_dispatch_queue_pending(native, queue.native);
137     }
138 
139     final int getError()
140     {
141         return wl_display_get_error(native);
142     }
143 
144     final immutable(WlInterface) getProtocolError(out uint code, out uint id)
145     {
146         const(wl_interface)* iface = null;
147         code = wl_display_get_protocol_error(native, &iface, &id);
148         if (iface) {
149             return new immutable WlInterface(cast(immutable)iface);
150         }
151         else {
152             return null;
153         }
154     }
155 
156     final int flush()
157     {
158         return wl_display_flush(native);
159     }
160 
161     final int roundtripQueue(WlEventQueue queue)
162     {
163         return wl_display_roundtrip_queue(native, queue.native);
164     }
165 
166     final int roundtrip()
167     {
168         return wl_display_roundtrip(native);
169     }
170 
171     final WlEventQueue createQueue()
172     {
173         return new WlEventQueue(wl_display_create_queue(native));
174     }
175 
176     final int prepareReadQueue(WlEventQueue queue)
177     {
178         return wl_display_prepare_read_queue(native, queue.native);
179     }
180 
181     final int prepareRead()
182     {
183         return wl_display_prepare_read(native);
184     }
185 
186     final void cancelRead()
187     {
188         wl_display_cancel_read(native);
189     }
190 
191     final int readEvents()
192     {
193         return wl_display_read_events(native);
194     }
195 }
196 
197 /// Wrapper around wl_proxy and base class for types generated by the protocol.
198 abstract class WlProxy
199 {
200     private wl_proxy* _proxy;
201 
202     private static WlProxy[wl_proxy*] proxyCache;
203 
204     protected this(wl_proxy* proxy)
205     {
206         _proxy = proxy;
207         proxyCache[proxy] = this;
208     }
209 
210     protected void destroyNotify()
211     {
212         proxyCache.remove(_proxy);
213         _proxy = null;
214     }
215 
216     static WlProxy get(wl_proxy* proxy)
217     {
218         auto pp = proxy in proxyCache;
219         if (pp) return *pp;
220         else return null;
221     }
222 
223     final @property inout(wl_proxy)* proxy() inout
224     {
225         return _proxy;
226     }
227 
228     /// Get the protocol version of WlDisplay.
229     final @property uint ver()
230     {
231         return wl_proxy_get_version(proxy);
232     }
233 
234     /// Get the id assigned to this object.
235     final @property uint id()
236     {
237         return wl_proxy_get_id(proxy);
238     }
239 
240     /// Get the class of this object.
241     final @property string class_()
242     {
243         import std..string : fromStringz;
244         return fromStringz(wl_proxy_get_class(proxy)).idup;
245     }
246 }
247 
248 immutable abstract class WlProxyInterface : WlInterface
249 {
250     this(immutable wl_interface* native)
251     {
252         super(native);
253     }
254 
255     abstract WlProxy makeProxy(wl_proxy* proxy) immutable;
256 }