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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Beryllium.MonoInput" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Beryllium.MonoInput" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Beryllium.MonoInput" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Beryllium.MonoInput --version 2.0.0
                    
#r "nuget: Beryllium.MonoInput, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Beryllium.MonoInput@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Beryllium.MonoInput&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Beryllium.MonoInput&version=2.0.0
                    
Install as a Cake Tool

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 Compatible and additional computed target framework versions.
.NET net10.0-windows10.0.19041 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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