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 /// Options that influence how the display is created, and how it will create
90 /// a Gfx-D instance.
91 struct DisplayCreateInfo
92 {
93     /// Order into which backend creation is tried.
94     /// The first successfully created backend is used.
95     Backend[] backendCreateOrder = [Backend.vulkan, Backend.gl3];
96 
97     version(linux)
98     {
99         /// Order into which display creation is tried on linux.
100         /// The first successfully created display is used.
101         /// If empty, Wayland will be tried if available, and XCB will be used as fallback.
102         LinuxDisplay[] linuxDisplayCreateOrder;
103     }
104 
105     /// Whether DebugCallback should be available. Only meaningful with Vulkan backend.
106     Flag!"debugCallback" debugCallbackEnabled = defaultDebugCb;
107     /// Whether validation should be enabled. Only meaningful with Vulkan backend.
108     Flag!"validation" validationEnabled = defaultValidation;
109 }
110 
111 /// Create a display for the running platform.
112 /// The display will load a backend instance during startup.
113 /// It will try the backends in the provided loadOrder.
114 /// On linux, more than one display implementation are provided. You may
115 /// use linuxDisplayOrder to choose. The first succesfully created display is returned.
116 Display createDisplay(DisplayCreateInfo createInfo)
117 {
118     version (linux)
119     {
120         import gfx.window.wayland : WaylandDisplay;
121         import gfx.window.xcb : XcbDisplay;
122         import std.process : environment;
123 
124         auto order = createInfo.linuxDisplayCreateOrder;
125         if (order.length == 0)
126         {
127             order = [
128                 environment["XDG_SESSION_TYPE"] == "wayland" ? LinuxDisplay.wayland : LinuxDisplay.xcb
129             ];
130         }
131 
132         foreach (ld; order)
133         {
134             try
135             {
136                 final switch (ld)
137                 {
138                 case LinuxDisplay.wayland:
139                     return new WaylandDisplay(createInfo);
140                 case LinuxDisplay.xcb:
141                     return new XcbDisplay(createInfo);
142                 }
143             }
144             catch (Exception ex)
145             {
146                 gfxWindowLog.warningf("Failed to create %s linux display:\n%s", ld, ex.msg);
147             }
148         }
149         throw new Exception("Could not create a functional display");
150     }
151     else version (Windows)
152     {
153         import gfx.window.win32 : Win32Display;
154 
155         return new Win32Display(createInfo);
156     }
157     else
158     {
159         pragma(msg, "Unsupported platform");
160         assert(false, "Unsupported platform");
161     }
162 }