Skip to content

Window Module

The Window module manages the application window, OpenGL context, and user input handling. It provides the foundation for rendering and user interaction in Astra applications.

Purpose

The Window module provides:

  • Window creation and management
  • OpenGL context initialization via GLFW and GLAD
  • Input event handling (keyboard and mouse movement)
  • Frame timing via the Time singleton
  • Window state management (open/close, resize, headless mode)

Key Components

Window Class

The core Window class (namespace astralix) manages the application window and its properties.

Key Methods:

  • create(id, title, width, height, headless) - Static factory; creates and returns a Ref<Window>
  • start() - Creates the GLFW window, initializes the OpenGL context (GLAD), and registers GLFW callbacks
  • update() - Polls GLFW events and processes pending key releases
  • swap() - Resets mouse delta, destroys released-key state, and calls glfwSwapBuffers
  • width() / height() - Return current window dimensions as int
  • id() - Returns the WindowID for this window
  • title() - Returns the window title string
  • is_open() - Returns true while the GLFW window should not close
  • close() - Terminates GLFW
  • value() - Returns the underlying GLFWwindow*
  • mouse() - Returns Ref<input::Mouse> for the window's mouse state
  • keyboard() - Returns Ref<input::Keyboard> for the window's keyboard state

Constructor parameters:

cpp
Window(WindowID& id, std::string& title, int& width, int& height, bool headless);

The headless flag suppresses GLFW window visibility and skips input callbacks, which is useful for headless/testing modes.

WindowManager

WindowManager is a singleton (extends BaseManager<WindowManager>) that tracks all windows and provides the concept of an active window. Access it via the inline helper window_manager().

Key Methods:

  • start() / update() / swap() / end() - Lifecycle forwarding to all managed windows
  • is_open() - Returns true while the active window is open
  • load_window(window) / load_windows({...}) - Register one or more windows
  • get_window_by_id(id) - Look up a window by WindowID
  • active_window() - Returns Ref<Window> for the currently active window
  • set_active_window_by_id(id) - Switch the active window
  • resize(width, height) - Resize the active window

Convenience macros:

cpp
ACTIVE_WINDOW_WIDTH()   // active window pixel width
ACTIVE_WINDOW_HEIGHT()  // active window pixel height

Input System

Input is exposed through free functions in the astralix::input namespace, all operating on the active window via window_manager(). There is no static Input class.

Keyboard:

cpp
input::IS_KEY_DOWN(input::KeyCode keycode)      // true while key is held
input::IS_KEY_RELEASED(input::KeyCode keycode)  // true on the frame the key was released

Mouse:

cpp
input::MOUSE_DELTA()      // returns Mouse::Position{double x, double y} - accumulated delta since last swap
input::HAS_MOUSE_MOVED()  // true if mouse moved since last swap
input::SET_MOUSE_POSITION(Mouse::Position& pos) // manually set mouse position

Mouse button state and scroll are not tracked by this module.

Time Class

Frame timing is provided by the Time singleton, separate from the Window class.

cpp
Time::init();                        // must be called once before use
Time* t = Time::get();

t->update();                         // call each frame (uses glfwGetTime() internally)
float dt  = t->get_deltatime();      // seconds elapsed since last update()
float now = t->get_current_time();   // current time in seconds

There is no GetFrameCount() method and no frame-count tracking.

Architecture

GLFW Integration

Astra uses GLFW for cross-platform window management and OpenGL context creation:

cpp
// In namespace astralix
class Window {
public:
    static Ref<Window> create(WindowID& id, std::string& title,
                              int& width, int& height, bool headless);
    void start();   // creates GLFW window + initializes OpenGL via GLAD
    void update();  // glfwPollEvents + key release processing
    void swap();    // mouse delta reset + glfwSwapBuffers

    GLFWwindow* value() const noexcept;
    int width()  const noexcept;
    int height() const noexcept;
    // ...
private:
    GLFWwindow* m_value;
    int m_width, m_height;
    std::string m_title;
    WindowID m_id;
    Ref<input::Keyboard> m_keyboard;
    Ref<input::Mouse> m_mouse;
    bool m_headless;
};

The OpenGL context is initialized directly inside Window::start() via gladLoadGLLoader. There is no separate GraphicsContext abstraction in this module.

Event System

The Window module dispatches events through the engine-level EventDispatcher and EventScheduler. There is no SetEventCallback method on Window.

Actual event types defined in this module:

Event classNamespaceFields
KeyPressedEventastralix::inputKeyCode key_code, WindowID window_id
KeyReleasedEventastralix::inputKeyCode key_code, WindowID window_id
MouseEventastralixdouble x, double y (absolute cursor position)

MouseEvent is fired only when the cursor moves and the mouse is not in "view mode" (cursor captured). Mouse button press/release and scroll events are not implemented.

Window resize is handled internally by a GLFW framebuffer-size callback (Window::resizing) that updates m_width/m_height and calls glViewport. There is no WindowResizeEvent or WindowCloseEvent class.

Key Features

Window Creation

cpp
// Create a window
WindowID id = /* your GUID */;
std::string title = "Astra Game";
int width = 1920, height = 1080;

Ref<Window> window = Window::create(id, title, width, height, /*headless=*/false);

// Register with the manager and make it active
window_manager()->load_window(window);
window_manager()->set_active_window_by_id(id);

// Initialize the GLFW window and OpenGL context
window->start();

Keyboard Input Polling

Check key state each frame using the free-function API:

cpp
void update() {
    if (input::IS_KEY_DOWN(input::KeyCode::W)) {
        MoveForward();
    }
    if (input::IS_KEY_DOWN(input::KeyCode::S)) {
        MoveBackward();
    }
    if (input::IS_KEY_RELEASED(input::KeyCode::Escape)) {
        // Key was released this frame
    }
}

Mouse Input Polling

Retrieve mouse movement delta each frame:

cpp
void update() {
    if (input::HAS_MOUSE_MOVED()) {
        auto delta = input::MOUSE_DELTA(); // Mouse::Position{double x, double y}
        RotateCamera(delta.x, delta.y);
    }
}

Frame Timing

cpp
// Initialize once at startup
Time::init();

// Each frame
Time::get()->update();
float deltaTime = Time::get()->get_deltatime();
float elapsed   = Time::get()->get_current_time();

Cursor Capture

The Window automatically toggles cursor capture when the Escape key is released. This is handled internally by Window::toggle_view_mouse. There is no public SetCursorMode API; cursor behavior is controlled via this built-in Escape-key toggle.

While the cursor is captured (GLFW_CURSOR_DISABLED), mouse events are suppressed. When capture is released (GLFW_CURSOR_NORMAL), MouseEvent is fired on cursor movement.

VSync

VSync is not configurable through the Window API. GLFW swap-interval control is not exposed; buffer swapping is performed unconditionally in Window::swap().

Integration with Other Modules

With the Application/Engine Loop

cpp
// Startup
Time::init();
window_manager()->load_window(Window::create(id, title, width, height, false));
window_manager()->set_active_window_by_id(id);
window_manager()->active_window()->start();

// Main loop
while (window_manager()->is_open()) {
    Time::get()->update();
    float deltaTime = Time::get()->get_deltatime();

    window_manager()->update();  // poll events, release keys

    // Game/editor update logic
    OnUpdate(deltaTime);

    // Render ...

    window_manager()->swap();    // reset mouse delta, swap buffers
}

With the Editor Module

The Editor (class Editor, not EditorLayer) listens to input::KeyPressedEvent and MouseEvent through the EventDispatcher, relying on the same IS_KEY_DOWN / MOUSE_DELTA functions for camera and viewport interaction.

Input Events

Keyboard Events

cpp
// astralix::input namespace
class KeyPressedEvent : public Event {
    KeyCode  key_code;
    WindowID window_id;
};

class KeyReleasedEvent : public Event {
    KeyCode  key_code;
    WindowID window_id;
};

Key events are scheduled via EventScheduler with SchedulerType::POST_FRAME.

Mouse Events

cpp
// astralix namespace
struct MouseEvent : public Event {
    double x;  // absolute cursor x
    double y;  // absolute cursor y
};

Mouse button pressed/released and scroll events are not implemented in this module.

Key Codes

Key codes are defined as a scoped enum in astralix::input::KeyCode. Values match GLFW constants directly:

cpp
namespace astralix::input {
    enum class KeyCode : int {
        Space = 32,
        A = 65, B = 66, /* ... */ W = 87, /* ... */ Z = 90,
        Escape = 256,
        Left = 263, Up = 265, Down = 264, Right = 262,
        LeftShift = 340, LeftControl = 341, LeftAlt = 342,
        // ... full set mirrors glfw3.h
    };
}

Usage:

cpp
input::IS_KEY_DOWN(input::KeyCode::W)
input::IS_KEY_RELEASED(input::KeyCode::Escape)

There is no Mouse::Button* namespace or mouse button state tracking in this module.

File Locations

src/modules/window/
├── window.hpp              # Window class declaration
├── window.cpp              # Window implementation
├── time.hpp                # Time singleton declaration
├── time.cpp                # Time singleton implementation
├── events/
│   ├── key-codes.hpp       # KeyCode enum class
│   ├── key-event.hpp       # KeyPressedEvent, KeyReleasedEvent, KeyboardListener
│   ├── keyboard.hpp        # Keyboard class (key state tracking)
│   ├── keyboard.cpp
│   ├── mouse.hpp           # Mouse class (position delta tracking)
│   ├── mouse.cpp
│   ├── mouse-event.hpp     # MouseEvent struct
│   └── mouse-listener.hpp  # MouseListener class
└── managers/
    ├── window-manager.hpp  # WindowManager singleton + input free functions
    └── window-manager.cpp
  • Application - Creates and manages the Window lifecycle
  • Engine - Receives input for game systems
  • Editor - Uses window input for editor UI

Best Practices

  1. Single Active Window: Use WindowManager to track windows and set one as active before calling input free functions
  2. Call start() after registration: Register the window with WindowManager before calling Window::start() so the manager can resolve windows during GLFW callbacks
  3. Update Time each frame: Call Time::get()->update() at the top of the game loop before querying delta time
  4. Handle resize via GLFW callback: Window resize is handled internally; m_width/m_height are always up to date after update()
  5. Mouse delta is frame-scoped: MOUSE_DELTA() accumulates between swap() calls; always read it before calling window_manager()->swap()

Performance Considerations

  • IS_KEY_DOWN / IS_KEY_RELEASED are O(1) hash-map lookups on the Keyboard object
  • Mouse delta is reset to zero every swap() call - always consume it before swapping
  • Time::get()->update() uses glfwGetTime() which is a thin OS timer query
  • There is no built-in frame-rate cap; add VSync or a sleep if needed at the application level

Common Patterns

Input Handling in a Player Controller

cpp
class PlayerController {
    void ProcessInput(float deltaTime) {
        glm::vec3 movement(0.0f);

        if (input::IS_KEY_DOWN(input::KeyCode::W))
            movement.z += 1.0f;
        if (input::IS_KEY_DOWN(input::KeyCode::S))
            movement.z -= 1.0f;
        if (input::IS_KEY_DOWN(input::KeyCode::A))
            movement.x -= 1.0f;
        if (input::IS_KEY_DOWN(input::KeyCode::D))
            movement.x += 1.0f;

        if (glm::length(movement) > 0.0f) {
            movement = glm::normalize(movement);
            MovePlayer(movement * speed * deltaTime);
        }
    }
};

Camera Rotation from Mouse Delta

cpp
void UpdateCamera(float deltaTime) {
    if (input::HAS_MOUSE_MOVED()) {
        auto delta = input::MOUSE_DELTA();
        camera.Yaw   += static_cast<float>(delta.x) * sensitivity;
        camera.Pitch += static_cast<float>(delta.y) * sensitivity;
    }
}

Headless Mode

cpp
// Create a window that is invisible and has no input callbacks
WindowID id = /* guid */;
std::string title = "headless";
int w = 1, h = 1;
auto window = Window::create(id, title, w, h, /*headless=*/true);
window_manager()->load_window(window);
window_manager()->set_active_window_by_id(id);
window->start();