├── 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 | 
6 | 
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 |
--------------------------------------------------------------------------------