What is IWindow

IWindow is a windowing library that is meant to be used with Vulkan or OpenGL.

The goal of IWindow is:

  • IWindow should be cross-platform and work with Win32 (Windows) and X11 (Linux) windowing apis but currently there is only a Win32 version.

How can I build IWindow?

IWindow uses the premake5 build system.

IWindow comes with premade premake projects to build easily to a static library. These projects are IWindowWin32GL and IWindowWin32Vk. As the name suggests GL is for OpenGL projects and Vk is for Vulkan. There is also a IWindowWin32All project for OpenGL and Vulkan.

Important Stuff You Should Know About

All functions/classes are in the IWindow namespace.

Window Class

The window class is where the magic happens. The window class as the name suggests is the class that is reponsible for creating the window on your screen.

The window class is kept in the IWindow.h header file.

This is what the window class .h file looks like (with some stuff taken out).

namespace IWindow {
    class Window {
    public:
Window() = default;
        Window(int64_t width, int64_t height, const std::string& title, int64_t x = 100, int64_t y = 100);
        ~Window(); 

        bool Create(int64_t width, int64_t height, const std::string& title, int64_t x = 100, int64_t y = 100);

        void Update();

        bool IsRunning();

        NativeWindowHandle& GetNativeWindowHandle(); 

        IVector2 GetWindowSize();
        IVector2 GetWindowPosition();

        IVector2 GetMousePosition();

        void SetWindowSize(int64_t width, int64_t height);
        void SetWindowPosition(int64_t x, int64_t y);

        bool IsKeyDown(Key key);
        template<typename... Args>
        bool IsKeyDown(Key key, Args... args) { return IsKeyDown(key) && IsKeyDown(args...); }

        bool IsKeyUp(Key key);
        template<typename... Args>
        bool IsKeyUp(Key key, Args... args) { return IsKeyUp(key) && IsKeyUp(args...); }

        bool IsMouseButtonDown(MouseButton button);
        template<typename... Args>
        bool IsMouseButtonDown(MouseButton button, Args... args) { return IsMouseButtonDown(button) && IsMouseButtonDown(args...); }

        bool IsMouseButtonDoubleClicked(MouseButton button);
        template<typename... Args>
        bool IsMouseButtonDoubleClicked(MouseButton button, Args... args) { return IsMouseButtonDoubleClicked(button) && IsMouseButtonDoubleClicked(args...); }

        bool IsMouseButtonUp(MouseButton button);
        template<typename... Args>
        bool IsMouseButtonUp(MouseButton button, Args... args) { return IsMouseButtonUp(button) && IsMouseButtonUp(args...); }

        Vector2 GetMouseScrollOffset();

        void SetUserPointer(void* ptr);
        void* GetUserPointer();

        void SetPosCallback(WindowPosCallback callback);
        void SetSizeCallback(WindowSizeCallback callback);
        void SetKeyCallback(KeyCallback callback);
        void SetMouseMoveCallback(MouseMoveCallback callback);
        void SetMouseButtonCallback(MouseButtonCallback callback);
        void SetMouseScrollCallback(MouseScrollCallback callback);

        Monitor GetPrimaryMonitor();
        std::vector<Monitor> GetAllMonitors();

        void Center(Monitor monitor);
        void Fullscreen(bool fullscreen, Monitor monitor);
        bool IsFullscreen();

        void SetIcon(Image image);
        void SetCursor(Image image, uint32_t hotX, uint32_t hotY);
        void SetIcon(NativeIconID iconID);
        void SetCursor(NativeCursorID cursorID);

        NativeDeviceContext& GetNativeDeviceContext();
        
        void operator=(Window&) = delete;
        Window(Window&) = delete;
        Window(Window&&) = delete;
    private:
        ...
    };
}

Its a lot to take in right even though this is only the public stuff. Dont worry we will be going through everything in the window class.

Main Functions

bool IWindow::Window::Create(int64_t width, int64_t height, const std::string& title, int64_t x = 100, int64_t y = 100) creates the window you could use the constructor (which takes in the same args) but Create returns a false if something fails.

Example:

    IWindow::Window window{};
    if (!window.Create(...)) return -1;
    
    // Or

    // Doesn't return bool if fails
    window = IWindow::Window(...);

bool IWindow::Window::IsRunning() checks if the window is still up and running. IsRunning with return false if a window is not running (e.g. when a user clicks the x in the top of the window isRunning will return false).

Example:

while (window.IsRunning()) {
    // do stuff
}

void IWindow::Window::Update() checks for events like input or if the window moved and should be called every frame.

Example:

    while(window.isRunning()) {
        // do stuff
        
        window.Update();
    }

Getters And Setters

IVector2 IWindow::Window::GetWindowSize() returns a IWindow::IVector2. Where x is the width and y is the height. See Vector.

where x and y are width and height of the window.

void IWindow::Window::SetWindowSize(int64_t width, int64_t height) takes in 2 int64_t (64 bit integer) width, height. This function sets the width and height of the window.

void IWindow::Window::SetWindowPosition(int64_t x, int64_t x) takes in 2 int64_t (64 bit integer) x, y. This function sets the window position.

IVector2 IWindow::Window::GetWindowPosition() return a IWindow::IVector2. Where x and y are the window position. See Vector.

The void IWindow::Window::SetPosCallback, void IWindow::Window::SetKeyCallback, etc. are called when an action like moving the window or a key is pressed happens. For more info goto the Callbacks section.

Example:

#include <iostream> // For std::cout

void WindowPosCallback(IWindow::Window& window, int64_t x, int64_t y) {
    int* example = (int*)window.GetUserPointer();
    std::cout << "User Pointer: " << example << '\n'; // Output: 10
    std::cout << "Window position: " << x << ", " << y << '\n';
}

An example of a `WindowPosCallback` is
```cpp
#include <iostream> // For std::cout

void WindowPosCallback(IWindow::Window& window, int64_t x, int64_t y) {
    std::cout << "Window position: " << x << ", " << y << '\n';
}

x and y are the x and y coordinates of the window.

void IWindow::Window::SetUserPointer(void* ptr) sets an internal pointer that can be retreived in a callback or some where else.

int main() {
    ...

    int example = 10;
    // window = IWindow::Window
    window.SetUserPointer(&example);
    window.SetPosCallback(WindowPosCallback);
    ...
}

void* IWindow::Window::GetUserPointer() gets the pointer set from IWindow::Window::SetUserPointer. nullptr if no pointer is set

Input Functions

The bool IWindow::Window::IsKeyDown(IWindow::Window::Key key) and IWindow::Window::IsKeyUp(IWindow::Window::Key key) functions check if a key is pressed or is released. A key callback could be used to handle this.

bool IWindow::Window::IsMouseButtonDown(IWindow::MouseButton button) and bool IWindow::Window::IsMouseButtonUp(IWindow::MouseButton button) functions check if a argument button is pressed or released.

bool IWindow::Window::IsMouseButtonDoubleClicked(IWindow::MouseButton button) checks if a argument button was pressed 2 in quick succesion.

The templated input functions allow you to pass in multiple buttons/keys and the function checks if all the buttons/keys are pressed/released.

Example:

...
// statment will be true if the left and right buttons are pressed
// window = IWindow::Window
if (window.isMouseButtonDown(IWindow::MouseButton::Left, IWindow::MouseButton::Right)) {
    ...
}

...

Vector2 IWindow::Window::GetMouseScrollOffset() gets the scrolls offset. The offsets will be 1 when scrolling forwards and -1 when scrolling backwards (towards the user). See Vectors.

IWindow::Monitor IWindow::Window::GetPrimaryMonitor() gets the primary monitor on your computor. See Monitors.

std::vector<IWindow::Monitor> IWindow::Window::GetAllMonitors() gets all the available monitors. See Monitors.

void IWindow::Window::Center(IWindow::Monitor monitor) centers the window to the center of the monitor provided. See Monitors.

void IWindow::Window::Fullscreen(bool fullscreen, IWindow::Monitor monitor) sets the window to fullscreen mode on the monitor provided if true and if false centers the window on the monitor provided and sets the windows width and height to the width and height of the window before fullscreen. See Monitors.

bool IWindow::Window::IsFullscreen() return true if window is fullscreen and false if not.

void IWindow::Window::SetIcon(Image image) sets the window icon to an image. The image has to have 4 channels, in order RGBA, 8 bits/1 byte per channel. The image has to starts at the top left corner.

void IWindow::Window::SetCursor(Image image, uint32_t hotX, uint32_t hotY). sets the cursor to an image. The cursor has to be inside the window to show image. The image has to have 4 channels, in order RGBA, 8 bits/1 byte per channel, the image has to start at the top left corner. hotX is the location of the x location that effects cursor events. hotY is similer but it is the y location. Think of the 'Hot' location has the place cursor events take place (e.g. when you click it will be at the hot location).

void IWindow::Window::SetIcon(NativeIconID iconID) sets the icon using a native icon id. See Enum Structs.

void IWindow::Window::SetCursor(NativeCursorID cursorID) sets the cursor using a native cursor id. See Enum Structs.

Advanced Functions

IWindow::Window::NativeWindowHandle& IWindow::Window::GetNativeWindowHandle() gets the internal windowing api's window handle (e.g. Win32: HWND, X11: Window).

IWindow::Window::NativeDeviceContext& IWindow::Window::GetNativeDeviceContext() gets the internal windowing api's graphics/device context (e.g. Win32: HDC, X11: GC).

Gamepad Input In IWindow

Gamepad input is pretty simple in IWindow.

There is a seperate class for gamepad input instead of being in the window class.

IWindow can only have upto 4 gamepads connected to the whole application.

The class is IWindow::Gamepad.

namespace IWindow {
    class Gamepad {
    public:
        Gamepad() = default;
        Gamepad(GamepadID gamepadIndex);

        bool IsLeftStickInDeadzone();
        bool IsRightStickInDeadzone();

        float LeftStickX();
        float LeftStickY();
        float RightStickX();
        float RightStickY();

        float LeftTrigger(); 
        float RightTrigger();

        bool IsButtonDown(GamepadButton button);
        template <typename ... Args>
        bool IsButtonDown(GamepadButton button, Args... args) { return IsButtonDown(button) && IsButtonDown(args...); }

        bool IsButtonUp(GamepadButton button);
        template <typename ... Args>
        bool IsButtonUp(GamepadButton button, Args... args) { return IsButtonUp(button) && IsButtonUp(args...); }

        GamepadState GetState();
        GamepadID GetID();
        bool IsConnected();

        static void SetConnectedCallback(GamepadConnectedCallback callback);

        static void SetUserPointer(GamepadID gid, void* ptr);
        static void* GetUserPointer(GamepadID gid);

        // 0.0f = cancel, 1.0f max speed
        void Rumble(float leftMotor = 0.0f, float rightMotor = 0.0f);

        void Update();
    };
}

Main Functions

IWindow::Gamepad::Gamepad(IWindow::GamepadID gamepadIndex) takes in 1 argument, IWindow::GamepadID gamepadIndex. Game index is one of the 4 available controller slots.

void IWindow::Gamepad::Update() updates the gamepad state. call this every frame.

bool IWindow::Gamepad::IsLeftStickInDeadzone() and bool IsRightStickInDeadzone() checks if the controller is in the dead zone of the left/right stick.

float IWindow::Gamepad::LeftStickX() and float IWindow::Gamepad::RightStickX() gets the value of how far is the sticks x axis is to the left (-1) or right (0) on the left/right stick.

float IWindow::Gamepad::LeftStickY() and float IWindow::Gamepad::RightStickY() gets the value of how far is the sticks y axis is to the bottom (-1) or top (1) on the left/right stick.

float IWindow::Gamepad::LeftTrigger() and float IWindow::Gamepad::RightTrigger() gets the value of how much the left/right trigger is down. The functions return 0 if the left/right trigger completly up or it will return 1 if the left/right trigger completly down.

bool IWindow::Gamepad::IsButtonDown(GamepadButton button) checks if argument button is being pressed. An example of a GamepadButton is GamepadButton::A and GamepadButton::B.

bool IWindow::Gamepad::IsButtonUp(GamepadButton button) checks if argument button is not being pressed.

IWindow::GamepadID IWindow::Gamepad::GetID() gets the id return the ID that was passed to the constructor.

bool IWindow::Gamepad::IsConnected() checks if the a controller with the ID of the value passed into the constructor is connected.

static void IWindow::Gamepad::SetConnectedCallback(GamepadConnectedCallback callback) sets the connected callback. Check out Callbacks for more info.

static void IWindow::Gamepad::SetUserPointer(IWindow::GamepadID gid, void* ptr) sets a pointer that is associated to the GamepadID. static void* IWindow::Gamepad::GetUserPointer(IWindow::GamepadID gid) gets the pointer associated with the GamepadID. nullptr if no pointer is set.

void IWindow::Gamepad::Rumble(float leftMotor = 0.0f, float rightMotor = 0.0f) vibrates the gamepads motors. leftMotor controlles the left motor on the gamepad and the rightMotor controlles the right motor on the gamepad. Setting the a value to 0 will stop vibrations and setting it to 1 will set the motors to the max speed.

Advanced Functions

GamepadState IWindow::Gamepad::GetState() get the native gamepad api's state (e.g. XInput: XINPUT_STATE).

Callbacks In IWindow

A callback is a function that is called during a certain event.

Window Callbacks

Window Position Callback

The window position callback is called when the window is being moved;

  • The IWindow::Window& argument is the window that called the callback.
  • The first int64_t argument is the new x position of the window.
  • The second int64_t argument is the new y position of the window.

The internal x and y position will be automatically updated. So there is no need to call window.SetPosition on the window.

To register a window position callback, call window.SetPosCallback(callback). Where window is a IWindow::Window.

An example of a window position callback is:

void WindowPosCallback(IWindow::Window& window, int64_t x, int64_t y) {
    std::cout << "Window position: " << x << ", " << y << '\n';
}

...

// IWindow::Window
window.SetPosCallback(WindowPosCallback);

Window Size Callback

The window size callback is called when the window is being resized;

  • The IWindow::Window& argument is the window that called the callback.
  • The first int64_t argument is the new width position of the window.
  • The second int64_t argument is the new height position of the window.

The internal width and height position will be automatically updated. So there is no need to call window.SetSize on the window.

To register a window size callback, call window.SetSizeCallback(callback). Where window is a IWindow::Window.

An example of a window size callback is:

void WindowSizeCallback(IWindow::Window& window, int64_t width, int64_t height) {
    std::cout << "Window size: " << width << ", " << height << '\n';
}

...

// window is a IWindow::Window
window.SetSizeCallback(WindowPosCallback);

Input Callbacks

Key Callback

The key callback is called when a key is pressed on the key board.

  • The IWindow::Window& argument is the window that called the callback.
  • The IWindow::Key argument is the key that was pressed (e.g. Key::A, Key::B, etc.)
  • The IWindow::InputState argument says if the key was pressed or released.

to register a key callback, call window.SetKeyCallback(). Where window is a IWindow::Window.

An example of a key callback is:

void KeyCallback(IWindow::Window& window, IWindow::Key key, IWindow::InputState state) {
    std::cout << "Key " << key << " was just " << state << '\n';
}

...

window.SetKeyCallback(KeyCallback);

Mouse Button Callback

The mouse button is called when a mouse button is pressed.

  • The IWindow::Window& is the window that called the callback.
  • The IWindow::MouseButton is what button was pressed.
  • The IWindow::InputState is what input state (e.g. Down, Up) the button is in.
  • The IWindow::ClickState is what click state the button is in (e.g. single clicked, double clicked).

to register a mouse button callback, call window.SetMouseButtonCallback(). Where window is a IWindow::Window.

An example of a mouse button callback is:

void MouseButtonCallback(IWindow::Window& window, IWindow::MouseButton button, IWindow::InputState iState, IWindow::ClickState cState) {

}

...

window.SetMouseButtonCallback(MouseButtonCallback);

Mouse Move Callback

The mouse move callback is called when the cursor moves.

  • The IWindow::Window& argument is the window that called the callback.
  • The first int64_t argument is the cursors x position.
  • The second int64_t argument is the cursors y position.

to register a mouse move callback for callback, call window.SetMouseMoveCallback(). Where window is a IWindow::Window.

An example of a mouse move callback is:

void MouseMoveCallback(IWindow::Window& window, int64_t x, int64_t y) {
    std::cout << "Mouse Moved: " << x << ", " << y << '\n';
}

...

// window = IWindow::Window
window.SetMouseMoveCallback(MouseMoveCallback);

Mouse Scroll Callback

The mouse scroll callback is called when the mouse scoll wheel is scrolled.

  • The IWindow::Window& argument is the window that called the callback.
  • the first float argument is the scroll offset on the x axis.
  • the second float argument is the scroll offset on the y axis.

The offsets will be 1 when scrolling forwards and -1 when scrolling backwards (towards the user).

An example of a mouse scroll callback is:

void MouseScrollCallback(IWindow::Window& window, float xOffset, float yOffset) {
    std::cout << "xOffset: " << xOffset << ", yOffset: " << yOffset << '\n';
}

...

// window = IWindow::Window
window.SetMouseScrollCallback(MouseScrollCallback);

Gamepad Connected Callback

The gamepad connected callback is called when a gamepad is connected.

  • The IWindow::GamepadID argument is the ID of the gamepad that was connected/disconnected.
  • The bool argument is if the gamepad was connected/disconnected. Connected if true and disconnected if false.

to register a gamepad connected callback, call IWindow::Gamepad::SetConnectedCallback(). The callback will be set for every gamepad thats why its static.

An example of a gamepad connected callback is:

void GamepadConnectedCallback(IWindow::GamepadID gid, bool isConnected) {
    const char* sConnected = "";

    switch (isConnected)
    {
    case true:
        sConnected = "connected";
        break;
    case false:
        sConnected = "disconnected";
    default:
        break;
    }


    std::cout << "Gamepad " << (int)gid << " was " << sConnected << "!\n";
}

...

IWindow::Gamepad::SetConnectedCallback(GamepadConnectedCallback);

Vulkan with IWindow

IWindow needs some isntance extensions to create a VkSurfaceKHR. On Win32 the extensions are VK_KHR_WIN32_SURFACE_EXTENSION_NAME and VK_KHR_SURFACE_EXTENSION_NAME. On X11 the extensions are VK_KHR_XLIB_SURFACE_EXTENSION_NAME and VK_KHR_SURFACE_EXTENSION_NAME.

All of the classes/functions in the page is on IWindowVK.h.

To get the required extensions IWindow has a function.

void IWindow::Vk::GetRequiredInstanceExtensions(std::vector<const char*>& extentionNames)

Example:

    ...

    std::vector<const char*> iWindowExtensionNames;
    IWindow::Vk::GetRequiredInstanceExtensions(iWindowExtensionNames);

    VkInstanceCreateInfo instanceInfo{};
    instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    instanceInfo.pApplicationInfo = &appInfo;
    instanceInfo.enabledExtensionCount = (uint32_t)iWindowExtensionNames.size();
    instanceInfo.ppEnabledExtensionNames = iWindowExtensionNames.data();

    ...

IWindow will create a VkSurfaceKHR for you since its platform dependent.

VkResult IWindow::Vk::CreateSurface(VkInstance instance, VkSurfaceKHR& surface, Window& window).

This is pretty self explanitory. The function takes in an instance, a surface that will be modified and a IWindow::Window&. The function will return the output of the vk create function.

OpenGL with IWindow

IWindow will handle creating the OpenGL context with the IWindow::GL::Context class and IWindow::GL::LoadOpenGLFunction(const char* name).

everything in this page is in the IWindowGL.h header file.

// in IWindowGL.h
namespace IWindow {
    namespace GL {
        class Context {
        public:
            Context() = default;
            Context(Window& window, uint16_t majorVersion, uint16_t minorVersion);
            ~Context();
            
            bool Create(Window& window, uint16_t majorVersion, uint16_t minorVersion);

            void MakeContextNotCurrent();
            void MakeContextCurrent();

            void SwapBuffers();

            void operator=(Context&) = delete;
            Context(Context&) = delete;
        };

        void* LoadOpenGLFunction(const char* name);
    }
}

The context class is less complecated than than the window class.

Main Functions

like the window class the Context class has two ways you can create it with and the same rules that applied to the window class applies here. These two ways are the constructor and the Create function. Create already makes the OpenGL context current.

After you create the context you can then initialize any opengl function loaders like glad and glew.

Example:

    ...
    IWindow::GL::Context glContext;

    if (!glContext.Create(...)) HandleError();

    ...

    while(window.IsRunning()) {
        ...
        window.Update();
    }

    ...

void IWindow::GL::Context::MakeContextCurrent() makes the OpenGL renderering context to be current.

void IWindow::GL::Context::SwapBuffers() Swaps the front and back framebuffers should be called every frame.

Example:

    ...
    while(window.IsRunning()) {
        window.Update();
        glContext.SwapBuffers();
    }
    ...

Advanced Functions

void* IWindow::GL::LoadOpenGLFunction(const char* name) loads a function from OpenGL the dlls.

Enum structs

IWindow::NativeIconID

This is an enum struct that contains all default window icons (for platforms that support it).

IWindow::NativeCursorID

This enum struct contains all default cursors images.

Image Struct

The image struct contains 3 members. width, height, and data.

  • int32_t width is the images width.
  • int32_t width is the images height.
  • uint8_t* data is the images pixel data and has to be a unsigned char*/uint8_t*.

Vectors

IWindow::IVector2

IWindow::IVector2 contains two members.

  • int64_t x is the x coordinate in the vector.
  • int64_t y is the y coordinate in the vector.

IWindow::Vector2

Window::IVector2 contains two members.

  • float x is the x coordinate in the vector.
  • float y is the y coordinate in the vector.

Monitors

An IWindow::Monitor is a struct that represents a real monitor.

  • IWindow::IVector2 size is the size in pixels of the monitor.
  • IWindow::IVector2 position is the position in pixels of the monitor. The top left of the monitor is the position (e.g. on the primary monitor, the position of the top left corner is 0, 0).
  • std::wstring name is a name the operating system gave the monitor.