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