1 /// Optional window package, mainly to run gfx-d examples
2 module gfx.window;
3
4 import gfx.core.rc : IAtomicRefCounted;
5 import gfx.core.log : LogTag;
6 import gfx.graal : Backend, Instance;
7 public import gfx.window.keys;
8
9 import std.typecons : Flag, No, Yes;
10
11 enum gfxWindowLogMask = 0x0800_0000;
12 package immutable gfxWindowLog = LogTag("GFX-WINDOW", gfxWindowLogMask);
13
14 struct KeyEvent
15 {
16 KeySym sym;
17 KeyCode code;
18 KeyMods mods;
19 string text;
20 }
21
22 struct MouseEvent
23 {
24 uint x;
25 uint y;
26 KeyMods mods;
27 }
28
29 alias MouseHandler = void delegate(MouseEvent ev);
30 alias KeyHandler = void delegate(KeyEvent ev);
31 alias ResizeHandler = void delegate(uint width, uint height);
32 alias CloseHandler = bool delegate();
33
34 interface Display : IAtomicRefCounted
35 {
36 @property Instance instance();
37 @property Window[] windows();
38 Window createWindow(string title = "Gfx-d Window");
39 void pollAndDispatch();
40 }
41
42 interface Window
43 {
44 import gfx.graal.presentation : Surface;
45
46 void show(uint width, uint height);
47 void close();
48
49 @property string title();
50 void setTitle(string title);
51
52 @property Surface surface();
53 @property bool closeFlag() const;
54 @property void closeFlag(in bool flag);
55
56 @property void onResize(ResizeHandler handler);
57 @property void onMouseMove(MouseHandler handler);
58 @property void onMouseOn(MouseHandler handler);
59 @property void onMouseOff(MouseHandler handler);
60 @property void onKeyOn(KeyHandler handler);
61 @property void onKeyOff(KeyHandler handler);
62 @property void onClose(CloseHandler handler);
63 }
64
65 version (linux)
66 {
67 /// Identifier of the display to use on linux
68 enum LinuxDisplay
69 {
70 /// Instantiate a wayland display
71 /// (no Client Side Decorations at this point)
72 wayland,
73 /// Instantiate an XCB display (X windowing system)
74 xcb,
75 }
76 }
77
78 debug
79 {
80 private enum defaultDebugCb = Yes.debugCallback;
81 private enum defaultValidation = Yes.validation;
82 }
83 else
84 {
85 private enum defaultDebugCb = No.debugCallback;
86 private enum defaultValidation = No.validation;
87 }
88
89
90 /// Options that influence how the display is created, and how it will create
91 /// a Gfx-D instance.
92 struct DisplayCreateInfo
93 {
94 /// Order into which backend creation is tried.
95 /// The first successfully created backend is used.
96 /// If empty, Vulkan is tried first, and OpenGL3 is tried as fallback if enabled and available
97 Backend[] backendCreateOrder;
98
99 version (linux)
100 {
101 /// Order into which display creation is tried on linux.
102 /// The first successfully created display is used.
103 /// If empty, Wayland will be tried if available and VkWayland enabled, and XCB will be used as fallback.
104 LinuxDisplay[] linuxDisplayCreateOrder;
105 }
106
107 /// Whether DebugCallback should be available. Only meaningful with Vulkan backend.
108 Flag!"debugCallback" debugCallbackEnabled = defaultDebugCb;
109 /// Whether validation should be enabled. Only meaningful with Vulkan backend.
110 Flag!"validation" validationEnabled = defaultValidation;
111 }
112
113 /// Create a display for the running platform.
114 /// The display will load a backend instance during startup.
115 /// It will try the backends in the provided loadOrder.
116 /// On linux, more than one display implementation are provided. You may
117 /// use linuxDisplayOrder to choose. The first succesfully created display is returned.
118 Display createDisplay(DisplayCreateInfo createInfo)
119 {
120 if (createInfo.backendCreateOrder.length == 0)
121 {
122 version(GfxGl3)
123 createInfo.backendCreateOrder = [Backend.vulkan, Backend.gl3];
124 else
125 createInfo.backendCreateOrder = [Backend.vulkan];
126 }
127
128 version (linux)
129 {
130 import gfx.window.xcb : XcbDisplay;
131 import std.process : environment;
132
133 auto order = createInfo.linuxDisplayCreateOrder;
134 if (order.length == 0)
135 {
136 version (VkWayland) {
137 order = environment["XDG_SESSION_TYPE"] == "wayland" ? [
138 LinuxDisplay.wayland, LinuxDisplay.xcb
139 ] : [LinuxDisplay.xcb];
140 }
141 else {
142 order = [LinuxDisplay.xcb];
143 }
144 }
145
146 foreach (ld; order)
147 {
148 try
149 {
150 final switch (ld)
151 {
152 case LinuxDisplay.wayland:
153 version (VkWayland) {
154 import gfx.window.wayland : WaylandDisplay;
155
156 return new WaylandDisplay(createInfo);
157 }
158 else {
159 assert(false, "Wayland support is disabled");
160 }
161 case LinuxDisplay.xcb:
162 return new XcbDisplay(createInfo);
163 }
164 }
165 catch (Exception ex)
166 {
167 gfxWindowLog.warningf("Failed to create %s linux display:\n%s", ld, ex.msg);
168 }
169 }
170 throw new Exception("Could not create a functional display");
171 }
172 else version (Windows)
173 {
174 import gfx.window.win32 : Win32Display;
175
176 return new Win32Display(createInfo);
177 }
178 else
179 {
180 pragma(msg, "Unsupported platform");
181 assert(false, "Unsupported platform");
182 }
183 }