1 | // LAF OS Library |
2 | // Copyright (C) 2018-2022 Igara Studio S.A. |
3 | // Copyright (C) 2012-2017 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifndef OS_SYSTEM_H_INCLUDED |
9 | #define OS_SYSTEM_H_INCLUDED |
10 | #pragma once |
11 | |
12 | #include "gfx/fwd.h" |
13 | #include "os/app_mode.h" |
14 | #include "os/capabilities.h" |
15 | #include "os/color_space.h" |
16 | #include "os/keys.h" |
17 | #include "os/ref.h" |
18 | #include "os/screen.h" |
19 | #include "os/window.h" |
20 | #include "os/window_spec.h" |
21 | |
22 | #include <functional> |
23 | #include <memory> |
24 | #include <stdexcept> |
25 | #include <string> |
26 | |
27 | namespace os { |
28 | |
29 | class ColorSpaceConversion; |
30 | class EventQueue; |
31 | class Font; |
32 | class FontManager; |
33 | class Logger; |
34 | class ; |
35 | class NativeDialogs; |
36 | class Surface; |
37 | class System; |
38 | |
39 | using SystemRef = Ref<System>; |
40 | |
41 | // TODO why we just don't return nullptr if the window creation fails? |
42 | // maybe an error handler function? |
43 | class WindowCreationException : public std::runtime_error { |
44 | public: |
45 | WindowCreationException(const char* msg) throw() |
46 | : std::runtime_error(msg) { } |
47 | }; |
48 | |
49 | // Windows-specific details: API to use to get tablet input information. |
50 | enum class TabletAPI { |
51 | // Default tablet API to use in the system (Windows Ink on |
52 | // Windows; only valid value on other systems). |
53 | Default = 0, |
54 | |
55 | // Use Windows 8/10 pointer messages (Windows Ink). |
56 | WindowsPointerInput = 0, |
57 | |
58 | // Use the Wintab API to get pressure information from packets but |
59 | // mouse movement from Windows system messages |
60 | // (WM_MOUSEMOVE). |
61 | Wintab = 1, |
62 | |
63 | // Use the Wintab API processing packets directly (pressure and |
64 | // stylus movement information). With this we might get more |
65 | // precision from the device (but still work-in-progress, some |
66 | // messages might be mixed up). |
67 | WintabPackets = 2, |
68 | }; |
69 | |
70 | class System : public RefCount { |
71 | protected: |
72 | virtual ~System() { } |
73 | public: |
74 | |
75 | // Windows-specific: The app name at the moment is used to receive |
76 | // DDE messages (WM_DDE_INITIATE) and convert WM_DDE_EXECUTE |
77 | // messages into Event::DropFiles. This allows to the user |
78 | // double-click files in the File Explorer and open the file in a |
79 | // running instance of your app. |
80 | // |
81 | // To receive DDE messages you have to configure the registry in |
82 | // this way (HKCR=HKEY_CLASSES_ROOT): |
83 | // |
84 | // HKCR\.appfile (Default)="AppFile" |
85 | // HKCR\AppFile (Default)="App File" |
86 | // HKCR\AppFile\shell\open\command (Default)="C:\\...\\AppName.EXE" |
87 | // HKCR\AppFile\shell\open\ddeexec (Default)="[open(\"%1\")]" |
88 | // HKCR\AppFile\shell\open\ddeexec\application (Default)="AppName" |
89 | // HKCR\AppFile\shell\open\ddeexec\topic (Default)="system" |
90 | // |
91 | // The default value of "HKCR\AppFile\shell\open\ddeexec\application" |
92 | // must match the "appName" given in this function. |
93 | virtual void setAppName(const std::string& appName) = 0; |
94 | |
95 | // We can use this function to create an application that can run |
96 | // in CLI and GUI mode depending on the given arguments, and in |
97 | // this way avoid to showing the app in the macOS dock bar if we |
98 | // are running in CLI only. |
99 | virtual void setAppMode(AppMode appMode) = 0; |
100 | |
101 | // Marks a specific file as a file that was processed in the CLI. |
102 | // Only useful on macOS to avoid generating DropFiles events for |
103 | // files that were processed from the CLI arguments directly. |
104 | virtual void markCliFileAsProcessed(const std::string& cliFile) = 0; |
105 | |
106 | // On macOS it calls [NSApplication finishLaunching] that will |
107 | // produce some extra events like [NSApplicationDelegate |
108 | // application:openFiles:] which generates os::Event::DropFiles |
109 | // events for each file specified in the command line. |
110 | // |
111 | // You can ignore those DropFiles events if you've already |
112 | // processed through the CLI arguments (app_main(argc, argv)) or |
113 | // you can use markCliFileAsProcessed() before calling this |
114 | // function. |
115 | virtual void finishLaunching() = 0; |
116 | |
117 | // We might need to call this function when the app is launched |
118 | // from Steam. It appears that there is a bug on macOS Steam |
119 | // client where the app is launched, activated, and then the Steam |
120 | // client is activated again. |
121 | virtual void activateApp() = 0; |
122 | |
123 | virtual Capabilities capabilities() const = 0; |
124 | bool hasCapability(Capabilities c) const { |
125 | return (int(capabilities()) & int(c)) == int(c); |
126 | } |
127 | |
128 | // Sets the specific API to use to process tablet/stylus/pen |
129 | // messages. |
130 | // |
131 | // It can be used to avoid loading wintab32.dll too (sometimes a |
132 | // program can be locked when we load the wintab32.dll, so we need |
133 | // a way to opt-out loading this library.) |
134 | virtual void setTabletAPI(TabletAPI api) = 0; |
135 | virtual TabletAPI tabletAPI() const = 0; |
136 | |
137 | // Sub-interfaces |
138 | virtual Logger* logger() = 0; |
139 | virtual Menus* () = 0; |
140 | virtual NativeDialogs* nativeDialogs() = 0; |
141 | virtual EventQueue* eventQueue() = 0; |
142 | |
143 | virtual bool gpuAcceleration() const = 0; |
144 | virtual void setGpuAcceleration(bool state) = 0; |
145 | |
146 | // Returns the main screen |
147 | virtual ScreenRef mainScreen() = 0; |
148 | |
149 | // Returns a list of screens attached to the computer. |
150 | virtual void listScreens(ScreenList& screens) = 0; |
151 | |
152 | virtual Window* defaultWindow() = 0; |
153 | |
154 | // Creates a new window in the operating system with the given |
155 | // specs (width, height, etc.). |
156 | virtual WindowRef makeWindow(const WindowSpec& spec) = 0; |
157 | |
158 | WindowRef makeWindow(const int contentWidth, |
159 | const int contentHeight, |
160 | const int scale = 1) { |
161 | return makeWindow(WindowSpec(contentWidth, contentHeight, scale)); |
162 | } |
163 | |
164 | virtual Ref<Surface> makeSurface(int width, int height, const os::ColorSpaceRef& colorSpace = nullptr) = 0; |
165 | virtual Ref<Surface> makeRgbaSurface(int width, int height, const os::ColorSpaceRef& colorSpace = nullptr) = 0; |
166 | virtual Ref<Surface> loadSurface(const char* filename) = 0; |
167 | virtual Ref<Surface> loadRgbaSurface(const char* filename) = 0; |
168 | |
169 | // Creates a new cursor with the given surface. |
170 | // |
171 | // Warning: On Windows there is a limit of 10,000 GDI objects per |
172 | // process and creating a cursor needs 3 GDI objects (a HCURSOR |
173 | // and two HBITMAPs). |
174 | virtual Ref<Cursor> makeCursor(const Surface* surface, |
175 | const gfx::Point& focus, |
176 | const int scale) = 0; |
177 | |
178 | // New font manager |
179 | virtual FontManager* fontManager() = 0; |
180 | |
181 | // Old font functions (to be removed) |
182 | virtual Ref<Font> loadSpriteSheetFont(const char* filename, int scale = 1) = 0; |
183 | virtual Ref<Font> loadTrueTypeFont(const char* filename, int height) = 0; |
184 | |
185 | // Returns true if the the given scancode key is pressed/actived. |
186 | virtual bool isKeyPressed(KeyScancode scancode) = 0; |
187 | |
188 | // Returns the active pressed modifiers. |
189 | virtual KeyModifiers keyModifiers() = 0; |
190 | |
191 | // Returns the latest unicode character that activated the given |
192 | // scancode. |
193 | virtual int getUnicodeFromScancode(KeyScancode scancode) = 0; |
194 | |
195 | // Indicates if you want to use dead keys or not. By default it's |
196 | // false, which behaves as regular shortcuts. You should set this |
197 | // to true when you're inside a text field in your app. |
198 | // |
199 | // TODO Improve this API using different input modes, |
200 | // e.g. GameLike, TextInput, TextInputWithDeadKeys |
201 | virtual void setTranslateDeadKeys(bool state) = 0; |
202 | |
203 | // Returns the mouse position in the screen. Try to avoid using |
204 | // this and prefer the Event mouse position. |
205 | virtual gfx::Point mousePosition() const = 0; |
206 | |
207 | // Sets the mouse position to a specific point in the screen. |
208 | virtual void setMousePosition(const gfx::Point& screenPosition) = 0; |
209 | |
210 | // Gets a color from the desktop in given screen position. |
211 | // |
212 | // WARNING for macOS: This function will ask the user for |
213 | // permissions to record the screen. If the app is not in a signed |
214 | // bundle, the color will be from the wallpaper, but if the |
215 | // function is used in a signed bundled app, the color will be |
216 | // from any opened window. |
217 | virtual gfx::Color getColorFromScreen(const gfx::Point& screenPosition) const = 0; |
218 | |
219 | // Color management |
220 | virtual void listColorSpaces( |
221 | std::vector<os::ColorSpaceRef>& list) = 0; |
222 | virtual os::ColorSpaceRef makeColorSpace( |
223 | const gfx::ColorSpaceRef& colorSpace) = 0; |
224 | virtual Ref<ColorSpaceConversion> convertBetweenColorSpace( |
225 | const os::ColorSpaceRef& src, |
226 | const os::ColorSpaceRef& dst) = 0; |
227 | |
228 | // Set a default color profile for all windows (nullptr to use the |
229 | // active monitor color profile and change it dynamically when the |
230 | // window changes to another monitor). |
231 | virtual void setWindowsColorSpace(const os::ColorSpaceRef& cs) = 0; |
232 | virtual os::ColorSpaceRef windowsColorSpace() = 0; |
233 | |
234 | // Function called to handle a "live resize"/resizing loop of a |
235 | // native window. If this is nullptr, an Event::ResizeWindow is |
236 | // generated when the resizing is finished. |
237 | // |
238 | // TODO I think we should have a SystemDelegate or something |
239 | // similar instead of a public property. |
240 | std::function<void(os::Window*)> handleWindowResize = nullptr; |
241 | |
242 | #if LAF_WINDOWS |
243 | // Only useful on Windows, the delegate must be a pointer to a |
244 | // WintabAPI::Delegate, and it must be deleted by the user |
245 | // manually (it's not owned by the os::System impl). |
246 | // |
247 | // This can be used to get information about the wintab32.dll |
248 | // vendor (company name, etc.) |
249 | virtual void setWintabDelegate(void* delegate) { } |
250 | #endif |
251 | }; |
252 | |
253 | SystemRef make_system(); |
254 | System* instance(); |
255 | void set_instance(System* system); |
256 | |
257 | } // namespace os |
258 | |
259 | #endif |
260 | |