This project is part of Weaver Game Engine. It creates a window that could be toggled between fullscreen and windowed mode, support OpenGL ES 2.0 functions and can read as input the keyboard and mouse. About the mouse, you can read its position, velocity and acceleration. About the keys and buttons, you can detect when they are pressed, released and also for how much time it is being pressed or was pressed before being released.
Despite being part of the Weaver Game Engine, it can be integrated in other projects. This is only a library with two files: window.c and window.h.
This is written in Literate Programming. And the code is tested on Linux, Open BSD, Windows 10 and Web Assembly. Therefore, if you plan to study how this library works, you can read the full source code with additional text explanations in weaver-window_en.pdf (english version) or in weaver-window_en.pdf (portuguese version).
This code is licensed under GNU Affero Gneral Public Licence v. 3. See the LICENSE file.
#include "window.h"
#include <time.h>
static struct _Wkeyboard keyboard;
static struct _Wmouse mouse;
int main(int argc, char **argv){
_Wcreate_window(&keyboard, &mouse);
// Create a fullscreen window that exits if the user press the ESC key for
// 2 or more seconds.
do{
// You can call OpenGL functions here.
_Wget_window_input(time(NULL), &keyboard, &mouse);
_Wrender_window();
#if defined(__EMSCRIPTEN__)
emscripten_sleep(10);
#endif
} while(keyboard.key[W_ESC] < 2);
_Wdestroy_window();
}
To compile and use on Linux you need the X11 and OpenGL ES
libraries. In Ubuntu, for example, these library is installed with
packages libx11-dev'' and
libgles2-mesa-dev''.
By default it enables OpenGL ES 3.0 on Linux and other X11 based systems and also in web browsers after compiling with Emscripten.
On Windows, as we do not have the same guarantee about the presence of OpenGL ES, it tries to create an OpenGL 4.1 context and enables only the functions present in OpenGL ES 2.0.
To create portable programs, you should use OpenGL ES 2.0 functions, even if you are running in a system with additional functions and extensions. If compiling to Web Assembly, also read the documentation to see the additional compilation flags needed depending of your OpenGL usage.
struct _Wkeyboard{
long key[W_KEYBOARD_SIZE + 1];
};
If you have a window, you can detect keyboard interactions with this structure. You should use a single keyboard struct for each window. Each physical key is mapped to a position in the key vector. The number in each position gives information about each key state:
- If the number is 0, this means that the key is not being pressed.
- If the number is 1, this means that the user began pessing this key now.
- If the number is >1, the key is being pressed and the number is how long time the user is pressing the key.
- If the number is <0, the key was released now and the opposite of such number is how long the key was pressed before being released.
The following global variables can be used to get the position associated with each key:
int W_BACKSPACE, W_TAB, W_ENTER, W_UP, W_DOWN, W_LEFT, W_RIGHT, W_0, W_1,
W_2, W_3, W_4, W_5, W_6, W_7, W_8, W_9, W_MINUS, W_PLUS, W_F1, W_F2,
W_F3, W_F4, W_F5, W_F6, W_F7, W_F8, W_F9, W_F10, W_F11, W_F12,
W_LEFT_SHIFT, W_RIGHT_SHIFT, W_LEFT_ALT, W_RIGHT_ALT, W_LEFT_CTRL,
W_RIGHT_CTRL, W_SPACE, W_A, W_B, W_C, W_D, W_E, W_F, W_G, W_H, W_I,
W_J, W_K, W_L, W_M, W_N, W_O, W_P, W_Q, W_R, W_S, W_T, W_U, W_V, W_W,
X_X, W_Y, W_Z, W_INSERT, W_HOME, W_PAGE_UP, W_DELETE, W_END,
W_PAGE_DOWN, W_ESC, W_ANY;
The key W_ANY
represents any key. And its position stores only 0 (if
none key is being pressed) or 1 (is some key is being pressed).
struct _Wmouse{
long button[W_MOUSE_SIZE];
int x, y, dx, dy, ddx, ddy;
};
If you have a window, you can detect mouse interactions with this structure. You should use a single mouse struct for each window. Each physical button is mapped to a position in the button vector. The number in each position gives information about each key state:
- If the number is 0, this means that the button is not being pressed.
- If the number is 1, this means that the user began pessing this button now.
- If the number is >1, the button is being pressed and the number is how long time the user is pressing the button.
- If the number is <0, the button was released now and the opposite of such number is how long the button was pressed before being released.
The following macros can be used to get the position associated with
each button: W_MOUSE_LEFT
, W_MOUSE_RIGHT
, W_MOUSE_MIDDLE
,
W_MOUSE_X1
, W_MOUSE_X2
.
The variables x
and y
stores the mouse pointer position. The
variables dx
and dy
are the pointer velocity. And the variables
ddx
and ddy
are the pointer acceleration.
Mouse coordinates are measured in pixels and use the lower left corner as the origin.
bool _Wcreate_window(struct _Wkeyboard *keyboard, struct _Wmouse *mouse);
This creates a window and return if the opperation was successfull. Only 1 window can exist at a given moment, so this function will fail if called more than once before destroying the previously created window.
By default, the window will be in fullscreen mode. This can be changed
defining the macro W_WINDOW_NO_FULLSCREEN
(see below).
The mouse and keyboard structures are initialized for the created window.
bool _Wdestroy_window(void);
This closes the window and return if the operation was successful. It fails if we have no window created.
bool _Wrender_window(void);
You should call this after each iteraction of the main loop of your program. This renders in the screen any OpenGL code called in the loop.
bool _Wget_screen_resolution(int *resolution_x, int *resolution_y);
Get the screen resolution and stores in the given pointers. If you have more than one screen, this function returns what your system recognizes as the main screen. It returns if the operation was successful.
bool _Wget_window_size(int *width, int *height);
It stores in the given pointers the window size in pixels. And returns if this was done successfully.
void _Wget_window_input(unsigned long long current_time, struct _Wkeyboard *keyboard, struct _Wmouse *mouse);
Updates the mouse and keyboard data sctructures. You should call this
function in each iteration of your main loop. You should pass as
argument the current time. In the sample code above in the beginning
of this page, we chose to pass the current time in seconds as returned
by the time
function. We did so to keep the exampe simple. But in
real programs probably you want to measure the time in more
granularity than in seconds.
A good choice is measuring the time in microseconds using system libraries. This will give you more precision measuring the time spent pressing each key or button.
void _Wflush_window_input(struct _Wkeyboard *keyboard, struct _Wmouse *mouse);
This cleans the mouse and keyboard structures. You should call it
before entering a new main loop. It is also useful to call it when you
spent some time without calling the _Wget_window_input
function and
do not want to bother with all the accumulated user input.
bool _Wis_fullscreen(void);
This returns if we are in a fullscreen window.
void _Wtoggle_fullscreen(void);
This function alternates between fullscreen and windowed mode.
bool _Wresize_window(int width, int height);
Resizes the window if we are in windowed mode (do not work on the fullscreen mode). The function returns if it succeeds.
void _Wset_resize_function(void (*func)(int, int, int, int));
The argument is a pointer to a function that should be executed each
time the window size changes. Which is after calling the function
_Wresize_window
or _Wtoggle_fullscreen
.
The function gets as argument the old window width, the old window height, the new window width and the new window height in this order.
You can pass NULL if you do not want to execute any custom operation after resizing.
This function is useful if you want to keep your interface with the same proportions after resizing, for example.
Defining the following macros can change this library default behaviour:
W_DEBUG_WINDOW
: Additional messages are printed in the screen in case of error.W_WINDOW_NAME
: The window name. To be shown in the title bar.W_WINDOW_NO_FULLSCREEN
: If defined, all new windows begin in windowed mode, not in fullscreen mode.W_WINDOW_SIZE_X
: Default window width when creating a non-fullscreen window.W_WINDOW_SIZE_Y
: Default window height when creating a non-fullscreen window.W_FORCE_LANDSCAPE
: If this macro is defined and if the window height is greater than window width, we swap coordinates x and y whem measuring mouse position.