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 final immutable(WlInterface) getProtocolError(out uint code, out uint id) 140 { 141 const(wl_interface)* iface = null; 142 code = wl_display_get_protocol_error(native, &iface, &id); 143 if (iface) { 144 return new immutable WlInterface(cast(immutable)iface); 145 } 146 else { 147 return null; 148 } 149 } 150 151 final int flush() 152 { 153 return wl_display_flush(native); 154 } 155 156 final int roundtripQueue(WlEventQueue queue) 157 { 158 return wl_display_roundtrip_queue(native, queue.native); 159 } 160 161 final int roundtrip() 162 { 163 return wl_display_roundtrip(native); 164 } 165 166 final WlEventQueue createQueue() 167 { 168 return new WlEventQueue(wl_display_create_queue(native)); 169 } 170 171 final int prepareReadQueue(WlEventQueue queue) 172 { 173 return wl_display_prepare_read_queue(native, queue.native); 174 } 175 176 final int prepareRead() 177 { 178 return wl_display_prepare_read(native); 179 } 180 181 final void cancelRead() 182 { 183 wl_display_cancel_read(native); 184 } 185 186 final int readEvents() 187 { 188 return wl_display_read_events(native); 189 } 190 } 191 192 /// Wrapper around wl_proxy and base class for types generated by the protocol. 193 abstract class WlProxy 194 { 195 private wl_proxy* _proxy; 196 197 private static WlProxy[wl_proxy*] proxyCache; 198 199 protected this(wl_proxy* proxy) 200 { 201 _proxy = proxy; 202 proxyCache[proxy] = this; 203 } 204 205 protected void destroyNotify() 206 { 207 proxyCache.remove(_proxy); 208 _proxy = null; 209 } 210 211 static WlProxy get(wl_proxy* proxy) 212 { 213 auto pp = proxy in proxyCache; 214 if (pp) return *pp; 215 else return null; 216 } 217 218 final @property inout(wl_proxy)* proxy() inout 219 { 220 return _proxy; 221 } 222 223 /// Get the protocol version of WlDisplay. 224 final @property uint ver() 225 { 226 return wl_proxy_get_version(proxy); 227 } 228 229 /// Get the id assigned to this object. 230 final @property uint id() 231 { 232 return wl_proxy_get_id(proxy); 233 } 234 235 /// Get the class of this object. 236 final @property string class_() 237 { 238 import std.string : fromStringz; 239 return fromStringz(wl_proxy_get_class(proxy)).idup; 240 } 241 } 242 243 immutable abstract class WlProxyInterface : WlInterface 244 { 245 this(immutable wl_interface* native) 246 { 247 super(native); 248 } 249 250 abstract WlProxy makeProxy(wl_proxy* proxy) immutable; 251 }