1 module compositor; 2 3 import backend; 4 import seat; 5 import shell; 6 import output; 7 import wayland.server; 8 import wayland.server.shm; 9 import wayland.native.server; 10 11 import std.algorithm; 12 import std.typecons : Flag; 13 import std.stdio; 14 import std.process; 15 import std.format; 16 import std.exception; 17 import core.time; 18 19 20 class Compositor : WlCompositor 21 { 22 private { 23 WlDisplay _display; 24 25 Backend _backend; 26 Seat _seat; 27 Shell _shell; 28 29 WlClient[] _clients; 30 Output[] _outputs; 31 uint _outputMaskShift; 32 33 MonoTime _startTime; 34 } 35 36 this(WlDisplay display) 37 { 38 this._display = display; 39 super(display, ver); 40 _seat = new Seat(this); 41 _shell = new Shell(this); 42 _startTime = MonoTime.currTime; 43 } 44 45 @property Duration time() 46 { 47 return MonoTime.currTime - _startTime; 48 } 49 50 @property WlDisplay display() 51 { 52 return _display; 53 } 54 55 @property Output[] outputs() 56 { 57 return _outputs; 58 } 59 60 @property Shell shell() 61 { 62 return _shell; 63 } 64 65 @property Seat seat() 66 { 67 return _seat; 68 } 69 70 void addOutput(Output output) 71 { 72 _outputMaskShift += 1; 73 output.mask = 1 << _outputMaskShift; 74 _outputs ~= output; 75 } 76 77 void exit() 78 { 79 _display.terminate(); 80 } 81 82 void eventExpose() 83 {} 84 85 void eventMouseMove(int x, int y) 86 {} 87 88 void eventMouseButton(int x, int y, int button, WlPointer.ButtonState state) 89 { 90 _shell.mouseButton(x, y, button, state); 91 } 92 93 void eventKey(int key, Flag!"down" down) 94 {} 95 96 void scheduleRepaint() 97 { 98 foreach (o; _outputs) { 99 o.scheduleRepaint(); 100 } 101 } 102 103 // WlCompositor 104 105 override WlSurface createSurface(WlClient cl, Resource, uint id) 106 { 107 return new Surface(this, cl, id); 108 } 109 110 override WlRegion createRegion(WlClient cl, Resource, uint id) 111 { 112 return new Region(cl, id); 113 } 114 115 private: 116 117 int run() 118 { 119 _display.initShm(); 120 121 _backend = Backend.create(); 122 _backend.initialize(new BackendConfig(false, 640, 480), this); 123 scope(exit) _backend.terminate(); 124 125 addOutput(_backend.createOutput()); 126 127 auto timer = _display.eventLoop.addTimer({ 128 spawnProcess([ 129 "wayland-tracker", "simple", 130 "-x", "protocol/wayland.xml", 131 "--", "examples/hello/wayland_hello" 132 ]); 133 return 1; 134 }); 135 timer.update(1000); 136 137 _display.addClientCreatedListener(&addClient); 138 139 scheduleRepaint(); 140 141 _display.run(); 142 143 return 0; 144 } 145 146 void addClient(WlClient cl) 147 { 148 _clients ~= cl; 149 cl.addDestroyListener(&removeClient); 150 // Some interfaces are implemented by libwayland-server (i.e. wl_shm, wl_shm_pool). 151 // Therefore, some resources are not created in the D code stack. 152 // Here we listen for the creation of buffer and wrap them with a D object. 153 cl.addNativeResourceCreatedListener((wl_resource* natRes) { 154 import core.stdc.string : strcmp; 155 if (strcmp(wl_resource_get_class(natRes), "wl_buffer") == 0) { 156 new Buffer(natRes); 157 } 158 }); 159 } 160 161 void removeClient(WlClient cl) 162 { 163 _clients = _clients.remove!(c => c is cl); 164 } 165 } 166 167 struct Rect { 168 int x; int y; int width; int height; 169 } 170 171 // dumbest possible region implementation 172 class Region : WlRegion 173 { 174 Rect[] rects; 175 176 this(WlClient cl, int id) 177 { 178 super(cl, WlRegion.ver, id); 179 } 180 181 override protected void destroy(WlClient cl) 182 {} 183 184 override protected void add(WlClient cl, 185 int x, 186 int y, 187 int width, 188 int height) 189 { 190 // add without checking for interference 191 rects ~= Rect(x, y, width, height); 192 } 193 194 override protected void subtract(WlClient cl, 195 int x, 196 int y, 197 int width, 198 int height) 199 { 200 // naive tentative. no complex algo to refont the rect list 201 immutable rs = Rect(x, y, width, height); 202 foreach (i, r; rects) { 203 if (r == rs) { 204 rects = rects.remove(i); 205 return; 206 } 207 } 208 } 209 } 210 211 212 class Buffer : WlBuffer 213 { 214 WlShmBuffer shmBuffer; 215 int width; 216 int height; 217 size_t stride; 218 WlShm.Format format; 219 220 this(wl_resource* natRes) { 221 super(natRes); 222 } 223 224 void fetch() 225 { 226 shmBuffer = enforce(WlShmBuffer.get(this)); 227 width = shmBuffer.width; 228 height = shmBuffer.height; 229 stride = shmBuffer.stride; 230 format = shmBuffer.format; 231 } 232 233 void[] beginAccess() 234 { 235 shmBuffer.beginAccess(); 236 return shmBuffer.data(); 237 } 238 239 void endAccess() 240 { 241 shmBuffer.endAccess(); 242 } 243 244 // WlBuffer 245 246 override protected void destroy(WlClient cl) 247 {} 248 } 249 250 class SurfaceState 251 { 252 // attach 253 bool newlyAttached; 254 int x; int y; 255 Buffer buffer; 256 257 // damage 258 Rect[] damageReg; 259 // damageBuffer 260 Rect[] damageBufferReg; 261 // setOpaqueRegion 262 Rect[] opaqueReg; 263 // setInputRegion 264 Rect[] inputReg; 265 266 void flushTo(SurfaceState state) 267 { 268 state.newlyAttached = newlyAttached; 269 state.x = x; 270 state.y = y; 271 state.buffer = buffer; 272 state.damageReg = damageReg; 273 state.damageBufferReg = damageBufferReg; 274 state.opaqueReg = opaqueReg; 275 state.inputReg = inputReg; 276 } 277 } 278 279 class AlreadyAssignedRoleException : Exception 280 { 281 this(string oldRole, string newRole) 282 { 283 super(format("Surface role already assigned: was '%s', tentative to assign '%s'.", oldRole, newRole)); 284 } 285 } 286 287 class Surface : WlSurface 288 { 289 private Compositor _comp; 290 private SurfaceState _pending; 291 private SurfaceState _state; 292 private string _role; 293 private uint _outputMask; 294 295 this(Compositor comp, WlClient cl, uint id) { 296 _comp = comp; 297 _pending = new SurfaceState; 298 _state = new SurfaceState; 299 super(cl, WlSurface.ver, id); 300 } 301 302 @property SurfaceState state() 303 { 304 return _state; 305 } 306 307 @property string role() 308 { 309 return _role; 310 } 311 312 void assignRole(string role) 313 { 314 if (_role.length && _role != role) 315 { 316 throw new AlreadyAssignedRoleException(_role, role); 317 } 318 } 319 320 @property uint outputMask() 321 { 322 return _outputMask; 323 } 324 325 @property void outputMask(uint mask) 326 { 327 _outputMask = mask; 328 } 329 330 void scheduleRepaint() 331 { 332 foreach (o; _comp.outputs) 333 { 334 if (_outputMask & o.mask) { 335 o.scheduleRepaint(); 336 } 337 } 338 } 339 340 // WlSurface 341 342 override protected void destroy(WlClient cl) 343 { 344 345 } 346 347 override protected void attach(WlClient cl, 348 WlBuffer buffer, 349 int x, 350 int y) 351 { 352 auto b = cast(Buffer)buffer; 353 _pending.buffer = b; 354 _pending.newlyAttached = true; 355 _pending.x = x; 356 _pending.y = y; 357 if (b) b.fetch(); 358 } 359 360 override protected void damage(WlClient cl, 361 int x, 362 int y, 363 int width, 364 int height) 365 { 366 _pending.damageReg ~= Rect(x, y, width, height); 367 } 368 369 override protected WlCallback frame(WlClient cl, 370 uint callback) 371 { 372 return null; 373 } 374 375 override protected void setOpaqueRegion(WlClient cl, 376 WlRegion region) 377 { 378 _pending.opaqueReg = (cast(Region)region).rects; 379 } 380 381 override protected void setInputRegion(WlClient cl, 382 WlRegion region) 383 { 384 _pending.inputReg = (cast(Region)region).rects; 385 } 386 387 override protected void commit(WlClient cl) 388 { 389 _pending.flushTo(_state); 390 scheduleRepaint(); 391 } 392 393 override protected void setBufferTransform(WlClient cl, 394 int transform) 395 {} 396 397 override protected void setBufferScale(WlClient cl, 398 int scale) 399 {} 400 401 override protected void damageBuffer(WlClient cl, 402 int x, 403 int y, 404 int width, 405 int height) 406 { 407 _pending.damageBufferReg ~= Rect(x, y, width, height); 408 } 409 } 410 411 412 int main() 413 { 414 auto display = WlDisplay.create(); 415 scope(exit) display.destroy(); 416 417 environment["WAYLAND_DISPLAY"] = display.addSocketAuto(); 418 419 auto comp = new Compositor(display); 420 scope(exit) comp.destroy(); 421 422 return comp.run(); 423 }