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 }