mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-15 00:11:55 +07:00
Merge pull request #6 from enduro2d/feature/glfw_input
Feature/glfw input
This commit is contained in:
@@ -5,6 +5,10 @@ project(enduro2d)
|
||||
# build mode
|
||||
#
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif(NOT CMAKE_BUILD_TYPE)
|
||||
|
||||
foreach(flags CMAKE_C_FLAGS_DEBUG
|
||||
CMAKE_CXX_FLAGS_DEBUG)
|
||||
set(${flags} "${${flags}} -D_DEBUG -DNRELEASE")
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
```
|
||||
levels, sinks
|
||||
```
|
||||
- [ ] `basic input system`
|
||||
- [x] `basic input system`
|
||||
```
|
||||
events, listeners, polling
|
||||
devices: mouse, keyboard
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
#include "_core.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
#include "input.hpp"
|
||||
#include "vfs.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
namespace e2d
|
||||
{
|
||||
class debug;
|
||||
class mouse;
|
||||
class keyboard;
|
||||
class input;
|
||||
class vfs;
|
||||
class window;
|
||||
}
|
||||
@@ -24,3 +27,71 @@ namespace e2d
|
||||
return modules::instance<ModuleT>();
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
enum class mouse_button : u8 {
|
||||
left,
|
||||
right,
|
||||
middle,
|
||||
x1,
|
||||
x2,
|
||||
unknown
|
||||
};
|
||||
|
||||
enum class keyboard_key : u16 {
|
||||
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9,
|
||||
|
||||
a, b, c, d, e, f, g, h, i, j, k, l, m,
|
||||
n, o, p, q, r, s, t, u, v, w, x, y, z,
|
||||
|
||||
f1, f2, f3, f4, f5,
|
||||
f6, f7, f8, f9, f10,
|
||||
f11, f12, f13, f14, f15,
|
||||
f16, f17, f18, f19, f20,
|
||||
f21, f22, f23, f24, f25,
|
||||
|
||||
minus, equal, backspace, section_sign, grave_accent,
|
||||
|
||||
lbracket, rbracket, semicolon, apostrophe, backslash,
|
||||
|
||||
comma, period, slash,
|
||||
|
||||
escape, tab, caps_lock, space, enter,
|
||||
|
||||
lshift, rshift, lcontrol, rcontrol,
|
||||
lalt, ralt, lsuper, rsuper, menu,
|
||||
|
||||
print_screen, scroll_lock, pause,
|
||||
|
||||
insert, del, home, end, page_up, page_down,
|
||||
|
||||
left, up, right, down,
|
||||
|
||||
kp_0, kp_1, kp_2, kp_3, kp_4,
|
||||
kp_5, kp_6, kp_7, kp_8, kp_9,
|
||||
|
||||
kp_num_lock, kp_divide, kp_multiply, kp_subtract,
|
||||
kp_add, kp_enter, kp_equal, kp_decimal,
|
||||
|
||||
unknown
|
||||
};
|
||||
|
||||
enum class mouse_button_action : u8 {
|
||||
press,
|
||||
release,
|
||||
unknown
|
||||
};
|
||||
|
||||
enum class keyboard_key_action : u8 {
|
||||
press,
|
||||
repeat,
|
||||
release,
|
||||
unknown
|
||||
};
|
||||
|
||||
const char* mouse_button_to_cstr(mouse_button btn) noexcept;
|
||||
const char* keyboard_key_to_cstr(keyboard_key key) noexcept;
|
||||
const char* mouse_button_action_to_cstr(mouse_button_action action) noexcept;
|
||||
const char* keyboard_key_action_to_cstr(keyboard_key_action action) noexcept;
|
||||
}
|
||||
|
||||
126
headers/enduro2d/core/input.hpp
Normal file
126
headers/enduro2d/core/input.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_core.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class mouse final : private noncopyable {
|
||||
public:
|
||||
mouse();
|
||||
~mouse() noexcept;
|
||||
|
||||
v2f cursor_pos() const noexcept;
|
||||
v2f scroll_delta() const noexcept;
|
||||
|
||||
bool is_any_button_pressed() const noexcept;
|
||||
bool is_any_button_just_pressed() const noexcept;
|
||||
bool is_any_button_just_released() const noexcept;
|
||||
|
||||
bool is_button_pressed(mouse_button btn) const noexcept;
|
||||
bool is_button_just_pressed(mouse_button btn) const noexcept;
|
||||
bool is_button_just_released(mouse_button btn) const noexcept;
|
||||
|
||||
void extract_pressed_buttons(std::vector<mouse_button>& dst) const;
|
||||
void extract_just_pressed_buttons(std::vector<mouse_button>& dst) const;
|
||||
void extract_just_released_buttons(std::vector<mouse_button>& dst) const;
|
||||
private:
|
||||
class state;
|
||||
friend class input;
|
||||
std::unique_ptr<state> state_;
|
||||
};
|
||||
|
||||
class keyboard final : private noncopyable {
|
||||
public:
|
||||
keyboard();
|
||||
~keyboard() noexcept;
|
||||
|
||||
str32 input_text() const;
|
||||
void extract_input_text(str32& dst) const;
|
||||
|
||||
bool is_any_key_pressed() const noexcept;
|
||||
bool is_any_key_just_pressed() const noexcept;
|
||||
bool is_any_key_just_released() const noexcept;
|
||||
|
||||
bool is_key_pressed(keyboard_key key) const noexcept;
|
||||
bool is_key_just_pressed(keyboard_key key) const noexcept;
|
||||
bool is_key_just_released(keyboard_key key) const noexcept;
|
||||
|
||||
void extract_pressed_keys(std::vector<keyboard_key>& dst) const;
|
||||
void extract_just_pressed_keys(std::vector<keyboard_key>& dst) const;
|
||||
void extract_just_released_keys(std::vector<keyboard_key>& dst) const;
|
||||
private:
|
||||
class state;
|
||||
friend class input;
|
||||
std::unique_ptr<state> state_;
|
||||
};
|
||||
|
||||
class input final : public module<input> {
|
||||
public:
|
||||
struct input_char_event {
|
||||
char32_t uchar = 0;
|
||||
input_char_event(char32_t uchar)
|
||||
: uchar(uchar) {}
|
||||
};
|
||||
struct move_cursor_event {
|
||||
v2f pos = v2f::zero();
|
||||
move_cursor_event(const v2f& pos)
|
||||
: pos(pos) {}
|
||||
};
|
||||
struct mouse_scroll_event {
|
||||
v2f delta = v2f::zero();
|
||||
mouse_scroll_event(const v2f& delta)
|
||||
: delta(delta) {}
|
||||
};
|
||||
struct mouse_button_event {
|
||||
mouse_button button = mouse_button::unknown;
|
||||
mouse_button_action action = mouse_button_action::unknown;
|
||||
mouse_button_event(mouse_button btn, mouse_button_action act)
|
||||
: button(btn)
|
||||
, action(act) {}
|
||||
|
||||
};
|
||||
struct keyboard_key_event {
|
||||
keyboard_key key = keyboard_key::unknown;
|
||||
keyboard_key_action action = keyboard_key_action::unknown;
|
||||
keyboard_key_event(keyboard_key key, keyboard_key_action act)
|
||||
: key(key)
|
||||
, action(act) {}
|
||||
};
|
||||
public:
|
||||
input();
|
||||
~input() noexcept;
|
||||
|
||||
const class mouse& mouse() const noexcept;
|
||||
const class keyboard& keyboard() const noexcept;
|
||||
|
||||
void post_event(input_char_event evt) noexcept;
|
||||
void post_event(move_cursor_event evt) noexcept;
|
||||
void post_event(mouse_scroll_event evt) noexcept;
|
||||
void post_event(mouse_button_event evt) noexcept;
|
||||
void post_event(keyboard_key_event evt) noexcept;
|
||||
|
||||
void frame_tick() noexcept;
|
||||
private:
|
||||
class state;
|
||||
std::unique_ptr<state> state_;
|
||||
};
|
||||
|
||||
class window_input_source : public window::event_listener {
|
||||
public:
|
||||
window_input_source(input& input) noexcept;
|
||||
void on_input_char(char32_t uchar) noexcept final;
|
||||
void on_move_cursor(const v2f& pos) noexcept final;
|
||||
void on_mouse_scroll(const v2f& delta) noexcept final;
|
||||
void on_mouse_button(mouse_button btn, mouse_button_action act) noexcept final;
|
||||
void on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept final;
|
||||
private:
|
||||
input& input_;
|
||||
};
|
||||
}
|
||||
@@ -35,10 +35,10 @@ namespace e2d
|
||||
template < typename T, typename... Args >
|
||||
bool register_scheme(str_view scheme, Args&&... args);
|
||||
bool register_scheme(str_view scheme, file_source_uptr source);
|
||||
bool unregister_scheme(str_view scheme) noexcept;
|
||||
bool unregister_scheme(str_view scheme);
|
||||
|
||||
bool register_scheme_alias(str_view scheme, url alias);
|
||||
bool unregister_scheme_alias(str_view scheme) noexcept;
|
||||
bool unregister_scheme_alias(str_view scheme);
|
||||
|
||||
bool exists(const url& url) const;
|
||||
input_stream_uptr open(const url& url) const;
|
||||
|
||||
@@ -18,6 +18,20 @@ namespace e2d
|
||||
};
|
||||
|
||||
class window final : public module<window> {
|
||||
public:
|
||||
class event_listener : private e2d::noncopyable {
|
||||
public:
|
||||
virtual ~event_listener() noexcept = default;
|
||||
virtual void on_input_char(char32_t uchar) noexcept;
|
||||
virtual void on_move_cursor(const v2f& pos) noexcept;
|
||||
virtual void on_mouse_scroll(const v2f& delta) noexcept;
|
||||
virtual void on_mouse_button(mouse_button btn, mouse_button_action act) noexcept;
|
||||
virtual void on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept;
|
||||
virtual void on_window_close() noexcept;
|
||||
virtual void on_window_focus(bool focused) noexcept;
|
||||
virtual void on_window_minimize(bool minimized) noexcept;
|
||||
};
|
||||
using event_listener_uptr = std::unique_ptr<event_listener>;
|
||||
public:
|
||||
window(const v2u& size, str_view title, bool vsync, bool fullscreen);
|
||||
~window() noexcept;
|
||||
@@ -37,6 +51,10 @@ namespace e2d
|
||||
bool toggle_vsync(bool yesno) noexcept;
|
||||
bool toggle_fullscreen(bool yesno) noexcept;
|
||||
|
||||
void hide_cursor() noexcept;
|
||||
void show_cursor() noexcept;
|
||||
bool is_cursor_hidden() const noexcept;
|
||||
|
||||
v2u real_size() const noexcept;
|
||||
v2u virtual_size() const noexcept;
|
||||
v2u framebuffer_size() const noexcept;
|
||||
@@ -48,9 +66,38 @@ namespace e2d
|
||||
void set_should_close(bool yesno) noexcept;
|
||||
|
||||
void swap_buffers() noexcept;
|
||||
static bool poll_events() noexcept;
|
||||
static bool frame_tick() noexcept;
|
||||
|
||||
template < typename T, typename... Args >
|
||||
T& register_event_listener(Args&&... args);
|
||||
event_listener& register_event_listener(event_listener_uptr listener);
|
||||
void unregister_event_listener(const event_listener& listener) noexcept;
|
||||
private:
|
||||
class state;
|
||||
std::unique_ptr<state> state_;
|
||||
};
|
||||
|
||||
class window_trace_event_listener final : public window::event_listener {
|
||||
public:
|
||||
window_trace_event_listener(debug& debug) noexcept;
|
||||
void on_input_char(char32_t uchar) noexcept final;
|
||||
void on_move_cursor(const v2f& pos) noexcept final;
|
||||
void on_mouse_scroll(const v2f& delta) noexcept final;
|
||||
void on_mouse_button(mouse_button btn, mouse_button_action act) noexcept final;
|
||||
void on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept final;
|
||||
void on_window_close() noexcept final;
|
||||
void on_window_focus(bool focused) noexcept final;
|
||||
void on_window_minimize(bool minimized) noexcept final;
|
||||
private:
|
||||
debug& debug_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename T, typename... Args >
|
||||
T& window::register_event_listener(Args&&... args) {
|
||||
return static_cast<T&>(
|
||||
register_event_listener(std::make_unique<T>(std::forward<Args>(args)...)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,23 @@
|
||||
using namespace e2d;
|
||||
|
||||
int main() {
|
||||
modules::initialize<debug>()
|
||||
.add_sink<debug_console_sink>();
|
||||
|
||||
input& i = modules::initialize<input>();
|
||||
debug& d = modules::initialize<debug>();
|
||||
window& w = modules::initialize<window>(
|
||||
v2u{640, 480}, "Enduro2D", false, false);
|
||||
v2u{640, 480}, "Enduro2D", true, false);
|
||||
|
||||
the<debug>()
|
||||
.trace("SAMPLE: window real size: %0", w.real_size())
|
||||
.trace("SAMPLE: window virtual size: %0", w.virtual_size())
|
||||
.trace("SAMPLE: window framebuffer size: %0", w.framebuffer_size());
|
||||
d.add_sink<debug_console_sink>();
|
||||
w.register_event_listener<window_input_source>(i);
|
||||
w.register_event_listener<window_trace_event_listener>(d);
|
||||
|
||||
auto closing_time = time::now_s() + make_seconds<i64>(5);
|
||||
while ( !w.should_close() && time::now_s() < closing_time ) {
|
||||
d.trace("SAMPLE: window real size: %0", w.real_size())
|
||||
.trace("SAMPLE: window virtual size: %0", w.virtual_size())
|
||||
.trace("SAMPLE: window framebuffer size: %0", w.framebuffer_size());
|
||||
|
||||
const keyboard& k = i.keyboard();
|
||||
while ( !w.should_close() && !k.is_key_just_released(keyboard_key::escape) ) {
|
||||
i.frame_tick();
|
||||
w.swap_buffers();
|
||||
window::poll_events();
|
||||
window::frame_tick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in enduro2d.hpp
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "../common.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
int main() {
|
||||
input& i = modules::initialize<input>();
|
||||
debug& d = modules::initialize<debug>();
|
||||
window& w = modules::initialize<window>(
|
||||
v2u{640, 480}, "Enduro2D", true, false);
|
||||
|
||||
d.add_sink<debug_console_sink>();
|
||||
w.register_event_listener<window_input_source>(i);
|
||||
w.register_event_listener<window_trace_event_listener>(d);
|
||||
|
||||
d.trace("SAMPLE: window real size: %0", w.real_size())
|
||||
.trace("SAMPLE: window virtual size: %0", w.virtual_size())
|
||||
.trace("SAMPLE: window framebuffer size: %0", w.framebuffer_size());
|
||||
|
||||
const keyboard& k = i.keyboard();
|
||||
while ( !w.should_close() && !k.is_key_just_released(keyboard_key::escape) ) {
|
||||
i.frame_tick();
|
||||
w.swap_buffers();
|
||||
window::frame_tick();
|
||||
}
|
||||
}
|
||||
|
||||
196
sources/enduro2d/core/_core.cpp
Normal file
196
sources/enduro2d/core/_core.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/core/_core.hpp>
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
const char* mouse_button_to_cstr(mouse_button btn) noexcept {
|
||||
#define DEFINE_CASE(x) case mouse_button::x: return #x
|
||||
switch ( btn ) {
|
||||
DEFINE_CASE(left);
|
||||
DEFINE_CASE(right);
|
||||
DEFINE_CASE(middle);
|
||||
DEFINE_CASE(x1);
|
||||
DEFINE_CASE(x2);
|
||||
DEFINE_CASE(unknown);
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected mouse button");
|
||||
return "";
|
||||
}
|
||||
#undef DEFINE_CASE
|
||||
}
|
||||
|
||||
const char* keyboard_key_to_cstr(keyboard_key key) noexcept {
|
||||
#define DEFINE_CASE(x) case keyboard_key::x: return #x
|
||||
switch ( key ) {
|
||||
DEFINE_CASE(_0);
|
||||
DEFINE_CASE(_1);
|
||||
DEFINE_CASE(_2);
|
||||
DEFINE_CASE(_3);
|
||||
DEFINE_CASE(_4);
|
||||
DEFINE_CASE(_5);
|
||||
DEFINE_CASE(_6);
|
||||
DEFINE_CASE(_7);
|
||||
DEFINE_CASE(_8);
|
||||
DEFINE_CASE(_9);
|
||||
|
||||
DEFINE_CASE(a);
|
||||
DEFINE_CASE(b);
|
||||
DEFINE_CASE(c);
|
||||
DEFINE_CASE(d);
|
||||
DEFINE_CASE(e);
|
||||
DEFINE_CASE(f);
|
||||
DEFINE_CASE(g);
|
||||
DEFINE_CASE(h);
|
||||
DEFINE_CASE(i);
|
||||
DEFINE_CASE(j);
|
||||
DEFINE_CASE(k);
|
||||
DEFINE_CASE(l);
|
||||
DEFINE_CASE(m);
|
||||
DEFINE_CASE(n);
|
||||
DEFINE_CASE(o);
|
||||
DEFINE_CASE(p);
|
||||
DEFINE_CASE(q);
|
||||
DEFINE_CASE(r);
|
||||
DEFINE_CASE(s);
|
||||
DEFINE_CASE(t);
|
||||
DEFINE_CASE(u);
|
||||
DEFINE_CASE(v);
|
||||
DEFINE_CASE(w);
|
||||
DEFINE_CASE(x);
|
||||
DEFINE_CASE(y);
|
||||
DEFINE_CASE(z);
|
||||
|
||||
DEFINE_CASE(f1);
|
||||
DEFINE_CASE(f2);
|
||||
DEFINE_CASE(f3);
|
||||
DEFINE_CASE(f4);
|
||||
DEFINE_CASE(f5);
|
||||
DEFINE_CASE(f6);
|
||||
DEFINE_CASE(f7);
|
||||
DEFINE_CASE(f8);
|
||||
DEFINE_CASE(f9);
|
||||
DEFINE_CASE(f10);
|
||||
DEFINE_CASE(f11);
|
||||
DEFINE_CASE(f12);
|
||||
DEFINE_CASE(f13);
|
||||
DEFINE_CASE(f14);
|
||||
DEFINE_CASE(f15);
|
||||
DEFINE_CASE(f16);
|
||||
DEFINE_CASE(f17);
|
||||
DEFINE_CASE(f18);
|
||||
DEFINE_CASE(f19);
|
||||
DEFINE_CASE(f20);
|
||||
DEFINE_CASE(f21);
|
||||
DEFINE_CASE(f22);
|
||||
DEFINE_CASE(f23);
|
||||
DEFINE_CASE(f24);
|
||||
DEFINE_CASE(f25);
|
||||
|
||||
DEFINE_CASE(minus);
|
||||
DEFINE_CASE(equal);
|
||||
DEFINE_CASE(backspace);
|
||||
DEFINE_CASE(section_sign);
|
||||
DEFINE_CASE(grave_accent);
|
||||
|
||||
DEFINE_CASE(lbracket);
|
||||
DEFINE_CASE(rbracket);
|
||||
DEFINE_CASE(semicolon);
|
||||
DEFINE_CASE(apostrophe);
|
||||
DEFINE_CASE(backslash);
|
||||
|
||||
DEFINE_CASE(comma);
|
||||
DEFINE_CASE(period);
|
||||
DEFINE_CASE(slash);
|
||||
|
||||
DEFINE_CASE(escape);
|
||||
DEFINE_CASE(tab);
|
||||
DEFINE_CASE(caps_lock);
|
||||
DEFINE_CASE(space);
|
||||
DEFINE_CASE(enter);
|
||||
|
||||
DEFINE_CASE(lshift);
|
||||
DEFINE_CASE(rshift);
|
||||
DEFINE_CASE(lcontrol);
|
||||
DEFINE_CASE(rcontrol);
|
||||
DEFINE_CASE(lalt);
|
||||
DEFINE_CASE(ralt);
|
||||
DEFINE_CASE(lsuper);
|
||||
DEFINE_CASE(rsuper);
|
||||
DEFINE_CASE(menu);
|
||||
|
||||
DEFINE_CASE(print_screen);
|
||||
DEFINE_CASE(scroll_lock);
|
||||
DEFINE_CASE(pause);
|
||||
|
||||
DEFINE_CASE(insert);
|
||||
DEFINE_CASE(del);
|
||||
DEFINE_CASE(home);
|
||||
DEFINE_CASE(end);
|
||||
DEFINE_CASE(page_up);
|
||||
DEFINE_CASE(page_down);
|
||||
|
||||
DEFINE_CASE(left);
|
||||
DEFINE_CASE(up);
|
||||
DEFINE_CASE(right);
|
||||
DEFINE_CASE(down);
|
||||
|
||||
DEFINE_CASE(kp_0);
|
||||
DEFINE_CASE(kp_1);
|
||||
DEFINE_CASE(kp_2);
|
||||
DEFINE_CASE(kp_3);
|
||||
DEFINE_CASE(kp_4);
|
||||
DEFINE_CASE(kp_5);
|
||||
DEFINE_CASE(kp_6);
|
||||
DEFINE_CASE(kp_7);
|
||||
DEFINE_CASE(kp_8);
|
||||
DEFINE_CASE(kp_9);
|
||||
|
||||
DEFINE_CASE(kp_num_lock);
|
||||
DEFINE_CASE(kp_divide);
|
||||
DEFINE_CASE(kp_multiply);
|
||||
DEFINE_CASE(kp_subtract);
|
||||
DEFINE_CASE(kp_add);
|
||||
DEFINE_CASE(kp_enter);
|
||||
DEFINE_CASE(kp_equal);
|
||||
DEFINE_CASE(kp_decimal);
|
||||
|
||||
DEFINE_CASE(unknown);
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected keyboard key");
|
||||
return "";
|
||||
}
|
||||
#undef DEFINE_CASE
|
||||
}
|
||||
|
||||
const char* mouse_button_action_to_cstr(mouse_button_action action) noexcept {
|
||||
#define DEFINE_CASE(x) case mouse_button_action::x: return #x
|
||||
switch ( action ) {
|
||||
DEFINE_CASE(press);
|
||||
DEFINE_CASE(release);
|
||||
DEFINE_CASE(unknown);
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected mouse button action");
|
||||
return "";
|
||||
}
|
||||
#undef DEFINE_CASE
|
||||
}
|
||||
|
||||
const char* keyboard_key_action_to_cstr(keyboard_key_action action) noexcept {
|
||||
#define DEFINE_CASE(x) case keyboard_key_action::x: return #x
|
||||
switch ( action ) {
|
||||
DEFINE_CASE(press);
|
||||
DEFINE_CASE(repeat);
|
||||
DEFINE_CASE(release);
|
||||
DEFINE_CASE(unknown);
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected keyboard key action");
|
||||
return "";
|
||||
}
|
||||
#undef DEFINE_CASE
|
||||
}
|
||||
}
|
||||
446
sources/enduro2d/core/input.cpp
Normal file
446
sources/enduro2d/core/input.cpp
Normal file
@@ -0,0 +1,446 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/core/input.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
enum class button_state : u8 {
|
||||
pressed,
|
||||
released,
|
||||
just_pressed,
|
||||
just_released
|
||||
};
|
||||
|
||||
void update_button_state(button_state& state) noexcept {
|
||||
if ( state == button_state::just_pressed ) {
|
||||
state = button_state::pressed;
|
||||
} else if ( state == button_state::just_released ) {
|
||||
state = button_state::released;
|
||||
}
|
||||
}
|
||||
|
||||
template < typename E >
|
||||
constexpr std::underlying_type_t<E> enum_to_number(E e) noexcept {
|
||||
return static_cast<std::underlying_type_t<E>>(e);
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// class mouse::state
|
||||
//
|
||||
|
||||
class mouse::state final : private e2d::noncopyable {
|
||||
public:
|
||||
mutable std::mutex mutex;
|
||||
v2f cursor_pos;
|
||||
v2f scroll_delta;
|
||||
std::array<button_state, enum_to_number(mouse_button::unknown) + 1> button_states;
|
||||
char _pad[2];
|
||||
public:
|
||||
state() noexcept {
|
||||
std::fill(
|
||||
button_states.begin(),
|
||||
button_states.end(),
|
||||
button_state::released);
|
||||
}
|
||||
|
||||
std::size_t button_index(mouse_button btn) const noexcept {
|
||||
const auto index = enum_to_number(btn);
|
||||
E2D_ASSERT(index < button_states.size());
|
||||
return math::numeric_cast<std::size_t>(index);
|
||||
}
|
||||
|
||||
void frame_tick() noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
scroll_delta = v2f::zero();
|
||||
std::for_each(
|
||||
button_states.begin(),
|
||||
button_states.end(),
|
||||
&update_button_state);
|
||||
}
|
||||
|
||||
void post_event(input::move_cursor_event evt) noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
cursor_pos = evt.pos;
|
||||
}
|
||||
|
||||
void post_event(input::mouse_scroll_event evt) noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
scroll_delta += evt.delta;
|
||||
}
|
||||
|
||||
void post_event(input::mouse_button_event evt) noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
const std::size_t index = button_index(evt.button);
|
||||
const button_state ms = button_states[index];
|
||||
switch ( evt.action ) {
|
||||
case mouse_button_action::press:
|
||||
if ( ms == button_state::released || ms == button_state::just_released ) {
|
||||
button_states[index] = button_state::just_pressed;
|
||||
}
|
||||
break;
|
||||
case mouse_button_action::release:
|
||||
if ( ms == button_state::just_pressed || ms == button_state::pressed ) {
|
||||
button_states[index] = button_state::just_released;
|
||||
}
|
||||
break;
|
||||
case mouse_button_action::unknown:
|
||||
break;
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected mouse action");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// class keyboard::state
|
||||
//
|
||||
|
||||
class keyboard::state final : private e2d::noncopyable {
|
||||
public:
|
||||
mutable std::mutex mutex;
|
||||
str32 input_text;
|
||||
std::array<button_state, enum_to_number(keyboard_key::unknown) + 1> key_states;
|
||||
public:
|
||||
state() noexcept {
|
||||
std::fill(
|
||||
key_states.begin(),
|
||||
key_states.end(),
|
||||
button_state::released);
|
||||
}
|
||||
|
||||
std::size_t key_index(keyboard_key key) const noexcept {
|
||||
const auto index = enum_to_number(key);
|
||||
E2D_ASSERT(index < key_states.size());
|
||||
return math::numeric_cast<std::size_t>(index);
|
||||
}
|
||||
|
||||
void frame_tick() noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
input_text.clear();
|
||||
std::for_each(
|
||||
key_states.begin(),
|
||||
key_states.end(),
|
||||
&update_button_state);
|
||||
}
|
||||
|
||||
void post_event(input::input_char_event evt) noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
input_text += evt.uchar;
|
||||
}
|
||||
|
||||
void post_event(input::keyboard_key_event evt) noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
const std::size_t index = key_index(evt.key);
|
||||
const button_state ks = key_states[index];
|
||||
switch ( evt.action ) {
|
||||
case keyboard_key_action::press:
|
||||
case keyboard_key_action::repeat:
|
||||
if ( ks == button_state::released || ks == button_state::just_released ) {
|
||||
key_states[index] = button_state::just_pressed;
|
||||
}
|
||||
break;
|
||||
case keyboard_key_action::release:
|
||||
if ( ks == button_state::pressed || ks == button_state::just_pressed ) {
|
||||
key_states[index] = button_state::just_released;
|
||||
}
|
||||
break;
|
||||
case keyboard_key_action::unknown:
|
||||
break;
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected key action");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// class mouse
|
||||
//
|
||||
|
||||
mouse::mouse()
|
||||
: state_(new state()) {}
|
||||
mouse::~mouse() noexcept = default;
|
||||
|
||||
v2f mouse::cursor_pos() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return state_->cursor_pos;
|
||||
}
|
||||
|
||||
v2f mouse::scroll_delta() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return state_->scroll_delta;
|
||||
}
|
||||
|
||||
bool mouse::is_any_button_pressed() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return std::any_of(
|
||||
state_->button_states.cbegin(),
|
||||
state_->button_states.cend(),
|
||||
[](button_state s) noexcept {
|
||||
return s == button_state::just_pressed
|
||||
|| s == button_state::pressed;
|
||||
});
|
||||
}
|
||||
|
||||
bool mouse::is_any_button_just_pressed() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return std::any_of(
|
||||
state_->button_states.cbegin(),
|
||||
state_->button_states.cend(),
|
||||
[](button_state s) noexcept {
|
||||
return s == button_state::just_pressed;
|
||||
});
|
||||
}
|
||||
|
||||
bool mouse::is_any_button_just_released() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return std::any_of(
|
||||
state_->button_states.cbegin(),
|
||||
state_->button_states.cend(),
|
||||
[](button_state s) noexcept {
|
||||
return s == button_state::just_released;
|
||||
});
|
||||
}
|
||||
|
||||
bool mouse::is_button_pressed(mouse_button btn) const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
const std::size_t index = state_->button_index(btn);
|
||||
const button_state ms = state_->button_states[index];
|
||||
return ms == button_state::just_pressed
|
||||
|| ms == button_state::pressed;
|
||||
}
|
||||
|
||||
bool mouse::is_button_just_pressed(mouse_button btn) const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
const std::size_t index = state_->button_index(btn);
|
||||
const button_state ms = state_->button_states[index];
|
||||
return ms == button_state::just_pressed;
|
||||
}
|
||||
|
||||
bool mouse::is_button_just_released(mouse_button btn) const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
const std::size_t index = state_->button_index(btn);
|
||||
const button_state ms = state_->button_states[index];
|
||||
return ms == button_state::just_released;
|
||||
}
|
||||
|
||||
void mouse::extract_pressed_buttons(std::vector<mouse_button>& dst) const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
for ( std::size_t i = 0; i < state_->button_states.size(); ++i ) {
|
||||
button_state ks = state_->button_states[i];
|
||||
if ( ks == button_state::just_pressed || ks == button_state::pressed ) {
|
||||
dst.push_back(static_cast<mouse_button>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouse::extract_just_pressed_buttons(std::vector<mouse_button>& dst) const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
for ( std::size_t i = 0; i < state_->button_states.size(); ++i ) {
|
||||
button_state ks = state_->button_states[i];
|
||||
if ( ks == button_state::just_pressed ) {
|
||||
dst.push_back(static_cast<mouse_button>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouse::extract_just_released_buttons(std::vector<mouse_button>& dst) const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
for ( std::size_t i = 0; i < state_->button_states.size(); ++i ) {
|
||||
button_state ks = state_->button_states[i];
|
||||
if ( ks == button_state::just_released ) {
|
||||
dst.push_back(static_cast<mouse_button>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// class keyboard
|
||||
//
|
||||
|
||||
keyboard::keyboard()
|
||||
: state_(new state()) {}
|
||||
keyboard::~keyboard() noexcept = default;
|
||||
|
||||
str32 keyboard::input_text() const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return state_->input_text;
|
||||
}
|
||||
|
||||
void keyboard::extract_input_text(str32& dst) const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
dst = state_->input_text;
|
||||
}
|
||||
|
||||
bool keyboard::is_any_key_pressed() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return std::any_of(
|
||||
state_->key_states.cbegin(),
|
||||
state_->key_states.cend(),
|
||||
[](button_state s) noexcept {
|
||||
return s == button_state::just_pressed
|
||||
|| s == button_state::pressed;
|
||||
});
|
||||
}
|
||||
|
||||
bool keyboard::is_any_key_just_pressed() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return std::any_of(
|
||||
state_->key_states.cbegin(),
|
||||
state_->key_states.cend(),
|
||||
[](button_state s) noexcept {
|
||||
return s == button_state::just_pressed;
|
||||
});
|
||||
}
|
||||
|
||||
bool keyboard::is_any_key_just_released() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return std::any_of(
|
||||
state_->key_states.cbegin(),
|
||||
state_->key_states.cend(),
|
||||
[](button_state s) noexcept {
|
||||
return s == button_state::just_released;
|
||||
});
|
||||
}
|
||||
|
||||
bool keyboard::is_key_pressed(keyboard_key key) const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
const std::size_t index = state_->key_index(key);
|
||||
const button_state ks = state_->key_states[index];
|
||||
return ks == button_state::just_pressed
|
||||
|| ks == button_state::pressed;
|
||||
}
|
||||
|
||||
bool keyboard::is_key_just_pressed(keyboard_key key) const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
const std::size_t index = state_->key_index(key);
|
||||
const button_state ks = state_->key_states[index];
|
||||
return ks == button_state::just_pressed;
|
||||
}
|
||||
|
||||
bool keyboard::is_key_just_released(keyboard_key key) const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
const std::size_t index = state_->key_index(key);
|
||||
const button_state ks = state_->key_states[index];
|
||||
return ks == button_state::just_released;
|
||||
}
|
||||
|
||||
void keyboard::extract_pressed_keys(std::vector<keyboard_key>& dst) const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
for ( std::size_t i = 0; i < state_->key_states.size(); ++i ) {
|
||||
button_state ks = state_->key_states[i];
|
||||
if ( ks == button_state::just_pressed || ks == button_state::pressed ) {
|
||||
dst.push_back(static_cast<keyboard_key>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void keyboard::extract_just_pressed_keys(std::vector<keyboard_key>& dst) const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
for ( std::size_t i = 0; i < state_->key_states.size(); ++i ) {
|
||||
button_state ks = state_->key_states[i];
|
||||
if ( ks == button_state::just_pressed ) {
|
||||
dst.push_back(static_cast<keyboard_key>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void keyboard::extract_just_released_keys(std::vector<keyboard_key>& dst) const {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
for ( std::size_t i = 0; i < state_->key_states.size(); ++i ) {
|
||||
button_state ks = state_->key_states[i];
|
||||
if ( ks == button_state::just_released ) {
|
||||
dst.push_back(static_cast<keyboard_key>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// class input::state
|
||||
//
|
||||
|
||||
class input::state final : private e2d::noncopyable {
|
||||
public:
|
||||
class mouse mouse;
|
||||
class keyboard keyboard;
|
||||
};
|
||||
|
||||
//
|
||||
// class input
|
||||
//
|
||||
|
||||
input::input()
|
||||
: state_(new state()) {}
|
||||
input::~input() noexcept = default;
|
||||
|
||||
const class mouse& input::mouse() const noexcept {
|
||||
return state_->mouse;
|
||||
}
|
||||
|
||||
const class keyboard& input::keyboard() const noexcept {
|
||||
return state_->keyboard;
|
||||
}
|
||||
|
||||
void input::post_event(input_char_event evt) noexcept {
|
||||
state_->keyboard.state_->post_event(evt);
|
||||
}
|
||||
|
||||
void input::post_event(move_cursor_event evt) noexcept {
|
||||
state_->mouse.state_->post_event(evt);
|
||||
}
|
||||
|
||||
void input::post_event(mouse_scroll_event evt) noexcept {
|
||||
state_->mouse.state_->post_event(evt);
|
||||
}
|
||||
|
||||
void input::post_event(mouse_button_event evt) noexcept {
|
||||
state_->mouse.state_->post_event(evt);
|
||||
}
|
||||
|
||||
void input::post_event(keyboard_key_event evt) noexcept {
|
||||
state_->keyboard.state_->post_event(evt);
|
||||
}
|
||||
|
||||
void input::frame_tick() noexcept {
|
||||
state_->mouse.state_->frame_tick();
|
||||
state_->keyboard.state_->frame_tick();
|
||||
}
|
||||
|
||||
//
|
||||
// class window_input_source
|
||||
//
|
||||
|
||||
window_input_source::window_input_source(input& input) noexcept
|
||||
: input_(input) {}
|
||||
|
||||
void window_input_source::on_input_char(char32_t uchar) noexcept {
|
||||
input_.post_event(input::input_char_event{uchar});
|
||||
}
|
||||
|
||||
void window_input_source::on_move_cursor(const v2f& pos) noexcept {
|
||||
input_.post_event(input::move_cursor_event{pos});
|
||||
}
|
||||
|
||||
void window_input_source::on_mouse_scroll(const v2f& delta) noexcept {
|
||||
input_.post_event(input::mouse_scroll_event{delta});
|
||||
}
|
||||
|
||||
void window_input_source::on_mouse_button(mouse_button btn, mouse_button_action act) noexcept {
|
||||
input_.post_event(input::mouse_button_event{btn, act});
|
||||
}
|
||||
|
||||
void window_input_source::on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept {
|
||||
E2D_UNUSED(scancode);
|
||||
input_.post_event(input::keyboard_key_event{key, act});
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ namespace e2d
|
||||
: false;
|
||||
}
|
||||
|
||||
bool vfs::unregister_scheme(str_view scheme) noexcept {
|
||||
bool vfs::unregister_scheme(str_view scheme) {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return state_->schemes.erase(scheme) > 0;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ namespace e2d
|
||||
std::make_pair(scheme, alias)).second;
|
||||
}
|
||||
|
||||
bool vfs::unregister_scheme_alias(str_view scheme) noexcept {
|
||||
bool vfs::unregister_scheme_alias(str_view scheme) {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
return state_->aliases.erase(scheme) > 0;
|
||||
}
|
||||
|
||||
@@ -5,3 +5,86 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/core/window.hpp>
|
||||
#include <enduro2d/core/debug.hpp>
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// class window::event_listener
|
||||
//
|
||||
|
||||
void window::event_listener::on_input_char(char32_t uchar) noexcept {
|
||||
E2D_UNUSED(uchar);
|
||||
}
|
||||
|
||||
void window::event_listener::on_move_cursor(const v2f& pos) noexcept {
|
||||
E2D_UNUSED(pos);
|
||||
}
|
||||
|
||||
void window::event_listener::on_mouse_scroll(const v2f& delta) noexcept {
|
||||
E2D_UNUSED(delta);
|
||||
}
|
||||
|
||||
void window::event_listener::on_mouse_button(mouse_button btn, mouse_button_action act) noexcept {
|
||||
E2D_UNUSED(btn, act);
|
||||
}
|
||||
|
||||
void window::event_listener::on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept {
|
||||
E2D_UNUSED(key, scancode, act);
|
||||
}
|
||||
|
||||
void window::event_listener::on_window_close() noexcept {
|
||||
}
|
||||
|
||||
void window::event_listener::on_window_focus(bool focused) noexcept {
|
||||
E2D_UNUSED(focused);
|
||||
}
|
||||
|
||||
void window::event_listener::on_window_minimize(bool minimized) noexcept {
|
||||
E2D_UNUSED(minimized);
|
||||
}
|
||||
|
||||
//
|
||||
// class trace_window_event_listener
|
||||
//
|
||||
|
||||
window_trace_event_listener::window_trace_event_listener(debug& debug) noexcept
|
||||
: debug_(debug) {}
|
||||
|
||||
void window_trace_event_listener::on_input_char(char32_t uchar) noexcept {
|
||||
debug_.trace("WINDOW: on_input_char(uchar: %0)", str32_view(&uchar, 1));
|
||||
}
|
||||
|
||||
void window_trace_event_listener::on_move_cursor(const v2f& pos) noexcept {
|
||||
debug_.trace("WINDOW: on_move_cursor(pos: %0)", pos);
|
||||
}
|
||||
|
||||
void window_trace_event_listener::on_mouse_scroll(const v2f& delta) noexcept {
|
||||
debug_.trace("WINDOW: on_scroll(delta: %0)", delta);
|
||||
}
|
||||
|
||||
void window_trace_event_listener::on_mouse_button(mouse_button btn, mouse_button_action act) noexcept {
|
||||
debug_.trace("WINDOW: on_mouse_button(btn: %0 act: %1)",
|
||||
mouse_button_to_cstr(btn),
|
||||
mouse_button_action_to_cstr(act));
|
||||
}
|
||||
|
||||
void window_trace_event_listener::on_keyboard_key(keyboard_key key, u32 scancode, keyboard_key_action act) noexcept {
|
||||
debug_.trace("WINDOW: on_keyboard_key(key: %0 scancode: %1 act: %2)",
|
||||
keyboard_key_to_cstr(key),
|
||||
scancode,
|
||||
keyboard_key_action_to_cstr(act));
|
||||
}
|
||||
|
||||
void window_trace_event_listener::on_window_close() noexcept {
|
||||
debug_.trace("WINDOW: on_window_close()");
|
||||
}
|
||||
|
||||
void window_trace_event_listener::on_window_focus(bool focused) noexcept {
|
||||
debug_.trace("WINDOW: on_window_focus(focused: %0)", focused);
|
||||
}
|
||||
|
||||
void window_trace_event_listener::on_window_minimize(bool minimized) noexcept {
|
||||
debug_.trace("WINDOW: on_window_minimize(minimized: %0)", minimized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,183 @@ namespace
|
||||
|
||||
std::mutex glfw_state::mutex_;
|
||||
std::shared_ptr<glfw_state> glfw_state::shared_state_;
|
||||
|
||||
mouse_button convert_glfw_mouse_button(int m) noexcept {
|
||||
#define DEFINE_CASE(x,y) case GLFW_MOUSE_BUTTON_##x: return mouse_button::y
|
||||
switch ( m ) {
|
||||
DEFINE_CASE(LEFT, left);
|
||||
DEFINE_CASE(RIGHT, right);
|
||||
DEFINE_CASE(MIDDLE, middle);
|
||||
DEFINE_CASE(4, x1);
|
||||
DEFINE_CASE(5, x2);
|
||||
default: return mouse_button::unknown;
|
||||
}
|
||||
#undef DEFINE_CASE
|
||||
}
|
||||
|
||||
keyboard_key convert_glfw_keyboard_key(int k) noexcept {
|
||||
#define DEFINE_CASE(x,y) case GLFW_KEY_##x: return keyboard_key::y
|
||||
switch ( k ) {
|
||||
DEFINE_CASE(0, _0);
|
||||
DEFINE_CASE(1, _1);
|
||||
DEFINE_CASE(2, _2);
|
||||
DEFINE_CASE(3, _3);
|
||||
DEFINE_CASE(4, _4);
|
||||
DEFINE_CASE(5, _5);
|
||||
DEFINE_CASE(6, _6);
|
||||
DEFINE_CASE(7, _7);
|
||||
DEFINE_CASE(8, _8);
|
||||
DEFINE_CASE(9, _9);
|
||||
|
||||
DEFINE_CASE(A, a);
|
||||
DEFINE_CASE(B, b);
|
||||
DEFINE_CASE(C, c);
|
||||
DEFINE_CASE(D, d);
|
||||
DEFINE_CASE(E, e);
|
||||
DEFINE_CASE(F, f);
|
||||
DEFINE_CASE(G, g);
|
||||
DEFINE_CASE(H, h);
|
||||
DEFINE_CASE(I, i);
|
||||
DEFINE_CASE(J, j);
|
||||
DEFINE_CASE(K, k);
|
||||
DEFINE_CASE(L, l);
|
||||
DEFINE_CASE(M, m);
|
||||
DEFINE_CASE(N, n);
|
||||
DEFINE_CASE(O, o);
|
||||
DEFINE_CASE(P, p);
|
||||
DEFINE_CASE(Q, q);
|
||||
DEFINE_CASE(R, r);
|
||||
DEFINE_CASE(S, s);
|
||||
DEFINE_CASE(T, t);
|
||||
DEFINE_CASE(U, u);
|
||||
DEFINE_CASE(V, v);
|
||||
DEFINE_CASE(W, w);
|
||||
DEFINE_CASE(X, x);
|
||||
DEFINE_CASE(Y, y);
|
||||
DEFINE_CASE(Z, z);
|
||||
|
||||
DEFINE_CASE(F1, f1);
|
||||
DEFINE_CASE(F2, f2);
|
||||
DEFINE_CASE(F3, f3);
|
||||
DEFINE_CASE(F4, f4);
|
||||
DEFINE_CASE(F5, f5);
|
||||
DEFINE_CASE(F6, f6);
|
||||
DEFINE_CASE(F7, f7);
|
||||
DEFINE_CASE(F8, f8);
|
||||
DEFINE_CASE(F9, f9);
|
||||
DEFINE_CASE(F10, f10);
|
||||
DEFINE_CASE(F11, f11);
|
||||
DEFINE_CASE(F12, f12);
|
||||
DEFINE_CASE(F13, f13);
|
||||
DEFINE_CASE(F14, f14);
|
||||
DEFINE_CASE(F15, f15);
|
||||
DEFINE_CASE(F16, f16);
|
||||
DEFINE_CASE(F17, f17);
|
||||
DEFINE_CASE(F18, f18);
|
||||
DEFINE_CASE(F19, f19);
|
||||
DEFINE_CASE(F20, f20);
|
||||
DEFINE_CASE(F21, f21);
|
||||
DEFINE_CASE(F22, f22);
|
||||
DEFINE_CASE(F23, f23);
|
||||
DEFINE_CASE(F24, f24);
|
||||
DEFINE_CASE(F25, f25);
|
||||
|
||||
DEFINE_CASE(MINUS, minus);
|
||||
DEFINE_CASE(EQUAL, equal);
|
||||
DEFINE_CASE(BACKSPACE, backspace);
|
||||
DEFINE_CASE(WORLD_1, section_sign);
|
||||
DEFINE_CASE(GRAVE_ACCENT, grave_accent);
|
||||
|
||||
DEFINE_CASE(LEFT_BRACKET, lbracket);
|
||||
DEFINE_CASE(RIGHT_BRACKET, rbracket);
|
||||
DEFINE_CASE(SEMICOLON, semicolon);
|
||||
DEFINE_CASE(APOSTROPHE, apostrophe);
|
||||
DEFINE_CASE(BACKSLASH, backslash);
|
||||
|
||||
DEFINE_CASE(COMMA, comma);
|
||||
DEFINE_CASE(PERIOD, period);
|
||||
DEFINE_CASE(SLASH, slash);
|
||||
|
||||
DEFINE_CASE(ESCAPE, escape);
|
||||
DEFINE_CASE(TAB, tab);
|
||||
DEFINE_CASE(CAPS_LOCK, caps_lock);
|
||||
DEFINE_CASE(SPACE, space);
|
||||
DEFINE_CASE(ENTER, enter);
|
||||
|
||||
DEFINE_CASE(LEFT_SHIFT, lshift);
|
||||
DEFINE_CASE(RIGHT_SHIFT, rshift);
|
||||
DEFINE_CASE(LEFT_CONTROL, lcontrol);
|
||||
DEFINE_CASE(RIGHT_CONTROL, rcontrol);
|
||||
DEFINE_CASE(LEFT_ALT, lalt);
|
||||
DEFINE_CASE(RIGHT_ALT, ralt);
|
||||
DEFINE_CASE(LEFT_SUPER, lsuper);
|
||||
DEFINE_CASE(RIGHT_SUPER, rsuper);
|
||||
DEFINE_CASE(MENU, menu);
|
||||
|
||||
DEFINE_CASE(PRINT_SCREEN, print_screen);
|
||||
DEFINE_CASE(SCROLL_LOCK, scroll_lock);
|
||||
DEFINE_CASE(PAUSE, pause);
|
||||
|
||||
DEFINE_CASE(INSERT, insert);
|
||||
DEFINE_CASE(DELETE, del);
|
||||
DEFINE_CASE(HOME, home);
|
||||
DEFINE_CASE(END, end);
|
||||
DEFINE_CASE(PAGE_UP, page_up);
|
||||
DEFINE_CASE(PAGE_DOWN, page_down);
|
||||
|
||||
DEFINE_CASE(LEFT, left);
|
||||
DEFINE_CASE(UP, up);
|
||||
DEFINE_CASE(RIGHT, right);
|
||||
DEFINE_CASE(DOWN, down);
|
||||
|
||||
DEFINE_CASE(KP_0, kp_0);
|
||||
DEFINE_CASE(KP_1, kp_1);
|
||||
DEFINE_CASE(KP_2, kp_2);
|
||||
DEFINE_CASE(KP_3, kp_3);
|
||||
DEFINE_CASE(KP_4, kp_4);
|
||||
DEFINE_CASE(KP_5, kp_5);
|
||||
DEFINE_CASE(KP_6, kp_6);
|
||||
DEFINE_CASE(KP_7, kp_7);
|
||||
DEFINE_CASE(KP_8, kp_8);
|
||||
DEFINE_CASE(KP_9, kp_9);
|
||||
|
||||
DEFINE_CASE(NUM_LOCK, kp_num_lock);
|
||||
DEFINE_CASE(KP_DIVIDE, kp_divide);
|
||||
DEFINE_CASE(KP_MULTIPLY, kp_multiply);
|
||||
DEFINE_CASE(KP_SUBTRACT, kp_subtract);
|
||||
DEFINE_CASE(KP_ADD, kp_add);
|
||||
DEFINE_CASE(KP_ENTER, kp_enter);
|
||||
DEFINE_CASE(KP_EQUAL, kp_equal);
|
||||
DEFINE_CASE(KP_DECIMAL, kp_decimal);
|
||||
|
||||
default: return keyboard_key::unknown;
|
||||
}
|
||||
#undef DEFINE_CASE
|
||||
}
|
||||
|
||||
mouse_button_action convert_glfw_mouse_button_action(int ma) noexcept {
|
||||
switch ( ma ) {
|
||||
case GLFW_PRESS:
|
||||
return mouse_button_action::press;
|
||||
case GLFW_RELEASE:
|
||||
return mouse_button_action::release;
|
||||
default:
|
||||
return mouse_button_action::unknown;
|
||||
}
|
||||
}
|
||||
|
||||
keyboard_key_action convert_glfw_keyboard_key_action(int ka) noexcept {
|
||||
switch ( ka ) {
|
||||
case GLFW_PRESS:
|
||||
return keyboard_key_action::press;
|
||||
case GLFW_REPEAT:
|
||||
return keyboard_key_action::repeat;
|
||||
case GLFW_RELEASE:
|
||||
return keyboard_key_action::release;
|
||||
default:
|
||||
return keyboard_key_action::unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
@@ -72,13 +249,18 @@ namespace e2d
|
||||
public:
|
||||
using window_uptr = std::unique_ptr<
|
||||
GLFWwindow, void(*)(GLFWwindow*)>;
|
||||
using listeners_t = std::vector<event_listener_uptr>;
|
||||
public:
|
||||
listeners_t listeners;
|
||||
std::recursive_mutex rmutex;
|
||||
glfw_state_ptr shared_state;
|
||||
window_uptr window;
|
||||
v2u virtual_size;
|
||||
str title;
|
||||
bool vsync = false;
|
||||
bool fullscreen = false;
|
||||
std::mutex mutex;
|
||||
bool cursor_hidden = false;
|
||||
char _pad[5];
|
||||
public:
|
||||
state(const v2u& size, str_view title, bool vsync, bool fullscreen)
|
||||
: shared_state(glfw_state::get_shared_state())
|
||||
@@ -87,16 +269,44 @@ namespace e2d
|
||||
, title(title)
|
||||
, vsync(vsync)
|
||||
, fullscreen(fullscreen)
|
||||
, cursor_hidden(false)
|
||||
{
|
||||
if ( !window ) {
|
||||
throw bad_window_operation();
|
||||
}
|
||||
glfwSetWindowUserPointer(window.get(), this);
|
||||
glfwSetCharCallback(window.get(), input_char_callback_);
|
||||
glfwSetCursorPosCallback(window.get(), move_cursor_callback_);
|
||||
glfwSetScrollCallback(window.get(), mouse_scroll_callback_);
|
||||
glfwSetMouseButtonCallback(window.get(), mouse_button_callback_);
|
||||
glfwSetKeyCallback(window.get(), keyboard_key_callback_);
|
||||
glfwSetWindowCloseCallback(window.get(), window_close_callback_);
|
||||
glfwSetWindowFocusCallback(window.get(), window_focus_callback_);
|
||||
glfwSetWindowIconifyCallback(window.get(), window_minimize_callback_);
|
||||
}
|
||||
|
||||
~state() noexcept {
|
||||
// reset window before shared state
|
||||
window.reset();
|
||||
}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
void for_all_listeners(const F& f, const Args&... args) noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(rmutex);
|
||||
for ( const event_listener_uptr& listener : listeners ) {
|
||||
if ( listener ) {
|
||||
stdex::invoke(f, *listener.get(), args...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event_listener& on_new_event_listener(event_listener& listener) noexcept {
|
||||
double cursor_x = 0.0, cursor_y = 0.0;
|
||||
E2D_ASSERT(window);
|
||||
glfwGetCursorPos(window.get(), &cursor_x, &cursor_y);
|
||||
listener.on_move_cursor(make_vec2(cursor_x, cursor_y).cast_to<f32>());
|
||||
return listener;
|
||||
}
|
||||
private:
|
||||
static window_uptr open_window_(
|
||||
const v2u& virtual_size, const str& title, bool vsync, bool fullscreen) noexcept
|
||||
@@ -129,66 +339,147 @@ namespace e2d
|
||||
}
|
||||
return {w, glfwDestroyWindow};
|
||||
}
|
||||
|
||||
static void input_char_callback_(GLFWwindow* window, u32 uchar) noexcept {
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_input_char,
|
||||
static_cast<char32_t>(uchar));
|
||||
}
|
||||
}
|
||||
|
||||
static void move_cursor_callback_(GLFWwindow* window, double pos_x, double pos_y) noexcept {
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_move_cursor,
|
||||
make_vec2(pos_x, pos_y).cast_to<f32>());
|
||||
}
|
||||
}
|
||||
|
||||
static void mouse_scroll_callback_(GLFWwindow* window, double delta_x, double delta_y) noexcept {
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_mouse_scroll,
|
||||
make_vec2(delta_x, delta_y).cast_to<f32>());
|
||||
}
|
||||
}
|
||||
|
||||
static void mouse_button_callback_(GLFWwindow* window, int button, int action, int mods) noexcept {
|
||||
E2D_UNUSED(mods);
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_mouse_button,
|
||||
convert_glfw_mouse_button(button),
|
||||
convert_glfw_mouse_button_action(action));
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_key_callback_(GLFWwindow* window, int key, int scancode, int action, int mods) noexcept {
|
||||
E2D_UNUSED(mods);
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_keyboard_key,
|
||||
convert_glfw_keyboard_key(key),
|
||||
math::numeric_cast<u32>(scancode),
|
||||
convert_glfw_keyboard_key_action(action));
|
||||
}
|
||||
}
|
||||
|
||||
static void window_close_callback_(GLFWwindow* window) noexcept {
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_window_close);
|
||||
}
|
||||
}
|
||||
|
||||
static void window_focus_callback_(GLFWwindow* window, int focused) noexcept {
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_window_focus,
|
||||
focused);
|
||||
}
|
||||
}
|
||||
|
||||
static void window_minimize_callback_(GLFWwindow* window, int minimized) noexcept {
|
||||
state* self = static_cast<state*>(glfwGetWindowUserPointer(window));
|
||||
if ( self ) {
|
||||
self->for_all_listeners(
|
||||
&event_listener::on_window_minimize,
|
||||
minimized);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// class window
|
||||
//
|
||||
|
||||
window::window(const v2u& size, str_view title, bool vsync, bool fullscreen)
|
||||
: state_(new state(size, title, vsync, fullscreen)) {}
|
||||
window::~window() noexcept = default;
|
||||
|
||||
void window::hide() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwHideWindow(state_->window.get());
|
||||
}
|
||||
|
||||
void window::show() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwShowWindow(state_->window.get());
|
||||
}
|
||||
|
||||
void window::restore() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwRestoreWindow(state_->window.get());
|
||||
}
|
||||
|
||||
void window::minimize() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwIconifyWindow(state_->window.get());
|
||||
}
|
||||
|
||||
bool window::visible() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
return glfwGetWindowAttrib(state_->window.get(), GLFW_VISIBLE);
|
||||
return GLFW_TRUE == glfwGetWindowAttrib(state_->window.get(), GLFW_VISIBLE);
|
||||
}
|
||||
|
||||
bool window::focused() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
return glfwGetWindowAttrib(state_->window.get(), GLFW_FOCUSED);
|
||||
return GLFW_TRUE == glfwGetWindowAttrib(state_->window.get(), GLFW_FOCUSED);
|
||||
}
|
||||
|
||||
bool window::minimized() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
return glfwGetWindowAttrib(state_->window.get(), GLFW_ICONIFIED);
|
||||
return GLFW_TRUE == glfwGetWindowAttrib(state_->window.get(), GLFW_ICONIFIED);
|
||||
}
|
||||
|
||||
bool window::vsync() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->vsync;
|
||||
}
|
||||
|
||||
bool window::fullscreen() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->fullscreen;
|
||||
}
|
||||
|
||||
bool window::toggle_vsync(bool yesno) noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwMakeContextCurrent(state_->window.get());
|
||||
glfwSwapInterval(yesno ? 1 : 0);
|
||||
state_->vsync = yesno;
|
||||
@@ -196,7 +487,7 @@ namespace e2d
|
||||
}
|
||||
|
||||
bool window::toggle_fullscreen(bool yesno) noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
if ( state_->fullscreen == yesno ) {
|
||||
return true;
|
||||
}
|
||||
@@ -211,6 +502,7 @@ namespace e2d
|
||||
v2i real_size = yesno
|
||||
? make_vec2(video_mode->width, video_mode->height)
|
||||
: state_->virtual_size.cast_to<i32>();
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwSetWindowMonitor(
|
||||
state_->window.get(),
|
||||
yesno ? monitor : nullptr,
|
||||
@@ -223,62 +515,97 @@ namespace e2d
|
||||
return true;
|
||||
}
|
||||
|
||||
v2u window::real_size() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
void window::hide_cursor() noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwSetInputMode(state_->window.get(), GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
state_->cursor_hidden = true;
|
||||
}
|
||||
|
||||
void window::show_cursor() noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwSetInputMode(state_->window.get(), GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
state_->cursor_hidden = false;
|
||||
}
|
||||
|
||||
bool window::is_cursor_hidden() const noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->cursor_hidden;
|
||||
}
|
||||
|
||||
v2u window::real_size() const noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
int w = 0, h = 0;
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwGetWindowSize(state_->window.get(), &w, &h);
|
||||
return make_vec2(w,h).cast_to<u32>();
|
||||
}
|
||||
|
||||
v2u window::virtual_size() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->virtual_size;
|
||||
}
|
||||
|
||||
v2u window::framebuffer_size() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
int w = 0, h = 0;
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwGetFramebufferSize(state_->window.get(), &w, &h);
|
||||
return make_vec2(w,h).cast_to<u32>();
|
||||
}
|
||||
|
||||
const str& window::title() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->title;
|
||||
}
|
||||
|
||||
void window::set_title(str_view title) {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->title = make_utf8(title);
|
||||
glfwSetWindowTitle(
|
||||
state_->window.get(), state_->title.c_str());
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwSetWindowTitle(state_->window.get(), state_->title.c_str());
|
||||
}
|
||||
|
||||
bool window::should_close() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
return glfwWindowShouldClose(state_->window.get());
|
||||
return GLFW_TRUE == glfwWindowShouldClose(state_->window.get());
|
||||
}
|
||||
|
||||
void window::set_should_close(bool yesno) noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwSetWindowShouldClose(
|
||||
state_->window.get(), yesno ? GLFW_TRUE : GLFW_FALSE);
|
||||
glfwSetWindowShouldClose(state_->window.get(), yesno ? GLFW_TRUE : GLFW_FALSE);
|
||||
}
|
||||
|
||||
void window::swap_buffers() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
E2D_ASSERT(state_->window);
|
||||
glfwSwapBuffers(state_->window.get());
|
||||
}
|
||||
|
||||
bool window::poll_events() noexcept {
|
||||
bool window::frame_tick() noexcept {
|
||||
return glfw_state::poll_events();
|
||||
}
|
||||
|
||||
window::event_listener& window::register_event_listener(event_listener_uptr listener) {
|
||||
E2D_ASSERT(listener);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->listeners.push_back(std::move(listener));
|
||||
return state_->on_new_event_listener(*state_->listeners.back());
|
||||
}
|
||||
|
||||
void window::unregister_event_listener(const event_listener& listener) noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
for ( auto iter = state_->listeners.begin(); iter != state_->listeners.end(); ) {
|
||||
if ( iter->get() == &listener ) {
|
||||
iter = state_->listeners.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,15 +12,20 @@ namespace e2d
|
||||
{
|
||||
class window::state final : private e2d::noncopyable {
|
||||
public:
|
||||
using listeners_t = std::vector<event_listener_uptr>;
|
||||
public:
|
||||
listeners_t listeners;
|
||||
std::recursive_mutex rmutex;
|
||||
v2u virtual_size;
|
||||
str title;
|
||||
bool vsync = false;
|
||||
bool fullscreen = false;
|
||||
bool cursor_hidden = false;
|
||||
bool should_close = false;
|
||||
bool visible = true;
|
||||
bool focused = true;
|
||||
bool minimized = false;
|
||||
std::mutex mutex;
|
||||
char _pad[1];
|
||||
public:
|
||||
state(const v2u& size, str_view title, bool vsync, bool fullscreen)
|
||||
: virtual_size(size)
|
||||
@@ -28,6 +33,16 @@ namespace e2d
|
||||
, vsync(vsync)
|
||||
, fullscreen(fullscreen) {}
|
||||
~state() noexcept = default;
|
||||
|
||||
template < typename F, typename... Args >
|
||||
void for_all_listeners(const F& f, const Args&... args) noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(rmutex);
|
||||
for ( const event_listener_uptr& listener : listeners ) {
|
||||
if ( listener ) {
|
||||
stdex::invoke(f, listener.get(), args...);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window::window(const v2u& size, str_view title, bool vsync, bool fullscreen)
|
||||
@@ -35,103 +50,150 @@ namespace e2d
|
||||
window::~window() noexcept = default;
|
||||
|
||||
void window::hide() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->visible = false;
|
||||
}
|
||||
|
||||
void window::show() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->visible = true;
|
||||
}
|
||||
|
||||
void window::restore() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
state_->minimized = false;
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
if ( !state_->focused ) {
|
||||
state_->focused = true;
|
||||
state_->for_all_listeners(&event_listener::on_window_focus, true);
|
||||
}
|
||||
if ( state_->minimized ) {
|
||||
state_->minimized = false;
|
||||
state_->for_all_listeners(&event_listener::on_window_minimize, false);
|
||||
}
|
||||
}
|
||||
|
||||
void window::minimize() noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
state_->minimized = true;
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
if ( state_->focused ) {
|
||||
state_->focused = false;
|
||||
state_->for_all_listeners(&event_listener::on_window_focus, false);
|
||||
}
|
||||
if ( !state_->minimized ) {
|
||||
state_->minimized = true;
|
||||
state_->for_all_listeners(&event_listener::on_window_minimize, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool window::visible() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->visible;
|
||||
}
|
||||
|
||||
bool window::focused() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->focused;
|
||||
}
|
||||
|
||||
bool window::minimized() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->minimized;
|
||||
}
|
||||
|
||||
bool window::vsync() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->vsync;
|
||||
}
|
||||
|
||||
bool window::fullscreen() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->fullscreen;
|
||||
}
|
||||
|
||||
bool window::toggle_vsync(bool yesno) noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->vsync = yesno;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool window::toggle_fullscreen(bool yesno) noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->fullscreen = yesno;
|
||||
return true;
|
||||
}
|
||||
|
||||
void window::hide_cursor() noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->cursor_hidden = true;
|
||||
}
|
||||
|
||||
void window::show_cursor() noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->cursor_hidden = false;
|
||||
}
|
||||
|
||||
bool window::is_cursor_hidden() const noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->cursor_hidden;
|
||||
}
|
||||
|
||||
v2u window::real_size() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->virtual_size;
|
||||
}
|
||||
|
||||
v2u window::virtual_size() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->virtual_size;
|
||||
}
|
||||
|
||||
v2u window::framebuffer_size() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->virtual_size;
|
||||
}
|
||||
|
||||
const str& window::title() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->title;
|
||||
}
|
||||
|
||||
void window::set_title(str_view title) {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->title = make_utf8(title);
|
||||
}
|
||||
|
||||
bool window::should_close() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
return state_->should_close;
|
||||
}
|
||||
|
||||
void window::set_should_close(bool yesno) noexcept {
|
||||
std::lock_guard<std::mutex> guard(state_->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->should_close = yesno;
|
||||
}
|
||||
|
||||
void window::swap_buffers() noexcept {
|
||||
}
|
||||
|
||||
bool window::poll_events() noexcept {
|
||||
bool window::frame_tick() noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
window::event_listener& window::register_event_listener(event_listener_uptr listener) {
|
||||
E2D_ASSERT(listener);
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
state_->listeners.push_back(std::move(listener));
|
||||
return *state_->listeners.back();
|
||||
}
|
||||
|
||||
void window::unregister_event_listener(const event_listener& listener) noexcept {
|
||||
std::lock_guard<std::recursive_mutex> guard(state_->rmutex);
|
||||
for ( auto iter = state_->listeners.begin(); iter != state_->listeners.end(); ) {
|
||||
if ( iter->get() == &listener ) {
|
||||
iter = state_->listeners.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
278
untests/sources/untests_core/input.cpp
Normal file
278
untests/sources/untests_core/input.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_core.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("input"){
|
||||
SECTION("mouse"){
|
||||
{
|
||||
input i;
|
||||
const mouse& m = i.mouse();
|
||||
|
||||
REQUIRE(math::approximately(m.cursor_pos(), v2f::zero()));
|
||||
REQUIRE(math::approximately(m.scroll_delta(), v2f::zero()));
|
||||
|
||||
i.post_event(input::move_cursor_event{v2f(10.f,20.f)});
|
||||
i.post_event(input::mouse_scroll_event{v2f(0.1f,0.2f)});
|
||||
REQUIRE(math::approximately(m.cursor_pos(), v2f(10.f,20.f)));
|
||||
REQUIRE(math::approximately(m.scroll_delta(), v2f(0.1f,0.2f)));
|
||||
|
||||
i.frame_tick();
|
||||
|
||||
REQUIRE(math::approximately(m.cursor_pos(), v2f(10.f,20.f)));
|
||||
REQUIRE(math::approximately(m.scroll_delta(), v2f::zero()));
|
||||
|
||||
i.post_event(input::move_cursor_event{v2f(12.f,22.f)});
|
||||
REQUIRE(math::approximately(m.cursor_pos(), v2f(12.f,22.f)));
|
||||
}
|
||||
{
|
||||
input i;
|
||||
const mouse& m = i.mouse();
|
||||
|
||||
REQUIRE_FALSE(m.is_any_button_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_released());
|
||||
REQUIRE_FALSE(m.is_button_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_released(mouse_button::left));
|
||||
|
||||
i.post_event(input::mouse_button_event{mouse_button::left, mouse_button_action::press});
|
||||
|
||||
REQUIRE(m.is_any_button_pressed());
|
||||
REQUIRE(m.is_any_button_just_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_released());
|
||||
REQUIRE(m.is_button_pressed(mouse_button::left));
|
||||
REQUIRE(m.is_button_just_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_released(mouse_button::left));
|
||||
|
||||
i.frame_tick();
|
||||
|
||||
REQUIRE(m.is_any_button_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_released());
|
||||
REQUIRE(m.is_button_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_released(mouse_button::left));
|
||||
|
||||
i.post_event(input::mouse_button_event{mouse_button::left, mouse_button_action::release});
|
||||
|
||||
REQUIRE_FALSE(m.is_any_button_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_pressed());
|
||||
REQUIRE(m.is_any_button_just_released());
|
||||
REQUIRE_FALSE(m.is_button_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_pressed(mouse_button::left));
|
||||
REQUIRE(m.is_button_just_released(mouse_button::left));
|
||||
|
||||
i.frame_tick();
|
||||
|
||||
REQUIRE_FALSE(m.is_any_button_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_pressed());
|
||||
REQUIRE_FALSE(m.is_any_button_just_released());
|
||||
REQUIRE_FALSE(m.is_button_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_pressed(mouse_button::left));
|
||||
REQUIRE_FALSE(m.is_button_just_released(mouse_button::left));
|
||||
}
|
||||
{
|
||||
input i;
|
||||
const mouse& m = i.mouse();
|
||||
|
||||
std::vector<mouse_button> buttons;
|
||||
m.extract_pressed_buttons(buttons);
|
||||
m.extract_just_pressed_buttons(buttons);
|
||||
m.extract_just_released_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 0);
|
||||
|
||||
buttons.clear();
|
||||
i.post_event(input::mouse_button_event{mouse_button::right, mouse_button_action::press});
|
||||
|
||||
m.extract_pressed_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 1);
|
||||
REQUIRE(buttons[0] == mouse_button::right);
|
||||
|
||||
m.extract_just_pressed_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 2);
|
||||
REQUIRE(buttons[0] == mouse_button::right);
|
||||
REQUIRE(buttons[1] == mouse_button::right);
|
||||
|
||||
m.extract_just_released_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 2);
|
||||
REQUIRE(buttons[0] == mouse_button::right);
|
||||
REQUIRE(buttons[1] == mouse_button::right);
|
||||
|
||||
buttons.clear();
|
||||
i.frame_tick();
|
||||
|
||||
m.extract_pressed_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 1);
|
||||
REQUIRE(buttons[0] == mouse_button::right);
|
||||
|
||||
m.extract_just_pressed_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 1);
|
||||
REQUIRE(buttons[0] == mouse_button::right);
|
||||
|
||||
m.extract_just_released_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 1);
|
||||
REQUIRE(buttons[0] == mouse_button::right);
|
||||
|
||||
buttons.clear();
|
||||
i.post_event(input::mouse_button_event{mouse_button::right, mouse_button_action::release});
|
||||
|
||||
m.extract_pressed_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 0);
|
||||
|
||||
m.extract_just_pressed_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 0);
|
||||
|
||||
m.extract_just_released_buttons(buttons);
|
||||
REQUIRE(buttons.size() == 1);
|
||||
REQUIRE(buttons[0] == mouse_button::right);
|
||||
}
|
||||
}
|
||||
SECTION("keyboard"){
|
||||
{
|
||||
input i;
|
||||
const keyboard& k = i.keyboard();
|
||||
|
||||
REQUIRE(k.input_text() == make_utf32(""));
|
||||
|
||||
i.post_event(input::input_char_event{'H'});
|
||||
i.post_event(input::input_char_event{'e'});
|
||||
|
||||
REQUIRE(k.input_text() == make_utf32("He"));
|
||||
|
||||
i.frame_tick();
|
||||
|
||||
REQUIRE(k.input_text() == make_utf32(""));
|
||||
}
|
||||
{
|
||||
input i;
|
||||
const keyboard& k = i.keyboard();
|
||||
|
||||
REQUIRE_FALSE(k.is_any_key_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_released());
|
||||
REQUIRE_FALSE(k.is_key_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_released(keyboard_key::enter));
|
||||
|
||||
i.post_event(input::keyboard_key_event{keyboard_key::enter, keyboard_key_action::press});
|
||||
|
||||
REQUIRE(k.is_any_key_pressed());
|
||||
REQUIRE(k.is_any_key_just_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_released());
|
||||
REQUIRE(k.is_key_pressed(keyboard_key::enter));
|
||||
REQUIRE(k.is_key_just_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_released(keyboard_key::enter));
|
||||
|
||||
i.frame_tick();
|
||||
|
||||
REQUIRE(k.is_any_key_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_released());
|
||||
REQUIRE(k.is_key_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_released(keyboard_key::enter));
|
||||
|
||||
i.post_event(input::keyboard_key_event{keyboard_key::enter, keyboard_key_action::repeat});
|
||||
|
||||
REQUIRE(k.is_any_key_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_released());
|
||||
REQUIRE(k.is_key_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_released(keyboard_key::enter));
|
||||
|
||||
i.post_event(input::keyboard_key_event{keyboard_key::enter, keyboard_key_action::release});
|
||||
|
||||
REQUIRE_FALSE(k.is_any_key_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_pressed());
|
||||
REQUIRE(k.is_any_key_just_released());
|
||||
REQUIRE_FALSE(k.is_key_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_pressed(keyboard_key::enter));
|
||||
REQUIRE(k.is_key_just_released(keyboard_key::enter));
|
||||
|
||||
i.frame_tick();
|
||||
|
||||
REQUIRE_FALSE(k.is_any_key_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_pressed());
|
||||
REQUIRE_FALSE(k.is_any_key_just_released());
|
||||
REQUIRE_FALSE(k.is_key_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_pressed(keyboard_key::enter));
|
||||
REQUIRE_FALSE(k.is_key_just_released(keyboard_key::enter));
|
||||
}
|
||||
{
|
||||
input i;
|
||||
const keyboard& k = i.keyboard();
|
||||
|
||||
std::vector<keyboard_key> keys;
|
||||
k.extract_pressed_keys(keys);
|
||||
k.extract_just_pressed_keys(keys);
|
||||
k.extract_just_released_keys(keys);
|
||||
REQUIRE(keys.size() == 0);
|
||||
|
||||
keys.clear();
|
||||
i.post_event(input::keyboard_key_event{keyboard_key::escape, keyboard_key_action::press});
|
||||
|
||||
k.extract_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
|
||||
k.extract_just_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 2);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
REQUIRE(keys[1] == keyboard_key::escape);
|
||||
|
||||
k.extract_just_released_keys(keys);
|
||||
REQUIRE(keys.size() == 2);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
REQUIRE(keys[1] == keyboard_key::escape);
|
||||
|
||||
keys.clear();
|
||||
i.frame_tick();
|
||||
|
||||
k.extract_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
|
||||
k.extract_just_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
|
||||
k.extract_just_released_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
|
||||
keys.clear();
|
||||
i.post_event(input::keyboard_key_event{keyboard_key::escape, keyboard_key_action::repeat});
|
||||
|
||||
k.extract_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
|
||||
k.extract_just_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
|
||||
k.extract_just_released_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
|
||||
keys.clear();
|
||||
i.post_event(input::keyboard_key_event{keyboard_key::escape, keyboard_key_action::release});
|
||||
|
||||
k.extract_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 0);
|
||||
|
||||
k.extract_just_pressed_keys(keys);
|
||||
REQUIRE(keys.size() == 0);
|
||||
|
||||
k.extract_just_released_keys(keys);
|
||||
REQUIRE(keys.size() == 1);
|
||||
REQUIRE(keys[0] == keyboard_key::escape);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user