Beryllium.MonoInput
2.0.0
Prefix Reserved
dotnet add package Beryllium.MonoInput --version 2.0.0
NuGet\Install-Package Beryllium.MonoInput -Version 2.0.0
<PackageReference Include="Beryllium.MonoInput" Version="2.0.0" />
<PackageVersion Include="Beryllium.MonoInput" Version="2.0.0" />
<PackageReference Include="Beryllium.MonoInput" />
paket add Beryllium.MonoInput --version 2.0.0
#r "nuget: Beryllium.MonoInput, 2.0.0"
#:package Beryllium.MonoInput@2.0.0
#addin nuget:?package=Beryllium.MonoInput&version=2.0.0
#tool nuget:?package=Beryllium.MonoInput&version=2.0.0
BerylliumMonoInput
Input library for MonoGame — keyboard, mouse, gamepad and MIDI behind one small API, with per-frame press and release detection built in.
MonoGame hands you a snapshot of the hardware each frame (Keyboard.GetState(), Mouse.GetState(), …). Answering "was this key just pressed?" means keeping last frame's snapshot yourself and diffing it, for every input you care about. BerylliumMonoInput does that bookkeeping and reports every key, button and note through one four-state lifecycle.
Requirements
- .NET 10, Windows 10 build 19041 or newer (the MIDI support uses WinRT, so the library is Windows-only)
- MonoGame 3.8.1 or newer, WindowsDX or DesktopGL — you reference MonoGame yourself; this package does not impose a version on your project
Install
dotnet add package Beryllium.MonoInput
The four states
Every input — a key, a mouse button, a pad button, a MIDI note — is always in one of four states:
| State | Meaning | Lasts |
|---|---|---|
Idle |
Not being touched | until pressed |
Pressed |
Just went down | exactly one frame |
Down |
Still held | from the second frame until release |
Up |
Just released | exactly one frame |
Because Pressed and Up last a single frame, you can react once per press instead of every frame the input is held.
The checks — IsKeyDown(…) on the keyboard, button.Down on mouse and pad buttons, and so on — read those states like this:
| Check | True when |
|---|---|
| Idle | the input is untouched |
| Pressed | first frame of a press only |
| Down | every frame the input is held, the first one included, as in MonoGame's own IsKeyDown |
| Up | the frame the input is released |
| Active | anything but Idle: pressed, held or just released |
So the Down check covers both the Pressed and Down states. Compare State == ButtonStates.Down if you need only the frames after the first.
Quick start
Call Update() once per frame for each device family you use, then read the results:
using BerylliumMonoInput.Inputs;
using BerylliumMonoInput.Inputs.GamePad;
using BerylliumMonoInput.Inputs.Keyboard;
using BerylliumMonoInput.Inputs.Midi;
using BerylliumMonoInput.Inputs.Mouse;
public class MyGame : Game
{
protected override void Update(GameTime gameTime)
{
KeyboardManager.Update();
MouseManager.Update();
GamePadManager.Update();
MidiManager.Update();
if (KeyboardManager.IsKeyPressed(Keys.Space)) Jump();
base.Update(gameTime);
}
protected override void UnloadContent()
{
MidiManager.Dispose(); // only needed if you use MIDI
base.UnloadContent();
}
}
Keyboard
if (KeyboardManager.IsKeyPressed(Keys.Space)) Jump(); // fires once, on the press
if (KeyboardManager.IsKeyDown(Keys.Right)) MoveRight(); // fires every frame the key is down
if (KeyboardManager.IsKeyUp(Keys.Escape)) OpenMenu(); // fires once, on the release
if (KeyboardManager.IsCtrlDown && KeyboardManager.IsKeyPressed(Keys.S)) Save();
// Every key that isn't idle, for text entry or key-binding screens
foreach (var key in KeyboardManager.ActiveKeys)
Console.WriteLine($"{key.Key} is {key.State}");
IsKeyIdle, IsKeyPressed, IsKeyDown, IsKeyUp and IsKeyActive are the five checks above.
IsCtrlDown, IsShiftDown and IsAltDown treat the left and right modifiers as one. Being Down checks, they turn false on the frame the modifier is let go, so Ctrl+S can't fire when S goes down just as Ctrl comes up.
ActiveKeys is a read-only live view, and iterating it doesn't allocate, so it's safe to walk every frame.
Mouse
var mouse = MouseManager.MouseStatus;
if (mouse.LeftButton.Pressed) StartDrag(mouse.Position);
if (mouse.LeftButton.Down) Drag(mouse.PositionDelta);
if (mouse.LeftButton.Up) EndDrag();
if (mouse.WheelDelta != 0) Zoom(mouse.WheelDelta);
MouseStatus exposes LeftButton, MiddleButton, RightButton, XButton1 and XButton2, the cursor Position with its per-frame PositionDelta, and both wheels as a running total (WheelCumulativeValue) plus a per-frame WheelDelta.
If you only need part of the picture, you can poll selectively with UpdatePositionData(), UpdateWheelsData() or UpdateButtonsStates() instead of the full Update().
GamePad
var pad = GamePadManager.GamePadsStatus[(int)PlayerIndex.One];
if (pad.IsConnected)
{
if (pad.A.Pressed) Jump();
if (pad.LeftStickShift != Vector2.Zero) Move(pad.LeftStickShift);
if (pad.RightTriggerValue > 0.5f)
GamePadManager.SetVibration(PlayerIndex.One, 0.4f, 0.4f);
}
All four player slots are tracked. Each GamePadStatus carries the fifteen buttons, both sticks and both triggers — each with a per-frame delta (LeftStickShiftDelta, RightTriggerValueDelta, …). When a controller disconnects its buttons release and its sticks return to zero, so held inputs never get stuck.
MIDI
Devices are discovered in the background and show up in MidiManager.Devices during MidiManager.Update() — so the list is still empty while your game initializes. React to OnDevicesChanged rather than reading it once, connect to a device by id, then read the notes that are sounding:
MidiManager.OnDevicesChanged += () =>
{
if (MidiManager.SelectedDevice == null && MidiManager.Devices.Count > 0)
_ = MidiManager.TryConnectToMidiDevice(MidiManager.Devices[0].Id);
};
// …each frame, after MidiManager.Update():
foreach (var note in MidiManager.ActiveNotes)
if (note.State == ButtonStates.Pressed)
PlaySound(note.Note);
TryConnectToMidiDevice reports its outcome through OnDeviceSelected: the device on success, null if it could not be opened. Calling it again before an earlier call finishes is fine — only the latest call counts.
Notes follow the same lifecycle as buttons, including notes shorter than a single frame — a fast tap still reports Pressed for one frame rather than being missed. When the device is unplugged or you switch to another one, the notes held on it release, just as a disconnected gamepad's buttons do. Call MidiManager.Dispose() when your game shuts down.
Events
Prefer callbacks to polling? Every manager raises events, all on your game thread from inside Update():
MouseManager.OnButtonsStatesChanged += buttons => Console.WriteLine(buttons);
MouseManager.OnPositionChanged += position => Console.WriteLine(position);
MouseManager.OnWheelDeltaChanged += delta => Console.WriteLine(delta);
GamePadManager.OnStatusChanged += player => Console.WriteLine($"Pad {player} changed");
MidiManager.OnDevicesChanged += () => RefreshDeviceList();
MidiManager.OnDeviceSelected += device => Console.WriteLine(device?.Name);
Mouse button changes are reported once per frame with a consistent snapshot of all five buttons, so a handler never sees a half-updated state.
Threading
Everything public is main-thread. MIDI messages arrive from Windows on background threads, but they are only queued there — the queue is drained inside MidiManager.Update(), on your game thread, so your handlers never run off-thread.
Architecture
For a short tour of the classes and how they fit together, see Docs/Architecture.md.
Building and testing
dotnet test BerylliumMonoInput.sln
The xUnit suite runs the library against official MonoGame 3.8.1, the oldest release it supports. Like the library, it needs Windows.
License
MIT © Beryllium Studio
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-windows10.0.19041 is compatible. |
-
net10.0-windows10.0.19041
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 98 | 9/26/2026 |