1 // Copyright © 2017 Rémi Thebault
2 module hello;
3 
4 // this is a port of https://github.com/hdante/hello_wayland
5 
6 import wayland.client;
7 import wayland.native.client;
8 import wayland.util;
9 import wayland.util.shm_helper;
10 
11 import core.sys.posix.sys.mman;
12 import core.sys.posix.unistd;
13 import std.algorithm;
14 import std.exception;
15 import std.stdio;
16 
17 enum winWidth = 320;
18 enum winHeight = 200;
19 enum cursorWidth = 100;
20 enum cursorHeight = 59;
21 enum cursorHotSpotX = 10;
22 enum cursorHotSpotY = 35;
23 
24 int main()
25 {
26 	version(WlDynamic) { wlClientDynLib.load(); }
27 
28 	auto hello = new Hello;
29 	hello.makeMemPool(cast(immutable(ubyte)[])import("images.bin"));
30 	hello.createSurface();
31 	hello.setupBuffers();
32 	hello.loop();
33 	hello.cleanUp();
34 	return 0;
35 }
36 
37 class Hello
38 {
39 	WlDisplay display;
40 	WlCompositor compositor;
41 	WlPointer pointer;
42 	WlKeyboard kbd;
43 	WlSeat seat;
44 	WlShell shell;
45 	WlShm shm;
46 
47 	int poolFd;
48 	ubyte* poolMem;
49 	size_t poolSize;
50 	WlShmPool pool;
51 	WlSurface surf;
52 	WlShellSurface shSurf;
53 	WlBuffer winBuf;
54 	WlBuffer cursorBuf;
55 	WlSurface cursorSurf;
56 	bool doneFlag;
57 
58 	this()
59 	{
60 		display = enforce(WlDisplay.connect());
61 		auto reg = display.getRegistry();
62 
63 		reg.onGlobal = &regGlobal;
64 
65 		display.roundtrip();
66 		reg.destroy();
67 	}
68 
69 	void regGlobal(WlRegistry reg, uint name, string iface, uint ver)
70 	{
71 		if(iface == WlCompositor.iface.name)
72 		{
73 			compositor = cast(WlCompositor)reg.bind(
74 				name, WlCompositor.iface, min(ver, 4)
75 			);
76 		}
77 		else if(iface == WlShm.iface.name)
78 		{
79 			shm = cast(WlShm)reg.bind(
80 				name, WlShm.iface, min(ver, 1)
81 			);
82 		}
83 		else if(iface == WlShell.iface.name)
84 		{
85 			shell = cast(WlShell)reg.bind(
86 				name, WlShell.iface, min(ver, 1)
87 			);
88 		}
89 		else if(iface == WlSeat.iface.name)
90 		{
91 			seat = cast(WlSeat)reg.bind(
92 				name, WlSeat.iface, min(ver, 2)
93 			);
94 			seat.onCapabilities = &seatCapChanged;
95 		}
96 	}
97 
98 	void seatCapChanged (WlSeat seat, WlSeat.Capability cap)
99 	{
100 		if ((cap & WlSeat.Capability.pointer) && !pointer)
101 		{
102 			writeln("setup");
103 			pointer = seat.getPointer();
104 			pointer.onEnter = &pointerEnter;
105 			pointer.onButton = &pointerButton;
106 		}
107 		else if (!(cap & WlSeat.Capability.pointer) && pointer)
108 		{
109 			pointer.destroy();
110 			pointer = null;
111 		}
112 
113 		if ((cap & WlSeat.Capability.keyboard) && !kbd)
114 		{
115 			kbd = seat.getKeyboard();
116 			kbd.onKey = &kbdKey;
117 		}
118 		else if (!(cap & WlSeat.Capability.keyboard) && kbd)
119 		{
120 			kbd.destroy();
121 			kbd = null;
122 		}
123 	}
124 
125 
126 	void makeMemPool(immutable(ubyte)[] imgData)
127 	{
128 		poolFd = createMmapableFile(imgData.length);
129 		poolMem = cast(ubyte*)mmap(
130 			null, imgData.length, PROT_READ|PROT_WRITE, MAP_SHARED, poolFd, 0
131 		);
132 		enforce(poolMem !is MAP_FAILED);
133 		poolSize = imgData.length;
134 		poolMem[0 .. poolSize] = imgData[];
135 		pool = enforce(shm.createPool(poolFd, cast(int)poolSize));
136 	}
137 
138 	void createSurface()
139 	{
140 		surf = enforce(compositor.createSurface());
141 		scope(failure) surf.destroy();
142 
143 		shSurf = shell.getShellSurface(surf);
144 		shSurf.onPing = (WlShellSurface wlShSurf, uint serial)
145 		{
146 			wlShSurf.pong(serial);
147 		};
148 
149 		shSurf.setToplevel();
150 
151 		cursorSurf = enforce(compositor.createSurface());
152 	}
153 
154 	void setupBuffers()
155 	{
156 		winBuf = pool.createBuffer(
157 			0, winWidth, winHeight, 4*winWidth, WlShm.Format.argb8888
158 		);
159 		cursorBuf = pool.createBuffer(
160 			winWidth*winHeight*4, cursorWidth, cursorHeight, 4*cursorWidth,
161 			WlShm.Format.argb8888
162 		);
163 
164 		surf.attach(winBuf, 0, 0);
165 		surf.commit();
166 	}
167 
168 	void pointerEnter(WlPointer pointer, uint serial, WlSurface surface,
169 						WlFixed surfaceX, WlFixed surfaceY)
170 	{
171 		cursorSurf.attach(cursorBuf, 0, 0);
172 		cursorSurf.commit();
173 		pointer.setCursor(serial, cursorSurf, cursorHotSpotX, cursorHotSpotY);
174 	}
175 
176 	void pointerButton(WlPointer, uint serial, uint time, uint button,
177 						WlPointer.ButtonState state)
178 	{
179 		doneFlag = true;
180 	}
181 
182 	void kbdKey(WlKeyboard keyboard, uint serial, uint time, uint key,
183 			WlKeyboard.KeyState state)
184 	{
185 		import linux.input : KEY_ESC;
186 
187 		if (key == KEY_ESC && state) doneFlag = true;
188 	}
189 
190 	void loop()
191 	{
192 		while (!doneFlag)
193 		{
194 			if (display.dispatch() < 0)
195 			{
196 				stderr.writeln("Main loop error");
197 				doneFlag = true;
198 			}
199 		}
200 	}
201 
202 	void cleanUp()
203 	{
204 		cursorBuf.destroy();
205 		cursorSurf.destroy();
206 		winBuf.destroy();
207 		shSurf.destroy();
208 		surf.destroy();
209 		pool.destroy();
210 		munmap(poolMem, poolSize);
211 		close(poolFd);
212 		pointer.destroy();
213 		kbd.destroy();
214 		seat.destroy();
215 		shell.destroy();
216 		shm.destroy();
217 		compositor.destroy();
218 		display.disconnect();
219 	}
220 }