Symphony Of Empires
state.cpp
Go to the documentation of this file.
1 // Eng3D - General purpouse game engine
2 // Copyright (C) 2021, Eng3D contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // state.cpp
20 //
21 // Abstract:
22 // Defines functions for the game state, these functions initialize the
23 // game to a "usable" state, which will allow it to manage resources and
24 // show them.
25 // ----------------------------------------------------------------------------
26 
27 // Required before GL/gl.h
28 #include <cstring>
29 #include <signal.h>
30 #ifdef E3D_TARGET_WINDOWS
31 # ifndef WINSOCK2_IMPORTED
32 # define WINSOCK2_IMPORTED
33 # include <winsock2.h>
34 # endif
35 # include <windows.h>
36 #endif
37 #ifdef E3D_TARGET_UNIX
38 # include <unistd.h>
39 # include <fenv.h>
40 #endif
41 #ifdef E3D_TARGET_SWITCH
42 # include <switch.h>
43 #endif
44 
45 #ifdef E3D_BACKEND_OPENGL
46 // MSVC does not know about glext, mingw does so we just use this ifdef
47 # include <GL/glew.h>
48 # ifndef _MSC_VER
49 # include <GL/glext.h>
50 # endif
51 # include <GL/gl.h>
52 #elif defined E3D_BACKEND_GLES
53 # include <GLES3/gl3.h>
54 #endif
55 #include <SDL.h>
56 #include <SDL_events.h>
57 #include <SDL_keycode.h>
58 #include <SDL_mouse.h>
59 #include <SDL_audio.h>
60 #include <SDL_joystick.h>
61 
62 #include "eng3d/state.hpp"
63 #include "eng3d/io.hpp"
64 #include "eng3d/audio.hpp"
65 #include "eng3d/texture.hpp"
66 #include "eng3d/material.hpp"
67 #include "eng3d/model.hpp"
68 #include "eng3d/log.hpp"
69 #include "eng3d/shader.hpp"
70 #include "eng3d/utils.hpp"
71 
72 // Used for the singleton
73 static Eng3D::State* g_state = nullptr;
74 
75 #if defined _WIN32
76 // Need to define setenv
77 int setenv(const char* name, const char* value, int overwrite)
78 {
79  int errcode = 0;
80  if(!overwrite) {
81  size_t envsize = 0;
82  errcode = getenv_s(&envsize, NULL, 0, name);
83  if(errcode || envsize) return errcode;
84  }
85  return _putenv_s(name, value);
86 }
87 #endif
88 #if defined E3D_BACKEND_OPENGL
89 // Callback function for printing debug statements
90 static void GLAPIENTRY GLDebugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei, const GLchar* msg, const void*) {
91  std::string _source;
92  switch(source) {
93  case GL_DEBUG_SOURCE_API:
94  _source = "API";
95  break;
96  case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
97  _source = "WINDOW SYSTEM";
98  break;
99  case GL_DEBUG_SOURCE_SHADER_COMPILER:
100  _source = "SHADER COMPILER";
101  break;
102  case GL_DEBUG_SOURCE_THIRD_PARTY:
103  _source = "THIRD PARTY";
104  break;
105  case GL_DEBUG_SOURCE_APPLICATION:
106  _source = "APPLICATION";
107  break;
108  case GL_DEBUG_SOURCE_OTHER:
109  _source = "UNKNOWN";
110  break;
111  default:
112  _source = "UNKNOWN";
113  break;
114  }
115 
116  std::string _type;
117  switch(type) {
118  case GL_DEBUG_TYPE_ERROR:
119  _type = "ERROR";
120  break;
121  case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
122  _type = "DEPRECATED BEHAVIOR";
123  break;
124  case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
125  _type = "UDEFINED BEHAVIOR";
126  break;
127  case GL_DEBUG_TYPE_PORTABILITY:
128  _type = "PORTABILITY";
129  break;
130  case GL_DEBUG_TYPE_PERFORMANCE:
131  _type = "PERFORMANCE";
132  break;
133  case GL_DEBUG_TYPE_OTHER:
134  _type = "OTHER";
135  break;
136  case GL_DEBUG_TYPE_MARKER:
137  _type = "MARKER";
138  break;
139  default:
140  _type = "UNKNOWN";
141  break;
142  }
143 
144  std::string _severity;
145  switch(severity) {
146  case GL_DEBUG_SEVERITY_HIGH:
147  _severity = "HIGH";
148  break;
149  case GL_DEBUG_SEVERITY_MEDIUM:
150  _severity = "MEDIUM";
151  break;
152  case GL_DEBUG_SEVERITY_LOW:
153  _severity = "LOW";
154  break;
155  case GL_DEBUG_SEVERITY_NOTIFICATION:
156  _severity = "NOTIFICATION";
157  return;
158  default:
159  _severity = "UNKNOWN";
160  break;
161  }
162 
163  std::unique_ptr<char[]> tmpbuf(new char[512]);
164  snprintf(tmpbuf.get(), 512, "%d: %s of %s severity, raised from %s: %s", id, _type.c_str(), _severity.c_str(), _source.c_str(), msg);
165  // Do not put double-newlines
166  if(std::strchr(tmpbuf.get(), '\n') == nullptr) {
167  Eng3D::Log::debug("opengl_msg", std::string(tmpbuf.get()) + "\n");
168  } else {
169  Eng3D::Log::debug("opengl_msg", tmpbuf.get());
170  }
171 }
172 #endif
173 
174 //
175 // Installer
176 //
178  : s{ _s }
179 {
180  // Make sure we're the only state being installed
181  if(g_state != nullptr)
182  CXX_THROW(std::runtime_error, "Duplicate instancing of GameState");
183  g_state = &s;
184 
185 #ifdef E3D_TARGET_UNIX
186  // Catch NaNs and stuff
187  //feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);
188 #endif
189 
190 #ifdef E3D_TARGET_SWITCH
191  ::consoleDebugInit(debugDevice_SVC);
192  Eng3D::Log::debug("engine", "Hello world!");
193  // Make sure to initialize RomFS properly
194  ::romfsInit();
195  ::chdir("romfs:/");
196 #endif
197 
198  const int seed = (int)((uint32_t)time(NULL) * (uint32_t)getpid());
199  Eng3D::Log::debug("engine", Eng3D::translate_format("Using random seed of %i", seed));
200  std::srand(seed);
201 
202 #if defined E3D_BACKEND_OPENGL || defined E3D_BACKEND_GLES
203  // Override (if not set already) MESA variables so it can work on old systems
204  setenv("MESA_GL_VERSION_OVERRIDE", "4.6", 0);
205  setenv("MESA_GLSL_VERSION_OVERRIDE", "440", 0);
206 #endif
207 
208  // Handle SIGPIPE for networking code
209 #ifndef _WIN32
210  struct sigaction sa{};
211  sa.sa_restorer = []() -> void {
212  Eng3D::Log::debug("sigpipe", translate("Caught a pipe signal"));
213  };
214  sigaction(SIGPIPE, &sa, NULL);
215 #endif
216  std::string canonical_name = translate("Symphony of Empires");
217 
218  // Startup-initialization of SDL
219  if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
220  CXX_THROW(std::runtime_error, Eng3D::translate_format("Failed to initialize SDL %s", SDL_GetError()));
221  SDL_ShowCursor(SDL_DISABLE);
222 #if defined E3D_BACKEND_OPENGL || defined E3D_BACKEND_GLES // Normal PC computer
223  SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
224  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
225 
226  // Create the initial window
227 #ifdef E3D_TARGET_SWITCH
228  s.width = 1280;
229  s.height = 720;
230 #else
231  s.width = 1024;
232  s.height = 720;
233 #endif
234  Eng3D::Log::debug("sdl2", Eng3D::translate_format("New window %u x %u", s.width, s.height));
235 #ifdef E3D_TARGET_SWITCH
236  s.window = SDL_CreateWindow(canonical_name.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, s.width, s.height, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE);
237 #else
238  s.window = SDL_CreateWindow(canonical_name.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, s.width, s.height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
239 #endif
240  if(s.window == nullptr)
241  CXX_THROW(std::runtime_error, Eng3D::translate_format("Failed to initialize SDL window %s", SDL_GetError()));
242 
243  // OpenGL configurations
244  s.context = static_cast<void*>(SDL_GL_CreateContext(s.window));
245  if(s.context == nullptr)
246  CXX_THROW(std::runtime_error, Eng3D::translate_format("Failed to initialize SDL context %s", SDL_GetError()));
247  SDL_GL_SetSwapInterval(1);
248 
249  Eng3D::Log::debug("opengl", Eng3D::translate_format("OpenGL Version: %s", (const char*)glGetString(GL_VERSION)));
250 # ifdef E3D_BACKEND_OPENGL
251  glewExperimental = GL_TRUE;
252  int r = glewInit();
253  if(r != GLEW_OK)
254  CXX_THROW(std::runtime_error, Eng3D::translate_format("Failed to ininitializeit GLEW %s", glewGetErrorString(r)));
255 # endif
256 
257  GLint size;
258  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
259  Eng3D::Log::debug("gamestate", Eng3D::translate_format("Maximum texture size: %zu", size));
260 
261 # ifdef E3D_BACKEND_OPENGL
262  glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST);
263 # ifndef NDEBUG
264  glEnable(GL_DEBUG_OUTPUT);
265  glDebugMessageCallback(GLDebugMessageCallback, 0);
266 # endif
267 # endif
268 
269 # ifdef E3D_BACKEND_OPENGL
270  glEnable(GL_MULTISAMPLE);
271  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
272  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 1);
273 # endif
274 
275  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
276  glEnable(GL_BLEND);
277  glEnable(GL_TEXTURE_2D);
278 # ifdef E3D_BACKEND_OPENGL
279  glEnableClientState(GL_VERTEX_ARRAY);
280  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
281 # endif
282 
283  //glEnable(GL_CULL_FACE);
284  //glCullFace(GL_BACK);
285  //glFrontFace(GL_CW);
286 
287  glEnable(GL_DEPTH_TEST);
288  glDepthMask(GL_TRUE);
289  glDepthFunc(GL_LEQUAL);
290 # ifdef E3D_BACKEND_OPENGL
291  glDepthRange(0.f, 1.f);
292 # else
293  glDepthRangef(0.f, 1.f);
294 # endif
295  glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
296  glLineWidth(4.f);
297 #endif
298 }
299 
301 {
302 #if defined E3D_BACKEND_OPENGL || defined E3D_BACKEND_GLES
303  SDL_GL_DeleteContext(s.context);
304 #endif
305  SDL_DestroyWindow(s.window);
306  SDL_QuitSubSystem(SDL_INIT_VIDEO);
307  SDL_Quit();
308 #ifdef E3D_TARGET_SWITCH
309  // Make sure to gracefully unmount
310  ::romfsExit();
311 #endif
312  g_state = nullptr;
313 }
314 
315 //
316 // State
317 //
318 Eng3D::State::State(const std::vector<std::string>& pkg_paths)
319  : string_man(*this),
320  installer(*this),
321  package_man(*this, pkg_paths), // Initialize the IO first, as other subsystems may require access to files (i.e the UI context)
322  audio_man(*this),
323  tex_man(*this),
324  material_man(*this),
325  model_man(*this),
326  ttf_man(*this),
327  ui_ctx(*this)
328 {
329  // Plugins system (still wip)
330 #if 0
331  for(const auto& plugin : Path::get_all("plugin.dll")) {
332 #ifdef E3D_TARGET_WINDOWS
333  HINSTANCE hGetProcIDDLL = LoadLibrary(plugin.c_str());
334  // This shouldn't happen - like ever!
335  if(!hGetProcIDDLL) {
336  Eng3D::Log::error("plugin", Eng3D::translate_format("DLL file %s not found", plugin.c_str()));
337  continue;
338  }
339 
340  typedef int(__stdcall* plugin_dll_entry_t)(const char* gameid, int gamever);
341  plugin_dll_entry_t entry = (plugin_dll_entry_t)GetProcAddress(hGetProcIDDLL, "__unirend_entry");
342  if(!entry) {
343  Eng3D::Log::warning("plugin", Eng3D::translate_format("Can't find __unirend_entry on %s", plugin.c_str()));
344  continue;
345  }
346 
347  int r = entry("SYMPHONY_EMPIRES", 0x00F0);
348  if(r != 0) {
349  Eng3D::Log::warning("plugin", Eng3D::translate_format("Error %i on plugin %s", i, plugin.c_str()));
350  }
351 #endif
352  }
353 #endif
354  ui_ctx.resize(width, height);
355 
356  // Initialize joysticks if any
357  SDL_JoystickOpen(0);
358  SDL_JoystickOpen(1);
359 }
360 
362  this->run = false;
363 }
364 
366  // Compile built-in shaders
367  const auto read_file = [this](const std::string& file_name) {
368  return this->package_man.get_unique("shaders/" + file_name)->read_all();
369  };
370  const auto load_fragment_shader = [read_file](std::string file_name) {
371  return std::make_unique<Eng3D::OpenGL::FragmentShader>(read_file(file_name));
372  };
373  const auto load_vertex_shader = [read_file](std::string file_name) {
374  return std::make_unique<Eng3D::OpenGL::VertexShader>(read_file(file_name));
375  };
376 
377  builtin_shaders.clear();
378 #if defined E3D_BACKEND_OPENGL || defined E3D_BACKEND_GLES
379  // Big library used mostly by every shader, compiled for faster linking or other stuff
380  builtin_shaders["fs_lib"] = load_fragment_shader("lib.fs");
381  // 2D generic fragment shader
382  builtin_shaders["fs_2d"] = load_fragment_shader("2d.fs");
383  // 2D generic vertex shader
384  builtin_shaders["vs_2d"] = load_vertex_shader("2d.vs");
385  // 3D generic fragment shader
386  builtin_shaders["fs_3d"] = load_fragment_shader("3d.fs");
387  // 3D generic vertex shader
388  builtin_shaders["vs_3d"] = load_vertex_shader("3d.vs");
389  // 3D tree fragment shader
390  builtin_shaders["fs_tree"] = load_fragment_shader("tree.fs");
391  // 3D tree vertex shader
392  builtin_shaders["vs_tree"] = load_vertex_shader("tree.vs");
393  builtin_shaders["vs_font_sdf"] = load_vertex_shader("font_sdf.vs");
394  builtin_shaders["fs_font_sdf"] = load_fragment_shader("font_sdf.fs");
395  // 2D Piechart shaders
396  builtin_shaders["vs_piechart"] = load_vertex_shader("piechart.vs");
397  builtin_shaders["fs_piechart"] = load_fragment_shader("piechart.fs");
398 #endif
399 }
400 
401 void Eng3D::State::clear() const {
402 #if defined E3D_BACKEND_OPENGL || defined E3D_BACKEND_GLES
403  glClearColor(0, 0, 0, 1);
404  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
405 # ifdef E3D_BACKEND_OPENGL
406  glClearDepth(1.f);
407 # endif
408 #else
410 #endif
411 }
412 
414 #if defined E3D_BACKEND_OPENGL || defined E3D_BACKEND_GLES
415  // Required by macOS
416  glBindFramebuffer(GL_FRAMEBUFFER, 0);
417  SDL_GL_SwapWindow(window);
418 #else
419 
420 #endif
421 }
422 
423 #include "eng3d/ui/ui.hpp"
424 #include "eng3d/ui/widget.hpp"
425 #include "eng3d/ui/window.hpp"
426 #include "eng3d/ui/text.hpp"
428  // Check window size every update needed cause the window sometimes changes size
429  // without calling the change window size event
430  SDL_GetWindowSize(this->window, &this->width, &this->height);
431  this->ui_ctx.resize(this->width, this->height);
432  this->handle_resize();
433 
434  SDL_Event event;
435  while(SDL_PollEvent(&event)) {
436  switch(event.type) {
437  case SDL_MOUSEBUTTONDOWN: {
440  e.type = e.from_sdl(event.button.button);
441  e.hold = true;
442  handle_mouse_btn(e);
443  } break;
444  case SDL_MOUSEBUTTONUP: {
447  e.type = e.from_sdl(event.button.button);
448  e.hold = false;
449  handle_mouse_btn(e);
450  } break;
451  case SDL_MOUSEMOTION: {
454  e.delta = this->mouse_pos - e.pos;
455  this->mouse_pos = e.pos;
456  handle_mouse_motion(e);
457  } break;
458  case SDL_MOUSEWHEEL: {
461  e.wheel.x = event.wheel.x;
462  e.wheel.y = event.wheel.y;
463  handle_mouse_wheel(e);
464  } break;
465  case SDL_KEYDOWN: {
466  Eng3D::Event::Key e{};
467  e.type = e.from_sdl(event.key.keysym.sym);
468  e.hold = true;
469  handle_key(e);
470  } break;
471  case SDL_KEYUP: {
472  Eng3D::Event::Key e{};
473  e.type = e.from_sdl(event.key.keysym.sym);
474  e.hold = false;
475  handle_key(e);
476  } break;
477  case SDL_TEXTINPUT: {
478  this->ui_ctx.check_text_input(event.text.text);
479  } break;
480  case SDL_QUIT:
481  this->run = false;
482  break;
483  default: break;
484  }
485  }
486 
487  const std::scoped_lock lock(this->ui_ctx.prompt_queue_mutex);
488  for(const auto& prompt : this->ui_ctx.prompt_queue) {
489  auto* win = new UI::Window(-256, 0, 512, 512);
490  win->origin = UI::Origin::CENTER_SCREEN;
491  win->set_text(prompt.first);
492  win->is_scroll = true;
493  win->set_close_btn_function([win](UI::Widget&) {
494  win->kill();
495  });
496  auto& txt = win->make_widget<UI::Text>(0, 0, win->width, win->height);
497  txt.text_color = Eng3D::Color{};
498  txt.set_text(prompt.second);
499  txt.is_scroll = true;
500  win->height = txt.y + txt.height;
501  win->y -= win->height / 2;
502  }
503  this->ui_ctx.prompt_queue.clear();
504  this->ui_ctx.clear_dead();
505 }
506 
507 void Eng3D::State::set_multisamples(int samples) const {
508 #ifdef E3D_BACKEND_OPENGL
509  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
510  SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
511 #endif
512 }
513 
515  return *g_state;
516 }
517 
522 void Eng3D::State::do_run(std::function<bool(void)> cond, std::function<void(void)> event, std::function<void(void)> render) {
523  this->current_frame_time = std::chrono::system_clock::now();
524  while(cond()) {
525  auto prev_num = std::chrono::duration<double>(this->current_frame_time.time_since_epoch()).count();
526  auto now_num = std::chrono::duration<double>(std::chrono::system_clock::now().time_since_epoch()).count();
527  this->current_frame_time = std::chrono::system_clock::now();
528  this->delta_time = now_num - prev_num;
529  event();
530  this->clear();
531  render();
532  if(this->show_ui) this->ui_ctx.render_all();
533  this->swap();
534  }
535 }
536 
538 
539 }
540 
542  if(!this->show_ui) return;
543  if(e.hold) {
545  if(this->ui_ctx.check_click(e.pos)) {
546  const std::scoped_lock lock(this->audio_man.sound_lock);
547  auto entries = this->package_man.get_multiple_prefix("sfx/click");
548  if(!entries.empty()) {
549  auto audio = this->audio_man.load(entries[rand() % entries.size()]->get_abs_path());
550  audio_man.sound_queue.push_back(audio);
551  }
552  return;
553  }
554  }
556  this->ui_ctx.check_mouse_released(e.pos);
557  }
558 }
559 
561  if(this->show_ui) {
562  this->ui_ctx.set_cursor_pos(e.pos);
563  this->ui_ctx.check_drag(e.pos);
564  this->ui_ctx.check_hover(e.pos);
565  }
566 }
567 
569  if(this->show_ui) {
570  this->ui_ctx.check_hover(e.pos);
571  if(this->ui_ctx.check_wheel(e.pos, e.wheel.y * 6)) return;
572  }
573 }
574 
576  if(e.hold) {
577  switch(e.type) {
579  this->run = !this->run;
580  break;
582  break;
583  default:
584  break;
585  }
586  }
587 }
Installer(Eng3D::State &s)
Definition: state.cpp:177
void swap()
Definition: state.cpp:413
void reload_shaders()
Definition: state.cpp:365
void do_event()
Definition: state.cpp:427
UI::Context ui_ctx
Definition: state.hpp:128
void set_multisamples(int samples) const
Definition: state.cpp:507
SDL_Window * window
Definition: state.hpp:107
State(const std::vector< std::string > &pkg_paths)
Definition: state.cpp:318
virtual void handle_key(const Eng3D::Event::Key &e)
Definition: state.cpp:575
virtual void handle_mouse_btn(const Eng3D::Event::MouseButton &e)
Definition: state.cpp:541
void do_run(std::function< bool(void)> cond, std::function< void(void)> event, std::function< void(void)> render)
Perform the main game loop.
Definition: state.cpp:522
static State & get_instance()
Definition: state.cpp:514
virtual void handle_mouse_motion(const Eng3D::Event::MouseMotion &e)
Definition: state.cpp:560
virtual void handle_mouse_wheel(const Eng3D::Event::MouseWheel &e)
Definition: state.cpp:568
void clear() const
Definition: state.cpp:401
virtual void handle_resize()
Definition: state.cpp:537
void resize(const int width, const int height)
Definition: ui.cpp:265
Multiline textbox that allows more descriptive paragraphs than the Label widget.
Definition: text.hpp:41
The master widget all the other widgets inherit from, do not use directly instead use one of the many...
Definition: widget.hpp:176
Eng3D::Color text_color
Definition: widget.hpp:332
Window widget, this widget is similar to a Group widget, the key difference is that this one can be m...
Definition: window.hpp:39
glm::ivec2 get_mouse_pos()
Definition: event.cpp:110
std::string translate(const std::string_view str)
Definition: string.cpp:76
void error(const std::string_view category, const std::string_view msg)
Definition: log.cpp:68
void debug(const std::string_view category, const std::string_view msg)
Definition: log.cpp:58
void warning(const std::string_view category, const std::string_view msg)
Definition: log.cpp:64
std::string translate_format(const std::string_view format, Args &&... args)
String formatter, with translation.
Definition: string.hpp:128
Primitive color type used through the engine.
Definition: color.hpp:32
Eng3D::Event::Key::Type type
Definition: event.hpp:48
bool hold
Whetever the key is being held.
Definition: event.hpp:50
Eng3D::Event::MouseButton::Type type
Button that is checked.
Definition: event.hpp:60
bool hold
Whetever the button is being held.
Definition: event.hpp:62
#define CXX_THROW(class,...)
Definition: utils.hpp:98