Compare commits

...

4 Commits

Author SHA1 Message Date
561e806b5f Merge remote-tracking branch 'origin/main'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m41s
2024-12-06 11:54:25 -05:00
fa807256af Actually Fixed IsKeyDown 2024-12-06 11:54:19 -05:00
b10a4d1d39 gamepad stuff
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 7m4s
2024-12-04 10:16:54 -05:00
a3adbb6266 Fix Mouse Button errors, implement MouseWheel events
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m42s
2024-12-03 21:24:50 -05:00
12 changed files with 280 additions and 116 deletions

View File

@@ -57,9 +57,9 @@ namespace ReWindow {
class MouseWheelEvent : public MouseEvent
{
public:
int wheel_dt;
int WheelMovement;
MouseWheelEvent() = default;
MouseWheelEvent(int wheel) : MouseEvent(), wheel_dt(wheel) {}
MouseWheelEvent(int wheel) : MouseEvent(), WheelMovement(wheel) {}
};
class MouseButtonDownEvent : public MouseEvent {
@@ -67,14 +67,14 @@ namespace ReWindow {
MouseButton Button;
MouseButtonDownEvent() = default;
MouseButtonDownEvent(MouseButton Button) : MouseEvent() {}
MouseButtonDownEvent(MouseButton button) : MouseEvent(), Button(button) {}
};
class MouseButtonUpEvent : public MouseEvent {
public:
MouseButton Button;
MouseButtonUpEvent() = default;
MouseButtonUpEvent(MouseButton Button) : MouseEvent() {}
MouseButtonUpEvent(MouseButton button) : MouseEvent(), Button(button) {}
};
class WindowResizeRequestEvent : public RWindowEvent

View File

@@ -10,17 +10,18 @@
#pragma once
namespace ReWindow
{
namespace ReWindow {
class Gamepad;
class Xbox360Gamepad;
class XboxOneGamepad;
class PS4Gamepad;
class Ultimate8BitDoPro2Gamepad;
}
class InputDevice {}; // TODO: Remember to break InputDevice into it's own file and not define it twice!!!
class ReWindow::Gamepad {
public:
virtual ~Gamepad() = default;
};
class Gamepad : public InputDevice
{
};
class XboxGamepad : public Gamepad {};
class PS4Gamepad : public Gamepad {};
}
class ReWindow::XboxOneGamepad : public Gamepad {};
class ReWindow::PS4Gamepad : public Gamepad {};

View File

@@ -9,67 +9,101 @@
/// @edit 2024-07-29
#include <string>
#include <utility>
#include <J3ML/LinearAlgebra.hpp>
namespace ReWindow {
class GamepadButton;
class GamepadTrigger;
class GamepadThumbstick;
}
class GamepadButton {
class ReWindow::GamepadButton {
protected:
std::string mnemonic_btn_code;
public:
explicit GamepadButton(const std::string& mnemonic) : mnemonic_btn_code(mnemonic) { }
[[nodiscard]] std::string GetMnemonicButtonCode() const { return mnemonic_btn_code; }
explicit GamepadButton(std::string mnemonic) : mnemonic_btn_code(std::move(mnemonic)){}
[[nodiscard]] std::string GetMnemonicButtonCode() const;
/// Compares two GamepadButtons by their mnemonic button codes, not their activation state.
bool operator ==(const GamepadButton& rhs) const;
};
class GamepadTrigger {
class ReWindow::GamepadTrigger {
protected:
std::string mnemonic_trigger_code;
/// A float value between 0 and 1 representing how much the trigger has been pushed in by.
float position = 0.0f;
/// The minimum possible movement of the trigger from the at-rest position.
/// Movements less than this won't count.
float dead_zone = 0.0f;
public:
/// Returns a float value between 0-1 representing how much the trigger has been pushed in by.
/// (0 being unpressed, 1 being fully pressed)
float GetActuation() const;
/// TODO: Might be more appropriate in the Gamepad class representation.
void SetActuationThreshold(float minimum = 0.01f) const;
void SetDeadzone(float minimum = 0.01f);
[[nodiscard]] float GetActuation() const;
public:
explicit GamepadTrigger(std::string mnemonic) : mnemonic_trigger_code(std::move(mnemonic)){}
};
class GamepadThumbstick
{
class ReWindow::GamepadThumbstick {
protected:
std::string mnemonic_stick_code;
// X -1 is all the way left, X +1 is all the way right.
// Y -1 is all the way down, Y +1 is all the way up.
Vector2 position = {0.0f, 0.0f};
// The minimum possible movement from the center in any direction.
float dead_zone = 0.0f;
public:
/// Returns a Vector2 value representing the x,y coordinates of the joystick, with 0,0 being the center (at rest).
/// This vector ranges from length = 0 to length = 1 (i.e. the unit circle).
[[nodiscard]] Vector2 GetPosition() const;
/// Sets the deadzone range of the thumbstick.
/// Deadzone controls how far the stick must be moved before any movement is actually reported.
/// This is because the thumbstick at-rest will often still report movement.
/// If gamecode is architected to use the thumbstick position as a direction, without factoring in magnitude, this would cause problems.
void SetDeadzone(float minimum = 0.01f) const;
void SetDeadzone(float minimum = 0.01f);
public:
explicit GamepadThumbstick(std::string mnemonic) : mnemonic_stick_code(std::move(mnemonic)){}
};
using J3ML::LinearAlgebra::Vector2;
namespace GamepadButtons {
static const GamepadButton X {"X"};
static const GamepadButton Y {"Y"};
static const GamepadButton A {"A"};
static const GamepadButton B {"B"};
namespace ReWindow::GamepadButtons {
static const GamepadButton Triangle("");
static const GamepadButton Square("");
static const GamepadButton Circle("");
static const GamepadButton Cross("X");
static const auto Triangle = Y;
static const auto Square = X;
static const auto Circle = A;
static const auto Cross = B;
// If you like xbox :shrug:
static const GamepadButton Y = Triangle;
static const GamepadButton X = Square;
static const GamepadButton A = Cross;
static const GamepadButton B = Circle;
static const GamepadButton LButton("LB");
static const GamepadButton RButton("RB");
static const GamepadButton LeftBumper {"LB"};
static const GamepadButton RightBumper {"RB"};
/* For controllers where L2 & R2 is a button or counts as one when pressed all the way down.
* Gamecube & PS2 controllers do this. - Redacted. */
static const GamepadButton LButton2("LB2");
static const GamepadButton RButton2("RB2");
// The buttons when you press in the sticks.
static const GamepadButton LButton3("LB3");
static const GamepadButton RButton3("RB3");
// Will eventually need to handle making it not possible to, for ex. Press left and right at the same time.
static const GamepadButton DPadUp("DPU");
static const GamepadButton DPadDown("DPD");
static const GamepadButton DPadLeft("DPL");
static const GamepadButton DPadRight("DPR");
static const GamepadButton Select("SEL");
static const GamepadButton Start("START");
}
namespace GamepadTriggers
{
static const GamepadTrigger Left;
static const GamepadTrigger Right;
namespace ReWindow::GamepadTriggers {
static const ReWindow::GamepadTrigger Left {"LT"};
static const ReWindow::GamepadTrigger Right {"RT"};
}
namespace ReWindow::GamepadThumbsticks {
static const GamepadThumbstick Left {"LS"};
static const GamepadThumbstick Right {"RS"};
}

View File

@@ -19,9 +19,9 @@ private:
public:
static std::vector<Key> GetKeyboard();
Key();
Key() = default;
Key(const char* charcode, X11Scancode scancode, WindowsScancode wSc);
const char* CharCode;
std::string Mnemonic;
X11Scancode x11ScanCode;
WindowsScancode winScanCode;
bool operator==(const Key& rhs) const;

View File

@@ -10,13 +10,16 @@
#pragma once
#include <string>
class MouseButton {
public:
MouseButton();
explicit MouseButton(const char* charcode, unsigned int index);
const char* CharCode;
std::string Mnemonic;
unsigned int ButtonIndex;
MouseButton() = default;
explicit MouseButton(const std::string& charcode, unsigned int index);
bool operator == (const MouseButton& mb) const;
bool operator<(const MouseButton& rhs) const;
MouseButton(const MouseButton&) = default;
@@ -25,14 +28,17 @@ public:
namespace MouseButtons
{
static const MouseButton Left {"l", 1};
static const MouseButton Right {"r", 2};
static const MouseButton Middle {"m", 3};
static const MouseButton MWheelUp {"1", 4};
static const MouseButton MWheelDown {"2", 5};
static const MouseButton Mouse4 {"4", 8};
static const MouseButton Mouse5 {"5", 9};
static const MouseButton Unimplemented {"u", 0};
static const MouseButton Left ("L", 1);
static const MouseButton Right ("R", 2);
static const MouseButton Middle ("M", 3);
static const MouseButton Mouse4 ("4", 8);
static const MouseButton Mouse5 ("5", 9);
static const MouseButton Unimplemented ("?", 0);
/// NOTE: IsMouseButtonDown will not return correctly for the mouse-wheel-buttons, because the action is effectively instantaneous.
static const MouseButton MWheelUp ("U", 4);
/// NOTE: IsMouseButtonDown will not return correctly for the mouse-wheel-buttons, because the action is effectively instantaneous.
static const MouseButton MWheelDown ("D", 5);
}
MouseButton GetMouseButtonFromXButton(unsigned int button);

View File

@@ -10,6 +10,7 @@
#include <rewindow/types/gamepadbutton.h>
#include <J3ML/LinearAlgebra.hpp>
#include <rewindow/types/WindowEvents.hpp>
#include <format>
enum class RWindowFlags: uint8_t {
@@ -29,8 +30,6 @@ enum class RenderingAPI: uint8_t {
VULKAN = 1,
};
namespace ReWindow
{
using J3ML::LinearAlgebra::Vector2;
@@ -47,8 +46,57 @@ namespace ReWindow
class MouseState {
public:
std::map<MouseButton, bool> PressedBtns;
Vector2 Pos;
struct
{
bool LMB = false;
bool RMB = false;
bool MMB = false;
bool SideButton1 = false;
bool SideButton2 = false;
bool MWheelUp = false;
bool MWheelDown = false;
} Buttons;
Vector2 Position;
int Wheel = 0;
[[nodiscard]] bool IsDown(const MouseButton& btn) const
{
if (btn == MouseButtons::Left) return Buttons.LMB;
if (btn == MouseButtons::Right) return Buttons.RMB;
if (btn == MouseButtons::Middle) return Buttons.MMB;
if (btn == MouseButtons::Mouse4) return Buttons.SideButton1;
if (btn == MouseButtons::Mouse5) return Buttons.SideButton2;
//if (btn == MouseButtons::MWheelUp) return Buttons.MWheelUp;
//if (btn == MouseButtons::MWheelDown) return Buttons.MWheelDown;
return false; // Unknown button?
}
void Set(const MouseButton& btn, bool state)
{
if (btn == MouseButtons::Left) Buttons.LMB = state;
if (btn == MouseButtons::Right) Buttons.RMB = state;
if (btn == MouseButtons::Middle) Buttons.MMB = state;
if (btn == MouseButtons::Mouse4) Buttons.SideButton1 = state;
if (btn == MouseButtons::Mouse5) Buttons.SideButton2 = state;
//if (btn == MouseButtons::MWheelUp) Buttons.MWheelUp = state;
//if (btn == MouseButtons::MWheelDown) Buttons.MWheelDown = state;
}
bool& operator[](const MouseButton& btn)
{
if (btn == MouseButtons::Left) return Buttons.LMB;
if (btn == MouseButtons::Right) return Buttons.RMB;
if (btn == MouseButtons::Middle) return Buttons.MMB;
if (btn == MouseButtons::Mouse4) return Buttons.SideButton1;
if (btn == MouseButtons::Mouse5) return Buttons.SideButton2;
//if (btn == MouseButtons::MWheelUp) return Buttons.MWheelUp;
//if (btn == MouseButtons::MWheelDown) return Buttons.MWheelDown;
throw std::runtime_error("Attempted to handle unmapped mouse button");
}
};
/// RWindow is a class implementation of a platform-independent window abstraction.
@@ -111,6 +159,8 @@ namespace ReWindow
/// @see GetAccurateMouseCoordinates().
Vector2 GetMouseCoordinates() const;
int GetMouseWheelPersistent() const { return currentMouse.Wheel;}
/// Sets which rendering API is to be used with this window.
void SetRenderer(RenderingAPI api);
@@ -149,7 +199,7 @@ namespace ReWindow
[[nodiscard]] bool IsKeyDown(Key key) const;
/// Returns whether the given mouse button is currently being pressed.
[[nodiscard]] bool IsMouseButtonDown(MouseButton button) const;
[[nodiscard]] bool IsMouseButtonDown(const MouseButton &button) const;
/// Sets whether or not to make the window fullscreen.
/// @note This is implemented per-OS, and as such, it simply requests the OS to do what we want. No guarantee about follow-through can be given.
@@ -301,6 +351,7 @@ namespace ReWindow
MouseState previousMouse;
RenderingAPI renderer = RenderingAPI::OPENGL;
//Vector2 mouse_coords = {0, 0};
@@ -340,9 +391,9 @@ namespace ReWindow
/// @note This will be invoked **after** the window-open procedure completes.
void processOnOpen();
/// Executes event handlers for mouse press events.
void processMousePress(MouseButton btn);
void processMousePress(const MouseButton& btn);
/// Executes event handlers for mouse release events.
void processMouseRelease(MouseButton btn);
void processMouseRelease(const MouseButton& btn);
/// Executes event handlers for window focus events.
void processFocusIn();
/// Executes event handlers for window unfocus events.

View File

@@ -32,6 +32,32 @@ class MyWindow : public ReWindow::RWindow {
auto pos = GetMouseCoordinates();
//std::cout << pos.x << ", " << pos.y << std::endl;
//if (IsMouseButtonDown(MouseButtons::MWheelUp))
//std::cout << "WheelUp Mouse Button" << std::endl;
//if (IsMouseButtonDown(MouseButtons::MWheelDown))
//std::cout << "WheelDown Mouse Button" << std::endl;
if (IsMouseButtonDown(MouseButtons::Left))
std::cout << "Left Mouse Button" << std::endl;
if (IsMouseButtonDown(MouseButtons::Right))
std::cout << "Right Mouse Button" << std::endl;
if (IsMouseButtonDown(MouseButtons::Middle))
std::cout << "Middle Mouse Button" << std::endl;
if (IsMouseButtonDown(MouseButtons::Mouse4))
std::cout << "Mouse4 Mouse Button" << std::endl;
if (IsMouseButtonDown(MouseButtons::Mouse5))
std::cout << "Mouse5 Mouse Button" << std::endl;
if (IsKeyDown(Keys::N))
std::cout << "Gotteem" << std::endl;
RWindow::OnRefresh(elapsed);
}
@@ -39,6 +65,18 @@ class MyWindow : public ReWindow::RWindow {
std::cout << "resized to " << e.Size.x << ", " << e.Size.y << std::endl;
return true;
}
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &e) override
{
std::cout << "Overload Down: " << e.Button.Mnemonic << std::endl;
RWindow::OnMouseButtonDown(e);
}
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &e) override
{
std::cout << "Overload Up: " << e.Button.Mnemonic << std::endl;
RWindow::OnMouseButtonUp(e);
}
};
int main() {
@@ -95,14 +133,19 @@ int main() {
window->OnKeyDownEvent += [&] (ReWindow::KeyDownEvent e) {
jlog::Debug(e.key.CharCode);
jlog::Debug(e.key.Mnemonic);
};
window->OnMouseButtonDownEvent += [&] (ReWindow::MouseButtonDownEvent e) {
jlog::Debug(e.Button.CharCode);
jlog::Debug(e.Button.Mnemonic + std::to_string(e.Button.ButtonIndex));
};
window->OnMouseWheelEvent += [&, window] (ReWindow::MouseWheelEvent e)
{
std::cout << window->GetMouseWheelPersistent() << std::endl;
};
while (window->IsAlive()) {
window->PollEvents();
window->ManagedRefresh();

View File

@@ -166,18 +166,34 @@ void RWindow::PollEvents() {
}
if (xev.type == ButtonRelease) {
Logger::Debug(std::format("Recieved event '{}'", "ButtonRelease"));
MouseButton button = GetMouseButtonFromXButton(xev.xbutton.button);
processMouseRelease(button);
// Mouse Wheel fires both the ButtonPress and ButtonRelease instantaneously.
// Therefore, we handle it as a specific MouseWheel event rather than a MouseButton event,
// and only call on ButtonPress, otherwise it will appear to duplicate the mouse wheel scroll.
if (xev.xbutton.button != 4 && xev.xbutton.button != 5) {
MouseButton button = GetMouseButtonFromXButton(xev.xbutton.button);
Logger::Debug(std::format("Recieved event '{}'", "ButtonRelease"));
processMouseRelease(button);
}
}
if (xev.type == ButtonPress) {
MouseButton button = GetMouseButtonFromXButton(xev.xbutton.button);
Logger::Debug(std::format("Window event: MouseButtonPress {}", button.CharCode));
// Mouse Wheel fires both the ButtonPress and ButtonRelease instantaneously.
// Therefore, we handle it as a specific MouseWheel event rather than a MouseButton event,
// and only call on ButtonPress, otherwise it will appear to duplicate the mouse wheel scroll.
if (xev.xbutton.button == 4) {
processMouseWheel(-1);
} else if (xev.xbutton.button == 5) {
processMouseWheel(1);
} else
{
MouseButton button = GetMouseButtonFromXButton(xev.xbutton.button);
processMousePress(button);
Logger::Debug(std::format("Window event: MouseButtonPress {}", button.Mnemonic));
processMousePress(button);
}
}
if (xev.type == Expose)
@@ -213,7 +229,7 @@ void RWindow::PollEvents() {
}
previousKeyboard = currentKeyboard;
previousMouse.PressedBtns = currentMouse.PressedBtns;
previousMouse.Buttons = currentMouse.Buttons;
}

View File

@@ -33,7 +33,7 @@ RWindow::RWindow(const std::string& title, int width, int height, RenderingAPI r
title(title), width(width), height(height), renderer(renderer) {}
Vector2 RWindow::GetMouseCoordinates() const {
return currentMouse.Pos;
return currentMouse.Position;
}
bool RWindow::GetFlag(RWindowFlags flag) const {
@@ -66,9 +66,9 @@ void RWindow::processFocusOut()
OnFocusLostEvent(event);
}
void RWindow::processMousePress(MouseButton btn)
void RWindow::processMousePress(const MouseButton& btn)
{
currentMouse.PressedBtns[btn] = true;
currentMouse.Set(btn, true);
auto eventData = MouseButtonDownEvent(btn);
OnMouseButtonDown(eventData);
OnMouseButtonDownEvent(eventData);
@@ -77,15 +77,15 @@ void RWindow::processMousePress(MouseButton btn)
void RWindow::processMouseMove(Vector2 last_pos, Vector2 new_pos)
{
currentMouse.Pos = new_pos;
currentMouse.Position = new_pos;
auto eventData = MouseMoveEvent(new_pos);
OnMouseMove(eventData);
OnMouseMoveEvent(eventData);
}
void RWindow::processMouseRelease(MouseButton btn)
void RWindow::processMouseRelease(const MouseButton& btn)
{
currentMouse.PressedBtns[btn] = false;
currentMouse.Set(btn, false);
auto eventData = MouseButtonUpEvent(btn);
OnMouseButtonUp(eventData);
OnMouseButtonUpEvent(eventData);
@@ -141,16 +141,15 @@ void RWindow::SetSize(const Vector2& size) {
}
bool RWindow::IsKeyDown(Key key) const {
for (const auto& pair : currentKeyboard.PressedKeys)
if (pair.first == key && pair.second)
return true;
return false;
if (currentKeyboard.PressedKeys.contains(key))
return currentKeyboard.PressedKeys.at(key);
return false; // NOTE: Key may not be mapped!!
}
bool RWindow::IsMouseButtonDown(MouseButton button) const {
bool RWindow::IsMouseButtonDown(const MouseButton &button) const {
// TODO: Implement MouseButton map
for (const auto& pair : currentMouse.PressedBtns)
return pair.first == button && pair.second;
return currentMouse.IsDown(button);
}
void RWindow::processKeyPress(Key key) {
@@ -188,12 +187,12 @@ void RWindow::Refresh() {
OnRefresh(delta_time);
// Only call once and cache the result.
currentMouse.Pos = GetAccurateMouseCoordinates();
currentMouse.Position = GetAccurateMouseCoordinates();
/// TODO: Implement optional minimum epsilon to trigger a Mouse Update.
if (currentMouse.Pos != previousMouse.Pos) {
processMouseMove(previousMouse.Pos, currentMouse.Pos);
previousMouse.Pos = currentMouse.Pos;
if (currentMouse.Position != previousMouse.Position) {
processMouseMove(previousMouse.Position, currentMouse.Position);
previousMouse.Position = currentMouse.Position;
}
}
@@ -224,9 +223,12 @@ void RWindow::UpdateFrameTiming(float frame_time) {
void RWindow::processMouseWheel(int scrolls)
{
currentMouse.Wheel += scrolls;
auto ev = MouseWheelEvent(scrolls);
OnMouseWheel(ev);
OnMouseWheelEvent(ev);
previousMouse.Wheel = currentMouse.Wheel;
}

View File

@@ -1,9 +1,25 @@
#include <rewindow/types/gamepadbutton.h>
bool GamepadButton::operator==(const GamepadButton &rhs) const {
bool ReWindow::GamepadButton::operator ==(const GamepadButton &rhs) const {
return this->GetMnemonicButtonCode() == rhs.GetMnemonicButtonCode();
}
void GamepadThumbstick::SetDeadzone(float minimum) const {
std::string ReWindow::GamepadButton::GetMnemonicButtonCode() const {
return mnemonic_btn_code;
}
void ReWindow::GamepadThumbstick::SetDeadzone(float minimum) {
dead_zone = minimum;
}
Vector2 ReWindow::GamepadThumbstick::GetPosition() const {
return position;
}
void ReWindow::GamepadTrigger::SetDeadzone(float minimum) {
dead_zone = minimum;
}
float ReWindow::GamepadTrigger::GetActuation() const {
return position;
}

View File

@@ -4,26 +4,22 @@
std::vector<Key> Key::GetKeyboard() { return keyboard; }
Key::Key() {
keyboard.push_back(*this);
}
Key::Key(const char* charcode, X11Scancode scancode, WindowsScancode sc)
: CharCode(charcode), x11ScanCode(scancode), winScanCode(sc)
: Mnemonic(charcode), x11ScanCode(scancode), winScanCode(sc)
{
//TODO doing this is what crashes the program.
keyboard.push_back(*this);
}
bool Key::operator==(const Key &rhs) const {
//This is not a good workaround.
return (this->x11ScanCode == rhs.x11ScanCode);
}
bool Key::operator<(const Key &rhs) const {
return (this->CharCode < rhs.CharCode);
return (this->Mnemonic < rhs.Mnemonic);
}

View File

@@ -1,12 +1,11 @@
#include <rewindow/types/mousebutton.h>
#include <string>
#include <X11/X.h>
#include "rewindow/logger/logger.h"
MouseButton::MouseButton() {
}
MouseButton::MouseButton(const char* charcode, unsigned int index) {
this->CharCode = charcode;
MouseButton::MouseButton(const std::string& charcode, unsigned int index) {
this->Mnemonic = charcode;
this->ButtonIndex = index;
}
@@ -15,7 +14,7 @@ bool MouseButton::operator==(const MouseButton &mb) const {
}
bool MouseButton::operator<(const MouseButton &rhs) const {
return (this->CharCode < rhs.CharCode);
return (this->ButtonIndex < rhs.ButtonIndex);
}
@@ -24,8 +23,8 @@ MouseButton GetMouseButtonFromXButton(unsigned int button) {
case 1: return MouseButtons::Left;
case 2: return MouseButtons::Middle;
case 3: return MouseButtons::Right;
case 4: return MouseButtons::MWheelUp;
case 5: return MouseButtons::MWheelDown;
//case 4: return MouseButtons::MWheelUp;
//case 5: return MouseButtons::MWheelDown;
//For *whatever* reason. These aren't in X.h
case 8: return MouseButtons::Mouse4;
case 9: return MouseButtons::Mouse5;