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 }