1 // Copyright © 2017 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.string : toStringz;
87         const(char)* displayName = name.length ? toStringz(name) : null;
88         auto nativeDpy = wl_display_connect(displayName);
89         return nativeDpy ? new WlDisplay(nativeDpy) : null;
90     }
91 
92     static WlDisplay connectToFd(in int fd)
93     {
94         auto nativeDpy = wl_display_connect_to_fd(fd);
95         return nativeDpy ? new WlDisplay(nativeDpy) : null;
96     }
97 
98     final override @property inout(wl_display)* native() inout
99     {
100         return cast(inout(wl_display)*)(proxy);
101     }
102 
103     final void disconnect()
104     {
105         wl_display_disconnect(native);
106         WlProxy.destroyNotify();
107     }
108 
109     final int getFd()
110     {
111         return wl_display_get_fd(native);
112     }
113 
114     final int dispatch()
115     {
116         return wl_display_dispatch(native);
117     }
118 
119     final int dispatchPending()
120     {
121         return wl_display_dispatch_pending(native);
122     }
123 
124     final int dispatchQueue(WlEventQueue queue)
125     {
126         return wl_display_dispatch_queue(native, queue.native);
127     }
128 
129     final int dispatchQueuePending(WlEventQueue queue)
130     {
131         return wl_display_dispatch_queue_pending(native, queue.native);
132     }
133 
134     final int getError()
135     {
136         return wl_display_get_error(native);
137     }
138 
139     //uint wl_display_get_protocol_error(wl_display* display, const(wl_interface)** iface, uint* id);
140 
141     final int flush()
142     {
143         return wl_display_flush(native);
144     }
145 
146     final int roundtripQueue(WlEventQueue queue)
147     {
148         return wl_display_roundtrip_queue(native, queue.native);
149     }
150 
151     final int roundtrip()
152     {
153         return wl_display_roundtrip(native);
154     }
155 
156     final WlEventQueue createQueue()
157     {
158         return new WlEventQueue(wl_display_create_queue(native));
159     }
160 
161     final int prepareReadQueue(WlEventQueue queue)
162     {
163         return wl_display_prepare_read_queue(native, queue.native);
164     }
165 
166     final int prepareRead()
167     {
168         return wl_display_prepare_read(native);
169     }
170 
171     final void cancelRead()
172     {
173         wl_display_cancel_read(native);
174     }
175 
176     final int readEvents()
177     {
178         return wl_display_read_events(native);
179     }
180 }
181 
182 /// Wrapper around wl_proxy and base class for types generated by the protocol.
183 abstract class WlProxy
184 {
185     private wl_proxy* _proxy;
186 
187     private static WlProxy[wl_proxy*] proxyCache;
188 
189     protected this(wl_proxy* proxy)
190     {
191         _proxy = proxy;
192         proxyCache[proxy] = this;
193     }
194 
195     protected void destroyNotify()
196     {
197         proxyCache.remove(_proxy);
198         _proxy = null;
199     }
200 
201     static WlProxy get(wl_proxy* proxy)
202     {
203         auto pp = proxy in proxyCache;
204         if (pp) return *pp;
205         else return null;
206     }
207 
208     final @property inout(wl_proxy)* proxy() inout
209     {
210         return _proxy;
211     }
212 
213     /// Get the protocol version of WlDisplay.
214     final @property uint ver()
215     {
216         return wl_proxy_get_version(proxy);
217     }
218 
219     /// Get the id assigned to this object.
220     final @property uint id()
221     {
222         return wl_proxy_get_id(proxy);
223     }
224 
225     /// Get the class of this object.
226     final @property string class_()
227     {
228         import std.string : fromStringz;
229         return fromStringz(wl_proxy_get_class(proxy)).idup;
230     }
231 }
232 
233 immutable abstract class WlProxyInterface : WlInterface
234 {
235     this(immutable wl_interface* native)
236     {
237         super(native);
238     }
239 
240     abstract WlProxy makeProxy(wl_proxy* proxy) immutable;
241 }