├── examples ├── hello │ ├── images │ │ ├── fish.png │ │ ├── window.png │ │ └── convert.py │ ├── dub.json │ └── source │ │ └── hello.d ├── connect │ ├── dub.json │ └── source │ │ └── connect.d ├── list_registry │ ├── dub.json │ └── source │ │ └── list_registry.d ├── compositor │ ├── dub.json │ └── source │ │ ├── backend │ │ ├── package.d │ │ └── x11.d │ │ ├── seat.d │ │ ├── output.d │ │ ├── shell.d │ │ └── compositor.d └── simple_egl │ ├── dub.json │ └── source │ └── simple_egl.d ├── .gitmodules ├── scanner ├── dub.json └── source │ ├── wayland │ └── scanner │ │ ├── package.d │ │ └── server.d │ └── arsd │ └── characterencodings.d ├── .gitignore ├── dub.json ├── egl ├── dub.json └── source │ └── wayland │ ├── native │ └── egl.d │ └── egl.d ├── cursor ├── dub.json └── source │ └── wayland │ ├── native │ └── cursor.d │ └── cursor.d ├── server ├── dub.json ├── gen_protocol.sh └── source │ └── wayland │ └── server │ ├── shm.d │ ├── listener.d │ ├── eventloop.d │ ├── package.d │ └── core.d ├── client ├── dub.json ├── gen_protocol.sh └── source │ └── wayland │ ├── client │ ├── package.d │ └── core.d │ └── native │ └── client.d ├── protocol └── wayland.dtd ├── common └── wayland │ └── util │ ├── shm_helper.d │ └── package.d ├── ReadMe.md └── License.txt /examples/hello/images/fish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtbo/wayland-d/HEAD/examples/hello/images/fish.png -------------------------------------------------------------------------------- /examples/hello/images/window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtbo/wayland-d/HEAD/examples/hello/images/window.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/DerelictGLES"] 2 | path = 3rdparty/DerelictGLES 3 | url = https://github.com/rtbo/DerelictGLES.git 4 | ignore = dirty 5 | -------------------------------------------------------------------------------- /scanner/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scanner", 3 | "targetType": "executable", 4 | "authors": [ 5 | "Rémi Thebault" 6 | ], 7 | "description": "Wayland protocol scanner", 8 | "copyright": "Copyright © 2017, Rémi Thebault", 9 | "license": "MIT" 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub/ 2 | dub.selections.json 3 | *.a 4 | *.bin 5 | protocol.d 6 | 7 | # Dub executables 8 | 9 | scanner/wayland_scanner 10 | 11 | examples/connect/wayland_connect 12 | examples/hello/wayland_hello 13 | examples/list_registry/wayland_list_registry 14 | examples/simple_egl/wayland_simple_egl 15 | examples/compositor/wayland_compositor 16 | -------------------------------------------------------------------------------- /examples/connect/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "connect", 3 | "targetType": "executable", 4 | "authors": [ 5 | "Rémi Thebault" 6 | ], 7 | "description": "Wayland connect client example.", 8 | "copyright": "Copyright © 2017, Rémi Thebault", 9 | "license": "MIT", 10 | "dependencies": { 11 | "wayland:client": { 12 | "path": "../.." 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /examples/list_registry/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "list_registry", 3 | "targetType": "executable", 4 | "authors": [ 5 | "Rémi Thebault" 6 | ], 7 | "description": "Wayland list_registry client example.", 8 | "copyright": "Copyright © 2017, Rémi Thebault", 9 | "license": "MIT", 10 | "dependencies": { 11 | "wayland:client": { 12 | "path": "../.." 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/connect/source/connect.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module connect; 3 | 4 | import wayland.client; 5 | import std.stdio; 6 | 7 | void main() 8 | { 9 | auto wl = WlDisplay.connect(); 10 | if (wl) 11 | { 12 | writeln("connected to wayland display version ", wl.ver); 13 | wl.disconnect(); 14 | writeln("disconnected"); 15 | } 16 | else 17 | { 18 | writeln("could not connect to wayland"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wayland", 3 | "targetType": "none", 4 | "authors": [ 5 | "Rémi Thebault" 6 | ], 7 | "description": "Wayland bindings for D", 8 | "copyright": "Copyright © 2017, Rémi Thebault", 9 | "license": "MIT", 10 | "subPackages": [ 11 | "./scanner/", 12 | "./client/", 13 | "./egl/", 14 | "./server/", 15 | "./cursor/", 16 | 17 | "./examples/connect/", 18 | "./examples/list_registry/", 19 | "./examples/simple_egl/", 20 | "./examples/hello/", 21 | "./examples/compositor/" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /examples/compositor/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compositor", 3 | "targetType": "executable", 4 | "authors": [ 5 | "Rémi Thebault" 6 | ], 7 | "description": "A wayland compositor example.", 8 | "copyright": "Copyright © 2017, Rémi Thebault", 9 | "license": "MIT", 10 | "dependencies": { 11 | "wayland:server": { 12 | "path": "../.." 13 | }, 14 | "xcb-d": "~>2.1.0", 15 | "xcb-util-wm-d": "~>0.5.0", 16 | "xkbcommon-d": "~>0.5.1", 17 | "xlib-d": "~>0.1.1" 18 | }, 19 | "libs" : ["xcb-shm"] 20 | } 21 | -------------------------------------------------------------------------------- /egl/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egl", 3 | "authors": [ 4 | "Rémi Thebault" 5 | ], 6 | "description": "D bindings to wayland-egl", 7 | "copyright": "Copyright © 2017, Rémi Thebault", 8 | "license": "MIT", 9 | "dependencies": { 10 | "wayland:client": { 11 | "path": ".." 12 | } 13 | }, 14 | "configurations": [ 15 | { 16 | "name": "static", 17 | "versions": ["WlStatic"], 18 | "libs": ["wayland-egl"] 19 | }, 20 | { 21 | "name": "dynamic", 22 | "versions": ["WlDynamic"], 23 | "dependencies": { 24 | "derelict-util": "~>2.0.0" 25 | } 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /examples/list_registry/source/list_registry.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module list_registry; 3 | 4 | import wayland.client; 5 | import std.exception; 6 | import std.stdio; 7 | 8 | void main() 9 | { 10 | auto display = enforce(WlDisplay.connect()); 11 | scope(exit) display.disconnect(); 12 | 13 | auto reg = enforce(display.getRegistry()); 14 | scope(exit) reg.destroy(); 15 | 16 | reg.onGlobal = (WlRegistry /+reg+/, uint /+name+/, string iface, uint /+ver+/) { 17 | writeln("registering ", iface); 18 | }; 19 | display.roundtrip(); 20 | } 21 | -------------------------------------------------------------------------------- /cursor/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cursor", 3 | "authors": [ 4 | "Rémi Thebault" 5 | ], 6 | "description": "D bindings to wayland-cursor", 7 | "copyright": "Copyright © 2017, Rémi Thebault", 8 | "license": "MIT", 9 | 10 | "dependencies": { 11 | "wayland:client": { 12 | "path": ".." 13 | } 14 | }, 15 | 16 | "configurations": [ 17 | { 18 | "name": "static", 19 | "versions": ["WlStatic"], 20 | "libs": ["wayland-cursor"], 21 | "subConfigurations": { 22 | "wayland:client": "static" 23 | } 24 | }, 25 | { 26 | "name": "dynamic", 27 | "versions": ["WlDynamic"], 28 | "dependencies": { 29 | "derelict-util": "~>2.0.0" 30 | }, 31 | "subConfigurations": { 32 | "wayland:client": "dynamic" 33 | } 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /examples/hello/images/convert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from PIL import Image 3 | from struct import pack 4 | import sys 5 | 6 | def pre(p): 7 | p = list(p) 8 | p[0] = p[0]*p[3]//255 9 | p[1] = p[1]*p[3]//255 10 | p[2] = p[2]*p[3]//255 11 | return p 12 | 13 | def write(i, o, X, Y): 14 | for y in range(Y): 15 | for x in range(X): 16 | p = pre(i.getpixel((x, y))) 17 | o.write(pack('4B', p[2], p[1], p[0], p[3])) 18 | 19 | 20 | i = Image.open(sys.argv[1]+'/images/fish.png') 21 | with open(sys.argv[1]+'/images/fish.bin', 'wb') as o: 22 | write(i, o, 100, 59) 23 | 24 | i = Image.open(sys.argv[1]+'/images/window.png') 25 | with open(sys.argv[1]+'/images/window.bin', 'wb') as o: 26 | write(i, o, 320, 200) 27 | -------------------------------------------------------------------------------- /server/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "authors": [ 4 | "Rémi Thebault" 5 | ], 6 | "description": "D bindings to wayland-server", 7 | "copyright": "Copyright © 2017, Rémi Thebault", 8 | "license": "MIT", 9 | "preGenerateCommands": [ 10 | "bash $PACKAGE_DIR/gen_protocol.sh" 11 | ], 12 | "sourcePaths": [ 13 | "source", 14 | "../common" 15 | ], 16 | "sourceFiles": [ 17 | "source/wayland/server/protocol.d" 18 | ], 19 | "importPaths": [ 20 | "source", 21 | "../common" 22 | ], 23 | "configurations": [ 24 | { 25 | "name": "static", 26 | "versions": ["WlStatic"], 27 | "libs": ["wayland-server"] 28 | }, 29 | { 30 | "name": "dynamic", 31 | "versions": ["WlDynamic"], 32 | "dependencies": { 33 | "derelict-util": "~>2.0.0" 34 | } 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /client/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "authors": [ 4 | "Rémi Thebault" 5 | ], 6 | "description": "D bindings to wayland-client", 7 | "copyright": "Copyright © 2017, Rémi Thebault", 8 | "license": "MIT", 9 | "dependencies": { 10 | "wayland": { "path": ".." } 11 | }, 12 | "preGenerateCommands": [ 13 | "bash $PACKAGE_DIR/gen_protocol.sh" 14 | ], 15 | "sourcePaths": [ 16 | "source", 17 | "../common" 18 | ], 19 | "sourceFiles": [ 20 | "source/wayland/client/protocol.d" 21 | ], 22 | "importPaths": [ 23 | "source", 24 | "../common" 25 | ], 26 | "configurations": [ 27 | { 28 | "name": "static", 29 | "versions": ["WlStatic"], 30 | "libs": ["wayland-client"] 31 | }, 32 | { 33 | "name": "dynamic", 34 | "versions": ["WlDynamic"], 35 | "dependencies": { 36 | "derelict-util": "~>2.0.0" 37 | } 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /client/gen_protocol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CLIENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | WL_D_DIR=$CLIENT_DIR/.. 5 | 6 | PROTOCOL=$WL_D_DIR/protocol/wayland.xml 7 | TARGET=$CLIENT_DIR/source/wayland/client/protocol.d 8 | declare -a DEPENDS=( 9 | $PROTOCOL 10 | $WL_D_DIR/scanner/source/wayland/scanner/package.d 11 | $WL_D_DIR/scanner/source/wayland/scanner/common.d 12 | $WL_D_DIR/scanner/source/wayland/scanner/client.d 13 | $WL_D_DIR/scanner/source/wayland/scanner/server.d 14 | ) 15 | 16 | for d in ${DEPENDS[@]}; do 17 | if [ $TARGET -ot ${d} ]; then 18 | cd $WL_D_DIR 19 | dub run wayland:scanner --build=release -- \ 20 | -c client \ 21 | -m wayland.client.protocol \ 22 | -i $PROTOCOL \ 23 | -o $TARGET 24 | exit $? 25 | fi 26 | done 27 | -------------------------------------------------------------------------------- /server/gen_protocol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SERVER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | WL_D_DIR=$SERVER_DIR/.. 5 | 6 | PROTOCOL=$WL_D_DIR/protocol/wayland.xml 7 | TARGET=$SERVER_DIR/source/wayland/server/protocol.d 8 | declare -a DEPENDS=( 9 | $PROTOCOL 10 | $WL_D_DIR/scanner/source/wayland/scanner/package.d 11 | $WL_D_DIR/scanner/source/wayland/scanner/common.d 12 | $WL_D_DIR/scanner/source/wayland/scanner/client.d 13 | $WL_D_DIR/scanner/source/wayland/scanner/server.d 14 | ) 15 | 16 | for d in ${DEPENDS[@]}; do 17 | if [ $TARGET -ot ${d} ]; then 18 | cd $WL_D_DIR 19 | dub run wayland:scanner --build=release -- \ 20 | -c server \ 21 | -m wayland.server.protocol \ 22 | -i $PROTOCOL \ 23 | -o $TARGET 24 | exit $? 25 | fi 26 | done 27 | -------------------------------------------------------------------------------- /examples/hello/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello", 3 | "targetType": "executable", 4 | "authors": [ 5 | "Rémi Thebault" 6 | ], 7 | "description": "Wayland hello client example.", 8 | "copyright": "Copyright © 2017, Rémi Thebault", 9 | "license": "MIT", 10 | "preGenerateCommands": [ 11 | "python $PACKAGE_DIR/images/convert.py $PACKAGE_DIR", 12 | "mkdir -p $PACKAGE_DIR/views", 13 | "cat $PACKAGE_DIR/images/window.bin $PACKAGE_DIR/images/fish.bin > $PACKAGE_DIR/views/images.bin" 14 | ], 15 | "dependencies": { 16 | "wayland:client": { 17 | "path": "../.." 18 | } 19 | }, 20 | "configurations": [ 21 | { 22 | "name": "static", 23 | "versions": ["WlStatic"], 24 | "subConfigurations": { 25 | "wayland:client": "static" 26 | } 27 | }, 28 | { 29 | "name": "dynamic", 30 | "versions": ["WlDynamic"], 31 | "subConfigurations": { 32 | "wayland:client": "dynamic" 33 | } 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /examples/compositor/source/backend/package.d: -------------------------------------------------------------------------------- 1 | module backend; 2 | 3 | import output; 4 | import compositor; 5 | import wayland.server; 6 | 7 | import std.typecons : Flag; 8 | 9 | class BackendConfig 10 | { 11 | this(bool fs, int w, int h) 12 | { 13 | fullscreen = fs; 14 | width = w; 15 | height = h; 16 | } 17 | 18 | bool fullscreen; 19 | // used if not fullscreen 20 | int width, height; 21 | } 22 | 23 | /// Interface that must implement backends. 24 | /// The compositor send requests to the backend using this interface. 25 | interface Backend 26 | { 27 | /// Creates backend with specified name. 28 | /// If name is empty, default backend is created. 29 | static Backend create(string name="") 30 | { 31 | if (name == "x11" || name == "") 32 | { 33 | import backend.x11; 34 | return new X11Backend; 35 | } 36 | return null; 37 | } 38 | 39 | /// Name of the backend. 40 | @property string name(); 41 | 42 | 43 | void initialize(BackendConfig config, Compositor comp); 44 | 45 | Output createOutput(); 46 | 47 | void terminate(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/simple_egl/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple_egl", 3 | "targetType": "executable", 4 | "authors": [ 5 | "Rémi Thebault" 6 | ], 7 | "description": "Wayland simple egl client example.", 8 | "copyright": "Copyright © 2017, Rémi Thebault", 9 | "license": "MIT", 10 | "preGenerateCommands": [ 11 | "dub run wayland:scanner -- -m zxdg_shell_v6 -i $PACKAGE_DIR/xdg-shell-unstable-v6.xml -o $PACKAGE_DIR/source/zxdg_shell_v6.d" 12 | ], 13 | "sourceFiles": [ 14 | "source/zxdg_shell_v6.d" 15 | ], 16 | "dependencies": { 17 | "wayland:client": { "path": "../.." }, 18 | "wayland:egl": { "path": "../.." }, 19 | "wayland:cursor": { "path": "../.." }, 20 | "derelict-gles": { 21 | "comment": "patched version of derelict-gles is needed", 22 | "path": "../../3rdparty/DerelictGLES" 23 | } 24 | }, 25 | "subConfigurations": { 26 | "derelict-gles": "wayland" 27 | }, 28 | "configurations": [ 29 | { 30 | "name": "static", 31 | "versions": ["WlStatic"], 32 | "subConfigurations": { 33 | "wayland:client": "static", 34 | "wayland:egl": "static", 35 | "wayland:cursor": "static" 36 | } 37 | }, 38 | { 39 | "name": "dynamic", 40 | "versions": ["WlDynamic"], 41 | "subConfigurations": { 42 | "wayland:client": "dynamic", 43 | "wayland:egl": "dynamic", 44 | "wayland:cursor": "dynamic" 45 | } 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /protocol/wayland.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /egl/source/wayland/native/egl.d: -------------------------------------------------------------------------------- 1 | module wayland.native.egl; 2 | 3 | import wayland.native.client; 4 | 5 | extern(C) struct wl_egl_window; 6 | 7 | version(WlDynamic) 8 | { 9 | extern(C) nothrow 10 | { 11 | alias da_wl_egl_window_create = wl_egl_window* function (wl_proxy* surface, int width, int height); 12 | 13 | alias da_wl_egl_window_destroy = void function (wl_egl_window* egl_window); 14 | 15 | alias da_wl_egl_window_resize = void function (wl_egl_window* egl_window, int width, int height, int dx, int dy); 16 | 17 | alias da_wl_egl_window_get_attached_size = void function (wl_egl_window* egl_window, int* width, int* height); 18 | } 19 | 20 | __gshared 21 | { 22 | da_wl_egl_window_create wl_egl_window_create; 23 | 24 | da_wl_egl_window_destroy wl_egl_window_destroy; 25 | 26 | da_wl_egl_window_resize wl_egl_window_resize; 27 | 28 | da_wl_egl_window_get_attached_size wl_egl_window_get_attached_size; 29 | } 30 | } 31 | 32 | version(WlStatic) 33 | { 34 | extern(C) nothrow 35 | { 36 | wl_egl_window* wl_egl_window_create(wl_proxy* surface, int width, int height); 37 | 38 | void wl_egl_window_destroy(wl_egl_window* egl_window); 39 | 40 | void wl_egl_window_resize(wl_egl_window* egl_window, int width, int height, int dx, int dy); 41 | 42 | void wl_egl_window_get_attached_size(wl_egl_window* egl_window, int* width, int* height); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /egl/source/wayland/egl.d: -------------------------------------------------------------------------------- 1 | module wayland.egl; 2 | 3 | import wayland.client; 4 | import wayland.native.egl; 5 | import wayland.util; 6 | 7 | class WlEglWindow : Native!wl_egl_window 8 | { 9 | mixin nativeImpl!(wl_egl_window); 10 | 11 | this(WlSurface surf, int width, int height) 12 | { 13 | _native = wl_egl_window_create(surf.proxy, width, height); 14 | } 15 | 16 | void destroy() 17 | { 18 | wl_egl_window_destroy(_native); 19 | _native = null; 20 | } 21 | 22 | void resize(int width, int height, int dx, int dy) 23 | { 24 | wl_egl_window_resize(_native, width, height, dx, dy); 25 | } 26 | 27 | @property int[2] attachedSize() 28 | { 29 | int[2] wh = -1; 30 | wl_egl_window_get_attached_size(_native, &wh[0], &wh[1]); 31 | return wh; 32 | } 33 | } 34 | 35 | version(WlDynamic) 36 | { 37 | import derelict.util.loader : SharedLibLoader; 38 | 39 | private class WlEglLoader : SharedLibLoader 40 | { 41 | this() 42 | { 43 | super("libwayland-egl.so"); 44 | } 45 | 46 | protected override void loadSymbols() 47 | { 48 | bindFunc( cast( void** )&wl_egl_window_create, "wl_egl_window_create" ); 49 | bindFunc( cast( void** )&wl_egl_window_destroy, "wl_egl_window_destroy" ); 50 | bindFunc( cast( void** )&wl_egl_window_resize, "wl_egl_window_resize" ); 51 | bindFunc( cast( void** )&wl_egl_window_get_attached_size, "wl_egl_window_get_attached_size" ); 52 | } 53 | } 54 | 55 | private __gshared WlEglLoader _loader; 56 | 57 | shared static this() 58 | { 59 | _loader = new WlEglLoader; 60 | } 61 | 62 | public @property SharedLibLoader wlEglDynLib() 63 | { 64 | return _loader; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /common/wayland/util/shm_helper.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module wayland.util.shm_helper; 3 | 4 | import std.exception : enforce; 5 | import core.sys.posix.stdlib : mkstemp; 6 | import core.sys.posix.unistd; 7 | import core.sys.posix.fcntl; 8 | import core.stdc.config : c_long; 9 | 10 | // createMmapableFile inspired from weston source code. 11 | 12 | /// Create a file of the given size suitable for mmap. 13 | /// The file is created under XDG_RUNTIME_DIR and should therfore not be 14 | /// backed stored on disk. The shared memory object will be freed by the kernel 15 | /// when it is closed and no more mapping is alive. 16 | int createMmapableFile(size_t size) 17 | { 18 | import std.process : environment; 19 | enum tmplt = "/wld-d-XXXXXX"; 20 | string base = environment.get("XDG_RUNTIME_DIR", "/tmp"); 21 | auto path = new char[base.length + tmplt.length + 1]; 22 | path[0 .. base.length] = base; 23 | path[base.length .. base.length+tmplt.length] = tmplt; 24 | path[base.length+tmplt.length] = '\0'; 25 | 26 | immutable fd = createTmpfileCloexec(path.ptr); 27 | enforce(fd > 0, "Could not open file for mmap at "~path[0 .. $-1]); 28 | 29 | scope(failure) close(fd); 30 | 31 | import std.format : format; 32 | enforce( 33 | ftruncate(fd, size) >= 0, 34 | format("Could not set mmap file %s to %s bytes", path[0 .. $-1], size) 35 | ); 36 | 37 | return fd; 38 | } 39 | 40 | private: 41 | 42 | int 43 | setCloexecOrClose(int fd) 44 | { 45 | assert(fd != -1); 46 | scope(failure) close(fd); 47 | 48 | auto flags = fcntl(fd, F_GETFD); 49 | enforce(flags != -1); 50 | enforce(fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1); 51 | 52 | return fd; 53 | } 54 | 55 | int 56 | createTmpfileCloexec(char* tmpname) 57 | { 58 | try 59 | { 60 | auto fd = mkstemp(tmpname); 61 | if (fd >= 0) 62 | { 63 | fd = setCloexecOrClose(fd); 64 | unlink(tmpname); 65 | } 66 | return fd; 67 | } 68 | catch(Exception ex) 69 | { 70 | return -1; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /server/source/wayland/server/shm.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module wayland.server.shm; 3 | 4 | import wayland.server.protocol : WlShm; 5 | import wayland.server.core; 6 | import wayland.native.server; 7 | import wayland.native.util; 8 | import wayland.util; 9 | 10 | class WlShmBuffer : Native!wl_shm_buffer 11 | { 12 | mixin nativeImpl!wl_shm_buffer; 13 | 14 | private this(wl_shm_buffer* native) 15 | { 16 | _native = native; 17 | ObjectCache.set(native, this); 18 | } 19 | 20 | static WlShmBuffer get(WlResource res) 21 | { 22 | auto natBuf = wl_shm_buffer_get(res.native); 23 | if (!natBuf) return null; 24 | auto buf = cast(WlShmBuffer)ObjectCache.get(natBuf); 25 | if (!buf) 26 | { 27 | buf = new WlShmBuffer(natBuf); 28 | } 29 | return buf; 30 | } 31 | 32 | void beginAccess() 33 | { 34 | wl_shm_buffer_begin_access(native); 35 | } 36 | 37 | void endAccess() 38 | { 39 | wl_shm_buffer_end_access(native); 40 | } 41 | 42 | @property void[] data() 43 | { 44 | auto dp = wl_shm_buffer_get_data(native); 45 | if (!dp) return null; 46 | return dp[0 .. height*stride]; 47 | } 48 | 49 | @property size_t stride() 50 | { 51 | return wl_shm_buffer_get_stride(native); 52 | } 53 | 54 | @property int width() 55 | { 56 | return wl_shm_buffer_get_width(native); 57 | } 58 | 59 | @property int height() 60 | { 61 | return wl_shm_buffer_get_height(native); 62 | } 63 | 64 | @property WlShm.Format format() 65 | { 66 | return cast(WlShm.Format)wl_shm_buffer_get_format(native); 67 | } 68 | 69 | WlShmPool refPool() 70 | { 71 | return new WlShmPool(wl_shm_buffer_ref_pool(native)); 72 | } 73 | } 74 | 75 | class WlShmPool : Native!wl_shm_pool 76 | { 77 | mixin nativeImpl!wl_shm_pool; 78 | 79 | private this(wl_shm_pool* native) 80 | { 81 | _native = native; 82 | } 83 | 84 | void unref() 85 | { 86 | wl_shm_pool_unref(native); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /examples/compositor/source/seat.d: -------------------------------------------------------------------------------- 1 | module seat; 2 | 3 | import compositor; 4 | import wayland.server; 5 | 6 | import std.algorithm; 7 | 8 | class Seat : WlSeat 9 | { 10 | Compositor comp; 11 | ClPointer[] pointers; 12 | 13 | this(Compositor comp) 14 | { 15 | super(comp.display, ver); 16 | this.comp = comp; 17 | } 18 | 19 | void mouseButton(WlClient cl, int button, WlPointer.ButtonState state) 20 | { 21 | foreach(p; pointers) 22 | { 23 | if (p.client is cl) 24 | { 25 | auto serial = comp.display.nextSerial(); 26 | auto time = cast(uint)comp.time.total!"msecs"; 27 | p.sendButton(serial, time, button, state); 28 | } 29 | } 30 | } 31 | 32 | override Resource bind(WlClient cl, uint ver, uint id) 33 | { 34 | auto res = super.bind(cl, ver, id); 35 | 36 | res.sendCapabilities(Capability.pointer | Capability.keyboard); 37 | if (ver >= nameSinceVersion) { 38 | res.sendName("seat"); 39 | } 40 | return res; 41 | } 42 | 43 | override WlPointer getPointer(WlClient cl, Resource res, uint id) 44 | { 45 | auto p = new ClPointer(this, cl, id); 46 | pointers ~= p; 47 | return p; 48 | } 49 | 50 | override WlTouch getTouch(WlClient cl, Resource res, uint id) 51 | { 52 | return null; 53 | } 54 | 55 | override WlKeyboard getKeyboard(WlClient cl, Resource res, uint id) 56 | { 57 | return new ClKeyboard(cl, id); 58 | } 59 | 60 | override void release(WlClient cl, Resource res) 61 | {} 62 | } 63 | 64 | class ClPointer : WlPointer 65 | { 66 | Seat seat; 67 | 68 | this(Seat seat, WlClient cl, uint id) 69 | { 70 | super(cl, ver, id); 71 | } 72 | 73 | override protected void setCursor(WlClient cl, 74 | uint serial, 75 | WlSurface surface, 76 | int hotspotX, 77 | int hotspotY) 78 | {} 79 | 80 | 81 | override protected void release(WlClient cl) 82 | { 83 | seat.pointers = seat.pointers.remove!(p => p is this); 84 | } 85 | } 86 | 87 | class ClKeyboard : WlKeyboard 88 | { 89 | this(WlClient cl, uint id) 90 | { 91 | super(cl, ver, id); 92 | } 93 | 94 | override protected void release(WlClient cl) 95 | {} 96 | } 97 | -------------------------------------------------------------------------------- /server/source/wayland/server/listener.d: -------------------------------------------------------------------------------- 1 | module wayland.server.listener; 2 | 3 | import wayland.native.server; 4 | import wayland.native.util; 5 | import wayland.util; 6 | 7 | 8 | class WlListener : Native!wl_listener 9 | { 10 | alias NotifyDg = void delegate(void* data); 11 | 12 | private wl_listener _native; 13 | private NotifyDg _notify; 14 | 15 | this() 16 | { 17 | wl_list_init(&_native.link); 18 | _native.notify = &wl_d_listener_notify; 19 | } 20 | 21 | this(NotifyDg notify) 22 | { 23 | this(); 24 | _notify = notify; 25 | } 26 | 27 | @property inout(wl_listener*) native() inout 28 | { 29 | return &_native; 30 | } 31 | 32 | @property NotifyDg notify() 33 | { 34 | return _notify; 35 | } 36 | 37 | @property void notify(NotifyDg notify) 38 | { 39 | _notify = notify; 40 | } 41 | } 42 | 43 | class WlSignal : Native!wl_signal 44 | { 45 | private wl_signal _native; 46 | WlListener[] _listeners; 47 | 48 | this() 49 | { 50 | wl_signal_init(&_native); 51 | } 52 | 53 | @property inout(wl_signal*) native() inout 54 | { 55 | return &_native; 56 | } 57 | 58 | void add(WlListener listener) 59 | { 60 | wl_signal_add(native, listener.native); 61 | _listeners ~= listener; 62 | } 63 | 64 | WlListener get(WlListener.NotifyDg notify) 65 | { 66 | foreach(l; _listeners) 67 | { 68 | if (l._notify is notify) 69 | { 70 | return l; 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | void emit(void* data) 77 | { 78 | wl_signal_emit(native, data); 79 | } 80 | } 81 | 82 | class Signal(Args...) 83 | { 84 | alias Listener = void delegate(Args args); 85 | 86 | private Listener[] _listeners; 87 | 88 | this() {} 89 | 90 | void add(Listener listener) 91 | { 92 | _listeners ~= listener; 93 | } 94 | 95 | void emit(Args args) 96 | { 97 | foreach(l; _listeners) 98 | { 99 | l(args); 100 | } 101 | } 102 | } 103 | 104 | private extern(C) nothrow 105 | { 106 | void wl_d_listener_notify(wl_listener* l, void* data) 107 | { 108 | nothrowFnWrapper!({ 109 | auto dl = cast(WlListener)( 110 | cast(void*)l - WlListener._native.offsetof 111 | ); 112 | assert(dl && (l is &dl._native)); 113 | if (dl._notify) dl._notify(data); 114 | }); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /scanner/source/wayland/scanner/package.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | /++ 3 | + Wayland scanner for D. 4 | + Scan wayland XML protocol and generates client or server code for that protocol. 5 | +/ 6 | module wayland.scanner; 7 | 8 | import wayland.scanner.common; 9 | import wayland.scanner.client; 10 | import wayland.scanner.server; 11 | 12 | import arsd.dom; 13 | 14 | import std.array; 15 | import std.getopt; 16 | import std.stdio; 17 | 18 | enum scannerVersion = "v0.3.1"; 19 | enum usageIntro = "wayland:scanner-"~scannerVersion~"\n"~ 20 | " A Wayland protocol scanner and D code generator.\n\n"; 21 | enum bindingsCopyright = "Copyright © 2017-2019 Rémi Thebault"; 22 | 23 | 24 | int main(string[] args) 25 | { 26 | auto opt = new Options; 27 | opt.cmdLine = args.join(" "); 28 | auto optHandler = getopt ( 29 | args, 30 | "code|c", "generated code: client|server [client]", &opt.code, 31 | "input|i", "input file [stdin]", &opt.inFile, 32 | "output|o", "output file [stdout]", &opt.outFile, 33 | "module|m", "D module name (required)", &opt.moduleName, 34 | ); 35 | 36 | if (optHandler.helpWanted) 37 | { 38 | defaultGetoptFormatter ( 39 | stdout.lockingTextWriter, 40 | usageIntro ~ "Options:", 41 | optHandler.options 42 | ); 43 | return 0; 44 | } 45 | 46 | if (opt.moduleName.empty) 47 | { 48 | defaultGetoptFormatter ( 49 | stderr.lockingTextWriter, 50 | usageIntro ~ 51 | "Error: D module name must be supplied with '--module' or '-m'\n\n" ~ 52 | "Options:", 53 | optHandler.options 54 | ); 55 | return 1; 56 | } 57 | 58 | try 59 | { 60 | File input = (opt.inFile.empty) ? stdin : File(opt.inFile, "r"); 61 | File output = (opt.outFile.empty) ? stdout : File(opt.outFile, "w"); 62 | 63 | string xmlStr; 64 | foreach (string l; lines(input)) 65 | { 66 | xmlStr ~= l; 67 | } 68 | auto xmlDoc = new Document; 69 | xmlDoc.parse(xmlStr, true, true); 70 | 71 | if (opt.code == GenCode.client) 72 | fact = new ClientFactory; 73 | else 74 | fact = new ServerFactory; 75 | 76 | auto p = fact.makeProtocol(xmlDoc.root); 77 | p.writeCode(new SourceFile(output), opt); 78 | } 79 | catch(Exception ex) 80 | { 81 | stderr.writeln("Error: "~ex.msg); 82 | return 1; 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | enum GenCode 89 | { 90 | client, 91 | server, 92 | } 93 | 94 | class Options 95 | { 96 | string cmdLine; 97 | 98 | string inFile; 99 | string outFile; 100 | string moduleName; 101 | 102 | GenCode code; 103 | } 104 | -------------------------------------------------------------------------------- /examples/compositor/source/output.d: -------------------------------------------------------------------------------- 1 | module output; 2 | 3 | import compositor; 4 | import wayland.server; 5 | 6 | import std.stdio; 7 | import core.time; 8 | 9 | 10 | class Output : WlOutput 11 | { 12 | private Compositor _comp; 13 | 14 | private uint _mask; 15 | private bool _repaintNeeded; 16 | private bool _repaintScheduled; 17 | private WlTimerEventSource _repaintTimer; 18 | private WlTimerEventSource _finishFrameTimer; 19 | private MonoTime _startRepaint; 20 | enum refreshNsecs = 1_000_000_000 / 60; 21 | 22 | 23 | this(Compositor comp) 24 | { 25 | _comp = comp; 26 | super(comp.display, 3); 27 | _repaintTimer = _comp.display.eventLoop.addTimer(&timerRepaint); 28 | _finishFrameTimer = _comp.display.eventLoop.addTimer(() { 29 | finishFrame(_startRepaint); 30 | return 1; 31 | }); 32 | 33 | } 34 | 35 | abstract @property int width(); 36 | abstract @property int height(); 37 | abstract @property uint[] buf(); 38 | abstract void blitBuf(); 39 | 40 | @property uint mask() 41 | { 42 | return _mask; 43 | } 44 | 45 | @property void mask(uint mask) 46 | { 47 | _mask = mask; 48 | } 49 | 50 | void scheduleRepaint() 51 | { 52 | _repaintNeeded = true; 53 | if (_repaintScheduled) return; 54 | _comp.display.eventLoop.addIdle(&idleRepaint); 55 | _repaintScheduled = true; 56 | } 57 | 58 | void idleRepaint() 59 | { 60 | startRepaintLoop(); 61 | } 62 | 63 | void startRepaintLoop() 64 | { 65 | finishFrame(MonoTime.currTime); 66 | } 67 | 68 | void finishFrame(MonoTime stamp) 69 | { 70 | auto now = MonoTime.currTime; 71 | auto gone = now - stamp; 72 | auto msecs = (refreshNsecs - gone.total!"nsecs")/1000000; 73 | 74 | if (msecs < 1) { 75 | timerRepaint(); 76 | } 77 | else { 78 | _repaintTimer.update(cast(uint)msecs); 79 | } 80 | } 81 | 82 | int timerRepaint() 83 | { 84 | _startRepaint = MonoTime.currTime; 85 | 86 | auto w = width; 87 | auto h = height; 88 | auto b = buf; 89 | 90 | // background: a 32*32 grid 91 | foreach (c; 0 .. w) 92 | { 93 | foreach (r; 0 .. h) 94 | { 95 | if (r % 32 == 0 || c % 32 == 0) 96 | { 97 | b[r*w + c] = 0; 98 | } 99 | else 100 | { 101 | b[r*w + c] = 0xffffffff; 102 | } 103 | } 104 | } 105 | 106 | _comp.shell.paint(this); 107 | 108 | blitBuf(); 109 | _finishFrameTimer.update(10); 110 | return 1; 111 | } 112 | 113 | // WlOutput 114 | override void release(WlClient cl, Resource res) 115 | {} 116 | } 117 | -------------------------------------------------------------------------------- /cursor/source/wayland/native/cursor.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | /// bindings to wayland-client-core.h 3 | module wayland.native.cursor; 4 | 5 | // Wayland client-core copyright: 6 | /* 7 | * Copyright © 2012 Intel Corporation 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining 10 | * a copy of this software and associated documentation files (the 11 | * "Software"), to deal in the Software without restriction, including 12 | * without limitation the rights to use, copy, modify, merge, publish, 13 | * distribute, sublicense, and/or sell copies of the Software, and to 14 | * permit persons to whom the Software is furnished to do so, subject to 15 | * the following conditions: 16 | * 17 | * The above copyright notice and this permission notice (including the 18 | * next paragraph) shall be included in all copies or substantial 19 | * portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | */ 30 | 31 | import wayland.native.client : wl_proxy; 32 | 33 | extern(C) 34 | { 35 | struct wl_cursor_theme; 36 | 37 | struct wl_cursor_image { 38 | uint width; /* actual width */ 39 | uint height; /* actual height */ 40 | uint hotspot_x; /* hot spot x (must be inside image) */ 41 | uint hotspot_y; /* hot spot y (must be inside image) */ 42 | uint delay; /* animation delay to next frame (ms) */ 43 | }; 44 | 45 | struct wl_cursor { 46 | uint image_count; 47 | wl_cursor_image** images; 48 | char* name; 49 | }; 50 | } 51 | 52 | version(WlDynamic) 53 | { 54 | extern(C) nothrow 55 | { 56 | alias da_wl_cursor_theme_load = wl_cursor_theme* function (const(char)* name, int size, wl_proxy* shm); 57 | 58 | alias da_wl_cursor_theme_destroy = void function (wl_cursor_theme* theme); 59 | 60 | alias da_wl_cursor_theme_get_cursor = wl_cursor* function (wl_cursor_theme* theme, const(char)* name); 61 | 62 | alias da_wl_cursor_image_get_buffer = wl_proxy* function (wl_cursor_image* image); 63 | 64 | alias da_wl_cursor_frame = int function (wl_cursor* cursor, uint time); 65 | 66 | alias da_wl_cursor_frame_and_duration = int function (wl_cursor* cursor, uint time, uint* duration); 67 | } 68 | 69 | __gshared 70 | { 71 | da_wl_cursor_theme_load wl_cursor_theme_load; 72 | 73 | da_wl_cursor_theme_destroy wl_cursor_theme_destroy; 74 | 75 | da_wl_cursor_theme_get_cursor wl_cursor_theme_get_cursor; 76 | 77 | da_wl_cursor_image_get_buffer wl_cursor_image_get_buffer; 78 | 79 | da_wl_cursor_frame wl_cursor_frame; 80 | 81 | da_wl_cursor_frame_and_duration wl_cursor_frame_and_duration; 82 | } 83 | } 84 | 85 | version(WlStatic) 86 | { 87 | extern(C) nothrow 88 | { 89 | wl_cursor_theme* wl_cursor_theme_load(const(char)* name, int size, wl_proxy* shm); 90 | 91 | void wl_cursor_theme_destroy(wl_cursor_theme* theme); 92 | 93 | wl_cursor* wl_cursor_theme_get_cursor(wl_cursor_theme* theme, const(char)* name); 94 | 95 | wl_proxy* wl_cursor_image_get_buffer(wl_cursor_image* image); 96 | 97 | int wl_cursor_frame(wl_cursor* cursor, uint time); 98 | 99 | int wl_cursor_frame_and_duration(wl_cursor* cursor, uint time, uint* duration); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /cursor/source/wayland/cursor.d: -------------------------------------------------------------------------------- 1 | module wayland.cursor; 2 | 3 | import wayland.native.cursor; 4 | import wayland.client; 5 | import wayland.util; 6 | 7 | import std.string; 8 | 9 | 10 | class WlCursorImage : Native!wl_cursor_image 11 | { 12 | mixin nativeImpl!(wl_cursor_image); 13 | 14 | this(wl_cursor_image* native) 15 | { 16 | _native = native; 17 | } 18 | 19 | @property uint width() 20 | { 21 | return native.width; 22 | } 23 | 24 | @property uint height() 25 | { 26 | return native.height; 27 | } 28 | 29 | @property uint hotspotX() 30 | { 31 | return native.hotspot_x; 32 | } 33 | 34 | @property uint hotspotY() 35 | { 36 | return native.hotspot_y; 37 | } 38 | 39 | @property uint delay() 40 | { 41 | return native.delay; 42 | } 43 | 44 | @property WlBuffer buffer() 45 | { 46 | auto nb = wl_cursor_image_get_buffer(native); 47 | if (!nb) return null; 48 | auto buf = cast(WlBuffer)WlProxy.get(nb); 49 | if (!buf) buf = cast(WlBuffer)WlBuffer.iface.makeProxy(nb); 50 | return buf; 51 | } 52 | } 53 | 54 | class WlCursor : Native!wl_cursor 55 | { 56 | mixin nativeImpl!(wl_cursor); 57 | 58 | private WlCursorImage[] _images; 59 | 60 | this(wl_cursor* native) 61 | { 62 | _native = native; 63 | } 64 | 65 | @property string name() 66 | { 67 | return fromStringz(native.name).idup; 68 | } 69 | 70 | @property WlCursorImage[] images() 71 | { 72 | import std.array : uninitializedArray; 73 | 74 | if (_images) return _images; 75 | 76 | _images = uninitializedArray!(WlCursorImage[])(native.image_count); 77 | foreach (i; 0 .. native.image_count) 78 | { 79 | _images[i] = new WlCursorImage(native.images[i]); 80 | } 81 | return _images; 82 | } 83 | 84 | int frame(uint time) 85 | { 86 | return wl_cursor_frame(native, time); 87 | } 88 | 89 | int frameAndDuration(uint time, out uint duration) 90 | { 91 | return wl_cursor_frame_and_duration(native, time, &duration); 92 | } 93 | } 94 | 95 | class WlCursorTheme : Native!wl_cursor_theme 96 | { 97 | mixin nativeImpl!(wl_cursor_theme); 98 | 99 | this(wl_cursor_theme* native) 100 | { 101 | _native = native; 102 | } 103 | 104 | static WlCursorTheme load(string name, size_t size, WlShm shm) 105 | { 106 | auto nn = name.length ? toStringz(name) : null; 107 | auto ct = wl_cursor_theme_load(nn, cast(int)size, shm.proxy); 108 | return ct ? new WlCursorTheme(ct) : null; 109 | } 110 | 111 | void destroy() 112 | { 113 | wl_cursor_theme_destroy(native); 114 | } 115 | 116 | WlCursor cursor(string name) 117 | { 118 | auto cp = wl_cursor_theme_get_cursor(native, toStringz(name)); 119 | return cp ? new WlCursor(cp) : null; 120 | } 121 | 122 | } 123 | 124 | version(WlDynamic) 125 | { 126 | import derelict.util.loader : SharedLibLoader; 127 | 128 | private class WlCursorLoader : SharedLibLoader 129 | { 130 | this() 131 | { 132 | super("libwayland-cursor.so"); 133 | } 134 | 135 | protected override void loadSymbols() 136 | { 137 | bindFunc( cast( void** )&wl_cursor_theme_load, "wl_cursor_theme_load" ); 138 | bindFunc( cast( void** )&wl_cursor_theme_destroy, "wl_cursor_theme_destroy" ); 139 | bindFunc( cast( void** )&wl_cursor_theme_get_cursor, "wl_cursor_theme_get_cursor" ); 140 | bindFunc( cast( void** )&wl_cursor_image_get_buffer, "wl_cursor_image_get_buffer" ); 141 | bindFunc( cast( void** )&wl_cursor_frame, "wl_cursor_frame" ); 142 | bindFunc( cast( void** )&wl_cursor_frame_and_duration, "wl_cursor_frame_and_duration" ); 143 | } 144 | } 145 | 146 | private __gshared WlCursorLoader _loader; 147 | 148 | shared static this() 149 | { 150 | _loader = new WlCursorLoader; 151 | } 152 | 153 | public @property SharedLibLoader wlCursorDynLib() 154 | { 155 | return _loader; 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /common/wayland/util/package.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module wayland.util; 3 | 4 | import wayland.native.util; 5 | 6 | /// Implemented by type that wrap a native wayland struct pointer. 7 | interface Native(wl_native) 8 | { 9 | /// Access the wrapped struct. 10 | @property inout(wl_native)* native() inout; 11 | } 12 | 13 | /// Utility mixin that implements Native for a type. 14 | mixin template nativeImpl(wl_native) 15 | { 16 | private wl_native* _native; 17 | 18 | public final override @property inout(wl_native)* native() inout 19 | { 20 | return _native; 21 | } 22 | } 23 | 24 | 25 | immutable class WlInterface 26 | { 27 | private immutable wl_interface* _native; 28 | 29 | immutable this(immutable wl_interface* native) 30 | { 31 | this._native = native; 32 | } 33 | 34 | @property immutable(wl_interface)* native() immutable 35 | { 36 | return _native; 37 | } 38 | 39 | @property string name() immutable 40 | { 41 | import std.string : fromStringz; 42 | return fromStringz(_native.name); 43 | } 44 | } 45 | 46 | /++ 47 | + Check for equality between two interfaces 48 | +/ 49 | bool wlIfaceEquals(immutable(WlInterface) a, immutable(WlInterface) b) 50 | { 51 | import core.stdc.string : strcmp; 52 | 53 | return a is b || strcmp(a._native.name, b._native.name) == 0; 54 | } 55 | 56 | /++ 57 | + Wraps a function literal into a try-catch statement. 58 | + 59 | + Use this in functions called by C as exception cannot propagate there. 60 | + The try-catch statement will print a warning if an exception is thrown. 61 | + The try-catch statement will terminate runtime and exit program if an error is thrown. 62 | +/ 63 | auto nothrowFnWrapper(alias fn)() nothrow 64 | { 65 | try 66 | { 67 | return fn(); 68 | } 69 | catch(Exception ex) 70 | { 71 | import std.exception : collectException; 72 | import std.stdio : stderr; 73 | collectException(stderr.writeln("wayland-d: error in listener stub: "~ex.msg)); 74 | } 75 | catch(Throwable err) 76 | { 77 | import core.runtime : Runtime; 78 | import core.stdc.stdlib : exit; 79 | import std.exception : collectException; 80 | import std.stdio : stderr; 81 | collectException(stderr.writeln("wayland-d: aborting due to error in listener stub: "~err.msg)); 82 | collectException(Runtime.terminate()); 83 | exit(1); 84 | } 85 | alias rt = typeof(fn()); 86 | static if (!is(rt == void)) 87 | { 88 | return rt.init; 89 | } 90 | } 91 | 92 | 93 | 94 | /// static cache of objects that are looked-up by the address of their native 95 | /// counter part 96 | struct ObjectCache 97 | { 98 | private __gshared Object[void*] _cache; 99 | 100 | static void set (void* native, Object obj) 101 | { 102 | _cache[native] = obj; 103 | } 104 | 105 | static Object get (void* native) 106 | { 107 | auto op = native in _cache; 108 | return op ? *op : null; 109 | } 110 | 111 | static void remove (void* native) 112 | { 113 | _cache.remove(native); 114 | } 115 | } 116 | 117 | /** 118 | * Fixed-point number 119 | * 120 | * A `WlFixed` is a 24.8 signed fixed-point number with a sign bit, 23 bits 121 | * of integer precision and 8 bits of decimal precision. 122 | */ 123 | struct WlFixed 124 | { 125 | private uint _raw; 126 | private union DI 127 | { 128 | long i; 129 | double d; 130 | } 131 | 132 | public this(uint raw) 133 | { 134 | _raw = raw; 135 | } 136 | 137 | /** 138 | * Converts an integer to a fixed-point number. 139 | */ 140 | public static WlFixed create(in int val) 141 | { 142 | return WlFixed(val * 256); 143 | } 144 | 145 | /** 146 | * Converts a floating-point number to a fixed-point number. 147 | */ 148 | public static WlFixed create(in double val) 149 | { 150 | DI u; 151 | u.d = val + (3L << (51 - 8)); 152 | return WlFixed(cast(uint)(u.i)); 153 | } 154 | 155 | @property uint raw() const 156 | { 157 | return _raw; 158 | } 159 | 160 | /** 161 | * Converts a fixed-point number to an integer. 162 | */ 163 | int opCast(T : int)() const 164 | { 165 | return _raw / 256; 166 | } 167 | 168 | /** 169 | * Converts a fixed-point number to a floating-point number. 170 | */ 171 | double opCast(T : double)() const 172 | { 173 | DI u; 174 | u.i = ((1023L + 44L) << 52) + (1L << 51) + f; 175 | return u.d - (3L << 43); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # Wayland D bindings 2 | 3 | D bindings to wayland. 4 | 5 | ![dub version](https://img.shields.io/dub/v/wayland.svg) 6 | ![dub downloads](https://img.shields.io/dub/dt/wayland.svg) 7 | 8 | There are several components: 9 | - __*Scanner*__: XML protocol parser and code generator. It generates high level objects. 10 | - support for client and server side code generation. 11 | - support foreign protocols (such as `xdg-shell`. See the [simple-egl example](https://github.com/rtbo/wayland-d/blob/master/examples/simple_egl/source/simple_egl.d)) 12 | 13 | - __*Client*__: client protocol and `libwayland-client` native API wrapped into higher level objects. 14 | 15 | - __*EGL*__: allow use of wayland-egl (see [this example](https://github.com/rtbo/wayland-d/blob/master/examples/simple_egl/source/simple_egl.d)). 16 | 17 | - __*Server*__: server side protocol and bindings to `libwayland-server` to allow the creation of a compositor. 18 | 19 | 20 | 21 | ## Scanner usage 22 | 23 | ```sh 24 | $ dub run wayland:scanner -- -h 25 | wayland:scanner-v0.1.0 26 | A Wayland protocol scanner and D code generator. 27 | 28 | Options: 29 | -c --code generated code: client|server [client] 30 | -i --input input file [stdin] 31 | -o --output output file [stdout] 32 | -m --module D module name (required) 33 | -h --help This help information. 34 | ``` 35 | 36 | 37 | ## Client usage 38 | 39 | Add the `wayland:client` dependency in your `dub.json`: 40 | ```json 41 | "dependencies": { 42 | "wayland:client": "~>0.1.0" 43 | } 44 | ``` 45 | The main wayland protocol is automatically generated by the scanner 46 | as a pre-build step of `wayland:client`. 47 | To use other protocols, the scanner must be used and XML protocol definition 48 | provided by the application. See the [simple-egl](https://github.com/rtbo/wayland-d/blob/master/examples/simple_egl/source/simple_egl.d) 49 | example that uses the `xdg-shell` protocol. 50 | 51 | ### Requests 52 | 53 | Requests are made by calling methods on the `WlProxy` objects generated by the 54 | protocol. For example: 55 | ```d 56 | WlSurface surf = makeSurf(); 57 | WlBuffer buf = makeBuf(); 58 | surf.attach(buf, 0, 0); 59 | ``` 60 | 61 | As described in the protocol, some requests are void, others return a new object. 62 | 63 | 64 | ### Events 65 | 66 | Events are listened to by registering a delegate in the `WlProxy` objects. 67 | See `WlRegistry.onGlobal` in the example hereunder. 68 | 69 | 70 | ### Example of client code 71 | 72 | ```d 73 | import wayland.client; 74 | import std.exception; 75 | import std.stdio; 76 | 77 | void main() 78 | { 79 | auto display = enforce(WlDisplay.connect()); 80 | scope(exit) display.disconnect(); 81 | 82 | auto reg = enforce(display.getRegistry()); 83 | scope(exit) reg.destroy(); 84 | 85 | reg.onGlobal = (WlRegistry /+reg+/, uint /+name+/, string iface, uint /+ver+/) { 86 | writeln("registering ", iface); 87 | }; 88 | display.roundtrip(); 89 | } 90 | ``` 91 | 92 | ## Server usage 93 | 94 | In the current implementation of the bindings, handling client requests is done 95 | by subclassing the `WlGlobal` and `WlResource` subclasses generated by the protocol. 96 | All protocol-generated classes that have requests are `abstract`. 97 | In the requests creating new resources, the application must therefore return 98 | a subclass object that implement the protocol requests. 99 | 100 | Events are sent to clients by calling the `send[EventName]` methods of resource objects. 101 | 102 | A part of the main protocol is implemented natively by `libwayland-server`. This 103 | is the case of `wl_shm` and `wl_shm_pool`. A consequence of this is that `wl_buffer` objects 104 | are not created under the application control. To create a `WlBuffer` subclass 105 | object that implement the requests, it is required to listen to native resource creation: 106 | 107 | ```d 108 | class Compositor : WlCompositor 109 | { 110 | ... 111 | void newClientConnection(WlClient cl) 112 | { 113 | cl.addNativeResourceCreatedListener((wl_resource* natRes) { 114 | import core.stdc.string : strcmp; 115 | if (strcmp(wl_resource_get_class(natRes), "wl_buffer") == 0) { 116 | new Buffer(natRes); 117 | } 118 | }); 119 | } 120 | } 121 | 122 | class Buffer : WlBuffer 123 | { 124 | ... 125 | } 126 | ``` 127 | 128 | See the [compositor](https://github.com/rtbo/wayland-d/blob/master/examples/compositor) example 129 | that implement a quick and dirty compositor. It is not a good design of compositor, it only 130 | illustrates the bindings mechanics. 131 | 132 | 133 | ## Playground 134 | 135 | You can run the examples if you are under a wayland compositor: 136 | ```sh 137 | dub run wayland:list_registry 138 | ``` 139 | For some of the examples, this only works if you `cd` first to the project root 140 | directory: 141 | 142 | ```sh 143 | git clone https://github.com/rtbo/wayland-d.git 144 | cd wayland-d 145 | dub run wayland:hello 146 | ``` 147 | -------------------------------------------------------------------------------- /client/source/wayland/client/package.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module wayland.client; 3 | 4 | public import wayland.client.core; 5 | public import wayland.client.protocol; 6 | 7 | version(WlDynamic) 8 | { 9 | import wayland.native.client; 10 | import wayland.native.util; 11 | 12 | import derelict.util.loader : SharedLibLoader; 13 | 14 | private class WlClientLoader : SharedLibLoader 15 | { 16 | this() 17 | { 18 | super("libwayland-client.so"); 19 | } 20 | 21 | protected override void loadSymbols() 22 | { 23 | bindFunc( cast( void** )&wl_event_queue_destroy, "wl_event_queue_destroy" ); 24 | bindFunc( cast( void** )&wl_proxy_marshal, "wl_proxy_marshal" ); 25 | bindFunc( cast( void** )&wl_proxy_marshal_array, "wl_proxy_marshal_array" ); 26 | bindFunc( cast( void** )&wl_proxy_create, "wl_proxy_create" ); 27 | bindFunc( cast( void** )&wl_proxy_create_wrapper, "wl_proxy_create_wrapper" ); 28 | bindFunc( cast( void** )&wl_proxy_wrapper_destroy, "wl_proxy_wrapper_destroy" ); 29 | bindFunc( cast( void** )&wl_proxy_marshal_constructor, "wl_proxy_marshal_constructor" ); 30 | bindFunc( cast( void** )&wl_proxy_marshal_constructor_versioned, "wl_proxy_marshal_constructor_versioned" ); 31 | bindFunc( cast( void** )&wl_proxy_marshal_array_constructor, "wl_proxy_marshal_array_constructor" ); 32 | bindFunc( cast( void** )&wl_proxy_marshal_array_constructor_versioned, "wl_proxy_marshal_array_constructor_versioned" ); 33 | bindFunc( cast( void** )&wl_proxy_destroy, "wl_proxy_destroy" ); 34 | bindFunc( cast( void** )&wl_proxy_add_listener, "wl_proxy_add_listener" ); 35 | bindFunc( cast( void** )&wl_proxy_get_listener, "wl_proxy_get_listener" ); 36 | bindFunc( cast( void** )&wl_proxy_add_dispatcher, "wl_proxy_add_dispatcher" ); 37 | bindFunc( cast( void** )&wl_proxy_set_user_data, "wl_proxy_set_user_data" ); 38 | bindFunc( cast( void** )&wl_proxy_get_user_data, "wl_proxy_get_user_data" ); 39 | bindFunc( cast( void** )&wl_proxy_get_version, "wl_proxy_get_version" ); 40 | bindFunc( cast( void** )&wl_proxy_get_id, "wl_proxy_get_id" ); 41 | bindFunc( cast( void** )&wl_proxy_get_class, "wl_proxy_get_class" ); 42 | bindFunc( cast( void** )&wl_proxy_set_queue, "wl_proxy_set_queue" ); 43 | bindFunc( cast( void** )&wl_display_connect, "wl_display_connect" ); 44 | bindFunc( cast( void** )&wl_display_connect_to_fd, "wl_display_connect_to_fd" ); 45 | bindFunc( cast( void** )&wl_display_disconnect, "wl_display_disconnect" ); 46 | bindFunc( cast( void** )&wl_display_get_fd, "wl_display_get_fd" ); 47 | bindFunc( cast( void** )&wl_display_dispatch, "wl_display_dispatch" ); 48 | bindFunc( cast( void** )&wl_display_dispatch_queue, "wl_display_dispatch_queue" ); 49 | bindFunc( cast( void** )&wl_display_dispatch_queue_pending, "wl_display_dispatch_queue_pending" ); 50 | bindFunc( cast( void** )&wl_display_dispatch_pending, "wl_display_dispatch_pending" ); 51 | bindFunc( cast( void** )&wl_display_get_error, "wl_display_get_error" ); 52 | bindFunc( cast( void** )&wl_display_get_protocol_error, "wl_display_get_protocol_error" ); 53 | bindFunc( cast( void** )&wl_display_flush, "wl_display_flush" ); 54 | bindFunc( cast( void** )&wl_display_roundtrip_queue, "wl_display_roundtrip_queue" ); 55 | bindFunc( cast( void** )&wl_display_roundtrip, "wl_display_roundtrip" ); 56 | bindFunc( cast( void** )&wl_display_create_queue, "wl_display_create_queue" ); 57 | bindFunc( cast( void** )&wl_display_prepare_read_queue, "wl_display_prepare_read_queue" ); 58 | bindFunc( cast( void** )&wl_display_prepare_read, "wl_display_prepare_read" ); 59 | bindFunc( cast( void** )&wl_display_cancel_read, "wl_display_cancel_read" ); 60 | bindFunc( cast( void** )&wl_display_read_events, "wl_display_read_events" ); 61 | bindFunc( cast( void** )&wl_log_set_handler_client, "wl_log_set_handler_client" ); 62 | 63 | bindFunc( cast( void** )&wl_list_init, "wl_list_init" ); 64 | bindFunc( cast( void** )&wl_list_insert, "wl_list_insert" ); 65 | bindFunc( cast( void** )&wl_list_remove, "wl_list_remove" ); 66 | bindFunc( cast( void** )&wl_list_length, "wl_list_length" ); 67 | bindFunc( cast( void** )&wl_list_empty, "wl_list_empty" ); 68 | bindFunc( cast( void** )&wl_list_insert_list, "wl_list_insert_list" ); 69 | bindFunc( cast( void** )&wl_array_init, "wl_array_init" ); 70 | bindFunc( cast( void** )&wl_array_release, "wl_array_release" ); 71 | bindFunc( cast( void** )&wl_array_add, "wl_array_add" ); 72 | bindFunc( cast( void** )&wl_array_copy, "wl_array_copy" ); 73 | } 74 | } 75 | 76 | private __gshared WlClientLoader _loader; 77 | 78 | shared static this() 79 | { 80 | _loader = new WlClientLoader; 81 | } 82 | 83 | public @property SharedLibLoader wlClientDynLib() 84 | { 85 | return _loader; 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /examples/hello/source/hello.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 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 = ®Global; 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 | } 221 | -------------------------------------------------------------------------------- /server/source/wayland/server/eventloop.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module wayland.server.eventloop; 3 | 4 | import wayland.native.server; 5 | import wayland.native.util; 6 | import wayland.util; 7 | 8 | class WlEventLoop : Native!wl_event_loop 9 | { 10 | mixin nativeImpl!(wl_event_loop); 11 | 12 | alias DestroyDg = void delegate(WlEventLoop loop); 13 | alias FdDg = int delegate (int fd, uint mask); 14 | alias TimerDg = int delegate (); 15 | alias SignalDg = int delegate (int sigNum); 16 | alias IdleDg = void delegate (); 17 | 18 | private wl_listener _destroyListener; 19 | private DestroyDg _onDestroy; 20 | 21 | this (wl_event_loop* native) 22 | { 23 | _native = native; 24 | ObjectCache.set(native, this); 25 | 26 | wl_list_init(&_destroyListener.link); 27 | _destroyListener.notify = &wl_d_eventloop_destroy; 28 | wl_event_loop_add_destroy_listener(native, &_destroyListener); 29 | } 30 | 31 | this() 32 | { 33 | this(wl_event_loop_create()); 34 | } 35 | 36 | void destroy() 37 | { 38 | wl_event_loop_destroy(_native); 39 | } 40 | 41 | @property void destroyListener(DestroyDg dg) 42 | { 43 | _onDestroy = dg; 44 | } 45 | 46 | @property int fd() 47 | { 48 | return wl_event_loop_get_fd(native); 49 | } 50 | 51 | int dispatch(int timeout) 52 | { 53 | return wl_event_loop_dispatch(native, timeout); 54 | } 55 | 56 | void dispatchIdle() 57 | { 58 | wl_event_loop_dispatch_idle(native); 59 | } 60 | 61 | WlFdEventSource addFd(int fd, uint mask, FdDg dg) 62 | { 63 | return new WlFdEventSource(native, fd, mask, dg); 64 | } 65 | 66 | WlTimerEventSource addTimer(TimerDg dg) 67 | { 68 | return new WlTimerEventSource(native, dg); 69 | } 70 | 71 | WlSignalEventSource addSignal(int signalNum, SignalDg dg) 72 | { 73 | return new WlSignalEventSource(native, signalNum, dg); 74 | } 75 | 76 | WlIdleEventSource addIdle(IdleDg dg) 77 | { 78 | return new WlIdleEventSource(native, dg); 79 | } 80 | } 81 | 82 | abstract class WlEventSource : Native!wl_event_source 83 | { 84 | mixin nativeImpl!(wl_event_source); 85 | 86 | this(wl_event_source* native) 87 | { 88 | _native = native; 89 | } 90 | 91 | int remove () 92 | { 93 | return wl_event_source_remove(native); 94 | } 95 | 96 | void check() 97 | { 98 | wl_event_source_check(native); 99 | } 100 | } 101 | 102 | class WlFdEventSource : WlEventSource 103 | { 104 | private WlEventLoop.FdDg dg; 105 | 106 | this (wl_event_loop* nativeLoop, int fd, uint mask, WlEventLoop.FdDg dg) 107 | { 108 | this.dg = dg; 109 | super(wl_event_loop_add_fd( 110 | nativeLoop, fd, mask, &wl_d_eventloop_fd, cast(void*)this 111 | )); 112 | } 113 | 114 | int update(uint mask) 115 | { 116 | return wl_event_source_fd_update(native, mask); 117 | } 118 | } 119 | 120 | class WlTimerEventSource : WlEventSource 121 | { 122 | private WlEventLoop.TimerDg dg; 123 | 124 | this (wl_event_loop* nativeLoop, WlEventLoop.TimerDg dg) 125 | { 126 | this.dg = dg; 127 | super(wl_event_loop_add_timer( 128 | nativeLoop, &wl_d_eventloop_timer, cast(void*)this 129 | )); 130 | } 131 | 132 | int update(uint msDelay) 133 | { 134 | return wl_event_source_timer_update(native, msDelay); 135 | } 136 | } 137 | 138 | class WlSignalEventSource : WlEventSource 139 | { 140 | private WlEventLoop.SignalDg dg; 141 | 142 | this (wl_event_loop* nativeLoop, int signalNum, WlEventLoop.SignalDg dg) 143 | { 144 | this.dg = dg; 145 | super(wl_event_loop_add_signal( 146 | nativeLoop, signalNum, &wl_d_eventloop_signal, cast(void*)this 147 | )); 148 | } 149 | } 150 | 151 | class WlIdleEventSource : WlEventSource 152 | { 153 | private WlEventLoop.IdleDg dg; 154 | 155 | this (wl_event_loop* nativeLoop, WlEventLoop.IdleDg dg) 156 | { 157 | this.dg = dg; 158 | super(wl_event_loop_add_idle( 159 | nativeLoop, &wl_d_eventloop_idle, cast(void*)this 160 | )); 161 | } 162 | } 163 | 164 | 165 | private extern(C) nothrow 166 | { 167 | 168 | void wl_d_eventloop_destroy(wl_listener*, void* data) 169 | { 170 | nothrowFnWrapper!({ 171 | auto el = cast(WlEventLoop)ObjectCache.get(data); 172 | assert(el, "wl_d_eventloop_destroy: could not get event loop from cache"); 173 | if (el._onDestroy) el._onDestroy(el); 174 | ObjectCache.remove(data); 175 | }); 176 | } 177 | 178 | int wl_d_eventloop_fd(int fd, uint mask, void* data) 179 | { 180 | return nothrowFnWrapper!({ 181 | auto src = cast(WlFdEventSource)data; 182 | return src.dg(fd, mask); 183 | }); 184 | } 185 | 186 | int wl_d_eventloop_timer(void* data) 187 | { 188 | return nothrowFnWrapper!({ 189 | auto src = cast(WlTimerEventSource)data; 190 | return src.dg(); 191 | }); 192 | } 193 | 194 | int wl_d_eventloop_signal(int sigNumber, void* data) 195 | { 196 | return nothrowFnWrapper!({ 197 | auto src = cast(WlSignalEventSource)data; 198 | return src.dg(sigNumber); 199 | }); 200 | } 201 | 202 | void wl_d_eventloop_idle(void* data) 203 | { 204 | nothrowFnWrapper!({ 205 | auto src = cast(WlIdleEventSource)data; 206 | src.dg(); 207 | }); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /examples/compositor/source/shell.d: -------------------------------------------------------------------------------- 1 | module shell; 2 | 3 | import compositor; 4 | import output; 5 | import wayland.server; 6 | 7 | import std.stdio; 8 | import std.algorithm; 9 | import std.range; 10 | 11 | class Shell : WlShell 12 | { 13 | Compositor comp; 14 | ShellSurface[] topLevels; 15 | 16 | this (Compositor comp) 17 | { 18 | super(comp.display, ver); 19 | this.comp = comp; 20 | } 21 | 22 | void addTopLevel(ShellSurface ss) 23 | { 24 | topLevels ~= ss; 25 | ss.addDestroyListener((WlResource res) { 26 | topLevels = topLevels.remove!(tl => res is tl); 27 | }); 28 | } 29 | 30 | void mouseButton(int x, int y, int button, WlPointer.ButtonState state) 31 | { 32 | foreach(tl; retro(topLevels)) 33 | { 34 | if (x >= tl.x && x < tl.x+tl.width && 35 | y >= tl.y && y < tl.y+tl.height) 36 | { 37 | comp.seat.mouseButton(tl.client, button, state); 38 | break; 39 | } 40 | } 41 | } 42 | 43 | // paint windows over background (last inserted on top) 44 | // a more general algorithm should be needed for other kinds of surfaces (eg. cursors) 45 | void paint(Output output) 46 | { 47 | immutable ow = output.width; 48 | immutable oh = output.height; 49 | auto ob = output.buf; 50 | foreach (tl; topLevels) 51 | { 52 | if (!(tl.surf.outputMask & output.mask)) continue; 53 | 54 | auto sb = tl.surf.state.buffer; 55 | auto sd = cast(uint[])sb.beginAccess(); 56 | scope(exit) sb.endAccess(); 57 | 58 | if (tl.unplaced) 59 | { 60 | tl.x = (ow - sb.width) / 2; 61 | tl.y = (oh - sb.height) / 2; 62 | tl.unplaced = false; 63 | } 64 | 65 | tl.width = sb.width; 66 | tl.height = sb.height; 67 | 68 | if (tl.x > ow) break; 69 | 70 | if (sb.format == WlShm.Format.xrgb8888) 71 | { 72 | // no blending 73 | foreach (r; 0 .. sb.height) 74 | { 75 | if (r + tl.y > oh) break; 76 | 77 | immutable srcFrom = r*sb.stride/4; 78 | immutable destFrom = (r+tl.y) * ow + tl.x; 79 | immutable copyWidth = min( 80 | sb.width, ow - tl.x 81 | ); 82 | ob[destFrom .. destFrom+copyWidth] = 83 | sd[srcFrom .. srcFrom+copyWidth]; 84 | } 85 | } 86 | else 87 | { 88 | assert(sb.format == WlShm.Format.argb8888); 89 | // inefficient blending 90 | foreach (r; 0 .. sb.height) 91 | { 92 | if (r + tl.y > oh) break; 93 | 94 | foreach (c; 0 .. sb.width) 95 | { 96 | if (c + tl.x > ow) break; 97 | 98 | immutable size_t srcInd = r*sb.stride/4 + c; 99 | immutable size_t destInd = (r+tl.y) * ow + tl.x+c; 100 | 101 | immutable uint dest = ob[destInd]; 102 | immutable uint aDest = (dest & 0xff000000) >>> 24; 103 | immutable uint rDest = (dest & 0xff0000) >>> 16; 104 | immutable uint gDest = (dest & 0xff00) >>> 8; 105 | immutable uint bDest = (dest & 0xff); 106 | 107 | immutable uint src = sd[srcInd]; 108 | immutable uint aSrc = (src & 0xff000000) >>> 24; 109 | immutable uint rSrc = (src & 0xff0000) >>> 16; 110 | immutable uint gSrc = (src & 0xff00) >>> 8; 111 | immutable uint bSrc = (src & 0xff); 112 | 113 | auto aRes = aSrc + aDest*(255 - aSrc) / 255; 114 | 115 | auto rRes = ((aSrc*rSrc) + (255-aSrc)*(aDest*rDest)/255) / aRes; 116 | auto gRes = ((aSrc*gSrc) + (255-aSrc)*(aDest*gDest)/255) / aRes; 117 | auto bRes = ((aSrc*bSrc) + (255-aSrc)*(aDest*bDest)/255) / aRes; 118 | 119 | if (aRes > 0xff) aRes = 0xff; 120 | if (rRes > 0xff) rRes = 0xff; 121 | if (gRes > 0xff) gRes = 0xff; 122 | if (bRes > 0xff) bRes = 0xff; 123 | 124 | ob[destInd] = aRes << 24 | rRes << 16 | gRes << 8 | bRes; 125 | } 126 | } 127 | } 128 | } 129 | } 130 | 131 | // WlShell 132 | 133 | override WlShellSurface getShellSurface(WlClient cl, Resource res, uint id, WlSurface surf) 134 | { 135 | return new ShellSurface(cl, id, cast(Surface)surf, res, comp); 136 | } 137 | } 138 | 139 | 140 | class ShellSurface : WlShellSurface 141 | { 142 | Surface surf; 143 | WlShell.Resource shRes; 144 | Compositor comp; 145 | bool unplaced = true; 146 | int x; int y; 147 | int width; int height; 148 | 149 | this(WlClient cl, uint id, Surface surf, WlShell.Resource shRes, Compositor comp) 150 | { 151 | super(cl, ver, id); 152 | this.surf = surf; 153 | this.shRes = shRes; 154 | this.comp = comp; 155 | } 156 | 157 | @property Shell shell() 158 | { 159 | return cast(Shell)shRes.outer; 160 | } 161 | 162 | // WlShellSurface 163 | 164 | override protected void pong(WlClient cl, 165 | uint serial) 166 | {} 167 | 168 | override protected void move(WlClient cl, 169 | WlSeat.Resource seat, 170 | uint serial) 171 | {} 172 | 173 | override protected void resize(WlClient cl, 174 | WlSeat.Resource seat, 175 | uint serial, 176 | Resize edges) 177 | {} 178 | 179 | 180 | override protected void setToplevel(WlClient cl) 181 | { 182 | try { 183 | surf.assignRole("shell"); 184 | auto output = comp.outputs[0]; 185 | surf.outputMask = surf.outputMask | output.mask; 186 | shell.addTopLevel(this); 187 | } 188 | catch (Exception ex) 189 | { 190 | shRes.postError(WlShell.Error.role, ex.msg); 191 | } 192 | } 193 | 194 | override protected void setTransient(WlClient cl, 195 | WlSurface parent, 196 | int x, 197 | int y, 198 | Transient flags) 199 | {} 200 | 201 | override protected void setFullscreen(WlClient cl, 202 | FullscreenMethod method, 203 | uint framerate, 204 | WlOutput.Resource outputRes) 205 | {} 206 | 207 | override protected void setPopup(WlClient cl, 208 | WlSeat.Resource seat, 209 | uint serial, 210 | WlSurface parent, 211 | int x, 212 | int y, 213 | Transient flags) 214 | {} 215 | 216 | override protected void setMaximized(WlClient cl, 217 | WlOutput.Resource output) 218 | {} 219 | 220 | override protected void setTitle(WlClient cl, 221 | string title) 222 | { 223 | 224 | } 225 | 226 | override protected void setClass(WlClient cl, 227 | string class_) 228 | {} 229 | 230 | } 231 | -------------------------------------------------------------------------------- /client/source/wayland/client/core.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module wayland.client.core; 3 | 4 | import core.memory : GC; 5 | 6 | import wayland.client.protocol : WlDisplay; 7 | import wayland.native.client; 8 | import wayland.native.util; 9 | import wayland.util; 10 | 11 | /++ 12 | + A queue for WlProxy object events. 13 | + 14 | + Event queues allows the events on a display to be handled in a thread-safe 15 | + manner. See WlDisplay for details. 16 | +/ 17 | class WlEventQueue : Native!wl_event_queue 18 | { 19 | mixin nativeImpl!(wl_event_queue); 20 | this(wl_event_queue* native) 21 | { 22 | _native = native; 23 | } 24 | } 25 | 26 | /++ 27 | + Represents a connection to the compositor and acts as a proxy to 28 | + the wl_display singleton object. 29 | + 30 | + A WlDisplay object represents a client connection to a Wayland 31 | + compositor. It is created with either WlDisplay.connect(), 32 | + WlDisplay.connectToFd() or WlDisplay.fromNative(). 33 | + A connection is terminated using disconnect(). 34 | + 35 | + A WlDisplay is also used as the WlProxy for the wl_display 36 | + singleton object on the compositor side. 37 | + 38 | + A WlDisplay object handles all the data sent from and to the 39 | + compositor. When a WlProxy marshals a request, it will write its wire 40 | + representation to the display's write buffer. The data is sent to the 41 | + compositor when the client calls WlDisplay.flush(). 42 | + 43 | + Incoming data is handled in two steps: queueing and dispatching. In the 44 | + queue step, the data coming from the display fd is interpreted and 45 | + added to a queue. On the dispatch step, the handler for the incoming 46 | + event set by the client on the corresponding WlProxy is called. 47 | + 48 | + A WlDisplay has at least one event queue, called the default 49 | + queue. Clients can create additional event queues with 50 | + WlDisplay.createQueue() and assign WlProxy's to it. Events 51 | + occurring in a particular proxy are always queued in its assigned queue. 52 | + A client can ensure that a certain assumption, such as holding a lock 53 | + or running from a given thread, is true when a proxy event handler is 54 | + called by assigning that proxy to an event queue and making sure that 55 | + this queue is only dispatched when the assumption holds. 56 | + 57 | + The default queue is dispatched by calling WlDisplay.dispatch(). 58 | + This will dispatch any events queued on the default queue and attempt 59 | + to read from the display fd if it's empty. Events read are then queued 60 | + on the appropriate queues according to the proxy assignment. 61 | + 62 | + A user created queue is dispatched with WlDisplay.dispatchQueue(). 63 | + This function behaves exactly the same as WlDisplay.dispatch() 64 | + but it dispatches given queue instead of the default queue. 65 | + 66 | + A real world example of event queue usage is Mesa's implementation of 67 | + eglSwapBuffers() for the Wayland platform. This function might need 68 | + to block until a frame callback is received, but dispatching the default 69 | + queue could cause an event handler on the client to start drawing 70 | + again. This problem is solved using another event queue, so that only 71 | + the events handled by the EGL code are dispatched during the block. 72 | + 73 | + This creates a problem where a thread dispatches a non-default 74 | + queue, reading all the data from the display fd. If the application 75 | + would call \em poll(2) after that it would block, even though there 76 | + might be events queued on the default queue. Those events should be 77 | + dispatched with WlDisplay.dispatchPending() or 78 | + WlDisplay.dispatchQueuePending() before flushing and blocking. 79 | +/ 80 | abstract class WlDisplayBase : WlProxy, Native!wl_display 81 | { 82 | protected this(wl_display* native) 83 | { 84 | super(cast(wl_proxy*)native); 85 | } 86 | 87 | static WlDisplay connect(in string name = null) 88 | { 89 | import std.exception : enforce, ErrnoException; 90 | import std.string : toStringz; 91 | 92 | const(char)* displayName = name.length ? toStringz(name) : null; 93 | auto nativeDpy = enforce!ErrnoException( 94 | wl_display_connect(displayName), 95 | "Could not get a display handle from Wayland" 96 | ); 97 | return new WlDisplay(nativeDpy); 98 | } 99 | 100 | static WlDisplay connectToFd(in int fd) 101 | { 102 | auto nativeDpy = wl_display_connect_to_fd(fd); 103 | return nativeDpy ? new WlDisplay(nativeDpy) : null; 104 | } 105 | 106 | /++ 107 | + Construct a WlDisplay from a native handle. 108 | + Useful for interaction with 3rd party libraries (e.g. GLFW) 109 | + that handle the connection. 110 | +/ 111 | static WlDisplay fromNative(wl_display* nativeDpy) 112 | { 113 | return new WlDisplay(nativeDpy); 114 | } 115 | 116 | final override @property inout(wl_display)* native() inout 117 | { 118 | return cast(inout(wl_display)*)(proxy); 119 | } 120 | 121 | final void disconnect() 122 | { 123 | wl_display_disconnect(native); 124 | WlProxy.destroyNotify(); 125 | } 126 | 127 | final int getFd() 128 | { 129 | return wl_display_get_fd(native); 130 | } 131 | 132 | final int dispatch() 133 | { 134 | return wl_display_dispatch(native); 135 | } 136 | 137 | final int dispatchPending() 138 | { 139 | return wl_display_dispatch_pending(native); 140 | } 141 | 142 | final int dispatchQueue(WlEventQueue queue) 143 | { 144 | return wl_display_dispatch_queue(native, queue.native); 145 | } 146 | 147 | final int dispatchQueuePending(WlEventQueue queue) 148 | { 149 | return wl_display_dispatch_queue_pending(native, queue.native); 150 | } 151 | 152 | final int getError() 153 | { 154 | return wl_display_get_error(native); 155 | } 156 | 157 | final immutable(WlInterface) getProtocolError(out uint code, out uint id) 158 | { 159 | const(wl_interface)* iface = null; 160 | code = wl_display_get_protocol_error(native, &iface, &id); 161 | if (iface) { 162 | return new immutable WlInterface(cast(immutable)iface); 163 | } 164 | else { 165 | return null; 166 | } 167 | } 168 | 169 | final int flush() 170 | { 171 | return wl_display_flush(native); 172 | } 173 | 174 | final int roundtripQueue(WlEventQueue queue) 175 | { 176 | return wl_display_roundtrip_queue(native, queue.native); 177 | } 178 | 179 | final int roundtrip() 180 | { 181 | return wl_display_roundtrip(native); 182 | } 183 | 184 | final WlEventQueue createQueue() 185 | { 186 | return new WlEventQueue(wl_display_create_queue(native)); 187 | } 188 | 189 | final int prepareReadQueue(WlEventQueue queue) 190 | { 191 | return wl_display_prepare_read_queue(native, queue.native); 192 | } 193 | 194 | final int prepareRead() 195 | { 196 | return wl_display_prepare_read(native); 197 | } 198 | 199 | final void cancelRead() 200 | { 201 | wl_display_cancel_read(native); 202 | } 203 | 204 | final int readEvents() 205 | { 206 | return wl_display_read_events(native); 207 | } 208 | } 209 | 210 | /// Wrapper around wl_proxy and base class for types generated by the protocol. 211 | abstract class WlProxy 212 | { 213 | private wl_proxy* _proxy; 214 | private void* _userData; 215 | 216 | protected this(wl_proxy* proxy) 217 | { 218 | _proxy = proxy; 219 | void* thisHandle = cast(void*) this; 220 | wl_proxy_set_user_data(proxy, thisHandle); 221 | GC.addRoot(thisHandle); 222 | GC.setAttr(thisHandle, GC.BlkAttr.NO_MOVE); 223 | } 224 | 225 | protected void destroyNotify() 226 | { 227 | _proxy = null; 228 | // destroy(this); // HACK no destructor is implemented so we can skip that. 229 | GC.free(cast(void*) this); 230 | } 231 | 232 | static WlProxy get(wl_proxy* proxy) 233 | { 234 | return cast(WlProxy) wl_proxy_get_user_data(proxy); 235 | } 236 | 237 | final @property inout(wl_proxy)* proxy() inout 238 | { 239 | return _proxy; 240 | } 241 | 242 | /// Get the protocol version of WlDisplay. 243 | final @property uint ver() 244 | { 245 | return wl_proxy_get_version(proxy); 246 | } 247 | 248 | /// Get the id assigned to this object. 249 | final @property uint id() 250 | { 251 | return wl_proxy_get_id(proxy); 252 | } 253 | 254 | /// Get the class of this object. 255 | final @property string class_() 256 | { 257 | import std.string : fromStringz; 258 | return fromStringz(wl_proxy_get_class(proxy)).idup; 259 | } 260 | 261 | final @property void userData(void* value) 262 | { 263 | _userData = value; 264 | } 265 | 266 | final @property void* userData() 267 | { 268 | return _userData; 269 | } 270 | } 271 | 272 | immutable abstract class WlProxyInterface : WlInterface 273 | { 274 | this(immutable wl_interface* native) 275 | { 276 | super(native); 277 | } 278 | 279 | abstract WlProxy makeProxy(wl_proxy* proxy) immutable; 280 | } 281 | -------------------------------------------------------------------------------- /server/source/wayland/server/package.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | /// bindings to wayland-server-core.h 3 | module wayland.server; 4 | 5 | public import wayland.server.core; 6 | public import wayland.server.protocol; 7 | public import wayland.server.eventloop; 8 | 9 | version(WlDynamic) 10 | { 11 | import wayland.native.server; 12 | import wayland.native.util; 13 | 14 | import derelict.util.loader : SharedLibLoader; 15 | 16 | private class WlServerLoader : SharedLibLoader 17 | { 18 | this() 19 | { 20 | super("libwayland-server.so"); 21 | } 22 | 23 | protected override void loadSymbols() 24 | { 25 | bindFunc( cast( void** )&wl_event_loop_create, "wl_event_loop_create" ); 26 | bindFunc( cast( void** )&wl_event_loop_destroy, "wl_event_loop_destroy" ); 27 | bindFunc( cast( void** )&wl_event_loop_add_fd, "wl_event_loop_add_fd" ); 28 | bindFunc( cast( void** )&wl_event_source_fd_update, "wl_event_source_fd_update" ); 29 | bindFunc( cast( void** )&wl_event_loop_add_timer, "wl_event_loop_add_timer" ); 30 | bindFunc( cast( void** )&wl_event_loop_add_signal, "wl_event_loop_add_signal" ); 31 | bindFunc( cast( void** )&wl_event_source_timer_update, "wl_event_source_timer_update" ); 32 | bindFunc( cast( void** )&wl_event_source_remove, "wl_event_source_remove" ); 33 | bindFunc( cast( void** )&wl_event_source_check, "wl_event_source_check" ); 34 | bindFunc( cast( void** )&wl_event_loop_dispatch, "wl_event_loop_dispatch" ); 35 | bindFunc( cast( void** )&wl_event_loop_dispatch_idle, "wl_event_loop_dispatch_idle" ); 36 | bindFunc( cast( void** )&wl_event_loop_add_idle, "wl_event_loop_add_idle" ); 37 | bindFunc( cast( void** )&wl_event_loop_get_fd, "wl_event_loop_get_fd" ); 38 | bindFunc( cast( void** )&wl_event_loop_add_destroy_listener, "wl_event_loop_add_destroy_listener" ); 39 | bindFunc( cast( void** )&wl_event_loop_get_destroy_listener, "wl_event_loop_get_destroy_listener" ); 40 | bindFunc( cast( void** )&wl_display_create, "wl_display_create" ); 41 | bindFunc( cast( void** )&wl_display_destroy, "wl_display_destroy" ); 42 | bindFunc( cast( void** )&wl_display_get_event_loop, "wl_display_get_event_loop" ); 43 | bindFunc( cast( void** )&wl_display_add_socket, "wl_display_add_socket" ); 44 | bindFunc( cast( void** )&wl_display_add_socket_auto, "wl_display_add_socket_auto" ); 45 | bindFunc( cast( void** )&wl_display_add_socket_fd, "wl_display_add_socket_fd" ); 46 | bindFunc( cast( void** )&wl_display_terminate, "wl_display_terminate" ); 47 | bindFunc( cast( void** )&wl_display_run, "wl_display_run" ); 48 | bindFunc( cast( void** )&wl_display_flush_clients, "wl_display_flush_clients" ); 49 | bindFunc( cast( void** )&wl_display_get_serial, "wl_display_get_serial" ); 50 | bindFunc( cast( void** )&wl_display_next_serial, "wl_display_next_serial" ); 51 | bindFunc( cast( void** )&wl_display_add_destroy_listener, "wl_display_add_destroy_listener" ); 52 | bindFunc( cast( void** )&wl_display_add_client_created_listener, "wl_display_add_client_created_listener" ); 53 | bindFunc( cast( void** )&wl_display_get_destroy_listener, "wl_display_get_destroy_listener" ); 54 | bindFunc( cast( void** )&wl_global_create, "wl_global_create" ); 55 | bindFunc( cast( void** )&wl_global_destroy, "wl_global_destroy" ); 56 | bindFunc( cast( void** )&wl_display_set_global_filter, "wl_display_set_global_filter" ); 57 | bindFunc( cast( void** )&wl_global_get_interface, "wl_global_get_interface" ); 58 | bindFunc( cast( void** )&wl_global_get_user_data, "wl_global_get_user_data" ); 59 | bindFunc( cast( void** )&wl_client_create, "wl_client_create" ); 60 | bindFunc( cast( void** )&wl_display_get_client_list, "wl_display_get_client_list" ); 61 | bindFunc( cast( void** )&wl_client_get_link, "wl_client_get_link" ); 62 | bindFunc( cast( void** )&wl_client_from_link, "wl_client_from_link" ); 63 | bindFunc( cast( void** )&wl_client_destroy, "wl_client_destroy" ); 64 | bindFunc( cast( void** )&wl_client_flush, "wl_client_flush" ); 65 | bindFunc( cast( void** )&wl_client_get_credentials, "wl_client_get_credentials" ); 66 | bindFunc( cast( void** )&wl_client_get_fd, "wl_client_get_fd" ); 67 | bindFunc( cast( void** )&wl_client_add_destroy_listener, "wl_client_add_destroy_listener" ); 68 | bindFunc( cast( void** )&wl_client_get_destroy_listener, "wl_client_get_destroy_listener" ); 69 | bindFunc( cast( void** )&wl_client_get_object, "wl_client_get_object" ); 70 | bindFunc( cast( void** )&wl_client_post_no_memory, "wl_client_post_no_memory" ); 71 | bindFunc( cast( void** )&wl_client_add_resource_created_listener, "wl_client_add_resource_created_listener" ); 72 | bindFunc( cast( void** )&wl_client_for_each_resource, "wl_client_for_each_resource" ); 73 | bindFunc( cast( void** )&wl_resource_post_event, "wl_resource_post_event" ); 74 | bindFunc( cast( void** )&wl_resource_post_event_array, "wl_resource_post_event_array" ); 75 | bindFunc( cast( void** )&wl_resource_queue_event, "wl_resource_queue_event" ); 76 | bindFunc( cast( void** )&wl_resource_queue_event_array, "wl_resource_queue_event_array" ); 77 | bindFunc( cast( void** )&wl_resource_post_error, "wl_resource_post_error" ); 78 | bindFunc( cast( void** )&wl_resource_post_no_memory, "wl_resource_post_no_memory" ); 79 | bindFunc( cast( void** )&wl_client_get_display, "wl_client_get_display" ); 80 | bindFunc( cast( void** )&wl_resource_create, "wl_resource_create" ); 81 | bindFunc( cast( void** )&wl_resource_set_implementation, "wl_resource_set_implementation" ); 82 | bindFunc( cast( void** )&wl_resource_set_dispatcher, "wl_resource_set_dispatcher" ); 83 | bindFunc( cast( void** )&wl_resource_destroy, "wl_resource_destroy" ); 84 | bindFunc( cast( void** )&wl_resource_get_id, "wl_resource_get_id" ); 85 | bindFunc( cast( void** )&wl_resource_get_link, "wl_resource_get_link" ); 86 | bindFunc( cast( void** )&wl_resource_from_link, "wl_resource_from_link" ); 87 | bindFunc( cast( void** )&wl_resource_find_for_client, "wl_resource_find_for_client" ); 88 | bindFunc( cast( void** )&wl_resource_get_client, "wl_resource_get_client" ); 89 | bindFunc( cast( void** )&wl_resource_set_user_data, "wl_resource_set_user_data" ); 90 | bindFunc( cast( void** )&wl_resource_get_user_data, "wl_resource_get_user_data" ); 91 | bindFunc( cast( void** )&wl_resource_get_version, "wl_resource_get_version" ); 92 | bindFunc( cast( void** )&wl_resource_set_destructor, "wl_resource_set_destructor" ); 93 | bindFunc( cast( void** )&wl_resource_instance_of, "wl_resource_instance_of" ); 94 | bindFunc( cast( void** )&wl_resource_get_class, "wl_resource_get_class" ); 95 | bindFunc( cast( void** )&wl_resource_add_destroy_listener, "wl_resource_add_destroy_listener" ); 96 | bindFunc( cast( void** )&wl_resource_get_destroy_listener, "wl_resource_get_destroy_listener" ); 97 | bindFunc( cast( void** )&wl_shm_buffer_get, "wl_shm_buffer_get" ); 98 | bindFunc( cast( void** )&wl_shm_buffer_begin_access, "wl_shm_buffer_begin_access" ); 99 | bindFunc( cast( void** )&wl_shm_buffer_end_access, "wl_shm_buffer_end_access" ); 100 | bindFunc( cast( void** )&wl_shm_buffer_get_data, "wl_shm_buffer_get_data" ); 101 | bindFunc( cast( void** )&wl_shm_buffer_get_stride, "wl_shm_buffer_get_stride" ); 102 | bindFunc( cast( void** )&wl_shm_buffer_get_format, "wl_shm_buffer_get_format" ); 103 | bindFunc( cast( void** )&wl_shm_buffer_get_width, "wl_shm_buffer_get_width" ); 104 | bindFunc( cast( void** )&wl_shm_buffer_get_height, "wl_shm_buffer_get_height" ); 105 | bindFunc( cast( void** )&wl_shm_buffer_ref_pool, "wl_shm_buffer_ref_pool" ); 106 | bindFunc( cast( void** )&wl_shm_pool_unref, "wl_shm_pool_unref" ); 107 | bindFunc( cast( void** )&wl_display_init_shm, "wl_display_init_shm" ); 108 | bindFunc( cast( void** )&wl_display_add_shm_format, "wl_display_add_shm_format" ); 109 | bindFunc( cast( void** )&wl_log_set_handler_server, "wl_log_set_handler_server" ); 110 | bindFunc( cast( void** )&wl_display_add_protocol_logger, "wl_display_add_protocol_logger" ); 111 | bindFunc( cast( void** )&wl_protocol_logger_destroy, "wl_protocol_logger_destroy" ); 112 | 113 | bindFunc( cast( void** )&wl_list_init, "wl_list_init" ); 114 | bindFunc( cast( void** )&wl_list_insert, "wl_list_insert" ); 115 | bindFunc( cast( void** )&wl_list_remove, "wl_list_remove" ); 116 | bindFunc( cast( void** )&wl_list_length, "wl_list_length" ); 117 | bindFunc( cast( void** )&wl_list_empty, "wl_list_empty" ); 118 | bindFunc( cast( void** )&wl_list_insert_list, "wl_list_insert_list" ); 119 | bindFunc( cast( void** )&wl_array_init, "wl_array_init" ); 120 | bindFunc( cast( void** )&wl_array_release, "wl_array_release" ); 121 | bindFunc( cast( void** )&wl_array_add, "wl_array_add" ); 122 | bindFunc( cast( void** )&wl_array_copy, "wl_array_copy" ); 123 | } 124 | } 125 | 126 | private __gshared WlServerLoader _loader; 127 | 128 | shared static this() 129 | { 130 | _loader = new WlServerLoader; 131 | } 132 | 133 | public @property SharedLibLoader wlServerDynLib() 134 | { 135 | return _loader; 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /examples/compositor/source/compositor.d: -------------------------------------------------------------------------------- 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 | } 424 | -------------------------------------------------------------------------------- /examples/simple_egl/source/simple_egl.d: -------------------------------------------------------------------------------- 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 | } 425 | -------------------------------------------------------------------------------- /server/source/wayland/server/core.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | module wayland.server.core; 3 | 4 | import wayland.server.protocol : WlDisplay, WlShm; 5 | import wayland.server.eventloop; 6 | import wayland.server.listener; 7 | import wayland.native.server; 8 | import wayland.native.util; 9 | import wayland.util; 10 | 11 | import std.string; 12 | import std.stdio; 13 | import std.exception : enforce; 14 | import core.sys.posix.sys.types; 15 | 16 | 17 | enum : uint 18 | { 19 | WL_EVENT_READABLE = 0x01, 20 | WL_EVENT_WRITABLE = 0x02, 21 | WL_EVENT_HANGUP = 0x04, 22 | WL_EVENT_ERROR = 0x08 23 | } 24 | 25 | 26 | class WlDisplayBase : Native!wl_display 27 | { 28 | mixin nativeImpl!(wl_display); 29 | 30 | alias DestroySig = Signal!(); 31 | alias ClientCreatedSig = Signal!(WlClient); 32 | 33 | private wl_listener _destroyListener; 34 | private DestroySig _destroySig; 35 | 36 | private wl_listener _clientCreatedListener; 37 | private ClientCreatedSig _clientCreatedSig; 38 | 39 | // one loop per display, so no need to use the object store 40 | private WlEventLoop _loop; 41 | 42 | private WlClient[] _clients; 43 | 44 | 45 | static WlDisplay create() 46 | { 47 | return new WlDisplay(wl_display_create()); 48 | } 49 | 50 | protected this (wl_display* native) 51 | { 52 | _native = native; 53 | ObjectCache.set(native, this); 54 | 55 | wl_list_init(&_destroyListener.link); 56 | _destroyListener.notify = &wl_d_display_destroy; 57 | wl_display_add_destroy_listener(native, &_destroyListener); 58 | 59 | wl_list_init(&_clientCreatedListener.link); 60 | _clientCreatedListener.notify = &wl_d_client_created; 61 | wl_display_add_client_created_listener(native, &_clientCreatedListener); 62 | } 63 | 64 | void destroy() 65 | { 66 | wl_display_destroy(native); 67 | } 68 | 69 | private DestroySig destroySig() 70 | { 71 | if (!_destroySig) _destroySig = new DestroySig(); 72 | return _destroySig; 73 | } 74 | 75 | private ClientCreatedSig clientCreatedSig() 76 | { 77 | if (!_clientCreatedSig) _clientCreatedSig = new ClientCreatedSig(); 78 | return _clientCreatedSig; 79 | } 80 | 81 | void addDestroyListener(DestroySig.Listener listener) 82 | { 83 | destroySig.add(listener); 84 | } 85 | 86 | void addClientCreatedListener(ClientCreatedSig.Listener listener) 87 | { 88 | clientCreatedSig.add(listener); 89 | } 90 | 91 | @property WlEventLoop eventLoop() 92 | { 93 | if (!_loop) _loop = new WlEventLoop(wl_display_get_event_loop(native)); 94 | return _loop; 95 | } 96 | 97 | int addSocket(string name) 98 | { 99 | return wl_display_add_socket(native, toStringz(name)); 100 | } 101 | 102 | string addSocketAuto() 103 | { 104 | return fromStringz(wl_display_add_socket_auto(native)).idup; 105 | } 106 | 107 | int addSocketFd(int fd) 108 | { 109 | return wl_display_add_socket_fd(native, fd); 110 | } 111 | 112 | void terminate() 113 | { 114 | wl_display_terminate(native); 115 | } 116 | 117 | void run() 118 | { 119 | wl_display_run(native); 120 | } 121 | 122 | void flushClients() 123 | { 124 | wl_display_flush_clients(native); 125 | } 126 | 127 | @property uint serial() 128 | { 129 | return wl_display_get_serial(native); 130 | } 131 | 132 | uint nextSerial() 133 | { 134 | return wl_display_next_serial(native); 135 | } 136 | 137 | WlClient createClient(int fd) 138 | { 139 | auto natCl = wl_client_create(native, fd); 140 | WlClient cl = cast(WlClient)ObjectCache.get(natCl); 141 | assert(cl, "could not retrieve client from obj cache"); 142 | return cl; 143 | } 144 | 145 | @property WlClient[] clients() 146 | { 147 | return _clients; 148 | } 149 | 150 | void initShm() 151 | { 152 | wl_display_init_shm(native); 153 | } 154 | 155 | void addShmFormat(WlShm.Format format) 156 | { 157 | wl_display_add_shm_format(native, cast(uint)format); 158 | } 159 | } 160 | 161 | 162 | abstract class WlGlobal : Native!wl_global 163 | { 164 | mixin nativeImpl!wl_global; 165 | 166 | this (wl_global* native) 167 | { 168 | _native = native; 169 | ObjectCache.set(native, this); 170 | } 171 | 172 | void destroy() 173 | { 174 | wl_global_destroy(_native); 175 | } 176 | } 177 | 178 | 179 | struct Credentials 180 | { 181 | pid_t pid; 182 | uid_t uid; 183 | gid_t gid; 184 | } 185 | 186 | final class WlClient : Native!wl_client 187 | { 188 | mixin nativeImpl!wl_client; 189 | 190 | alias DestroySig = Signal!(WlClient); 191 | alias NativeResourceCreatedSig = Signal!(wl_resource*); 192 | 193 | private wl_listener _destroyListener; 194 | private DestroySig _destroySig; 195 | 196 | private wl_listener _resourceCreatedListener; 197 | private NativeResourceCreatedSig _nativeResourceCreatedSig; 198 | 199 | this (wl_client* native) 200 | { 201 | _native = native; 202 | ObjectCache.set(native, this); 203 | 204 | wl_list_init(&_destroyListener.link); 205 | _destroyListener.notify = &wl_d_client_destroy; 206 | wl_client_add_destroy_listener(native, &_destroyListener); 207 | 208 | wl_list_init(&_resourceCreatedListener.link); 209 | _resourceCreatedListener.notify = &wl_d_client_resource_created; 210 | wl_client_add_resource_created_listener(native, &_resourceCreatedListener); 211 | } 212 | 213 | void destroy() 214 | { 215 | wl_client_destroy(native); 216 | } 217 | 218 | private DestroySig destroySig() 219 | { 220 | if (!_destroySig) _destroySig = new DestroySig(); 221 | return _destroySig; 222 | } 223 | 224 | private NativeResourceCreatedSig nativeResourceCreatedSig() 225 | { 226 | if (!_nativeResourceCreatedSig) _nativeResourceCreatedSig = new NativeResourceCreatedSig(); 227 | return _nativeResourceCreatedSig; 228 | } 229 | 230 | void addDestroyListener(DestroySig.Listener listener) 231 | { 232 | destroySig.add(listener); 233 | } 234 | 235 | void addNativeResourceCreatedListener(NativeResourceCreatedSig.Listener listener) 236 | { 237 | nativeResourceCreatedSig.add(listener); 238 | } 239 | 240 | void flush() 241 | { 242 | wl_client_flush(native); 243 | } 244 | 245 | @property Credentials credentials() 246 | { 247 | Credentials res; 248 | wl_client_get_credentials(native, &res.pid, &res.uid, &res.gid); 249 | return res; 250 | } 251 | 252 | @property int fd() 253 | { 254 | return wl_client_get_fd(native); 255 | } 256 | 257 | WlResource object(uint id) 258 | { 259 | auto natRes = wl_client_get_object(native, id); 260 | if (!natRes) return null; 261 | auto res = cast(WlResource)ObjectCache.get(natRes); 262 | assert(res); 263 | return res; 264 | } 265 | 266 | void postNoMemory() 267 | { 268 | wl_client_post_no_memory(native); 269 | } 270 | 271 | @property WlDisplay display() 272 | { 273 | auto natDpy = wl_client_get_display(native); 274 | assert(natDpy); 275 | auto dpy = cast(WlDisplay)ObjectCache.get(natDpy); 276 | assert(dpy); 277 | return dpy; 278 | } 279 | } 280 | 281 | abstract class WlResource : Native!wl_resource 282 | { 283 | mixin nativeImpl!wl_resource; 284 | 285 | alias DestroySig = Signal!(WlResource); 286 | 287 | private wl_listener _destroyListener; 288 | private DestroySig _destroySig; 289 | 290 | this (wl_resource* native) 291 | { 292 | _native = native; 293 | ObjectCache.set(native, this); 294 | 295 | wl_list_init(&_destroyListener.link); 296 | _destroyListener.notify = &wl_d_resource_destroy; 297 | wl_resource_add_destroy_listener(native, &_destroyListener); 298 | } 299 | 300 | void destroy() 301 | { 302 | wl_resource_destroy(native); 303 | } 304 | 305 | private DestroySig destroySig() 306 | { 307 | if (!_destroySig) _destroySig = new DestroySig(); 308 | return _destroySig; 309 | } 310 | 311 | void addDestroyListener(DestroySig.Listener listener) 312 | { 313 | destroySig.add(listener); 314 | } 315 | 316 | @property uint id() 317 | { 318 | return wl_resource_get_id(native); 319 | } 320 | 321 | @property WlClient client() 322 | { 323 | auto natCl = wl_resource_get_client(native); 324 | assert(natCl); 325 | auto cl = cast(WlClient)ObjectCache.get(natCl); 326 | assert(cl); 327 | return cl; 328 | } 329 | 330 | @property int ver() 331 | { 332 | return wl_resource_get_version(native); 333 | } 334 | 335 | @property string cls() 336 | { 337 | return fromStringz(wl_resource_get_class(native)).idup; 338 | } 339 | 340 | void postError(Args...)(uint code, string fmt, Args args) 341 | { 342 | wl_resource_post_error(native, code, toStringz(format(fmt, args))); 343 | } 344 | } 345 | 346 | private extern(C) nothrow 347 | { 348 | void wl_d_display_destroy(wl_listener*, void* data) 349 | { 350 | nothrowFnWrapper!({ 351 | auto dpy = cast(WlDisplayBase)ObjectCache.get(data); 352 | assert(dpy, "wl_d_display_destroy: could not get display from cache"); 353 | if (dpy._destroySig) dpy._destroySig.emit(); 354 | ObjectCache.remove(data); 355 | }); 356 | } 357 | 358 | void wl_d_client_created(wl_listener*, void* data) 359 | { 360 | nothrowFnWrapper!({ 361 | auto natCl = cast(wl_client*)data; 362 | auto natDpy = wl_client_get_display(natCl); 363 | auto dpy = cast(WlDisplayBase)ObjectCache.get(natDpy); 364 | assert(dpy, "wl_d_client_created: could not get display from cache"); 365 | 366 | auto cl = new WlClient(natCl); 367 | dpy._clients ~= cl; 368 | if (dpy._clientCreatedSig) dpy._clientCreatedSig.emit(cl); 369 | }); 370 | } 371 | 372 | void wl_d_client_destroy(wl_listener*, void* data) 373 | { 374 | nothrowFnWrapper!({ 375 | auto natCl = cast(wl_client*)data; 376 | auto natDpy = wl_client_get_display(natCl); 377 | auto dpy = cast(WlDisplayBase)ObjectCache.get(natDpy); 378 | assert(dpy, "wl_d_client_destroy: could not get display from cache"); 379 | WlClient cl = cast(WlClient)ObjectCache.get(natCl); 380 | assert(cl, "wl_d_client_destroy: could not get client from cache"); 381 | 382 | import std.algorithm : remove; 383 | if (cl._destroySig) cl._destroySig.emit(cl); 384 | dpy._clients = dpy._clients.remove!(c => c is cl); 385 | ObjectCache.remove(natCl); 386 | }); 387 | } 388 | 389 | void wl_d_client_resource_created(wl_listener*, void* data) 390 | { 391 | nothrowFnWrapper!({ 392 | auto natRes = cast(wl_resource*)data; 393 | auto natCl = wl_resource_get_client(natRes); 394 | auto cl = cast(WlClient)ObjectCache.get(natCl); 395 | assert(cl); 396 | if (cl._nativeResourceCreatedSig) cl._nativeResourceCreatedSig.emit(natRes); 397 | }); 398 | } 399 | 400 | void wl_d_resource_destroy(wl_listener*, void* data) 401 | { 402 | nothrowFnWrapper!({ 403 | auto natRes = cast(wl_resource*)data; 404 | 405 | auto res = cast(WlResource)ObjectCache.get(natRes); 406 | if (res && res._destroySig) res._destroySig.emit(res); 407 | 408 | ObjectCache.remove(natRes); 409 | }); 410 | } 411 | } -------------------------------------------------------------------------------- /examples/compositor/source/backend/x11.d: -------------------------------------------------------------------------------- 1 | module backend.x11; 2 | 3 | import backend; 4 | import compositor; 5 | import output; 6 | 7 | import wayland.server; 8 | import wayland.util.shm_helper; 9 | import linux.input; 10 | import xcb.xcb; 11 | import xcb.xkb; 12 | import xcb.shm; 13 | import X11.Xlib; 14 | import X11.Xlib_xcb; 15 | import xkbcommon.xkbcommon; 16 | import xkbcommon.x11; 17 | 18 | import std.exception; 19 | import std.stdio; 20 | import core.stdc.stdlib; 21 | import core.sys.posix.sys.mman; 22 | import core.sys.posix.unistd; 23 | 24 | /// X11 backend implementation 25 | final class X11Backend : Backend 26 | { 27 | private { 28 | BackendConfig config; 29 | Compositor comp; 30 | 31 | WlEventLoop loop; 32 | WlFdEventSource xcbSource; 33 | 34 | Display* dpy; 35 | xcb_connection_t* conn; 36 | Atoms atoms; 37 | 38 | X11Output[] _outputs; 39 | } 40 | 41 | 42 | override @property string name() 43 | { 44 | return "x11"; 45 | } 46 | 47 | override void initialize(BackendConfig config, Compositor comp) 48 | { 49 | this.config = config; 50 | this.comp = comp; 51 | loop = comp.display.eventLoop; 52 | 53 | dpy = XOpenDisplay(null); 54 | if (dpy is null) throw new Exception("can't open X11 display"); 55 | 56 | scope(failure) XCloseDisplay(dpy); 57 | 58 | conn = XGetXCBConnection(dpy); 59 | XSetEventQueueOwner(dpy, XCBOwnsEventQueue); 60 | 61 | if (xcb_connection_has_error(conn)) 62 | { 63 | throw new Exception("XCB connection has error"); 64 | } 65 | 66 | atoms = new Atoms(conn); 67 | 68 | xcbSource = loop.addFd( 69 | xcb_get_file_descriptor(conn), 70 | WL_EVENT_READABLE, 71 | &handleEvent 72 | ); 73 | xcbSource.check(); 74 | } 75 | 76 | override Output createOutput() 77 | { 78 | auto res = new X11Output(this); 79 | res.initShm(); 80 | _outputs ~= res; 81 | return res; 82 | } 83 | 84 | override void terminate() 85 | { 86 | xcbSource.remove(); 87 | XCloseDisplay(dpy); 88 | } 89 | 90 | private: 91 | 92 | 93 | @property xcb_screen_t* defaultScreen() 94 | { 95 | int num = XDefaultScreen(dpy); 96 | 97 | auto iter = xcb_setup_roots_iterator(xcb_get_setup(conn)); 98 | while (num && iter.rem) 99 | { 100 | xcb_screen_next(&iter); 101 | --num; 102 | } 103 | return iter.data; 104 | } 105 | 106 | int handleEvent(int, uint mask) 107 | { 108 | int count; 109 | while(1) 110 | { 111 | auto ev = (mask & WL_EVENT_READABLE) ? 112 | xcb_poll_for_event(conn) : 113 | xcb_poll_for_queued_event(conn); 114 | if (!ev) break; 115 | 116 | auto respType = ev.response_type & ~0x80; 117 | switch(respType) 118 | { 119 | case XCB_BUTTON_PRESS: 120 | case XCB_BUTTON_RELEASE: 121 | deliverButtonEvent(cast(xcb_button_press_event_t*)ev); 122 | break; 123 | case XCB_CLIENT_MESSAGE: 124 | auto clEv = cast(xcb_client_message_event_t*)ev; 125 | auto atom = clEv.data.data32[0]; 126 | auto win = clEv.window; 127 | if (atom == atoms.wm_delete_window) 128 | { 129 | loop.addIdle({ 130 | foreach(op; _outputs) 131 | { 132 | if (op._win == win) 133 | { 134 | destroyOutput(op); 135 | break; 136 | } 137 | } 138 | }); 139 | } 140 | break; 141 | default: 142 | break; 143 | } 144 | 145 | ++count; 146 | } 147 | return count; 148 | } 149 | 150 | void deliverButtonEvent(xcb_button_press_event_t* ev) 151 | { 152 | uint but; 153 | switch (ev.detail) { 154 | case 1: 155 | but = BTN_LEFT; 156 | break; 157 | case 2: 158 | but = BTN_MIDDLE; 159 | break; 160 | case 3: 161 | but = BTN_RIGHT; 162 | break; 163 | default: 164 | stderr.writeln("X11 backend unknown button code: ", ev.detail); 165 | break; 166 | } 167 | 168 | immutable state = ev.response_type == XCB_BUTTON_PRESS ? 169 | WlPointer.ButtonState.pressed : WlPointer.ButtonState.released; 170 | 171 | comp.eventMouseButton(ev.event_x, ev.event_y, but, state); 172 | } 173 | 174 | void destroyOutput(X11Output op) 175 | { 176 | import std.algorithm : remove; 177 | _outputs = _outputs.remove!(o => o is op); 178 | op.destroy(); 179 | if (!_outputs.length) comp.exit(); 180 | } 181 | } 182 | 183 | private: 184 | 185 | 186 | struct WmNormalHints 187 | { 188 | enum minSize = 16; 189 | enum maxSize = 32; 190 | 191 | uint flags; 192 | uint[4] pad; 193 | int minWidth, minHeight; 194 | int maxWidth, maxHeight; 195 | int widthInc, heightInc; 196 | int minAspectX, minAspectY; 197 | int maxAspectX, maxAspectY; 198 | int baseWidth, baseHeight; 199 | int winGravity; 200 | } 201 | 202 | 203 | xcb_visualtype_t* findVisualById(xcb_screen_t* screen, xcb_visualid_t id) 204 | { 205 | xcb_depth_iterator_t i; 206 | xcb_visualtype_iterator_t j; 207 | for (i = xcb_screen_allowed_depths_iterator(screen); 208 | i.rem; 209 | xcb_depth_next(&i)) { 210 | for (j = xcb_depth_visuals_iterator(i.data); 211 | j.rem; 212 | xcb_visualtype_next(&j)) { 213 | if (j.data.visual_id == id) 214 | return j.data; 215 | } 216 | } 217 | return null; 218 | } 219 | 220 | ubyte getDepthOfVisual(xcb_screen_t* screen, xcb_visualid_t id) 221 | { 222 | xcb_depth_iterator_t i; 223 | xcb_visualtype_iterator_t j; 224 | for (i = xcb_screen_allowed_depths_iterator(screen); 225 | i.rem; 226 | xcb_depth_next(&i)) { 227 | for (j = xcb_depth_visuals_iterator(i.data); 228 | j.rem; 229 | xcb_visualtype_next(&j)) { 230 | if (j.data.visual_id == id) 231 | return i.data.depth; 232 | } 233 | } 234 | return 0; 235 | } 236 | 237 | final class X11Output : Output 238 | { 239 | private X11Backend _backend; 240 | private xcb_connection_t* _conn; 241 | private xcb_screen_t* _screen; 242 | private Atoms _atoms; 243 | private bool _fullscreen; 244 | private int _width; 245 | private int _height; 246 | private int _widthDPI; 247 | private int _heightDPI; 248 | private xcb_window_t _win; 249 | 250 | private ubyte _depth; 251 | private xcb_format_t* _xcbFmt; 252 | private int _shmFd; 253 | private xcb_shm_seg_t _shmSeg; 254 | private uint[] _buf; 255 | private xcb_gcontext_t _gc; 256 | 257 | this (X11Backend backend) 258 | { 259 | _backend = backend; 260 | _conn = backend.conn; 261 | _screen = backend.defaultScreen; 262 | _atoms = backend.atoms; 263 | _fullscreen = backend.config.fullscreen; 264 | _width = backend.config.width; 265 | _height = backend.config.height; 266 | _widthDPI = cast(int) (25.4 * _screen.width_in_pixels) / _screen.width_in_millimeters; 267 | _heightDPI = cast(int) (25.4 * _screen.height_in_pixels) / _screen.height_in_millimeters; 268 | 269 | super(backend.comp); 270 | 271 | assert(_fullscreen || _width*_height > 0); 272 | 273 | immutable uint mask = XCB_CW_EVENT_MASK; 274 | uint[2] values = [ 275 | XCB_EVENT_MASK_EXPOSURE | 276 | XCB_EVENT_MASK_STRUCTURE_NOTIFY | 277 | XCB_EVENT_MASK_KEY_PRESS | 278 | XCB_EVENT_MASK_KEY_RELEASE | 279 | XCB_EVENT_MASK_BUTTON_PRESS | 280 | XCB_EVENT_MASK_BUTTON_RELEASE | 281 | XCB_EVENT_MASK_POINTER_MOTION | 282 | XCB_EVENT_MASK_ENTER_WINDOW | 283 | XCB_EVENT_MASK_LEAVE_WINDOW | 284 | XCB_EVENT_MASK_KEYMAP_STATE | 285 | XCB_EVENT_MASK_FOCUS_CHANGE, 286 | 0 287 | ]; 288 | _win = xcb_generate_id(_conn); 289 | xcb_create_window(_conn, 290 | cast(ubyte)XCB_COPY_FROM_PARENT, 291 | _win, 292 | _screen.root, 293 | 0, 0, 294 | cast(ushort)_width, cast(ushort)_height, 295 | 0, 296 | XCB_WINDOW_CLASS_INPUT_OUTPUT, 297 | _screen.root_visual, 298 | mask, values.ptr); 299 | 300 | if (_fullscreen) 301 | { 302 | xcb_change_property(_conn, XCB_PROP_MODE_REPLACE, _win, 303 | _atoms.net_wm_state, 304 | XCB_ATOM_ATOM, 32, 1, &_atoms.net_wm_state_fullscreen); 305 | } 306 | else 307 | { 308 | WmNormalHints hints; 309 | hints.flags = hints.maxSize | hints.minSize; 310 | hints.minWidth = _width; 311 | hints.minHeight = _height; 312 | hints.maxWidth = _width; 313 | hints.maxHeight = _height; 314 | xcb_change_property(_conn, XCB_PROP_MODE_REPLACE, _win, 315 | _atoms.wm_normal_hints, 316 | _atoms.wm_size_hints, 32, 317 | hints.sizeof / 4, 318 | cast(ubyte*)&hints); 319 | } 320 | 321 | enum title = "Wayland compositor"; 322 | xcb_change_property(_conn, XCB_PROP_MODE_REPLACE, _win, 323 | _atoms.net_wm_name, _atoms.utf8_string, 8, 324 | title.length, title.ptr); 325 | 326 | xcb_change_property (_conn, XCB_PROP_MODE_REPLACE, _win, 327 | _atoms.wm_protocols, 328 | XCB_ATOM_ATOM, 32, 1, &_atoms.wm_delete_window); 329 | xcb_map_window(_conn, _win); 330 | xcb_flush(_conn); 331 | } 332 | 333 | void initShm() 334 | { 335 | auto shmExt = xcb_get_extension_data(_conn, &xcb_shm_id); 336 | enforce(shmExt && shmExt.present); 337 | auto visType = findVisualById(_screen, _screen.root_visual); 338 | _depth = getDepthOfVisual(_screen, _screen.root_visual); 339 | 340 | for (auto fmt = xcb_setup_pixmap_formats_iterator(xcb_get_setup(_conn)); 341 | fmt.rem; 342 | xcb_format_next(&fmt)) { 343 | if (fmt.data.depth == _depth) { 344 | _xcbFmt = fmt.data; 345 | break; 346 | } 347 | } 348 | enforce(_xcbFmt && _xcbFmt.bits_per_pixel == 32); 349 | 350 | immutable segSize = _width*_height*4; 351 | 352 | _shmFd = createMmapableFile(segSize); 353 | auto ptr = cast(ubyte*)enforce(mmap( 354 | null, segSize, PROT_READ | PROT_WRITE, MAP_SHARED, _shmFd, 0 355 | )); 356 | _buf = cast(uint[])(ptr[0 .. segSize]); 357 | _shmSeg = xcb_generate_id(_conn); 358 | auto err = xcb_request_check(_conn, xcb_shm_attach_fd_checked( 359 | _conn, _shmSeg, _shmFd, 0 360 | )); 361 | enforce(!err); 362 | 363 | _gc = xcb_generate_id(_conn); 364 | xcb_create_gc(_conn, _gc, _win, 0, null); 365 | } 366 | 367 | override @property int width() 368 | { 369 | return _width; 370 | } 371 | 372 | override @property int height() 373 | { 374 | return _height; 375 | } 376 | 377 | override @property uint[] buf() 378 | { 379 | return _buf; 380 | } 381 | 382 | override void blitBuf() 383 | { 384 | auto err = xcb_request_check(_conn, 385 | xcb_shm_put_image_checked( 386 | _conn, _win, _gc, 387 | cast(ushort)_width, cast(ushort)_height, 0, 0, 388 | cast(ushort)_width, cast(ushort)_height, 0, 0, 389 | _depth, XCB_IMAGE_FORMAT_Z_PIXMAP, 390 | 0, _shmSeg, 0 391 | ) 392 | ); 393 | if (err) { 394 | stderr.writeln("error while blitting x11"); 395 | } 396 | } 397 | 398 | override void destroy() 399 | { 400 | super.destroy(); 401 | xcb_unmap_window(_conn, _win); 402 | xcb_destroy_window(_conn, _win); 403 | xcb_flush(_conn); 404 | } 405 | } 406 | 407 | 408 | final class Atoms 409 | { 410 | xcb_atom_t wm_protocols; 411 | xcb_atom_t wm_normal_hints; 412 | xcb_atom_t wm_size_hints; 413 | xcb_atom_t wm_delete_window; 414 | xcb_atom_t wm_class; 415 | xcb_atom_t net_wm_name; 416 | xcb_atom_t net_supporting_wm_check; 417 | xcb_atom_t net_supported; 418 | xcb_atom_t net_wm_icon; 419 | xcb_atom_t net_wm_state; 420 | xcb_atom_t net_wm_state_fullscreen; 421 | xcb_atom_t str; 422 | xcb_atom_t utf8_string; 423 | xcb_atom_t cardinal; 424 | xcb_atom_t xkb_names; 425 | 426 | this(xcb_connection_t* conn) 427 | { 428 | enum numAtoms = 15; 429 | struct AtomName 430 | { 431 | string name; 432 | xcb_atom_t* atom; 433 | } 434 | AtomName[numAtoms] atomNames = [ 435 | AtomName("WM_PROTOCOLS", &wm_protocols), 436 | AtomName("WM_NORMAL_HINTS", &wm_normal_hints), 437 | AtomName("WM_SIZE_HINTS", &wm_size_hints), 438 | AtomName("WM_DELETE_WINDOW", &wm_delete_window), 439 | AtomName("WM_CLASS", &wm_class), 440 | AtomName("_NET_WM_NAME", &net_wm_name), 441 | AtomName("_NET_WM_ICON", &net_wm_icon), 442 | AtomName("_NET_WM_STATE", &net_wm_state), 443 | AtomName("_NET_WM_STATE_FULLSCREEN", &net_wm_state_fullscreen), 444 | AtomName("_NET_SUPPORTING_WM_CHECK", &net_supporting_wm_check), 445 | AtomName("_NET_SUPPORTED", &net_supported), 446 | AtomName("STRING", &str), 447 | AtomName("UTF8_STRING", &utf8_string), 448 | AtomName("CARDINAL", &cardinal), 449 | AtomName("_XKB_RULES_NAMES", &xkb_names), 450 | ]; 451 | 452 | xcb_intern_atom_cookie_t[numAtoms] cookies = void; 453 | foreach (i; 0..numAtoms) 454 | { 455 | cookies[i] = xcb_intern_atom (conn, 0, 456 | cast(ushort)atomNames[i].name.length, 457 | atomNames[i].name.ptr); 458 | } 459 | foreach (i; 0..numAtoms) 460 | { 461 | auto rep = xcb_intern_atom_reply(conn, cookies[i], null); 462 | *atomNames[i].atom = rep.atom; 463 | free(rep); 464 | } 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /client/source/wayland/native/client.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | /// bindings to wayland-client-core.h 3 | module wayland.native.client; 4 | 5 | // Wayland client-core copyright: 6 | /* 7 | * Copyright © 2008 Kristian Høgsberg 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining 10 | * a copy of this software and associated documentation files (the 11 | * "Software"), to deal in the Software without restriction, including 12 | * without limitation the rights to use, copy, modify, merge, publish, 13 | * distribute, sublicense, and/or sell copies of the Software, and to 14 | * permit persons to whom the Software is furnished to do so, subject to 15 | * the following conditions: 16 | * 17 | * The above copyright notice and this permission notice (including the 18 | * next paragraph) shall be included in all copies or substantial 19 | * portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | */ 30 | 31 | import wayland.native.util; 32 | 33 | extern (C) nothrow 34 | { 35 | /** \class wl_proxy 36 | * 37 | * \brief Represents a protocol object on the client side. 38 | * 39 | * A wl_proxy acts as a client side proxy to an object existing in the 40 | * compositor. The proxy is responsible for converting requests made by the 41 | * clients with \ref wl_proxy_marshal() into Wayland's wire format. Events 42 | * coming from the compositor are also handled by the proxy, which will in 43 | * turn call the handler set with \ref wl_proxy_add_listener(). 44 | * 45 | * \note With the exception of function \ref wl_proxy_set_queue(), functions 46 | * accessing a wl_proxy are not normally used by client code. Clients 47 | * should normally use the higher level iface generated by the scanner to 48 | * interact with compositor objects. 49 | * 50 | */ 51 | struct wl_proxy; 52 | 53 | /** \class wl_display 54 | * 55 | * \brief Represents a connection to the compositor and acts as a proxy to 56 | * the wl_display singleton object. 57 | * 58 | * A wl_display object represents a client connection to a Wayland 59 | * compositor. It is created with either \ref wl_display_connect() or 60 | * \ref wl_display_connect_to_fd(). A connection is terminated using 61 | * \ref wl_display_disconnect(). 62 | * 63 | * A wl_display is also used as the \ref wl_proxy for the wl_display 64 | * singleton object on the compositor side. 65 | * 66 | * A wl_display object handles all the data sent from and to the 67 | * compositor. When a \ref wl_proxy marshals a request, it will write its wire 68 | * representation to the display's write buffer. The data is sent to the 69 | * compositor when the client calls \ref wl_display_flush(). 70 | * 71 | * Incoming data is handled in two steps: queueing and dispatching. In the 72 | * queue step, the data coming from the display fd is interpreted and 73 | * added to a queue. On the dispatch step, the handler for the incoming 74 | * event set by the client on the corresponding \ref wl_proxy is called. 75 | * 76 | * A wl_display has at least one event queue, called the default 77 | * queue. Clients can create additional event queues with \ref 78 | * wl_display_create_queue() and assign \ref wl_proxy's to it. Events 79 | * occurring in a particular proxy are always queued in its assigned queue. 80 | * A client can ensure that a certain assumption, such as holding a lock 81 | * or running from a given thread, is true when a proxy event handler is 82 | * called by assigning that proxy to an event queue and making sure that 83 | * this queue is only dispatched when the assumption holds. 84 | * 85 | * The default queue is dispatched by calling \ref wl_display_dispatch(). 86 | * This will dispatch any events queued on the default queue and attempt 87 | * to read from the display fd if it's empty. Events read are then queued 88 | * on the appropriate queues according to the proxy assignment. 89 | * 90 | * A user created queue is dispatched with \ref wl_display_dispatch_queue(). 91 | * This function behaves exactly the same as wl_display_dispatch() 92 | * but it dispatches given queue instead of the default queue. 93 | * 94 | * A real world example of event queue usage is Mesa's implementation of 95 | * eglSwapBuffers() for the Wayland platform. This function might need 96 | * to block until a frame callback is received, but dispatching the default 97 | * queue could cause an event handler on the client to start drawing 98 | * again. This problem is solved using another event queue, so that only 99 | * the events handled by the EGL code are dispatched during the block. 100 | * 101 | * This creates a problem where a thread dispatches a non-default 102 | * queue, reading all the data from the display fd. If the application 103 | * would call \em poll(2) after that it would block, even though there 104 | * might be events queued on the default queue. Those events should be 105 | * dispatched with \ref wl_display_dispatch_pending() or \ref 106 | * wl_display_dispatch_queue_pending() before flushing and blocking. 107 | */ 108 | struct wl_display; 109 | 110 | /** \class wl_event_queue 111 | * 112 | * \brief A queue for \ref wl_proxy object events. 113 | * 114 | * Event queues allows the events on a display to be handled in a thread-safe 115 | * manner. See \ref wl_display for details. 116 | * 117 | */ 118 | struct wl_event_queue; 119 | 120 | // used for wl_proxy_add_listener 121 | alias void_func_t = void function(); 122 | } 123 | 124 | version (WlDynamic) 125 | { 126 | extern (C) nothrow 127 | { 128 | alias da_wl_event_queue_destroy = void function(wl_event_queue* queue); 129 | 130 | alias da_wl_proxy_marshal = void function(wl_proxy* p, uint opcode, ...); 131 | 132 | alias da_wl_proxy_marshal_array = void function(wl_proxy* p, uint opcode, wl_argument* args); 133 | 134 | alias da_wl_proxy_create = wl_proxy* function(wl_proxy* factory, const(wl_interface)* iface); 135 | 136 | alias da_wl_proxy_create_wrapper = void* function(void* proxy); 137 | 138 | alias da_wl_proxy_wrapper_destroy = void function(void* proxy_wrapper); 139 | 140 | alias da_wl_proxy_marshal_constructor = wl_proxy* function(wl_proxy* proxy, 141 | uint opcode, const(wl_interface)* iface, ...); 142 | 143 | alias da_wl_proxy_marshal_constructor_versioned = wl_proxy* function(wl_proxy* proxy, 144 | uint opcode, const(wl_interface)* iface, uint ver, ...); 145 | 146 | alias da_wl_proxy_marshal_array_constructor = wl_proxy* function(wl_proxy* proxy, 147 | uint opcode, wl_argument* args, const(wl_interface)* iface); 148 | 149 | alias da_wl_proxy_marshal_array_constructor_versioned = wl_proxy* function(wl_proxy* proxy, 150 | uint opcode, wl_argument* args, const(wl_interface)* iface, uint ver); 151 | 152 | alias da_wl_proxy_destroy = void function(wl_proxy* proxy); 153 | 154 | alias da_wl_proxy_add_listener = int function(wl_proxy* proxy, 155 | void_func_t* impl, void* data); 156 | 157 | alias da_wl_proxy_get_listener = const(void)* function(wl_proxy* proxy); 158 | 159 | alias da_wl_proxy_add_dispatcher = int function(wl_proxy* proxy, 160 | wl_dispatcher_func_t dispatcher_func, const(void)* dispatcher_data, void* data); 161 | 162 | alias da_wl_proxy_set_user_data = void function(wl_proxy* proxy, void* user_data); 163 | 164 | alias da_wl_proxy_get_user_data = void* function(wl_proxy* proxy); 165 | 166 | alias da_wl_proxy_get_version = uint function(wl_proxy* proxy); 167 | 168 | alias da_wl_proxy_get_id = uint function(wl_proxy* proxy); 169 | 170 | alias da_wl_proxy_get_class = const(char)* function(wl_proxy* proxy); 171 | 172 | alias da_wl_proxy_set_queue = void function(wl_proxy* proxy, wl_event_queue* queue); 173 | 174 | alias da_wl_display_connect = wl_display* function(const(char)* name); 175 | 176 | alias da_wl_display_connect_to_fd = wl_display* function(int fd); 177 | 178 | alias da_wl_display_disconnect = void function(wl_display* display); 179 | 180 | alias da_wl_display_get_fd = int function(wl_display* display); 181 | 182 | alias da_wl_display_dispatch = int function(wl_display* display); 183 | 184 | alias da_wl_display_dispatch_queue = int function(wl_display* display, 185 | wl_event_queue* queue); 186 | 187 | alias da_wl_display_dispatch_queue_pending = int function(wl_display* display, 188 | wl_event_queue* queue); 189 | 190 | alias da_wl_display_dispatch_pending = int function(wl_display* display); 191 | 192 | alias da_wl_display_get_error = int function(wl_display* display); 193 | 194 | alias da_wl_display_get_protocol_error = uint function(wl_display* display, 195 | const(wl_interface)** iface, uint* id); 196 | 197 | alias da_wl_display_flush = int function(wl_display* display); 198 | 199 | alias da_wl_display_roundtrip_queue = int function(wl_display* display, 200 | wl_event_queue* queue); 201 | 202 | alias da_wl_display_roundtrip = int function(wl_display* display); 203 | 204 | alias da_wl_display_create_queue = wl_event_queue* function(wl_display* display); 205 | 206 | alias da_wl_display_prepare_read_queue = int function(wl_display* display, 207 | wl_event_queue* queue); 208 | 209 | alias da_wl_display_prepare_read = int function(wl_display* display); 210 | 211 | alias da_wl_display_cancel_read = void function(wl_display* display); 212 | 213 | alias da_wl_display_read_events = int function(wl_display* display); 214 | 215 | alias da_wl_log_set_handler_client = void function(wl_log_func_t handler); 216 | } 217 | 218 | __gshared 219 | { 220 | da_wl_event_queue_destroy wl_event_queue_destroy; 221 | 222 | da_wl_proxy_marshal wl_proxy_marshal; 223 | 224 | da_wl_proxy_marshal_array wl_proxy_marshal_array; 225 | 226 | da_wl_proxy_create wl_proxy_create; 227 | 228 | da_wl_proxy_create_wrapper wl_proxy_create_wrapper; 229 | 230 | da_wl_proxy_wrapper_destroy wl_proxy_wrapper_destroy; 231 | 232 | da_wl_proxy_marshal_constructor wl_proxy_marshal_constructor; 233 | 234 | da_wl_proxy_marshal_constructor_versioned wl_proxy_marshal_constructor_versioned; 235 | 236 | da_wl_proxy_marshal_array_constructor wl_proxy_marshal_array_constructor; 237 | 238 | da_wl_proxy_marshal_array_constructor_versioned wl_proxy_marshal_array_constructor_versioned; 239 | 240 | da_wl_proxy_destroy wl_proxy_destroy; 241 | 242 | da_wl_proxy_add_listener wl_proxy_add_listener; 243 | 244 | da_wl_proxy_get_listener wl_proxy_get_listener; 245 | 246 | da_wl_proxy_add_dispatcher wl_proxy_add_dispatcher; 247 | 248 | da_wl_proxy_set_user_data wl_proxy_set_user_data; 249 | 250 | da_wl_proxy_get_user_data wl_proxy_get_user_data; 251 | 252 | da_wl_proxy_get_version wl_proxy_get_version; 253 | 254 | da_wl_proxy_get_id wl_proxy_get_id; 255 | 256 | da_wl_proxy_get_class wl_proxy_get_class; 257 | 258 | da_wl_proxy_set_queue wl_proxy_set_queue; 259 | 260 | da_wl_display_connect wl_display_connect; 261 | 262 | da_wl_display_connect_to_fd wl_display_connect_to_fd; 263 | 264 | da_wl_display_disconnect wl_display_disconnect; 265 | 266 | da_wl_display_get_fd wl_display_get_fd; 267 | 268 | da_wl_display_dispatch wl_display_dispatch; 269 | 270 | da_wl_display_dispatch_queue wl_display_dispatch_queue; 271 | 272 | da_wl_display_dispatch_queue_pending wl_display_dispatch_queue_pending; 273 | 274 | da_wl_display_dispatch_pending wl_display_dispatch_pending; 275 | 276 | da_wl_display_get_error wl_display_get_error; 277 | 278 | da_wl_display_get_protocol_error wl_display_get_protocol_error; 279 | 280 | da_wl_display_flush wl_display_flush; 281 | 282 | da_wl_display_roundtrip_queue wl_display_roundtrip_queue; 283 | 284 | da_wl_display_roundtrip wl_display_roundtrip; 285 | 286 | da_wl_display_create_queue wl_display_create_queue; 287 | 288 | da_wl_display_prepare_read_queue wl_display_prepare_read_queue; 289 | 290 | da_wl_display_prepare_read wl_display_prepare_read; 291 | 292 | da_wl_display_cancel_read wl_display_cancel_read; 293 | 294 | da_wl_display_read_events wl_display_read_events; 295 | 296 | da_wl_log_set_handler_client wl_log_set_handler_client; 297 | } 298 | } 299 | 300 | version (WlStatic) 301 | { 302 | extern (C) nothrow 303 | { 304 | void wl_event_queue_destroy(wl_event_queue* queue); 305 | 306 | void wl_proxy_marshal(wl_proxy* p, uint opcode, ...); 307 | 308 | void wl_proxy_marshal_array(wl_proxy* p, uint opcode, wl_argument* args); 309 | 310 | wl_proxy* wl_proxy_create(wl_proxy* factory, const(wl_interface)* iface); 311 | 312 | void* wl_proxy_create_wrapper(void* proxy); 313 | 314 | void wl_proxy_wrapper_destroy(void* proxy_wrapper); 315 | 316 | wl_proxy* wl_proxy_marshal_constructor(wl_proxy* proxy, uint opcode, 317 | const(wl_interface)* iface, ...); 318 | 319 | wl_proxy* wl_proxy_marshal_constructor_versioned(wl_proxy* proxy, 320 | uint opcode, const(wl_interface)* iface, uint ver, ...); 321 | 322 | wl_proxy* wl_proxy_marshal_array_constructor(wl_proxy* proxy, 323 | uint opcode, wl_argument* args, const(wl_interface)* iface); 324 | 325 | wl_proxy* wl_proxy_marshal_array_constructor_versioned(wl_proxy* proxy, 326 | uint opcode, wl_argument* args, const(wl_interface)* iface, uint ver); 327 | 328 | void wl_proxy_destroy(wl_proxy* proxy); 329 | 330 | int wl_proxy_add_listener(wl_proxy* proxy, void_func_t* impl, void* data); 331 | 332 | const(void)* wl_proxy_get_listener(wl_proxy* proxy); 333 | 334 | int wl_proxy_add_dispatcher(wl_proxy* proxy, 335 | wl_dispatcher_func_t dispatcher_func, const(void)* dispatcher_data, void* data); 336 | 337 | void wl_proxy_set_user_data(wl_proxy* proxy, void* user_data); 338 | 339 | void* wl_proxy_get_user_data(wl_proxy* proxy); 340 | 341 | uint wl_proxy_get_version(wl_proxy* proxy); 342 | 343 | uint wl_proxy_get_id(wl_proxy* proxy); 344 | 345 | const(char)* wl_proxy_get_class(wl_proxy* proxy); 346 | 347 | void wl_proxy_set_queue(wl_proxy* proxy, wl_event_queue* queue); 348 | 349 | wl_display* wl_display_connect(const(char)* name); 350 | 351 | wl_display* wl_display_connect_to_fd(int fd); 352 | 353 | void wl_display_disconnect(wl_display* display); 354 | 355 | int wl_display_get_fd(wl_display* display); 356 | 357 | int wl_display_dispatch(wl_display* display); 358 | 359 | int wl_display_dispatch_queue(wl_display* display, wl_event_queue* queue); 360 | 361 | int wl_display_dispatch_queue_pending(wl_display* display, wl_event_queue* queue); 362 | 363 | int wl_display_dispatch_pending(wl_display* display); 364 | 365 | int wl_display_get_error(wl_display* display); 366 | 367 | uint wl_display_get_protocol_error(wl_display* display, 368 | const(wl_interface)** iface, uint* id); 369 | 370 | int wl_display_flush(wl_display* display); 371 | 372 | int wl_display_roundtrip_queue(wl_display* display, wl_event_queue* queue); 373 | 374 | int wl_display_roundtrip(wl_display* display); 375 | 376 | wl_event_queue* wl_display_create_queue(wl_display* display); 377 | 378 | int wl_display_prepare_read_queue(wl_display* display, wl_event_queue* queue); 379 | 380 | int wl_display_prepare_read(wl_display* display); 381 | 382 | void wl_display_cancel_read(wl_display* display); 383 | 384 | int wl_display_read_events(wl_display* display); 385 | 386 | void wl_log_set_handler_client(wl_log_func_t handler); 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /scanner/source/arsd/characterencodings.d: -------------------------------------------------------------------------------- 1 | // helper program is in ~me/encodings.d to make more tables from wikipedia 2 | 3 | /** 4 | This is meant to help get data from the wild into utf8 strings 5 | so you can work with them easily inside D. 6 | 7 | The main function is convertToUtf8(), which takes a byte array 8 | of your raw data (a byte array because it isn't really a D string 9 | yet until it is utf8), and a runtime string telling it's current 10 | encoding. 11 | 12 | The current encoding argument is meant to come from the data's 13 | metadata, and is flexible on exact format - it is case insensitive 14 | and takes several variations on the names. 15 | 16 | This way, you should be able to send it the encoding string directly 17 | from an XML document, a HTTP header, or whatever you have, and it 18 | ought to just work. 19 | 20 | Example: 21 | auto data = cast(immutable(ubyte)[]) 22 | std.file.read("my-windows-file.txt"); 23 | string utf8String = convertToUtf8(data, "windows-1252"); 24 | // utf8String can now be used 25 | 26 | 27 | The encodings currently implemented for decoding are: 28 | UTF-8 (a no-op; it simply casts the array to string) 29 | UTF-16, 30 | UTF-32, 31 | Windows-1252, 32 | ISO 8859 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, and 16. 33 | 34 | It treats ISO 8859-1, Latin-1, and Windows-1252 the same way, since 35 | those labels are pretty much de-facto the same thing in wild documents. 36 | 37 | 38 | This module currently makes no attempt to look at control characters. 39 | */ 40 | module arsd.characterencodings; 41 | 42 | import std.string; 43 | import std.array; 44 | import std.conv; 45 | 46 | /// Like convertToUtf8, but if the encoding is unknown, it just strips all chars > 127 and calls it done instead of throwing 47 | string convertToUtf8Lossy(immutable(ubyte)[] data, string dataCharacterEncoding) { 48 | try { 49 | auto ret = convertToUtf8(data, dataCharacterEncoding); 50 | import std.utf; 51 | validate(ret); 52 | return ret; 53 | } catch(Exception e) { 54 | string ret; 55 | foreach(b; data) 56 | if(b < 128) 57 | ret ~= b; 58 | return ret; 59 | } 60 | } 61 | 62 | /// Takes data from a given character encoding and returns it as UTF-8 63 | string convertToUtf8(immutable(ubyte)[] data, string dataCharacterEncoding) { 64 | // just to normalize the passed string... 65 | auto encoding = dataCharacterEncoding.toLower(); 66 | encoding = encoding.replace(" ", ""); 67 | encoding = encoding.replace("-", ""); 68 | encoding = encoding.replace("_", ""); 69 | // should be good enough. 70 | 71 | switch(encoding) { 72 | default: 73 | throw new Exception("I don't know how to convert " ~ dataCharacterEncoding ~ " to UTF-8"); 74 | // since the input is immutable, these are ok too. 75 | // just want to cover all the bases with one runtime function. 76 | case "utf16": 77 | case "utf16le": 78 | return to!string(cast(wstring) data); 79 | case "utf32": 80 | case "utf32le": 81 | return to!string(cast(dstring) data); 82 | // FIXME: does the big endian to little endian conversion work? 83 | case "ascii": 84 | case "usascii": // utf-8 is a superset of ascii 85 | case "utf8": 86 | return cast(string) data; 87 | // and now the various 8 bit encodings we support. 88 | case "windows1252": 89 | return decodeImpl(data, ISO_8859_1, Windows_1252); 90 | case "windows1251": 91 | return decodeImpl(data, Windows_1251, Windows_1251_Lower); 92 | case "koi8r": 93 | return decodeImpl(data, KOI8_R, KOI8_R_Lower); 94 | case "latin1": 95 | case "iso88591": 96 | // Why am I putting Windows_1252 here? A lot of 97 | // stuff in the wild is mislabeled, so this will 98 | // do some good in the Just Works department. 99 | // Regardless, I don't handle the 100 | // control char set in that zone anyway right now. 101 | return decodeImpl(data, ISO_8859_1, Windows_1252); 102 | case "iso88592": 103 | return decodeImpl(data, ISO_8859_2); 104 | case "iso88593": 105 | return decodeImpl(data, ISO_8859_3); 106 | case "iso88594": 107 | return decodeImpl(data, ISO_8859_4); 108 | case "iso88595": 109 | return decodeImpl(data, ISO_8859_5); 110 | case "iso88596": 111 | return decodeImpl(data, ISO_8859_6); 112 | case "iso88597": 113 | return decodeImpl(data, ISO_8859_7); 114 | case "iso88598": 115 | return decodeImpl(data, ISO_8859_8); 116 | case "iso88599": 117 | return decodeImpl(data, ISO_8859_9); 118 | case "iso885910": 119 | return decodeImpl(data, ISO_8859_10); 120 | case "iso885911": 121 | return decodeImpl(data, ISO_8859_11); 122 | case "iso885913": 123 | return decodeImpl(data, ISO_8859_13); 124 | case "iso885914": 125 | return decodeImpl(data, ISO_8859_14); 126 | case "iso885915": 127 | return decodeImpl(data, ISO_8859_15); 128 | case "iso885916": 129 | return decodeImpl(data, ISO_8859_16); 130 | } 131 | 132 | assert(0); 133 | } 134 | 135 | /// Tries to determine the current encoding based on the content. 136 | /// Only really helps with the UTF variants. 137 | /// Returns null if it can't be reasonably sure. 138 | string tryToDetermineEncoding(in ubyte[] rawdata) { 139 | import std.utf; 140 | try { 141 | validate!string(cast(string) rawdata); 142 | // the odds of non stuff validating as utf-8 are pretty low 143 | return "UTF-8"; 144 | } catch(UTFException t) { 145 | // it's definitely not UTF-8! 146 | // we'll look at the first few characters. If there's a 147 | // BOM, it's probably UTF-16 or UTF-32 148 | 149 | if(rawdata.length > 4) { 150 | // not checking for utf8 bom; if it was that, we 151 | // wouldn't be here. 152 | if(rawdata[0] == 0xff && rawdata[1] == 0xfe) 153 | return "UTF-16 LE"; 154 | else if(rawdata[0] == 0xfe && rawdata[1] == 0xff) 155 | return "UTF-16 BE"; 156 | else if(rawdata[0] == 0x00 && rawdata[1] == 0x00 157 | && rawdata[2] == 0xfe && rawdata[3] == 0xff) 158 | return "UTF-32 BE"; 159 | else if(rawdata[0] == 0xff && rawdata[1] == 0xfe 160 | && rawdata[2] == 0x00 && rawdata[3] == 0x00) 161 | return "UTF-32 LE"; 162 | else { 163 | // this space is intentionally left blank 164 | } 165 | } 166 | } 167 | 168 | // we don't know with enough confidence. The app will have to find another way. 169 | return null; 170 | } 171 | 172 | // this function actually does the work, using the translation tables 173 | // below. 174 | string decodeImpl(in ubyte[] data, in dchar[] chars160to255, in dchar[] chars128to159 = null, in dchar[] chars0to127 = null) 175 | in { 176 | assert(chars160to255.length == 256 - 160); 177 | assert(chars128to159 is null || chars128to159.length == 160 - 128); 178 | assert(chars0to127 is null || chars0to127.length == 128 - 0); 179 | } 180 | out(ret) { 181 | import std.utf; 182 | validate(ret); 183 | } 184 | do { 185 | string utf8; 186 | 187 | /// I'm sure this could be a lot more efficient, but whatever, it 188 | /// works. 189 | foreach(octet; data) { 190 | if(octet < 128) { 191 | if(chars0to127 !is null) 192 | utf8 ~= chars0to127[octet]; 193 | else 194 | utf8 ~= cast(char) octet; // ascii is the same 195 | } else if(octet < 160) { 196 | if(chars128to159 !is null) 197 | utf8 ~= chars128to159[octet - 128]; 198 | else 199 | utf8 ~= " "; 200 | } else { 201 | utf8 ~= chars160to255[octet - 160]; 202 | } 203 | } 204 | 205 | return utf8; 206 | } 207 | 208 | 209 | // Here come the translation tables. 210 | 211 | // this table gives characters for decimal 128 through 159. 212 | // the < 128 characters are the same as ascii, and > 159 the same as 213 | // iso 8859 1, seen below. 214 | immutable dchar[] Windows_1252 = [ 215 | '€', ' ', '‚', 'ƒ', '„', '…', '†', '‡', 216 | 'ˆ', '‰', 'Š', '‹', 'Œ', ' ', 'Ž', ' ', 217 | ' ', '‘', '’', '“', '”', '•', '–', '—', 218 | '˜', '™', 'š', '›', 'œ', ' ', 'ž', 'Ÿ']; 219 | 220 | // the following tables give the characters from decimal 160 up to 255 221 | // in the given encodings. 222 | 223 | immutable dchar[] ISO_8859_1 = [ 224 | ' ', '¡', '¢', '£', '¤', '¥', '¦', '§', 225 | '¨', '©', 'ª', '«', '¬', '­', '®', '¯', 226 | '°', '±', '²', '³', '´', 'µ', '¶', '·', 227 | '¸', '¹', 'º', '»', '¼', '½', '¾', '¿', 228 | 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 229 | 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 230 | 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '×', 231 | 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 232 | 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 233 | 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 234 | 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 235 | 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ', 'ÿ']; 236 | 237 | immutable dchar[] ISO_8859_2 = [ 238 | ' ', 'Ą', '˘', 'Ł', '¤', 'Ľ', 'Ś', '§', 239 | '¨', 'Š', 'Ş', 'Ť', 'Ź', '­', 'Ž', 'Ż', 240 | '°', 'ą', '˛', 'ł', '´', 'ľ', 'ś', 'ˇ', 241 | '¸', 'š', 'ş', 'ť', 'ź', '˝', 'ž', 'ż', 242 | 'Ŕ', 'Á', 'Â', 'Ă', 'Ä', 'Ĺ', 'Ć', 'Ç', 243 | 'Č', 'É', 'Ę', 'Ë', 'Ě', 'Í', 'Î', 'Ď', 244 | 'Đ', 'Ń', 'Ň', 'Ó', 'Ô', 'Ő', 'Ö', '×', 245 | 'Ř', 'Ů', 'Ú', 'Ű', 'Ü', 'Ý', 'Ţ', 'ß', 246 | 'ŕ', 'á', 'â', 'ă', 'ä', 'ĺ', 'ć', 'ç', 247 | 'č', 'é', 'ę', 'ë', 'ě', 'í', 'î', 'ď', 248 | 'đ', 'ń', 'ň', 'ó', 'ô', 'ő', 'ö', '÷', 249 | 'ř', 'ů', 'ú', 'ű', 'ü', 'ý', 'ţ', '˙']; 250 | 251 | immutable dchar[] ISO_8859_3 = [ 252 | ' ', 'Ħ', '˘', '£', '¤', ' ', 'Ĥ', '§', 253 | '¨', 'İ', 'Ş', 'Ğ', 'Ĵ', '­', ' ', 'Ż', 254 | '°', 'ħ', '²', '³', '´', 'µ', 'ĥ', '·', 255 | '¸', 'ı', 'ş', 'ğ', 'ĵ', '½', ' ', 'ż', 256 | 'À', 'Á', 'Â', ' ', 'Ä', 'Ċ', 'Ĉ', 'Ç', 257 | 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 258 | ' ', 'Ñ', 'Ò', 'Ó', 'Ô', 'Ġ', 'Ö', '×', 259 | 'Ĝ', 'Ù', 'Ú', 'Û', 'Ü', 'Ŭ', 'Ŝ', 'ß', 260 | 'à', 'á', 'â', ' ', 'ä', 'ċ', 'ĉ', 'ç', 261 | 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 262 | ' ', 'ñ', 'ò', 'ó', 'ô', 'ġ', 'ö', '÷', 263 | 'ĝ', 'ù', 'ú', 'û', 'ü', 'ŭ', 'ŝ', '˙']; 264 | 265 | immutable dchar[] ISO_8859_4 = [ 266 | ' ', 'Ą', 'ĸ', 'Ŗ', '¤', 'Ĩ', 'Ļ', '§', 267 | '¨', 'Š', 'Ē', 'Ģ', 'Ŧ', '­', 'Ž', '¯', 268 | '°', 'ą', '˛', 'ŗ', '´', 'ĩ', 'ļ', 'ˇ', 269 | '¸', 'š', 'ē', 'ģ', 'ŧ', 'Ŋ', 'ž', 'ŋ', 270 | 'Ā', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Į', 271 | 'Č', 'É', 'Ę', 'Ë', 'Ė', 'Í', 'Î', 'Ī', 272 | 'Đ', 'Ņ', 'Ō', 'Ķ', 'Ô', 'Õ', 'Ö', '×', 273 | 'Ø', 'Ų', 'Ú', 'Û', 'Ü', 'Ũ', 'Ū', 'ß', 274 | 'ā', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'į', 275 | 'č', 'é', 'ę', 'ë', 'ė', 'í', 'î', 'ī', 276 | 'đ', 'ņ', 'ō', 'ķ', 'ô', 'õ', 'ö', '÷', 277 | 'ø', 'ų', 'ú', 'û', 'ü', 'ũ', 'ū', '˙']; 278 | 279 | immutable dchar[] ISO_8859_5 = [ 280 | ' ', 'Ё', 'Ђ', 'Ѓ', 'Є', 'Ѕ', 'І', 'Ї', 281 | 'Ј', 'Љ', 'Њ', 'Ћ', 'Ќ', '­', 'Ў', 'Џ', 282 | 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 283 | 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 284 | 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 285 | 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я', 286 | 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 287 | 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 288 | 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 289 | 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я', 290 | '№', 'ё', 'ђ', 'ѓ', 'є', 'ѕ', 'і', 'ї', 291 | 'ј', 'љ', 'њ', 'ћ', 'ќ', '§', 'ў', 'џ']; 292 | 293 | immutable dchar[] ISO_8859_6 = [ 294 | ' ', ' ', ' ', ' ', '¤', ' ', ' ', ' ', 295 | ' ', ' ', ' ', ' ', '،', '­', ' ', ' ', 296 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 297 | ' ', ' ', ' ', '؛', ' ', ' ', ' ', '؟', 298 | ' ', 'ء', 'آ', 'أ', 'ؤ', 'إ', 'ئ', 'ا', 299 | 'ب', 'ة', 'ت', 'ث', 'ج', 'ح', 'خ', 'د', 300 | 'ذ', 'ر', 'ز', 'س', 'ش', 'ص', 'ض', 'ط', 301 | 'ظ', 'ع', 'غ', ' ', ' ', ' ', ' ', ' ', 302 | 'ـ', 'ف', 'ق', 'ك', 'ل', 'م', 'ن', 'ه', 303 | 'و', 'ى', 'ي', 'ً', 'ٌ', 'ٍ', 'َ', 'ُ', 304 | 'ِ', 'ّ', 'ْ', ' ', ' ', ' ', ' ', ' ', 305 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']; 306 | 307 | immutable dchar[] ISO_8859_7 = [ 308 | ' ', '‘', '’', '£', '€', '₯', '¦', '§', 309 | '¨', '©', 'ͺ', '«', '¬', '­', ' ', '―', 310 | '°', '±', '²', '³', '΄', '΅', 'Ά', '·', 311 | 'Έ', 'Ή', 'Ί', '»', 'Ό', '½', 'Ύ', 'Ώ', 312 | 'ΐ', 'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 313 | 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', 314 | 'Π', 'Ρ', ' ', 'Σ', 'Τ', 'Υ', 'Φ', 'Χ', 315 | 'Ψ', 'Ω', 'Ϊ', 'Ϋ', 'ά', 'έ', 'ή', 'ί', 316 | 'ΰ', 'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 317 | 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 318 | 'π', 'ρ', 'ς', 'σ', 'τ', 'υ', 'φ', 'χ', 319 | 'ψ', 'ω', 'ϊ', 'ϋ', 'ό', 'ύ', 'ώ', ' ']; 320 | 321 | immutable dchar[] ISO_8859_8 = [ 322 | ' ', ' ', '¢', '£', '¤', '¥', '¦', '§', 323 | '¨', '©', '×', '«', '¬', '­', '®', '¯', 324 | '°', '±', '²', '³', '´', 'µ', '¶', '·', 325 | '¸', '¹', '÷', '»', '¼', '½', '¾', ' ', 326 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 327 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 328 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 329 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', '‗', 330 | 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 331 | 'ט', 'י', 'ך', 'כ', 'ל', 'ם', 'מ', 'ן', 332 | 'נ', 'ס', 'ע', 'ף', 'פ', 'ץ', 'צ', 'ק', 333 | // v v those are wrong 334 | 'ר', 'ש', 'ת', ' ', ' ', ' ', ' ', ' ']; // FIXME: those ones marked wrong are supposed to be left to right and right to left markers, not spaces. lol maybe it isn't wrong 335 | 336 | immutable dchar[] ISO_8859_9 = [ 337 | ' ', '¡', '¢', '£', '¤', '¥', '¦', '§', 338 | '¨', '©', 'ª', '«', '¬', '­', '®', '¯', 339 | '°', '±', '²', '³', '´', 'µ', '¶', '·', 340 | '¸', '¹', 'º', '»', '¼', '½', '¾', '¿', 341 | 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 342 | 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 343 | 'Ğ', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '×', 344 | 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'İ', 'Ş', 'ß', 345 | 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 346 | 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 347 | 'ğ', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 348 | 'ø', 'ù', 'ú', 'û', 'ü', 'ı', 'ş', 'ÿ']; 349 | 350 | immutable dchar[] ISO_8859_10 = [ 351 | ' ', 'Ą', 'Ē', 'Ģ', 'Ī', 'Ĩ', 'Ķ', '§', 352 | 'Ļ', 'Đ', 'Š', 'Ŧ', 'Ž', '­', 'Ū', 'Ŋ', 353 | '°', 'ą', 'ē', 'ģ', 'ī', 'ĩ', 'ķ', '·', 354 | 'ļ', 'đ', 'š', 'ŧ', 'ž', '―', 'ū', 'ŋ', 355 | 'Ā', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Į', 356 | 'Č', 'É', 'Ę', 'Ë', 'Ė', 'Í', 'Î', 'Ï', 357 | 'Ð', 'Ņ', 'Ō', 'Ó', 'Ô', 'Õ', 'Ö', 'Ũ', 358 | 'Ø', 'Ų', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 359 | 'ā', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'į', 360 | 'č', 'é', 'ę', 'ë', 'ė', 'í', 'î', 'ï', 361 | 'ð', 'ņ', 'ō', 'ó', 'ô', 'õ', 'ö', 'ũ', 362 | 'ø', 'ų', 'ú', 'û', 'ü', 'ý', 'þ', 'ĸ']; 363 | 364 | immutable dchar[] ISO_8859_11 = [ 365 | ' ', 'ก', 'ข', 'ฃ', 'ค', 'ฅ', 'ฆ', 'ง', 366 | 'จ', 'ฉ', 'ช', 'ซ', 'ฌ', 'ญ', 'ฎ', 'ฏ', 367 | 'ฐ', 'ฑ', 'ฒ', 'ณ', 'ด', 'ต', 'ถ', 'ท', 368 | 'ธ', 'น', 'บ', 'ป', 'ผ', 'ฝ', 'พ', 'ฟ', 369 | 'ภ', 'ม', 'ย', 'ร', 'ฤ', 'ล', 'ฦ', 'ว', 370 | 'ศ', 'ษ', 'ส', 'ห', 'ฬ', 'อ', 'ฮ', 'ฯ', 371 | 'ะ', 'ั', 'า', 'ำ', 'ิ', 'ี', 'ึ', 'ื', 372 | 'ุ', 'ู', 'ฺ', ' ', ' ', ' ', ' ', '฿', 373 | 'เ', 'แ', 'โ', 'ใ', 'ไ', 'ๅ', 'ๆ', '็', 374 | '่', '้', '๊', '๋', '์', 'ํ', '๎', '๏', 375 | '๐', '๑', '๒', '๓', '๔', '๕', '๖', '๗', 376 | '๘', '๙', '๚', '๛', ' ', ' ', ' ', ' ']; 377 | 378 | immutable dchar[] ISO_8859_13 = [ 379 | ' ', '”', '¢', '£', '¤', '„', '¦', '§', 380 | 'Ø', '©', 'Ŗ', '«', '¬', '­', '®', 'Æ', 381 | '°', '±', '²', '³', '“', 'µ', '¶', '·', 382 | 'ø', '¹', 'ŗ', '»', '¼', '½', '¾', 'æ', 383 | 'Ą', 'Į', 'Ā', 'Ć', 'Ä', 'Å', 'Ę', 'Ē', 384 | 'Č', 'É', 'Ź', 'Ė', 'Ģ', 'Ķ', 'Ī', 'Ļ', 385 | 'Š', 'Ń', 'Ņ', 'Ó', 'Ō', 'Ő', 'Ö', '×', 386 | 'Ų', 'Ł', 'Ś', 'Ū', 'Ü', 'Ż', 'Ž', 'ß', 387 | 'ą', 'į', 'ā', 'ć', 'ä', 'å', 'ę', 'ē', 388 | 'č', 'é', 'ź', 'ė', 'ģ', 'ķ', 'ī', 'ļ', 389 | 'š', 'ń', 'ņ', 'ó', 'ō', 'ő', 'ö', '÷', 390 | 'ų', 'ł', 'ś', 'ū', 'ü', 'ż', 'ž', '’']; 391 | 392 | immutable dchar[] ISO_8859_14 = [ 393 | ' ', 'Ḃ', 'ḃ', '£', 'Ċ', 'ċ', 'Ḋ', '§', 394 | 'Ẁ', '©', 'Ẃ', 'ḋ', 'Ỳ', '­', '®', 'Ÿ', 395 | 'Ḟ', 'ḟ', 'Ġ', 'ġ', 'Ṁ', 'ṁ', '¶', 'Ṗ', 396 | 'ẁ', 'ṗ', 'ẃ', 'Ṡ', 'ỳ', 'Ẅ', 'ẅ', 'ṡ', 397 | 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 398 | 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 399 | 'Ŵ', 'Ñ', 'Ò', 'Ó', 'Ô', 'Ő', 'Ö', 'Ṫ', 400 | 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Ŷ', 'ß', 401 | 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 402 | 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 403 | 'ŵ', 'ñ', 'ò', 'ó', 'ô', 'ő', 'ö', 'ṫ', 404 | 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ŷ', 'ÿ']; 405 | 406 | immutable dchar[] ISO_8859_15 = [ 407 | ' ', '¡', '¢', '£', '€', '¥', 'Š', '§', 408 | 'š', '©', 'ª', '«', '¬', '­', '®', '¯', 409 | '°', '±', '²', '³', 'Ž', 'µ', '¶', '·', 410 | 'ž', '¹', 'º', '»', 'Œ', 'œ', 'Ÿ', '¿', 411 | 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 412 | 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 413 | 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Ő', 'Ö', '×', 414 | 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 415 | 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 416 | 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 417 | 'ð', 'ñ', 'ò', 'ó', 'ô', 'ő', 'ö', '÷', 418 | 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ', 'ÿ']; 419 | 420 | immutable dchar[] ISO_8859_16 = [ 421 | ' ', 'Ą', 'ą', 'Ł', '€', '„', 'Š', '§', 422 | 'š', '©', 'Ș', '«', 'Ź', '­', 'ź', 'Ż', 423 | '°', '±', 'Č', 'ł', 'Ž', '”', '¶', '·', 424 | 'ž', 'č', 'ș', '»', 'Œ', 'œ', 'Ÿ', 'ż', 425 | 'À', 'Á', 'Â', 'Ă', 'Ä', 'Ć', 'Æ', 'Ç', 426 | 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 427 | 'Ð', 'Ń', 'Ò', 'Ó', 'Ô', 'Ő', 'Ö', 'Ś', 428 | 'Ű', 'Ù', 'Ú', 'Û', 'Ü', 'Ę', 'Ț', 'ß', 429 | 'à', 'á', 'â', 'ă', 'ä', 'ć', 'æ', 'ç', 430 | 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 431 | 'đ', 'ń', 'ò', 'ó', 'ô', 'ő', 'ö', 'ś', 432 | 'ű', 'ù', 'ú', 'û', 'ü', 'ę', 'ț', 'ÿ']; 433 | 434 | immutable dchar[] KOI8_R_Lower = [ 435 | '─', '│', '┌', '┐', '└', '┘', '├', '┤', 436 | '┬', '┴', '┼', '▀', '▄', '█', '▌', '▐', 437 | '░', '▒', '▓', '⌠', '■', '∙', '√', '≈', 438 | '≤', '≥', '\u00a0', '⌡', '°', '²', '·', '÷']; 439 | 440 | immutable dchar[] KOI8_R = [ 441 | '═', '║', '╒', 'ё', '╓', '╔', '╕', '╖', 442 | '╗', '╘', '╙', '╚', '╛', '╜', '╝', '╞', 443 | '╟', '╠', '╡', 'ё', '╢', '╣', '╤', '╥', 444 | '╦', '╧', '╨', '╩', '╪', '╫', '╬', '©', 445 | 'ю', 'а', 'б', 'ц', 'д', 'е', 'ф', 'г', 446 | 'х', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 447 | 'п', 'я', 'р', 'с', 'т', 'у', 'ж', 'в', 448 | 'ь', 'ы', 'з', 'ш', 'э', 'щ', 'ч', 'ъ', 449 | 'ю', 'а', 'б', 'ц', 'д', 'е', 'ф', 'г', 450 | 'х', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 451 | 'п', 'я', 'р', 'с', 'т', 'у', 'ж', 'в', 452 | 'ь', 'ы', 'з', 'ш', 'э', 'щ', 'ч', 'ъ']; 453 | 454 | immutable dchar[] Windows_1251_Lower = [ 455 | 'Ђ', 'Ѓ', '‚', 'ѓ', '„', '…', '†', '‡', 456 | '€', '‰', 'Љ', '‹', 'Њ', 'Ќ', 'Ћ', 'Џ', 457 | 'ђ', '‘', '’', '“', '”', '•', '–', '—', 458 | ' ', '™', 'љ', '›', 'њ', 'ќ', 'ћ', 'џ']; 459 | 460 | immutable dchar[] Windows_1251 = [ 461 | ' ', 'Ў', 'ў', 'Ј', '¤', 'Ґ', '¦', '§', 462 | 'Ё', '©', 'Є', '«', '¬', '­', '®', 'Ї', 463 | '°', '±', 'І', 'і', 'ґ', 'µ', '¶', '·', 464 | 'ё', '№', 'є', '»', 'ј', 'Ѕ', 'ѕ', 'ї', 465 | 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З', 466 | 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 467 | 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 468 | 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я', 469 | 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 470 | 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 471 | 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 472 | 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я']; 473 | 474 | 475 | -------------------------------------------------------------------------------- /scanner/source/wayland/scanner/server.d: -------------------------------------------------------------------------------- 1 | // Copyright © 2017-2021 Rémi Thebault 2 | /++ 3 | + Wayland scanner for D. 4 | + Generation of server protocol code. 5 | +/ 6 | module wayland.scanner.server; 7 | 8 | import wayland.scanner; 9 | import wayland.scanner.common; 10 | 11 | import arsd.dom; 12 | 13 | import std.algorithm; 14 | import std.format; 15 | import std.range; 16 | 17 | alias Interface = wayland.scanner.common.Interface; 18 | 19 | class ServerFactory : Factory 20 | { 21 | override Protocol makeProtocol(Element el) 22 | { 23 | return new ServerProtocol(el); 24 | } 25 | override Interface makeInterface(Element el, Protocol protocol) 26 | { 27 | return new ServerInterface(el, protocol); 28 | } 29 | override Message makeMessage(Element el, Interface iface) 30 | { 31 | return new ServerMessage(el, iface); 32 | } 33 | override Arg makeArg(Element el) 34 | { 35 | return new ServerArg(el); 36 | } 37 | } 38 | 39 | 40 | // Server bindings implementation notes: 41 | // There are two kind of server objects: globals and resources. 42 | // 43 | // Globals inherit WlGlobal and are to be created by the compositor at startup. 44 | // Each created global is announced by the registry to the client. 45 | // `[Global].bind` function create a `[Global].Resource` object whose implementation 46 | // is set automatically to the outer Global object, which MUST override the abstract 47 | // request handlers. 48 | // 49 | // Resources inherit WlResource. They are created by global or other parent resource 50 | // objects upon client requests. These resources must be subclassed by the application 51 | // and their abstract request handlers must be overriden. 52 | 53 | 54 | class ServerArg : Arg 55 | { 56 | this(Element el) 57 | { 58 | super(el); 59 | } 60 | 61 | override @property string dType() const 62 | { 63 | final switch(type) { 64 | case ArgType.Int: 65 | case ArgType.UInt: 66 | case ArgType.Fixed: 67 | case ArgType.String: 68 | case ArgType.Array: 69 | case ArgType.Fd: 70 | return Arg.dType; 71 | case ArgType.NewId: 72 | return "uint"; 73 | case ArgType.Object: 74 | if (iface.length) 75 | { 76 | auto i = ServerInterface.get(iface); 77 | if (i && i.isGlobal) { 78 | return i.dName ~ ".Resource"; 79 | } 80 | else if (i && !i.isGlobal) { 81 | return i.dName; 82 | } 83 | } 84 | return "WlResource"; 85 | } 86 | } 87 | 88 | override @property string cType() const 89 | { 90 | final switch(type) { 91 | case ArgType.Int: 92 | case ArgType.UInt: 93 | case ArgType.Fixed: 94 | case ArgType.String: 95 | case ArgType.Array: 96 | case ArgType.Fd: 97 | case ArgType.Object: 98 | return Arg.cType; 99 | case ArgType.NewId: 100 | return "uint"; 101 | } 102 | } 103 | 104 | override @property string cCastExpr() const 105 | { 106 | final switch(type) { 107 | case ArgType.Int: 108 | case ArgType.UInt: 109 | case ArgType.Fixed: 110 | case ArgType.String: 111 | case ArgType.NewId: 112 | case ArgType.Array: 113 | case ArgType.Fd: 114 | return Arg.cCastExpr; 115 | case ArgType.Object: 116 | return format("%s.native", paramName); 117 | } 118 | } 119 | } 120 | 121 | 122 | class ServerMessage : Message 123 | { 124 | this (Element el, Interface iface) 125 | { 126 | super(el, iface); 127 | } 128 | 129 | @property ServerInterface svIface() 130 | { 131 | return cast(ServerInterface)iface; 132 | } 133 | 134 | @property auto svArgs() 135 | { 136 | return args.map!(a => cast(ServerArg)a); 137 | } 138 | 139 | @property string reqMethodName() 140 | { 141 | return camelName(name); 142 | } 143 | 144 | @property string sendName() 145 | { 146 | return "send" ~ titleCamelName(name); 147 | } 148 | 149 | @property string privRqListenerStubName() 150 | { 151 | return format("wl_d_%s_%s", ifaceName, name); 152 | } 153 | 154 | @property string[] reqRtArgs() 155 | { 156 | string[] rtArgs = [ "WlClient cl" ]; 157 | if (iface.isGlobal) rtArgs ~= format( 158 | "%s res", svIface.selfResType(Yes.local) 159 | ); 160 | foreach (a; args) { 161 | if (a.type == ArgType.NewId && !a.iface.length) 162 | { 163 | rtArgs ~= [ 164 | "string iface", "uint ver", format("uint %s", a.paramName) 165 | ]; 166 | } 167 | else { 168 | rtArgs ~= format("%s %s", a.dType, a.paramName); 169 | } 170 | } 171 | return rtArgs; 172 | } 173 | 174 | @property string reqRetStr() 175 | { 176 | final switch(reqType) 177 | { 178 | case ReqType.newObj: 179 | return ifaceDName(reqRet.iface); 180 | case ReqType.dynObj: 181 | return "WlResource"; 182 | case ReqType.void_: 183 | return "void"; 184 | } 185 | } 186 | 187 | void writeReqMethodDecl(SourceFile sf, string[] attrs) 188 | { 189 | writeFnSigRaw(sf, attrs.join(" "), reqRetStr, reqMethodName, reqRtArgs); 190 | sf.writeln(";"); 191 | } 192 | 193 | 194 | void writeSendResMethod(SourceFile sf) 195 | { 196 | description.writeCode(sf); 197 | string[] rtArgs; 198 | string[] exprs = [ 199 | "this.native", 200 | opCodeName, 201 | ]; 202 | foreach (arg; args) 203 | { 204 | rtArgs ~= format("%s %s", arg.dType, arg.paramName); 205 | exprs ~= arg.cCastExpr; 206 | } 207 | sf.writeFnSig("void", sendName, rtArgs); 208 | sf.writeFnBody([], "wl_resource_post_event", exprs, []); 209 | } 210 | 211 | void writePrivRqListenerStub(SourceFile sf) 212 | { 213 | string[] rtArgs = [ 214 | "wl_client* natCl", "wl_resource* natRes", 215 | ]; 216 | string[] exprs = [ 217 | "cast(WlClient)ObjectCache.get(natCl)", 218 | ]; 219 | if (iface.isGlobal) exprs ~= "_res"; 220 | foreach (a; args) { 221 | if (a.type == ArgType.Object) 222 | { 223 | rtArgs ~= format("wl_resource* %s", a.paramName); 224 | // TODO: check if wl_resource_get_user_data could work here 225 | exprs ~= format("cast(%s)ObjectCache.get(%s)", a.dType, a.paramName); 226 | } 227 | else if (a.type == ArgType.NewId && !a.iface.length) 228 | { 229 | rtArgs ~= [ 230 | "const(char)* iface", "uint ver", format("uint %s", a.paramName) 231 | ]; 232 | exprs ~= [ 233 | "fromStringz(iface).idup", "ver", a.paramName 234 | ]; 235 | } 236 | else { 237 | rtArgs ~= format("%s %s", a.cType, a.paramName); 238 | exprs ~= a.dCastExpr(ifaceName); 239 | } 240 | } 241 | writeFnSig(sf, "void", privRqListenerStubName, rtArgs); 242 | sf.bracedBlock!({ 243 | sf.writeln("nothrowFnWrapper!({"); 244 | sf.indentedBlock!({ 245 | immutable resType = svIface.selfResType(No.local); 246 | sf.writeln("auto _res = cast(%s)wl_resource_get_user_data(natRes);", resType); 247 | immutable outer = iface.isGlobal ? ".outer" : ""; 248 | writeFnExpr(sf, format("_res%s.%s", outer, reqMethodName), exprs); 249 | }); 250 | sf.writeln("});"); 251 | }); 252 | } 253 | 254 | void writePrivStubSig(SourceFile sf) 255 | { 256 | string[] rtArgs = [ 257 | "wl_client* natCl", "wl_resource* natRes", 258 | ]; 259 | foreach (a; args) { 260 | if (a.type == ArgType.Object) 261 | { 262 | rtArgs ~= format("wl_resource* %s", a.paramName); 263 | } 264 | else if (a.type == ArgType.NewId && !a.iface.length) 265 | { 266 | rtArgs ~= [ 267 | "const(char)* iface", "uint ver", format("uint %s", a.paramName) 268 | ]; 269 | } 270 | else { 271 | rtArgs ~= format("%s %s", a.cType, a.paramName); 272 | } 273 | } 274 | writeFnPointer(sf, name, "void", rtArgs); 275 | } 276 | } 277 | 278 | 279 | class ServerInterface : Interface 280 | { 281 | 282 | 283 | static ServerInterface get(string name) 284 | { 285 | auto i = Interface.get(name); 286 | if (i) return cast(ServerInterface)i; 287 | else return null; 288 | } 289 | 290 | 291 | this (Element el, Protocol protocol) 292 | { 293 | super(el, protocol); 294 | } 295 | 296 | @property auto svRequests() 297 | { 298 | return requests.map!(m => cast(ServerMessage)m); 299 | } 300 | 301 | @property auto svEvents() 302 | { 303 | return events.map!(m => cast(ServerMessage)m); 304 | } 305 | 306 | string selfResType(Flag!"local" local) 307 | { 308 | if (local) { 309 | return isGlobal ? "Resource" : dName; 310 | } 311 | else { 312 | return dName ~ (isGlobal ? ".Resource" : ""); 313 | } 314 | } 315 | 316 | @property string bindFuncName() 317 | { 318 | return format("wl_d_bind_%s", name); 319 | } 320 | 321 | @property string listenerStubsStructName() 322 | { 323 | return format("%sListenersAggregate", titleCamelName(name)); 324 | } 325 | 326 | @property string listenerStubsSymbol() 327 | { 328 | return format("%sListeners", camelName(name)); 329 | } 330 | 331 | override void writeCode(SourceFile sf) 332 | { 333 | description.writeCode(sf); 334 | immutable heritage = name == "wl_display" ? " : WlDisplayBase" : 335 | (isGlobal ? " : WlGlobal" : " : WlResource"); 336 | immutable attrs = name == "wl_display" ? "" : 337 | (requests.length ? "abstract " : ""); 338 | sf.writeln("%sclass %s%s", attrs, dName, heritage); 339 | sf.bracedBlock!({ 340 | writeVersion(sf); 341 | sf.writeln(); 342 | if (name == "wl_display") 343 | { 344 | sf.writeln("this(wl_display* native)"); 345 | sf.bracedBlock!({ 346 | sf.writeln("super(native);"); 347 | }); 348 | sf.writeln(); 349 | } 350 | 351 | writeIfaceAccess(sf); 352 | 353 | writeConstants(sf); 354 | 355 | foreach (en; enums) 356 | { 357 | sf.writeln(); 358 | en.writeCode(sf); 359 | } 360 | 361 | if (name != "wl_display") 362 | { 363 | if (isGlobal) 364 | { 365 | writeGlobalCode(sf); 366 | } 367 | else 368 | { 369 | writeResourceCode(sf); 370 | } 371 | } 372 | }); 373 | } 374 | 375 | void writeIfaceAccess(SourceFile sf) 376 | { 377 | sf.writeln("/// Access to the interface of \"%s.%s\"", protocol.name, name); 378 | sf.writeln("static @property immutable(WlInterface) iface()"); 379 | sf.bracedBlock!({ 380 | sf.writeln("return %sIface;", camelName(name)); 381 | }); 382 | } 383 | 384 | void writeConstants(SourceFile sf) 385 | { 386 | if (events.length) 387 | { 388 | sf.writeln(); 389 | foreach(i, msg; events) 390 | { 391 | sf.writeln("/// Op-code of %s.%s.", name, msg.name); 392 | sf.writeln("enum %s = %d;", msg.opCodeName, i); 393 | } 394 | sf.writeln(); 395 | foreach(msg; events) 396 | { 397 | sf.writeln( 398 | "/// Version of %s protocol introducing %s.%s.", 399 | protocol.name, name, msg.name 400 | ); 401 | sf.writeln("enum %sSinceVersion = %d;", camelName(msg.name), msg.since); 402 | } 403 | } 404 | if (requests.length) 405 | { 406 | sf.writeln(); 407 | foreach(msg; requests) 408 | { 409 | sf.writeln( 410 | "/// %s protocol version introducing %s.%s.", 411 | protocol.name, name, msg.name 412 | ); 413 | sf.writeln("enum %sSinceVersion = %d;", camelName(msg.name), msg.since); 414 | } 415 | } 416 | } 417 | 418 | void writeGlobalCode(SourceFile sf) 419 | { 420 | sf.writeln(); 421 | sf.writeln("protected this(WlDisplay dpy, uint ver)"); 422 | sf.bracedBlock!({ 423 | sf.writeln("super(wl_global_create("); 424 | sf.indentedBlock!({ 425 | sf.writeln("dpy.native, iface.native, ver, cast(void*)this, &%s", bindFuncName); 426 | }); 427 | sf.writeln("));"); 428 | }); 429 | sf.writeln(); 430 | sf.writeln("/// Create a Resource object when a client connects."); 431 | sf.writeln("Resource bind(WlClient cl, uint ver, uint id)"); 432 | sf.bracedBlock!({ 433 | sf.writeln("return new Resource(cl, ver, id);"); 434 | }); 435 | foreach(rq; svRequests) 436 | { 437 | sf.writeln(); 438 | rq.description.writeCode(sf); 439 | rq.writeReqMethodDecl(sf, ["abstract", "protected"]); 440 | } 441 | sf.writeln(); 442 | writeResourceCodeForGlobal(sf); 443 | } 444 | 445 | void writeResourceCodeForGlobal(SourceFile sf) 446 | { 447 | sf.writeln("class Resource : WlResource"); 448 | sf.bracedBlock!({ 449 | writeResourceCtors(sf, []); 450 | foreach(ev; svEvents) 451 | { 452 | sf.writeln(); 453 | ev.writeSendResMethod(sf); 454 | } 455 | }); 456 | } 457 | 458 | void writeResourceCode(SourceFile sf) 459 | { 460 | sf.writeln(); 461 | writeResourceCtors(sf, ["protected"]); 462 | foreach(rq; svRequests) 463 | { 464 | sf.writeln(); 465 | rq.description.writeCode(sf); 466 | rq.writeReqMethodDecl(sf, ["abstract", "protected"]); 467 | } 468 | foreach(ev; svEvents) 469 | { 470 | sf.writeln(); 471 | ev.writeSendResMethod(sf); 472 | } 473 | } 474 | 475 | void writeResourceCtors(SourceFile sf, string[] attrs) 476 | { 477 | immutable attrStr = attrs.join(" ") ~ (attrs.length ? " " : ""); 478 | sf.writeln("%sthis(WlClient cl, uint ver, uint id)", attrStr); 479 | sf.bracedBlock!({ 480 | immutable natExpr = "wl_resource_create(cl.native, iface.native, ver, id)"; 481 | if (requests.length) 482 | { 483 | sf.writeln("auto native = %s;", natExpr); 484 | writeFnExpr(sf, "wl_resource_set_implementation", [ 485 | "native", format("&%s", listenerStubsSymbol), 486 | "cast(void*)this", "null" 487 | ]); 488 | sf.writeln("super(native);"); 489 | } 490 | else 491 | { 492 | sf.writeln("super(%s);", natExpr); 493 | } 494 | }); 495 | sf.writeln(); 496 | sf.writeln("%sthis(wl_resource* natRes)", attrStr); 497 | sf.bracedBlock!({ 498 | sf.writeln("super(natRes);"); 499 | }); 500 | } 501 | 502 | void writePrivBindStub(SourceFile sf) 503 | { 504 | sf.writeln("void %s(wl_client* natCl, void* data, uint ver, uint id)", bindFuncName); 505 | sf.bracedBlock!({ 506 | sf.writeln("nothrowFnWrapper!({"); 507 | sf.indentedBlock!({ 508 | sf.writeln("auto g = cast(%s)data;", dName); 509 | sf.writeln("auto cl = cast(WlClient)ObjectCache.get(natCl);"); 510 | sf.writeln(`assert(g && cl, "%s: could not get global or client from cache");`, bindFuncName); 511 | sf.writeln("g.bind(cl, ver, id);"); 512 | }); 513 | sf.writeln("});"); 514 | }); 515 | } 516 | 517 | void writePrivRqListenerStubs(SourceFile sf) 518 | { 519 | sf.writeln("// %s listener stubs", name); 520 | foreach(rq; svRequests) 521 | { 522 | sf.writeln(); 523 | rq.writePrivRqListenerStub(sf); 524 | } 525 | 526 | sf.writeln(); 527 | sf.writeln("struct %s", listenerStubsStructName); 528 | sf.bracedBlock!({ 529 | foreach (rq; svRequests) { 530 | rq.writePrivStubSig(sf); 531 | } 532 | }); 533 | } 534 | 535 | void writePrivRqListenerStubsSymbol(SourceFile sf) 536 | { 537 | sf.writeln("__gshared %s = %s (", listenerStubsSymbol, listenerStubsStructName); 538 | sf.indentedBlock!({ 539 | foreach(rq; svRequests) { 540 | sf.writeln("&%s,", rq.privRqListenerStubName); 541 | } 542 | }); 543 | sf.writeln(");"); 544 | } 545 | } 546 | 547 | class ServerProtocol : Protocol 548 | { 549 | this(Element el) 550 | { 551 | super(el); 552 | } 553 | 554 | @property auto svIfaces() 555 | { 556 | return ifaces.map!(i => cast(ServerInterface)i); 557 | } 558 | 559 | @property auto svGlobalIfaces() 560 | { 561 | return svIfaces.filter!(i => i.isGlobal); 562 | } 563 | 564 | @property auto svIfacesWithRq() 565 | { 566 | return svIfaces.filter!(i => i.requests.length > 0); 567 | } 568 | 569 | override void writeCode(SourceFile sf, in Options opt) 570 | { 571 | writeHeader(sf, opt); 572 | if (name == "wayland") sf.writeln("import wayland.server.core;"); 573 | else sf.writeln("import wayland.server;"); 574 | sf.writeln("import wayland.native.server;"); 575 | sf.writeln("import wayland.native.util;"); 576 | sf.writeln("import wayland.util;"); 577 | sf.writeln("import std.string : toStringz, fromStringz;"); 578 | sf.writeln(); 579 | 580 | foreach(iface; ifaces) 581 | { 582 | iface.writeCode(sf); 583 | sf.writeln(); 584 | } 585 | 586 | // writing private code 587 | sf.writeln("private:"); 588 | sf.writeln(); 589 | 590 | writePrivIfaces(sf); 591 | sf.writeln(); 592 | 593 | sf.writeln("extern(C) nothrow"); 594 | sf.bracedBlock!({ 595 | bool needNL; 596 | foreach(iface; svGlobalIfaces.filter!(i=>i.name != "wl_display")) 597 | { 598 | if (needNL) sf.writeln(); 599 | else needNL = true; 600 | iface.writePrivBindStub(sf); 601 | } 602 | 603 | foreach(iface; svIfacesWithRq.filter!(i=>i.name != "wl_display")) 604 | { 605 | if (needNL) sf.writeln(); 606 | else needNL = true; 607 | iface.writePrivRqListenerStubs(sf); 608 | } 609 | }); 610 | foreach(iface; svIfacesWithRq.filter!(i=>i.name != "wl_display")) 611 | { 612 | sf.writeln(); 613 | iface.writePrivRqListenerStubsSymbol(sf); 614 | } 615 | } 616 | 617 | override void writePrivIfaces(SourceFile sf) 618 | { 619 | foreach(iface; ifaces) 620 | { 621 | sf.writeln("immutable WlInterface %sIface;", camelName(iface.name)); 622 | } 623 | 624 | writeNativeIfaces(sf); 625 | } 626 | 627 | override void writeNativeIfacesAssignment(SourceFile sf) 628 | { 629 | foreach (iface; ifaces) 630 | { 631 | sf.writeln("%sIface = new immutable WlInterface ( &wl_ifaces[%s] );", 632 | camelName(iface.name), indexSymbol(iface.name) 633 | ); 634 | } 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | All code in wayland-d is released under the terms of the MIT license, at the 2 | exception of the file common/linux/input.d, which is released under the terms of 3 | the GNU General Public License version 2 as published by the Free Software 4 | Foundation. 5 | 6 | Wayland-d copyright: 7 | 8 | Copyright © 2017-2021 Rémi THEBAULT 9 | 10 | 11 | MIT license (all code except common/linux/input.d): 12 | ___________________________________________________ 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy of 15 | this software and associated documentation files (the "Software"), to deal in 16 | the Software without restriction, including without limitation the rights to 17 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 18 | the Software, and to permit persons to whom the Software is furnished to do so, 19 | subject to the following conditions: 20 | 21 | * The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 26 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 27 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 28 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | ___________________________________________________ 31 | 32 | 33 | GNU GPL version 2 (common/linux/input.d): 34 | ___________________________________________________ 35 | 36 | GNU GENERAL PUBLIC LICENSE 37 | Version 2, June 1991 38 | 39 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 40 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 41 | Everyone is permitted to copy and distribute verbatim copies 42 | of this license document, but changing it is not allowed. 43 | 44 | Preamble 45 | 46 | The licenses for most software are designed to take away your 47 | freedom to share and change it. By contrast, the GNU General Public 48 | License is intended to guarantee your freedom to share and change free 49 | software--to make sure the software is free for all its users. This 50 | General Public License applies to most of the Free Software 51 | Foundation's software and to any other program whose authors commit to 52 | using it. (Some other Free Software Foundation software is covered by 53 | the GNU Library General Public License instead.) You can apply it to 54 | your programs, too. 55 | 56 | When we speak of free software, we are referring to freedom, not 57 | price. Our General Public Licenses are designed to make sure that you 58 | have the freedom to distribute copies of free software (and charge for 59 | this service if you wish), that you receive source code or can get it 60 | if you want it, that you can change the software or use pieces of it 61 | in new free programs; and that you know you can do these things. 62 | 63 | To protect your rights, we need to make restrictions that forbid 64 | anyone to deny you these rights or to ask you to surrender the rights. 65 | These restrictions translate to certain responsibilities for you if you 66 | distribute copies of the software, or if you modify it. 67 | 68 | For example, if you distribute copies of such a program, whether 69 | gratis or for a fee, you must give the recipients all the rights that 70 | you have. You must make sure that they, too, receive or can get the 71 | source code. And you must show them these terms so they know their 72 | rights. 73 | 74 | We protect your rights with two steps: (1) copyright the software, and 75 | (2) offer you this license which gives you legal permission to copy, 76 | distribute and/or modify the software. 77 | 78 | Also, for each author's protection and ours, we want to make certain 79 | that everyone understands that there is no warranty for this free 80 | software. If the software is modified by someone else and passed on, we 81 | want its recipients to know that what they have is not the original, so 82 | that any problems introduced by others will not reflect on the original 83 | authors' reputations. 84 | 85 | Finally, any free program is threatened constantly by software 86 | patents. We wish to avoid the danger that redistributors of a free 87 | program will individually obtain patent licenses, in effect making the 88 | program proprietary. To prevent this, we have made it clear that any 89 | patent must be licensed for everyone's free use or not licensed at all. 90 | 91 | The precise terms and conditions for copying, distribution and 92 | modification follow. 93 | 94 | GNU GENERAL PUBLIC LICENSE 95 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 96 | 97 | 0. This License applies to any program or other work which contains 98 | a notice placed by the copyright holder saying it may be distributed 99 | under the terms of this General Public License. The "Program", below, 100 | refers to any such program or work, and a "work based on the Program" 101 | means either the Program or any derivative work under copyright law: 102 | that is to say, a work containing the Program or a portion of it, 103 | either verbatim or with modifications and/or translated into another 104 | language. (Hereinafter, translation is included without limitation in 105 | the term "modification".) Each licensee is addressed as "you". 106 | 107 | Activities other than copying, distribution and modification are not 108 | covered by this License; they are outside its scope. The act of 109 | running the Program is not restricted, and the output from the Program 110 | is covered only if its contents constitute a work based on the 111 | Program (independent of having been made by running the Program). 112 | Whether that is true depends on what the Program does. 113 | 114 | 1. You may copy and distribute verbatim copies of the Program's 115 | source code as you receive it, in any medium, provided that you 116 | conspicuously and appropriately publish on each copy an appropriate 117 | copyright notice and disclaimer of warranty; keep intact all the 118 | notices that refer to this License and to the absence of any warranty; 119 | and give any other recipients of the Program a copy of this License 120 | along with the Program. 121 | 122 | You may charge a fee for the physical act of transferring a copy, and 123 | you may at your option offer warranty protection in exchange for a fee. 124 | 125 | 2. You may modify your copy or copies of the Program or any portion 126 | of it, thus forming a work based on the Program, and copy and 127 | distribute such modifications or work under the terms of Section 1 128 | above, provided that you also meet all of these conditions: 129 | 130 | a) You must cause the modified files to carry prominent notices 131 | stating that you changed the files and the date of any change. 132 | 133 | b) You must cause any work that you distribute or publish, that in 134 | whole or in part contains or is derived from the Program or any 135 | part thereof, to be licensed as a whole at no charge to all third 136 | parties under the terms of this License. 137 | 138 | c) If the modified program normally reads commands interactively 139 | when run, you must cause it, when started running for such 140 | interactive use in the most ordinary way, to print or display an 141 | announcement including an appropriate copyright notice and a 142 | notice that there is no warranty (or else, saying that you provide 143 | a warranty) and that users may redistribute the program under 144 | these conditions, and telling the user how to view a copy of this 145 | License. (Exception: if the Program itself is interactive but 146 | does not normally print such an announcement, your work based on 147 | the Program is not required to print an announcement.) 148 | 149 | These requirements apply to the modified work as a whole. If 150 | identifiable sections of that work are not derived from the Program, 151 | and can be reasonably considered independent and separate works in 152 | themselves, then this License, and its terms, do not apply to those 153 | sections when you distribute them as separate works. But when you 154 | distribute the same sections as part of a whole which is a work based 155 | on the Program, the distribution of the whole must be on the terms of 156 | this License, whose permissions for other licensees extend to the 157 | entire whole, and thus to each and every part regardless of who wrote it. 158 | 159 | Thus, it is not the intent of this section to claim rights or contest 160 | your rights to work written entirely by you; rather, the intent is to 161 | exercise the right to control the distribution of derivative or 162 | collective works based on the Program. 163 | 164 | In addition, mere aggregation of another work not based on the Program 165 | with the Program (or with a work based on the Program) on a volume of 166 | a storage or distribution medium does not bring the other work under 167 | the scope of this License. 168 | 169 | 3. You may copy and distribute the Program (or a work based on it, 170 | under Section 2) in object code or executable form under the terms of 171 | Sections 1 and 2 above provided that you also do one of the following: 172 | 173 | a) Accompany it with the complete corresponding machine-readable 174 | source code, which must be distributed under the terms of Sections 175 | 1 and 2 above on a medium customarily used for software interchange; or, 176 | 177 | b) Accompany it with a written offer, valid for at least three 178 | years, to give any third party, for a charge no more than your 179 | cost of physically performing source distribution, a complete 180 | machine-readable copy of the corresponding source code, to be 181 | distributed under the terms of Sections 1 and 2 above on a medium 182 | customarily used for software interchange; or, 183 | 184 | c) Accompany it with the information you received as to the offer 185 | to distribute corresponding source code. (This alternative is 186 | allowed only for noncommercial distribution and only if you 187 | received the program in object code or executable form with such 188 | an offer, in accord with Subsection b above.) 189 | 190 | The source code for a work means the preferred form of the work for 191 | making modifications to it. For an executable work, complete source 192 | code means all the source code for all modules it contains, plus any 193 | associated interface definition files, plus the scripts used to 194 | control compilation and installation of the executable. However, as a 195 | special exception, the source code distributed need not include 196 | anything that is normally distributed (in either source or binary 197 | form) with the major components (compiler, kernel, and so on) of the 198 | operating system on which the executable runs, unless that component 199 | itself accompanies the executable. 200 | 201 | If distribution of executable or object code is made by offering 202 | access to copy from a designated place, then offering equivalent 203 | access to copy the source code from the same place counts as 204 | distribution of the source code, even though third parties are not 205 | compelled to copy the source along with the object code. 206 | 207 | 4. You may not copy, modify, sublicense, or distribute the Program 208 | except as expressly provided under this License. Any attempt 209 | otherwise to copy, modify, sublicense or distribute the Program is 210 | void, and will automatically terminate your rights under this License. 211 | However, parties who have received copies, or rights, from you under 212 | this License will not have their licenses terminated so long as such 213 | parties remain in full compliance. 214 | 215 | 5. You are not required to accept this License, since you have not 216 | signed it. However, nothing else grants you permission to modify or 217 | distribute the Program or its derivative works. These actions are 218 | prohibited by law if you do not accept this License. Therefore, by 219 | modifying or distributing the Program (or any work based on the 220 | Program), you indicate your acceptance of this License to do so, and 221 | all its terms and conditions for copying, distributing or modifying 222 | the Program or works based on it. 223 | 224 | 6. Each time you redistribute the Program (or any work based on the 225 | Program), the recipient automatically receives a license from the 226 | original licensor to copy, distribute or modify the Program subject to 227 | these terms and conditions. You may not impose any further 228 | restrictions on the recipients' exercise of the rights granted herein. 229 | You are not responsible for enforcing compliance by third parties to 230 | this License. 231 | 232 | 7. If, as a consequence of a court judgment or allegation of patent 233 | infringement or for any other reason (not limited to patent issues), 234 | conditions are imposed on you (whether by court order, agreement or 235 | otherwise) that contradict the conditions of this License, they do not 236 | excuse you from the conditions of this License. If you cannot 237 | distribute so as to satisfy simultaneously your obligations under this 238 | License and any other pertinent obligations, then as a consequence you 239 | may not distribute the Program at all. For example, if a patent 240 | license would not permit royalty-free redistribution of the Program by 241 | all those who receive copies directly or indirectly through you, then 242 | the only way you could satisfy both it and this License would be to 243 | refrain entirely from distribution of the Program. 244 | 245 | If any portion of this section is held invalid or unenforceable under 246 | any particular circumstance, the balance of the section is intended to 247 | apply and the section as a whole is intended to apply in other 248 | circumstances. 249 | 250 | It is not the purpose of this section to induce you to infringe any 251 | patents or other property right claims or to contest validity of any 252 | such claims; this section has the sole purpose of protecting the 253 | integrity of the free software distribution system, which is 254 | implemented by public license practices. Many people have made 255 | generous contributions to the wide range of software distributed 256 | through that system in reliance on consistent application of that 257 | system; it is up to the author/donor to decide if he or she is willing 258 | to distribute software through any other system and a licensee cannot 259 | impose that choice. 260 | 261 | This section is intended to make thoroughly clear what is believed to 262 | be a consequence of the rest of this License. 263 | 264 | 8. If the distribution and/or use of the Program is restricted in 265 | certain countries either by patents or by copyrighted interfaces, the 266 | original copyright holder who places the Program under this License 267 | may add an explicit geographical distribution limitation excluding 268 | those countries, so that distribution is permitted only in or among 269 | countries not thus excluded. In such case, this License incorporates 270 | the limitation as if written in the body of this License. 271 | 272 | 9. The Free Software Foundation may publish revised and/or new versions 273 | of the General Public License from time to time. Such new versions will 274 | be similar in spirit to the present version, but may differ in detail to 275 | address new problems or concerns. 276 | 277 | Each version is given a distinguishing version number. If the Program 278 | specifies a version number of this License which applies to it and "any 279 | later version", you have the option of following the terms and conditions 280 | either of that version or of any later version published by the Free 281 | Software Foundation. If the Program does not specify a version number of 282 | this License, you may choose any version ever published by the Free Software 283 | Foundation. 284 | 285 | 10. If you wish to incorporate parts of the Program into other free 286 | programs whose distribution conditions are different, write to the author 287 | to ask for permission. For software which is copyrighted by the Free 288 | Software Foundation, write to the Free Software Foundation; we sometimes 289 | make exceptions for this. Our decision will be guided by the two goals 290 | of preserving the free status of all derivatives of our free software and 291 | of promoting the sharing and reuse of software generally. 292 | 293 | NO WARRANTY 294 | 295 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 296 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 297 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 298 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 299 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 300 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 301 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 302 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 303 | REPAIR OR CORRECTION. 304 | 305 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 306 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 307 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 308 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 309 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 310 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 311 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 312 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 313 | POSSIBILITY OF SUCH DAMAGES. 314 | 315 | END OF TERMS AND CONDITIONS 316 | 317 | How to Apply These Terms to Your New Programs 318 | 319 | If you develop a new program, and you want it to be of the greatest 320 | possible use to the public, the best way to achieve this is to make it 321 | free software which everyone can redistribute and change under these terms. 322 | 323 | To do so, attach the following notices to the program. It is safest 324 | to attach them to the start of each source file to most effectively 325 | convey the exclusion of warranty; and each file should have at least 326 | the "copyright" line and a pointer to where the full notice is found. 327 | 328 | 329 | Copyright (C) 330 | 331 | This program is free software; you can redistribute it and/or modify 332 | it under the terms of the GNU General Public License as published by 333 | the Free Software Foundation; either version 2 of the License, or 334 | (at your option) any later version. 335 | 336 | This program is distributed in the hope that it will be useful, 337 | but WITHOUT ANY WARRANTY; without even the implied warranty of 338 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 339 | GNU General Public License for more details. 340 | 341 | You should have received a copy of the GNU General Public License 342 | along with this program; if not, write to the Free Software 343 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 344 | 345 | 346 | Also add information on how to contact you by electronic and paper mail. 347 | 348 | If the program is interactive, make it output a short notice like this 349 | when it starts in an interactive mode: 350 | 351 | Gnomovision version 69, Copyright (C) year name of author 352 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 353 | This is free software, and you are welcome to redistribute it 354 | under certain conditions; type `show c' for details. 355 | 356 | The hypothetical commands `show w' and `show c' should show the appropriate 357 | parts of the General Public License. Of course, the commands you use may 358 | be called something other than `show w' and `show c'; they could even be 359 | mouse-clicks or menu items--whatever suits your program. 360 | 361 | You should also get your employer (if you work as a programmer) or your 362 | school, if any, to sign a "copyright disclaimer" for the program, if 363 | necessary. Here is a sample; alter the names: 364 | 365 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 366 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 367 | 368 | , 1 April 1989 369 | Ty Coon, President of Vice 370 | 371 | This General Public License does not permit incorporating your program into 372 | proprietary programs. If your program is a subroutine library, you may 373 | consider it more useful to permit linking proprietary applications with the 374 | library. If this is what you want to do, use the GNU Library General 375 | Public License instead of this License. 376 | 377 | ___________________________________________________ 378 | --------------------------------------------------------------------------------