The idea is to provide a window
class that can handle events (window management, input devices) and provide either a simple renderer (see below) or a context for another graphics library in a portable way.
Libraries with the same purpose (and more):
Events are generated by the operating system and are catched by the application. In the meantime, they are put in an event queue, waiting to be processed. Generally, this queue can be accessed in two ways: polling an event in the queue (non-blocking), waiting for an event in the queue (blocking).
There can be many event types. For now, we only consider basic window events, keyboard/mouse events. So no controller, no touch events.
Generally, all event structures are put in a tagged union. For this proposal, we use std::variant
(C++17).
See also:
enum class keyboard_keycode {
// see http://wiki.libsdl.org/SDL_Scancode
};
enum class keyboard_scancode {
// see http://wiki.libsdl.org/SDL_Keycode
};
enum class keyboard_modifiers { // BitmakType
none = /* unspecfied */,
alt = /* unspecfied */,
control = /* unspecfied */,
shift = /* unspecfied */,
super = /* unspecfied */,
};
enum class mouse_button {
left,
middle,
right,
x1,
x2,
other,
};
namespace window_events {
struct resized {
vec2i size;
};
struct closed {
};
struct focus_gained {
};
struct focus_lost {
};
struct keyboard_event_data {
keyboard_keycode keycode;
keyboard_scancode scancode;
keyboard_modifiers modifiers;
};
struct keyboard_key_pressed : keyboard_event_data {
};
struct keyboard_key_repeated : keyboard_event_data {
};
struct keyboard_key_released : keyboard_event_data {
};
struct mouse_button_data {
mouse_button button;
vec2i position;
};
struct mouse_button_pressed : mouse_button_data {
};
struct mouse_button_released : mouse_button_data {
};
struct mouse_moved {
vec2i position;
};
struct mouse_wheel_scrolled {
vec2i offset;
};
struct mouse_entered {
};
struct mouse_left {
};
}
using window_event = std::variant<
window_events::resized,
window_events::closed,
window_events::focus_gained,
window_events::focus_lost,
window_events::keyboard_key_pressed,
window_events::keyboard_key_repeated,
window_events::keyboard_key_released,
window_events::mouse_button_pressed,
window_events::mouse_button_released,
window_events::mouse_moved,
window_events::mouse_wheel_scrolled,
window_events::mouse_entered,
window_events::mouse_left
>;
A window is provided by the operating system and can handle events. It can also create a renderer. A window is created with a title, a size and some properties (resizable, visible, decorated) that can be changed later.
The close()
method indicates that the user intends to close the window. The real shutdown of the window happens in the destructor. This is done to simplify the window lifetime which is the same as the object lifetime.
Event handling uses std::optional<window_event>
. A std::nullopt
value indicates that no event is available.
See also:
enum class window_flags { // BitmakType
none = /* unspecfied */,
resizable = /* unspecfied */,
visible = /* unspecfied */,
decorated = /* unspecfied */,
fullscreen = /* unspecfied */,
};
inline constexpr window_flags default_window_flags = /* unspecfied */;
class window {
public:
window(std::string_view title, vec2i size, window_flags hints = default_window_flags);
~window();
window(const window&) = delete;
window& operator=(const window&) = delete;
window(window&& other);
window& operator=(window&& other);
// lifetime
bool is_open() const;
void close();
// properties
std::string_view get_title() const;
void set_title(std::string_view title);
vec2i get_position() const;
void set_position(vec2i position);
vec2i get_size() const;
void set_size(vec2i size);
window_flags get_flags() const;
void set_resizable(bool resizable = true);
void set_visible(bool visible = true);
void set_decorated(bool decorated = true);
void set_fullscreen(bool fullscreen = true);
// state
bool is_minimized() const;
void minimize();
bool is_maximized() const;
void mazimize();
void restore();
// events
std::optional<window_event> poll_event();
std::optional<window_event> wait_event();
// renderer
renderer get_renderer();
};
The idea is to provide a very simple renderer. Simple means that you can only draw:
- rectangles (filled or not)
- circles (filled or not)
With a std::span
type, it would be easy to also handle:
- list of rectangles
- list of circles
- list of points to draw a line
No special types are provided for rectangles and circles.
The renderer makes a difference between a position on the screen (vec2i
in pixels) and coordinates in the world (vec2f
in arbitrary dimensions). To translate from coordinates to position, a view is defined by the center of the view and the size of the view that should be displayed on the screen.
See also:
class renderer {
public:
~renderer();
vec2i get_size();
void set_view_center(vec2f center);
vec2f get_view_center() const;
void set_view_size(vec2f size);
vec2f get_view_size() const;
vec2f get_coords_from_position(vec2i position);
void clear(color4f color);
void fill_rectangle(vec2f coords, vec2f size, color4f color);
void draw_rectangle(vec2f coords, vec2f size, color4f color);
void fill_circle(vec2f center, float radius, color4f color);
void draw_circle(vec2f center, float radius, color4f color);
void display();
private:
renderer(/* implementation defined */);
};