| 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 | |