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 }