1 module simple_egl; 2 3 // port of this example: 4 // https://github.com/eyelash/tutorials/blob/master/wayland-egl.c 5 6 import wayland.client; 7 import wayland.egl; 8 import wayland.cursor; 9 import wayland.util; 10 import wayland.native.util; 11 import zxdg_shell_v6; 12 import derelict.gles.egl; 13 import derelict.gles.gles2; 14 15 import std.exception; 16 import std.format; 17 import std.stdio; 18 import std.string; 19 import core.time; 20 21 enum winWidth = 256; 22 enum winHeight = 256; 23 24 class Display 25 { 26 WlDisplay display; 27 WlCompositor compositor; 28 WlShm shm; 29 WlSeat seat; 30 ZxdgShellV6 shell; 31 WlCursorTheme cursorTheme; 32 WlCursor defaultCursor; 33 WlSurface cursorSurf; 34 WlPointer pointer; 35 WlKeyboard kbd; 36 37 EGLDisplay eglDisplay; 38 EGLContext eglContext; 39 EGLConfig config; 40 41 EglWindow window; 42 43 this() 44 { 45 display = enforce(WlDisplay.connect()); 46 auto reg = display.getRegistry(); 47 reg.onGlobal = (WlRegistry reg, uint name, string iface, uint ver) 48 { 49 import std.algorithm : min; 50 if (iface == WlCompositor.iface.name) 51 { 52 compositor = cast(WlCompositor)reg.bind(name, WlCompositor.iface, min(ver, 4)); 53 } 54 else if (iface == WlShm.iface.name) 55 { 56 shm = cast(WlShm)reg.bind(name, WlShm.iface, min(ver, 1)); 57 cursorTheme = enforce( 58 WlCursorTheme.load(null, 32, shm), 59 "Unable to load default cursor theme" 60 ); 61 defaultCursor = enforce( 62 cursorTheme.cursor("left_ptr"), 63 "Unable to load default left pointer" 64 ); 65 } 66 else if (iface == WlSeat.iface.name) 67 { 68 seat = cast(WlSeat)reg.bind(name, WlSeat.iface, min(ver, 1)); 69 seat.onCapabilities = &seatCapChanged; 70 71 } 72 else if (iface == ZxdgShellV6.iface.name) 73 { 74 shell = cast(ZxdgShellV6)reg.bind(name, ZxdgShellV6.iface, min(ver, 1)); 75 shell.onPing = (ZxdgShellV6 shell, uint serial) { 76 shell.pong(serial); 77 }; 78 } 79 }; 80 display.roundtrip(); 81 reg.destroy(); 82 83 cursorSurf = compositor.createSurface(); 84 85 DerelictEGL.load(); 86 87 eglDisplay = enforce(eglGetDisplay (cast(void*)display.proxy)); 88 enforce(eglInitialize(eglDisplay, null, null) == EGL_TRUE); 89 enforce(eglBindAPI (EGL_OPENGL_ES_API) == GL_TRUE); 90 91 92 int[] ctxAttribs = [ 93 EGL_CONTEXT_CLIENT_VERSION, 2, 94 EGL_NONE 95 ]; 96 int[] attributes = [ 97 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 98 EGL_RED_SIZE, 8, 99 EGL_GREEN_SIZE, 8, 100 EGL_BLUE_SIZE, 8, 101 EGL_ALPHA_SIZE, 8, 102 EGL_BUFFER_SIZE, 32, 103 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 104 EGL_NONE 105 ]; 106 EGLint numConfig; 107 enforce(eglChooseConfig (eglDisplay, attributes.ptr, &config, 1, &numConfig) == GL_TRUE); 108 eglContext = enforce(eglCreateContext (eglDisplay, config, EGL_NO_CONTEXT, ctxAttribs.ptr)); 109 } 110 111 112 void seatCapChanged (WlSeat seat, WlSeat.Capability cap) 113 { 114 if ((cap & WlSeat.Capability.pointer) && !pointer) 115 { 116 pointer = seat.getPointer(); 117 pointer.onEnter = &pointerEnter; 118 pointer.onButton = &pointerButton; 119 } 120 else if (!(cap & WlSeat.Capability.pointer) && pointer) 121 { 122 pointer.destroy(); 123 pointer = null; 124 } 125 126 if ((cap & WlSeat.Capability.keyboard) && !kbd) 127 { 128 kbd = seat.getKeyboard(); 129 kbd.onKey = &kbdKey; 130 } 131 else if (!(cap & WlSeat.Capability.keyboard) && kbd) 132 { 133 kbd.destroy(); 134 kbd = null; 135 } 136 } 137 138 void pointerEnter(WlPointer pointer, uint serial, WlSurface surface, 139 WlFixed sx, WlFixed sy) 140 { 141 if (defaultCursor) 142 { 143 auto img = defaultCursor.images[0]; 144 auto buf = img.buffer; 145 if (!buf) return; 146 pointer.setCursor(serial, cursorSurf, img.hotspotX, img.hotspotY); 147 cursorSurf.attach(buf, 0, 0); 148 cursorSurf.damage(0, 0, img.width, img.height); 149 cursorSurf.commit(); 150 } 151 } 152 153 void pointerButton(WlPointer pointer, uint serial, uint time, uint button, 154 WlPointer.ButtonState state) 155 { 156 import linux.input : BTN_LEFT; 157 158 if (!window || !window.topLevel) return; 159 160 if (button == BTN_LEFT && state == WlPointer.ButtonState.pressed) 161 { 162 window.topLevel.move(seat, serial); 163 } 164 } 165 166 void kbdKey(WlKeyboard keyboard, uint serial, uint time, uint key, 167 WlKeyboard.KeyState state) 168 { 169 import linux.input : KEY_ESC; 170 171 if (!window) return; 172 173 if (key == KEY_ESC && state) window.running = false; 174 } 175 176 void destroy() 177 { 178 eglDestroyContext(eglDisplay, eglContext); 179 eglTerminate(eglDisplay); 180 display.disconnect(); 181 } 182 183 } 184 185 class EglWindow 186 { 187 Display dpy; 188 189 WlSurface surf; 190 ZxdgSurfaceV6 xdgSurf; 191 ZxdgToplevelV6 topLevel; 192 193 WlEglWindow eglWin; 194 EGLSurface eglSurf; 195 196 GLuint program; 197 GLuint vbo; 198 GLuint posAttrib = 0; 199 GLuint colAttrib = 1; 200 GLuint rotationUnif; 201 202 MonoTime startTime; 203 bool running = true; 204 bool configured; 205 206 this (Display dpy) 207 { 208 this.dpy = dpy; 209 210 surf = enforce(dpy.compositor.createSurface()); 211 xdgSurf = enforce(dpy.shell.getXdgSurface(surf)); 212 topLevel = enforce(xdgSurf.getToplevel()); 213 214 topLevel.onConfigure = &onTLConfigure; 215 topLevel.onClose = &onTLClose; 216 topLevel.setTitle("wayland-d - EGL window"); 217 218 xdgSurf.onConfigure = (ZxdgSurfaceV6 surf, uint serial) 219 { 220 surf.ackConfigure(serial); 221 configured = true; 222 }; 223 224 eglWin = new WlEglWindow(surf, winWidth, winHeight); 225 eglSurf = enforce(eglCreateWindowSurface(dpy.eglDisplay, dpy.config, cast(void*)eglWin.native, null)); 226 227 enforce(eglMakeCurrent (dpy.eglDisplay, eglSurf, eglSurf, dpy.eglContext) == GL_TRUE); 228 229 DerelictGLES2.load(&loadSymbol); 230 DerelictGLES2.reload(); 231 writeln("created OpenGLES context: ", fromStringz(glGetString(GL_VERSION))); 232 233 initGl(); 234 startTime = MonoTime.currTime(); 235 surf.commit(); 236 } 237 238 void onTLConfigure(ZxdgToplevelV6, int width, int height, wl_array* states) 239 { 240 if (eglWin) eglWin.resize(winWidth, winHeight, 100, 100); 241 } 242 243 void onTLClose(ZxdgToplevelV6) 244 { 245 running = false; 246 } 247 248 GLuint createShader(string source, GLenum stage) 249 { 250 const(GLchar)* srcPtr = source.ptr; 251 auto srcLen = cast(GLint)source.length; 252 auto sh = glCreateShader(stage); 253 glShaderSource(sh, 1, &srcPtr, &srcLen); 254 glCompileShader(sh); 255 GLint status; 256 glGetShaderiv(sh, GL_COMPILE_STATUS, &status); 257 if (status == GL_FALSE) 258 { 259 char[1024] log; 260 GLsizei len; 261 glGetShaderInfoLog(sh, 1024, &len, log.ptr); 262 throw new Exception(format( 263 "%s shader compilation failed:\n%s", 264 stage == GL_VERTEX_SHADER ? "vertex" : "fragment", 265 log[0 .. len].idup)); 266 } 267 268 return sh; 269 } 270 271 GLuint buildProgram() 272 { 273 immutable vertSrc = " 274 uniform mat4 rotation; 275 attribute vec4 pos; 276 attribute vec4 col; 277 varying vec4 v_col; 278 void main() { 279 gl_Position = rotation * pos; 280 v_col = col; 281 } 282 "; 283 immutable fragSrc = " 284 precision mediump float; 285 varying vec4 v_col; 286 void main() { 287 gl_FragColor = v_col; 288 } 289 "; 290 auto vertSh = createShader(vertSrc, GL_VERTEX_SHADER); 291 auto fragSh = createShader(fragSrc, GL_FRAGMENT_SHADER); 292 293 GLuint program = glCreateProgram(); 294 glAttachShader(program, vertSh); 295 glAttachShader(program, fragSh); 296 glLinkProgram(program); 297 GLint status; 298 glGetProgramiv(program, GL_LINK_STATUS, &status); 299 enforce(status); 300 301 glDeleteShader(vertSh); 302 glDeleteShader(fragSh); 303 304 return program; 305 } 306 307 void initGl() 308 { 309 program = buildProgram(); 310 311 glGenBuffers(1, &vbo); 312 glBindBuffer(GL_ARRAY_BUFFER, vbo); 313 314 immutable float[] colorTriangle = [ 315 // pos 316 0f, 0.8f, 0f, 1f, 317 -0.7f, -0.6f, 0f, 1f, 318 0.7f, -0.6f, 0f, 1f, 319 // col 320 1f, 0.3f, 0.3f, 0.5f, 321 0.3f, 1f, 0.3f, 0.5f, 322 0.3f, 0.3f, 1f, 0.5f, 323 ]; 324 325 glBufferData(GL_ARRAY_BUFFER, 326 colorTriangle.length*4, 327 cast(const(void*))colorTriangle.ptr, 328 GL_STATIC_DRAW); 329 330 glUseProgram(program); 331 glBindAttribLocation(program, posAttrib, "pos"); 332 glBindAttribLocation(program, colAttrib, "col"); 333 rotationUnif = glGetUniformLocation(program, "rotation"); 334 335 glBindBuffer(GL_ARRAY_BUFFER, 0); 336 } 337 338 void draw() 339 { 340 import std.math : sin, cos, PI; 341 342 glViewport(0, 0, winWidth, winHeight); 343 glClearColor (0.15f, 0.15f, 0.15f, 0.5f); 344 glClear (GL_COLOR_BUFFER_BIT); 345 346 immutable speedDiv = 5f; 347 immutable msecs = (MonoTime.currTime - startTime).total!"msecs"; 348 immutable angle = ((msecs / speedDiv) % 360) * PI / 180f; 349 350 immutable s = sin(angle); 351 immutable c = cos(angle); 352 immutable float[16] mat = [ 353 c, 0, s, 0, 354 0, 1, 0, 0, 355 -s, 0, c, 0, 356 0, 0, 0, 1, 357 ]; 358 359 glUniformMatrix4fv(rotationUnif, 1, GL_FALSE, mat.ptr); 360 glBindBuffer(GL_ARRAY_BUFFER, vbo); 361 glVertexAttribPointer(posAttrib, 4, GL_FLOAT, GL_FALSE, 0, null); 362 glVertexAttribPointer(colAttrib, 4, GL_FLOAT, GL_FALSE, 0, cast(void*)(3*4*4)); 363 glEnableVertexAttribArray(posAttrib); 364 glEnableVertexAttribArray(colAttrib); 365 366 glDrawArrays(GL_TRIANGLES, 0, 3); 367 368 glDisableVertexAttribArray(colAttrib); 369 glDisableVertexAttribArray(posAttrib); 370 glBindBuffer(GL_ARRAY_BUFFER, 0); 371 372 eglSwapBuffers (dpy.eglDisplay, eglSurf); 373 } 374 375 void destroy() 376 { 377 glUseProgram(0); 378 glDeleteProgram(program); 379 glDeleteBuffers(1, &vbo); 380 eglDestroySurface(dpy.eglDisplay, eglSurf); 381 eglWin.destroy(); 382 topLevel.destroy(); 383 xdgSurf.destroy(); 384 surf.destroy(); 385 } 386 } 387 388 void* loadSymbol(string name) 389 { 390 import std.format : format; 391 import std.string : toStringz; 392 393 auto sym = enforce ( 394 eglGetProcAddress(toStringz(name)), 395 format("Failed to load symbol %s: 0x%x", name, eglGetError()) 396 ); 397 return sym; 398 } 399 400 int main () 401 { 402 version(WlDynamic) 403 { 404 wlClientDynLib.load(); 405 wlEglDynLib.load(); 406 wlCursorDynLib.load(); 407 } 408 409 auto dpy = new Display(); 410 scope(exit) dpy.destroy(); 411 auto win = new EglWindow(dpy); 412 scope(exit) win.destroy(); 413 414 dpy.window = win; 415 416 while(win.running) 417 { 418 if (win.configured) dpy.display.dispatch(); 419 else dpy.display.dispatchPending(); 420 win.draw(); 421 } 422 423 return 0; 424 }