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.
Monitors Related Functions
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 aunsigned 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 is0, 0
).std::wstring name
is a name the operating system gave the monitor.