Crates.io | nuhxboard |
lib.rs | nuhxboard |
version | 0.6.0 |
source | src |
created_at | 2024-01-30 02:49:59.506144 |
updated_at | 2024-12-24 23:42:46.14629 |
description | Cross-platform input visualizer |
homepage | |
repository | https://github.com/justDeeevin/NuhxBoard |
max_upload_size | |
id | 1119762 |
size | 426,843 |
Nohboard is great! But it’s only for Windows. The only alternative is Tiyenti’s KBDisplay, which is quite nice, but limited in functionality. My primary goal with this project is to replicate the functionality of NohBoard in a cross-compatible manner. More specifically, I want to be able to feed in any NohBoard config file and have near-identical output to NohBoard.
I may add functionality where I think it would fit, but I want to prioritize interoperability with NohBoard. Call it just another incentive for gamers to switch to Linux.
NuhxBoard is made with customizability in mind. Every part of its appearance and behavior is configurable. At its core, NuhxBoard is an app that loads keyboard layouts and styles. A keyboard layout defines the positions, shapes, and behaviors of keys. It also defines the dimensions of the window. A style defines colors, fonts, and images.
Keyboard layouts are grouped into categories, and styles (aside from global ones) correspond to specific keyboard layouts.
Keyboards are located in ~/.local/share/NuhxBoard/keyboards
. Here’s the general structure of that directory:
This folder will be populated on first run with some example keyboards and categories. You can inspect it yourself to get a good idea of how this looks in practice.
To load a keyboard and style, right-click anywhere in NuhxBoard to open the global context menu and click on "Load Keyboard". This will open a new window. The drop-down list labeled "Categories" allows you to select a category. When a category has been selected, the keyboards available in that category will appear in a list on the left side of the vertical line. When you click on one of these options, your selection of keyboard layout will be loaded, and that keyboard layout’s available styles will appear in a list on the right side of the vertical line. When you click on one of these options, your selection of style will be loaded. You can change your selection of keyboard layout and style at any time through this interface.
As previously stated, keyboard layouts define key positions, shapes, and behaviors, as well as window dimensions. Keyboard layouts are defined by a JSON file, keyboard.json
, in their corresponding named directory. Here’s what the type definition for a keyboard layout looks like in rust:
#[derive(Serialize, Deserialize, Default, Debug, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct Layout {
pub version: Option<u8>,
pub width: f32,
pub height: f32,
pub elements: Vec<BoardElement>,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(tag = "__type")]
pub enum BoardElement {
KeyboardKey(KeyboardKeyDefinition),
MouseKey(MouseKeyDefinition),
MouseScroll(MouseKeyDefinition),
MouseSpeedIndicator(MouseSpeedIndicatorDefinition),
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct KeyboardKeyDefinition {
pub id: u32,
pub boundaries: Vec<SerializablePoint>,
pub text_position: SerializablePoint,
pub key_codes: Vec<u32>,
pub text: String,
pub shift_text: String,
pub change_on_caps: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct MouseKeyDefinition {
pub id: u32,
pub boundaries: Vec<SerializablePoint>,
pub text_position: SerializablePoint,
pub key_codes: Vec<u32>,
pub text: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct MouseSpeedIndicatorDefinition {
pub id: u32,
pub location: SerializablePoint,
pub radius: f32,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct SerializablePoint {
pub x: f32,
pub y: f32,
}
If you can make sense of that, then good for you! Otherwise, here’s an actual explanation of how a keyboard layout is defined.
All points are represented as an object with an X
and Y
property.
There are four kinds of elements: KeyboardKeys, MouseKeys, MouseScrolls, and MouseSpeedIndicators. Each item in the list of elements indicates what kind it is by having a __type
property.
These properties are shared by KeyboardKeys, MouseKeys, and MouseScrolls.
In addition to the shared properties, KeyboardKeys have the following properties:
true
for letters and false
for symbols).MouseSpeedIndicators are drawn differently, behave differently, and thus are defined differently. They have IDs, but none of the other shared properties.
MouseSpeedIndicators are made up of a filled inner circle and an unfilled outer ring. There is a triangle extending to a point along the outer ring. The direction of the triangle indicates the direction of the velocity of the mouse, and the closness of the triangle’s end to the outer ring indicates the magnitude.
Styles describe colors, fonts, and images with which to display a keyboard layout. Proper styling is crucial to making a good keyboard layout.
Again, here’s the type definition in rust:
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct Style {
pub background_color: NohRgb,
pub background_image_file_name: Option<String>,
pub default_key_style: KeyStyle,
pub default_mouse_speed_indicator_style: MouseSpeedIndicatorStyle,
pub element_styles: Vec<ElementStyle>,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
pub struct NohRgb {
#[serde(rename = "Red")]
pub red: f32,
#[serde(rename = "Green")]
pub green: f32,
#[serde(rename = "Blue")]
pub blue: f32,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct KeyStyle {
pub loose: KeySubStyle,
pub pressed: KeySubStyle,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct KeySubStyle {
pub background: NohRgb,
pub text: NohRgb,
pub outline: NohRgb,
pub show_outline: bool,
pub outline_width: u32,
pub font: Font,
pub background_image_file_name: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct Font {
pub font_family: String,
pub size: f32,
pub style: u8,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct MouseSpeedIndicatorStyle {
pub inner_color: NohRgb,
pub outer_color: NohRgb,
pub outline_width: f32,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "PascalCase")]
pub struct ElementStyle {
pub key: u32,
pub value: ElementStyleUnion,
}
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(tag = "__type")]
pub enum ElementStyleUnion {
KeyStyle(KeyStyle),
MouseSpeedIndicatorStyle(MouseSpeedIndicatorStyle),
}
All images are stored in the images
directory in the category. Images are refferred to by name, including the file extension.
All colors are represented as an object with three properties: Red
, Green
, and Blue
. Each is an integer between 0 and 255.
Key
property, which is the Id of the element to which the style should be applied, and a Value
property, which is either a KeyStyle or a MouseSpeedIndicatorStyle. Again, each item indicates its type with the __type
property.KeyStyles just list which style to use for when a key is Pressed
or Loose
(not pressed). The actual style is defined in the KeySubStyle object, with these properties:
3
, which is 0011
in binary.In the global context menu, there is a "Settings" button, which opens a window with the following options:
You can graphically manipulate every part of a keyboard layout and style. In the global context menu, press "Start Editing" to enter edit mode. In this mode, you can
Most fields will instantly update as you change them. The exception is font families and images. In order to apply your changes to these fields, you must press Enter while the text box has focus.
You can use CTRL+Z to undo element movements, and CTRL+SHIFT+Z to redo them.
NuhxBoard will lack native Wayland support for the foreseeable future. This is due to the fact that Wayland provides no protocol for global input listening. However, through XWayland, NuhxBoard will still receive events when X11 applications have focus. The staggering majority of games with some version of Linux support (be it through proton or native support) use X11, so the primary usecase for NuhxBoard (being game recording) still works well enough.
Doesn’t work right now. :(
Support is in the works.
NuhxBoard is currently only on crates.io. It can also be installed with Cargo Binstall. You can also install NuhxBoard using the option matching your platform on the latest release page. NixOS users can use the provided flake (output packages.${system}.nuhxboard
or packages.${system}.default
).
NuhxBoard will detect if any app files are missing and replace them automatically. This includes
NuhxBoard.json
file containing app settings and saved state doesn’t exist, it’ll be populated with defaults.keyboards
directory is empty or doesn’t exist, then nuhxboard will download a pack of example keyboards to use.https://github.com/justDeeevin/NuhxBoard/assets/90054389/36dc9cf6-3b23-435c-a742-18dddf9c7c19
Configurable like NohBoard:
https://github.com/justDeeevin/NuhxBoard/assets/90054389/80c69a52-e76d-4715-a22c-78db34743959