Compare commits
10 Commits
a622f8560b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c866d1dfd7 | |||
| e9949a7526 | |||
| f6ec4492f1 | |||
| 81971410ce | |||
| 87490152bd | |||
| 112820bf1f | |||
| 85f1154c03 | |||
| 37d834d80b | |||
| 3db5b5e094 | |||
| 5b27529cf8 |
169
Cargo.lock
generated
169
Cargo.lock
generated
@@ -103,6 +103,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aliasable"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aligned"
|
name = "aligned"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -2376,6 +2382,19 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-graphics-simulator"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31606a4fb7d9d3a79a38d27bc2954cfa98682c8fea4b22c09a442785a80424e"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"embedded-graphics",
|
||||||
|
"image",
|
||||||
|
"ouroboros",
|
||||||
|
"sdl2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encase"
|
name = "encase"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -2748,6 +2767,7 @@ dependencies = [
|
|||||||
name = "g13-driver"
|
name = "g13-driver"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
"embedded-graphics-core",
|
"embedded-graphics-core",
|
||||||
"nusb",
|
"nusb",
|
||||||
]
|
]
|
||||||
@@ -2776,16 +2796,20 @@ dependencies = [
|
|||||||
"g13-driver",
|
"g13-driver",
|
||||||
"input-linux",
|
"input-linux",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "g13-os"
|
name = "g13-os"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"ctrlc",
|
||||||
"embedded-graphics",
|
"embedded-graphics",
|
||||||
|
"embedded-graphics-simulator",
|
||||||
"g13-driver",
|
"g13-driver",
|
||||||
"mlua",
|
"mlua",
|
||||||
|
"time",
|
||||||
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3074,6 +3098,12 @@ dependencies = [
|
|||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -3447,15 +3477,6 @@ dependencies = [
|
|||||||
"imgref",
|
"imgref",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "luau0-src"
|
|
||||||
version = "0.18.2+luau708"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "eed3214ab526e7e7b76c3f324a965d363db94a99ae67f65e67ec6fc499eb5e6d"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mach2"
|
name = "mach2"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -3545,17 +3566,6 @@ dependencies = [
|
|||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio"
|
|
||||||
version = "1.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mlua"
|
name = "mlua"
|
||||||
version = "0.11.6"
|
version = "0.11.6"
|
||||||
@@ -3586,7 +3596,6 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"luau0-src",
|
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4204,6 +4213,30 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ouroboros"
|
||||||
|
version = "0.18.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
|
||||||
|
dependencies = [
|
||||||
|
"aliasable",
|
||||||
|
"ouroboros_macro",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ouroboros_macro"
|
||||||
|
version = "0.18.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"proc-macro2-diagnostics",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owned_ttf_parser"
|
name = "owned_ttf_parser"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
@@ -4434,6 +4467,19 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2-diagnostics"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "profiling"
|
name = "profiling"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
@@ -4855,6 +4901,29 @@ dependencies = [
|
|||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sdl2"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"sdl2-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sdl2-sys"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"version-compare",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "self_cell"
|
name = "self_cell"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@@ -4941,16 +5010,6 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook-registry"
|
|
||||||
version = "1.4.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
|
||||||
dependencies = [
|
|
||||||
"errno",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@@ -5041,16 +5100,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "socket2"
|
|
||||||
version = "0.6.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.60.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -5308,24 +5357,7 @@ version = "1.49.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
|
||||||
"mio",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
|
||||||
"socket2",
|
|
||||||
"tokio-macros",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-macros"
|
|
||||||
version = "2.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5557,6 +5589,12 @@ version = "0.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version-compare"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -5573,12 +5611,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasip2"
|
name = "wasip2"
|
||||||
version = "1.0.2+wasi-0.2.9"
|
version = "1.0.2+wasi-0.2.9"
|
||||||
@@ -5752,6 +5784,7 @@ checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dlib",
|
"dlib",
|
||||||
"log",
|
"log",
|
||||||
|
"once_cell",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6633,6 +6666,12 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
|
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yazi"
|
name = "yazi"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ edition = "2024"
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
g13-driver.path = "./driver"
|
g13-driver.path = "./driver"
|
||||||
|
crossbeam-channel = "0.5.15"
|
||||||
|
|||||||
54
apps/main/main.lua
Normal file
54
apps/main/main.lua
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
name = "DVD"
|
||||||
|
authors = { "AviiNL" }
|
||||||
|
description = "Bouncing 'dvd' Logo"
|
||||||
|
|
||||||
|
local width = g13.display_width
|
||||||
|
local height = g13.display_height
|
||||||
|
|
||||||
|
local x = 22.0
|
||||||
|
local y = 6.0
|
||||||
|
|
||||||
|
local x_direction = 1
|
||||||
|
local y_direction = 1
|
||||||
|
|
||||||
|
local speed = 20
|
||||||
|
|
||||||
|
local size = 3
|
||||||
|
|
||||||
|
function setup()
|
||||||
|
joy.deadzone = 40
|
||||||
|
end
|
||||||
|
|
||||||
|
function update(delta)
|
||||||
|
x = x + speed * delta * joy.x * 1.2
|
||||||
|
y = y + speed * delta * joy.y
|
||||||
|
|
||||||
|
local color_x = (x / width) * 255
|
||||||
|
local color_y = (y / height) * 255
|
||||||
|
local color_z = 255 - (color_x + color_y) / 2
|
||||||
|
|
||||||
|
g13.clear()
|
||||||
|
|
||||||
|
g13.set_color(color_x / 2, color_y / 2, color_z / 2)
|
||||||
|
|
||||||
|
g13.text("Hello World!", 1, 1, true, 0, 0, 1)
|
||||||
|
|
||||||
|
for sy = -size, size do
|
||||||
|
for sx = -size, size do
|
||||||
|
g13.set_pixel(x + sx, y + sy, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function dump(o)
|
||||||
|
if type(o) == 'table' then
|
||||||
|
local s = '{ '
|
||||||
|
for k, v in pairs(o) do
|
||||||
|
if type(k) ~= 'number' then k = '"' .. k .. '"' end
|
||||||
|
s = s .. '[' .. k .. '] = ' .. dump(v) .. ','
|
||||||
|
end
|
||||||
|
return s .. '} '
|
||||||
|
else
|
||||||
|
return tostring(o)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -6,3 +6,4 @@ edition.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-graphics-core = "0.4.0"
|
embedded-graphics-core = "0.4.0"
|
||||||
nusb = { version = "0.2.1", features = ["tokio"] }
|
nusb = { version = "0.2.1", features = ["tokio"] }
|
||||||
|
crossbeam-channel.workspace = true
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ pub enum Button {
|
|||||||
Stick1,
|
Stick1,
|
||||||
Stick2,
|
Stick2,
|
||||||
Stick3,
|
Stick3,
|
||||||
|
|
||||||
|
StickUp,
|
||||||
|
StickDown,
|
||||||
|
StickLeft,
|
||||||
|
StickRight,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Button {
|
impl Button {
|
||||||
@@ -51,7 +56,7 @@ impl Button {
|
|||||||
[
|
[
|
||||||
Action, Screen1, Screen2, Screen3, Screen4, Light, M1, M2, M3, MR, G1, G2, G3, G4, G5,
|
Action, Screen1, Screen2, Screen3, Screen4, Light, M1, M2, M3, MR, G1, G2, G3, G4, G5,
|
||||||
G6, G7, G8, G9, G10, G11, G12, G13, G14, G15, G16, G17, G18, G19, G20, G21, G22,
|
G6, G7, G8, G9, G10, G11, G12, G13, G14, G15, G16, G17, G18, G19, G20, G21, G22,
|
||||||
Stick1, Stick2, Stick3,
|
Stick1, Stick2, Stick3, StickUp, StickDown, StickLeft, StickRight,
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
pub mod joystick;
|
pub mod joystick;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::{HashMap, hash_map::Entry},
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crossbeam_channel::{Receiver, bounded};
|
||||||
use embedded_graphics_core::{
|
use embedded_graphics_core::{
|
||||||
|
Pixel,
|
||||||
pixelcolor::BinaryColor,
|
pixelcolor::BinaryColor,
|
||||||
prelude::{Dimensions, DrawTarget, Point, Size},
|
prelude::{Dimensions, DrawTarget, Point, Size},
|
||||||
primitives::Rectangle,
|
primitives::Rectangle,
|
||||||
@@ -16,66 +19,88 @@ use nusb::{
|
|||||||
transfer::{ControlOut, ControlType, In, Interrupt, Out, Recipient},
|
transfer::{ControlOut, ControlType, In, Interrupt, Out, Recipient},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::joystick::Button;
|
||||||
|
|
||||||
pub const G13_LCD_COLUMNS: i32 = 160;
|
pub const G13_LCD_COLUMNS: i32 = 160;
|
||||||
pub const G13_LCD_ROWS: i32 = 43;
|
pub const G13_LCD_ROWS: i32 = 43;
|
||||||
pub const G13_LCD_BYTES_PER_ROW: i32 = G13_LCD_COLUMNS / 8;
|
pub const G13_LCD_BYTES_PER_ROW: i32 = G13_LCD_COLUMNS / 8;
|
||||||
pub const G13_LCD_BUF_SIZE: i32 = (G13_LCD_ROWS + 5) * G13_LCD_BYTES_PER_ROW;
|
pub const G13_LCD_BUF_SIZE: i32 = (G13_LCD_ROWS + 5) * G13_LCD_BYTES_PER_ROW;
|
||||||
|
|
||||||
#[derive(Clone)]
|
const DEADZONE: f32 = 10.0;
|
||||||
|
const CENTER: f32 = 127.5;
|
||||||
|
const RANGE: f32 = 117.5;
|
||||||
|
|
||||||
|
const BUTTON_MAP: &[(Button, usize, u8)] = &[
|
||||||
|
// buf[6]
|
||||||
|
(Button::Action, 6, 0b00000001),
|
||||||
|
(Button::Screen1, 6, 0b00000010),
|
||||||
|
(Button::Screen2, 6, 0b00000100),
|
||||||
|
(Button::Screen3, 6, 0b00001000),
|
||||||
|
(Button::Screen4, 6, 0b00010000),
|
||||||
|
(Button::M1, 6, 0b00100000),
|
||||||
|
(Button::M2, 6, 0b01000000),
|
||||||
|
(Button::M3, 6, 0b10000000),
|
||||||
|
// buf[7]
|
||||||
|
(Button::Light, 7, 0b01000000),
|
||||||
|
(Button::MR, 7, 0b00000001),
|
||||||
|
(Button::Stick1, 7, 0b00000010),
|
||||||
|
(Button::Stick2, 7, 0b00000100),
|
||||||
|
(Button::Stick3, 7, 0b00001000),
|
||||||
|
// buf[3]
|
||||||
|
(Button::G1, 3, 0b00000001),
|
||||||
|
(Button::G2, 3, 0b00000010),
|
||||||
|
(Button::G3, 3, 0b00000100),
|
||||||
|
(Button::G4, 3, 0b00001000),
|
||||||
|
(Button::G5, 3, 0b00010000),
|
||||||
|
(Button::G6, 3, 0b00100000),
|
||||||
|
(Button::G7, 3, 0b01000000),
|
||||||
|
(Button::G8, 3, 0b10000000),
|
||||||
|
// buf[4]
|
||||||
|
(Button::G9, 4, 0b00000001),
|
||||||
|
(Button::G10, 4, 0b00000010),
|
||||||
|
(Button::G11, 4, 0b00000100),
|
||||||
|
(Button::G12, 4, 0b00001000),
|
||||||
|
(Button::G13, 4, 0b00010000),
|
||||||
|
(Button::G14, 4, 0b00100000),
|
||||||
|
(Button::G15, 4, 0b01000000),
|
||||||
|
(Button::G16, 4, 0b10000000),
|
||||||
|
// buf[5]
|
||||||
|
(Button::G17, 5, 0b00000001),
|
||||||
|
(Button::G18, 5, 0b00000010),
|
||||||
|
(Button::G19, 5, 0b00000100),
|
||||||
|
(Button::G20, 5, 0b00001000),
|
||||||
|
(Button::G21, 5, 0b00010000),
|
||||||
|
(Button::G22, 5, 0b00100000),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Vec2<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub x: T,
|
||||||
|
pub y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec2<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub fn new(x: T, y: T) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct G13 {
|
pub struct G13 {
|
||||||
|
rx: Option<Receiver<G13Event>>,
|
||||||
interface: Interface,
|
interface: Interface,
|
||||||
img_buffer: Arc<RwLock<[u8; G13_LCD_BUF_SIZE as usize + 8]>>,
|
img_buffer: Arc<RwLock<[u8; G13_LCD_BUF_SIZE as usize + 8]>>,
|
||||||
state: Arc<RwLock<State>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
pub struct State {
|
|
||||||
pub x: i32,
|
|
||||||
pub y: i32,
|
|
||||||
pub buttons: Buttons,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
pub struct Buttons {
|
|
||||||
pub action: bool,
|
|
||||||
pub screen1: bool,
|
|
||||||
pub screen2: bool,
|
|
||||||
pub screen3: bool,
|
|
||||||
pub screen4: bool,
|
|
||||||
pub light: bool,
|
|
||||||
pub m1: bool,
|
|
||||||
pub m2: bool,
|
|
||||||
pub m3: bool,
|
|
||||||
pub mr: bool,
|
|
||||||
pub g1: bool,
|
|
||||||
pub g2: bool,
|
|
||||||
pub g3: bool,
|
|
||||||
pub g4: bool,
|
|
||||||
pub g5: bool,
|
|
||||||
pub g6: bool,
|
|
||||||
pub g7: bool,
|
|
||||||
pub g8: bool,
|
|
||||||
pub g9: bool,
|
|
||||||
pub g10: bool,
|
|
||||||
pub g11: bool,
|
|
||||||
pub g12: bool,
|
|
||||||
pub g13: bool,
|
|
||||||
pub g14: bool,
|
|
||||||
pub g15: bool,
|
|
||||||
pub g16: bool,
|
|
||||||
pub g17: bool,
|
|
||||||
pub g18: bool,
|
|
||||||
pub g19: bool,
|
|
||||||
pub g20: bool,
|
|
||||||
pub g21: bool,
|
|
||||||
pub g22: bool,
|
|
||||||
pub stick1: bool,
|
|
||||||
pub stick2: bool,
|
|
||||||
pub stick3: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl G13 {
|
impl G13 {
|
||||||
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let img_buffer = [0u8; G13_LCD_BUF_SIZE as usize + 8];
|
||||||
|
|
||||||
let Ok(mut devices) = nusb::list_devices().wait() else {
|
let Ok(mut devices) = nusb::list_devices().wait() else {
|
||||||
return Err("No devices found".into());
|
return Err("No devices found".into());
|
||||||
};
|
};
|
||||||
@@ -96,82 +121,118 @@ impl G13 {
|
|||||||
Err(e) => return Err(format!("Unable to claim interface: {:#?}", e).into()),
|
Err(e) => return Err(format!("Unable to claim interface: {:#?}", e).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let img_buffer = [0u8; G13_LCD_BUF_SIZE as usize + 8];
|
let input = G13Input::new(interface.clone());
|
||||||
let mut buffer = [0u8; G13_LCD_BUF_SIZE as usize + 32 + 8];
|
let (tx, rx) = bounded(0);
|
||||||
buffer[0] = 0x03;
|
|
||||||
buffer[32..G13_LCD_BUF_SIZE as usize + 32 + 8].copy_from_slice(&img_buffer);
|
|
||||||
|
|
||||||
let mut w = interface.endpoint::<Interrupt, Out>(0x02)?.writer(64);
|
let _tx = tx.clone();
|
||||||
w.write_all(&buffer)?;
|
std::thread::spawn(move || {
|
||||||
w.flush()?;
|
for event in input {
|
||||||
|
let Some(event) = event else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if _tx.send(event).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
interface,
|
interface,
|
||||||
img_buffer: Arc::new(RwLock::new(img_buffer)),
|
img_buffer: Arc::new(RwLock::new(img_buffer)),
|
||||||
state: Arc::new(RwLock::new(Default::default())),
|
rx: Some(rx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(&self) -> State {
|
pub fn events(&mut self) -> Receiver<G13Event> {
|
||||||
*self.state.read().expect("Poisoned")
|
self.rx.take().expect("events() can only be called once")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum G13Event {
|
||||||
|
Axis(f32, f32),
|
||||||
|
Button(Button, (bool, bool)), // (old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct G13Input {
|
||||||
|
interface: Interface,
|
||||||
|
buttons: HashMap<Button, bool>,
|
||||||
|
axis: Vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl G13Input {
|
||||||
|
pub fn new(interface: Interface) -> Self {
|
||||||
|
Self {
|
||||||
|
interface,
|
||||||
|
buttons: HashMap::new(),
|
||||||
|
axis: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self) -> Result<State, Box<dyn std::error::Error>> {
|
fn handle_button(&mut self, button: Button, value: bool) -> Option<G13Event> {
|
||||||
let mut state = self.state();
|
if let Entry::Vacant(e) = self.buttons.entry(button) {
|
||||||
|
e.insert(value);
|
||||||
|
if value {
|
||||||
|
return Some(G13Event::Button(button, (!value, value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut reader = self.interface.endpoint::<Interrupt, In>(0x81)?.reader(8);
|
let prev = self.buttons[&button];
|
||||||
|
if self.buttons[&button] == value {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.buttons.insert(button, value);
|
||||||
|
Some(G13Event::Button(button, (prev, value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for G13Input {
|
||||||
|
type Item = Option<G13Event>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let mut reader = self
|
||||||
|
.interface
|
||||||
|
.endpoint::<Interrupt, In>(0x81)
|
||||||
|
.ok()?
|
||||||
|
.reader(8);
|
||||||
let mut buf = [0; 8];
|
let mut buf = [0; 8];
|
||||||
reader.read_exact(&mut buf)?;
|
reader.read_exact(&mut buf).ok()?;
|
||||||
|
|
||||||
state.x = (((buf[1] as f64) / 256.0 * 1024.0) - 512.0) as i32;
|
for &(button, index, mask) in BUTTON_MAP {
|
||||||
state.y = (((buf[2] as f64) / 256.0 * 1024.0) - 512.0) as i32;
|
if let Some(event) = self.handle_button(button, buf[index] & mask != 0) {
|
||||||
|
return Some(Some(event));
|
||||||
state.buttons.action = buf[6] & 0b00000001 != 0;
|
}
|
||||||
state.buttons.screen1 = buf[6] & 0b00000010 != 0;
|
|
||||||
state.buttons.screen2 = buf[6] & 0b00000100 != 0;
|
|
||||||
state.buttons.screen3 = buf[6] & 0b00001000 != 0;
|
|
||||||
state.buttons.screen4 = buf[6] & 0b00010000 != 0;
|
|
||||||
state.buttons.light = buf[7] & 0b0100000 != 0;
|
|
||||||
|
|
||||||
state.buttons.m1 = buf[6] & 0b00100000 != 0;
|
|
||||||
state.buttons.m2 = buf[6] & 0b01000000 != 0;
|
|
||||||
state.buttons.m3 = buf[6] & 0b10000000 != 0;
|
|
||||||
state.buttons.mr = buf[7] & 0b00000001 != 0;
|
|
||||||
|
|
||||||
state.buttons.g1 = buf[3] & 0b00000001 != 0;
|
|
||||||
state.buttons.g2 = buf[3] & 0b00000010 != 0;
|
|
||||||
state.buttons.g3 = buf[3] & 0b00000100 != 0;
|
|
||||||
state.buttons.g4 = buf[3] & 0b00001000 != 0;
|
|
||||||
state.buttons.g5 = buf[3] & 0b00010000 != 0;
|
|
||||||
state.buttons.g6 = buf[3] & 0b00100000 != 0;
|
|
||||||
state.buttons.g7 = buf[3] & 0b01000000 != 0;
|
|
||||||
state.buttons.g8 = buf[3] & 0b10000000 != 0;
|
|
||||||
|
|
||||||
state.buttons.g9 = buf[4] & 0b00000001 != 0;
|
|
||||||
state.buttons.g10 = buf[4] & 0b00000010 != 0;
|
|
||||||
state.buttons.g11 = buf[4] & 0b00000100 != 0;
|
|
||||||
state.buttons.g12 = buf[4] & 0b00001000 != 0;
|
|
||||||
state.buttons.g13 = buf[4] & 0b00010000 != 0;
|
|
||||||
state.buttons.g14 = buf[4] & 0b00100000 != 0;
|
|
||||||
state.buttons.g15 = buf[4] & 0b01000000 != 0;
|
|
||||||
state.buttons.g16 = buf[4] & 0b10000000 != 0;
|
|
||||||
|
|
||||||
state.buttons.g17 = buf[5] & 0b00000001 != 0;
|
|
||||||
state.buttons.g18 = buf[5] & 0b00000010 != 0;
|
|
||||||
state.buttons.g19 = buf[5] & 0b00000100 != 0;
|
|
||||||
state.buttons.g20 = buf[5] & 0b00001000 != 0;
|
|
||||||
state.buttons.g21 = buf[5] & 0b00010000 != 0;
|
|
||||||
state.buttons.g22 = buf[5] & 0b00100000 != 0;
|
|
||||||
|
|
||||||
state.buttons.stick1 = buf[7] & 0b00000010 != 0;
|
|
||||||
state.buttons.stick2 = buf[7] & 0b00000100 != 0;
|
|
||||||
state.buttons.stick3 = buf[7] & 0b00001000 != 0;
|
|
||||||
|
|
||||||
*self.state.write().expect("Poisoned") = state;
|
|
||||||
|
|
||||||
Ok(state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let axis = Vec2::new(buf[1], buf[2]);
|
||||||
|
|
||||||
|
let mut input = Vec2::new(axis.x as f32 - CENTER, axis.y as f32 - CENTER);
|
||||||
|
|
||||||
|
input.x = apply_deadzone(input.x);
|
||||||
|
input.y = apply_deadzone(input.y);
|
||||||
|
|
||||||
|
if self.axis == input {
|
||||||
|
return Some(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.axis = input;
|
||||||
|
|
||||||
|
Some(Some(G13Event::Axis(input.x / RANGE, input.y / RANGE)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_deadzone(v: f32) -> f32 {
|
||||||
|
if v.abs() <= DEADZONE {
|
||||||
|
0.0
|
||||||
|
} else if v > 0.0 {
|
||||||
|
v - DEADZONE
|
||||||
|
} else {
|
||||||
|
v + DEADZONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl G13 {
|
||||||
pub fn set_lcd_color(&self, r: u8, g: u8, b: u8) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn set_lcd_color(&self, r: u8, g: u8, b: u8) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
self.interface
|
self.interface
|
||||||
.control_out(
|
.control_out(
|
||||||
@@ -190,7 +251,7 @@ impl G13 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn flush(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let img_buffer = self.img_buffer.read().expect("Poisoned");
|
let img_buffer = self.img_buffer.read().expect("Poisoned");
|
||||||
|
|
||||||
let mut buffer = [0u8; G13_LCD_BUF_SIZE as usize + 32 + 8];
|
let mut buffer = [0u8; G13_LCD_BUF_SIZE as usize + 32 + 8];
|
||||||
@@ -202,6 +263,10 @@ impl G13 {
|
|||||||
w.flush()?;
|
w.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> Size {
|
||||||
|
Size::new(G13_LCD_COLUMNS as u32, G13_LCD_ROWS as u32)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dimensions for G13 {
|
impl Dimensions for G13 {
|
||||||
@@ -221,16 +286,17 @@ impl DrawTarget for G13 {
|
|||||||
where
|
where
|
||||||
I: IntoIterator<Item = embedded_graphics_core::Pixel<Self::Color>>,
|
I: IntoIterator<Item = embedded_graphics_core::Pixel<Self::Color>>,
|
||||||
{
|
{
|
||||||
|
let bb = self.bounding_box();
|
||||||
let mut img_buffer = self.img_buffer.write().expect("Poisoned");
|
let mut img_buffer = self.img_buffer.write().expect("Poisoned");
|
||||||
for p in pixels {
|
for Pixel(pos, color) in pixels {
|
||||||
if p.0.x < 0 || p.0.x > G13_LCD_COLUMNS - 1 || p.0.y < 0 || p.0.y > G13_LCD_ROWS - 1 {
|
if !bb.contains(pos) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = p.0.x + (p.0.y / 8) * (G13_LCD_BYTES_PER_ROW) * 8;
|
let offset = pos.x + (pos.y / 8) * (G13_LCD_BYTES_PER_ROW) * 8;
|
||||||
let mask = 1 << (p.0.y.rem_euclid(8));
|
let mask = 1 << (pos.y.rem_euclid(8));
|
||||||
|
|
||||||
if p.1.is_on() {
|
if color.is_on() {
|
||||||
img_buffer[offset as usize] |= mask;
|
img_buffer[offset as usize] |= mask;
|
||||||
} else {
|
} else {
|
||||||
img_buffer[offset as usize] &= !mask;
|
img_buffer[offset as usize] &= !mask;
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ version.workspace = true
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
g13-driver.workspace = true
|
crossbeam-channel.workspace = true
|
||||||
|
ctrlc = "3.5"
|
||||||
embedded-graphics = "0.8"
|
embedded-graphics = "0.8"
|
||||||
mlua = { version = "0.11.6", features = ["luau-jit", "async", "macros", "serde"] }
|
g13-driver.workspace = true
|
||||||
|
mlua = { version = "0.11.6", features = ["lua54", "async", "macros", "serde"] }
|
||||||
|
time = { version = "0.3.47", features = ["formatting", "macros"] }
|
||||||
|
|
||||||
|
embedded-graphics-simulator = "0.7.0"
|
||||||
|
winit = "0.30.12"
|
||||||
|
|||||||
36
g13-os/src/app/mod.rs
Normal file
36
g13-os/src/app/mod.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
pub(super) mod pages;
|
||||||
|
pub(super) mod widgets;
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use g13_driver::{G13, G13Event};
|
||||||
|
|
||||||
|
use crate::app::pages::StartPage;
|
||||||
|
|
||||||
|
use super::app::pages::Page;
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
pages: VecDeque<Box<dyn Page<Display = G13>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut pages: VecDeque<Box<dyn Page<Display = G13>>> = VecDeque::new();
|
||||||
|
|
||||||
|
pages.push_back(Box::new(StartPage::new()));
|
||||||
|
|
||||||
|
Self { pages }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, event: G13Event) {
|
||||||
|
if let Some(last_page) = self.pages.iter_mut().last() {
|
||||||
|
last_page.update(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self, display: &mut G13) {
|
||||||
|
if let Some(last_page) = self.pages.iter_mut().last() {
|
||||||
|
last_page.draw(display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
g13-os/src/app/pages/mod.rs
Normal file
7
g13-os/src/app/pages/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::app::widgets::Widget;
|
||||||
|
|
||||||
|
mod start;
|
||||||
|
|
||||||
|
pub trait Page: Widget {}
|
||||||
|
|
||||||
|
pub use start::*;
|
||||||
73
g13-os/src/app/pages/start.rs
Normal file
73
g13-os/src/app/pages/start.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use embedded_graphics::{
|
||||||
|
mono_font::ascii::FONT_6X10,
|
||||||
|
pixelcolor::BinaryColor,
|
||||||
|
prelude::Point,
|
||||||
|
text::{Alignment, Baseline},
|
||||||
|
};
|
||||||
|
use g13_driver::{G13, G13Event, Vec2};
|
||||||
|
|
||||||
|
use crate::app::widgets::{Widget, label::Label};
|
||||||
|
|
||||||
|
pub struct StartPage {
|
||||||
|
joy: Vec2<f32>,
|
||||||
|
children: Vec<Box<dyn Widget<Display = G13>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StartPage {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let children: Vec<Box<dyn Widget<Display = G13>>> = vec![Box::new(Label::new(
|
||||||
|
"Hello World!",
|
||||||
|
&FONT_6X10,
|
||||||
|
BinaryColor::On,
|
||||||
|
Point::new(10, 0),
|
||||||
|
Alignment::Left,
|
||||||
|
Baseline::Top,
|
||||||
|
))];
|
||||||
|
|
||||||
|
Self {
|
||||||
|
joy: Vec2::default(),
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for StartPage {
|
||||||
|
type Display = G13;
|
||||||
|
|
||||||
|
fn update(&mut self, event: g13_driver::G13Event) {
|
||||||
|
if let G13Event::Axis(x, y) = event {
|
||||||
|
self.joy.x = x;
|
||||||
|
self.joy.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is stupid.
|
||||||
|
// Need a way to keep some reference to widgets to manipulate them later
|
||||||
|
// preferably as their original type so we don't need this downcast bs
|
||||||
|
self.children[0]
|
||||||
|
.as_any_mut()
|
||||||
|
.downcast_mut::<Label>()
|
||||||
|
.unwrap()
|
||||||
|
.text(&format!(
|
||||||
|
"{} {}",
|
||||||
|
(self.joy.x * 512.0) as i32,
|
||||||
|
(self.joy.y * 512.0) as i32
|
||||||
|
));
|
||||||
|
|
||||||
|
for child in self.children.iter_mut() {
|
||||||
|
child.update(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self, display: &mut Self::Display) {
|
||||||
|
for child in self.children.iter_mut() {
|
||||||
|
child.draw(display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marker to indicate page
|
||||||
|
impl super::Page for StartPage {}
|
||||||
73
g13-os/src/app/widgets/label.rs
Normal file
73
g13-os/src/app/widgets/label.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use embedded_graphics::{
|
||||||
|
mono_font::{MonoFont, MonoTextStyle},
|
||||||
|
pixelcolor::BinaryColor,
|
||||||
|
prelude::*,
|
||||||
|
text::{Alignment, Baseline, Text, TextStyle, TextStyleBuilder},
|
||||||
|
};
|
||||||
|
use g13_driver::G13;
|
||||||
|
|
||||||
|
// const CHARACTER_STYLE: MonoTextStyle<'static, BinaryColor> =
|
||||||
|
// MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
|
||||||
|
|
||||||
|
// const TEXT_STYLE: TextStyle = TextStyleBuilder::new()
|
||||||
|
// .baseline(Baseline::Top)
|
||||||
|
// .alignment(Alignment::Left)
|
||||||
|
// .build();
|
||||||
|
|
||||||
|
pub struct Label {
|
||||||
|
text: String,
|
||||||
|
position: Point,
|
||||||
|
character_style: MonoTextStyle<'static, BinaryColor>,
|
||||||
|
text_style: TextStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Label {
|
||||||
|
pub fn new(
|
||||||
|
text: &str,
|
||||||
|
font: &'static MonoFont<'static>,
|
||||||
|
color: BinaryColor,
|
||||||
|
position: Point,
|
||||||
|
alignment: Alignment,
|
||||||
|
baseline: Baseline,
|
||||||
|
) -> Self {
|
||||||
|
let character_style: MonoTextStyle<'static, BinaryColor> = MonoTextStyle::new(font, color);
|
||||||
|
|
||||||
|
let text_style: TextStyle = TextStyleBuilder::new()
|
||||||
|
.baseline(baseline)
|
||||||
|
.alignment(alignment)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
text: text.to_owned(),
|
||||||
|
position,
|
||||||
|
character_style,
|
||||||
|
text_style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(&mut self, text: &str) {
|
||||||
|
self.text = text.to_owned();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Widget for Label {
|
||||||
|
type Display = G13;
|
||||||
|
|
||||||
|
fn update(&mut self, _: g13_driver::G13Event) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self, display: &mut Self::Display) {
|
||||||
|
let _ = Text::with_text_style(
|
||||||
|
&self.text,
|
||||||
|
self.position,
|
||||||
|
self.character_style,
|
||||||
|
self.text_style,
|
||||||
|
)
|
||||||
|
.draw(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
16
g13-os/src/app/widgets/mod.rs
Normal file
16
g13-os/src/app/widgets/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod label;
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
use embedded_graphics::draw_target::DrawTarget;
|
||||||
|
use g13_driver::G13Event;
|
||||||
|
|
||||||
|
pub trait Widget {
|
||||||
|
type Display: DrawTarget;
|
||||||
|
|
||||||
|
fn update(&mut self, event: G13Event);
|
||||||
|
|
||||||
|
fn draw(&mut self, display: &mut Self::Display);
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
}
|
||||||
@@ -1,87 +1,32 @@
|
|||||||
use std::{
|
mod app;
|
||||||
fs,
|
|
||||||
thread::sleep,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use g13_driver::{G13, G13_LCD_COLUMNS, G13_LCD_ROWS};
|
use std::time::{Duration, Instant};
|
||||||
use mlua::{ExternalResult, Lua};
|
|
||||||
|
use embedded_graphics::pixelcolor::BinaryColor;
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
|
||||||
|
use g13_driver::G13;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let g13 = G13::new()?;
|
let mut g13 = G13::new()?;
|
||||||
let lua = Lua::new();
|
|
||||||
let globals = lua.globals();
|
|
||||||
|
|
||||||
let g13_table = lua.create_table()?;
|
let mut app = App::new();
|
||||||
|
|
||||||
let _g13 = g13.clone();
|
let events = g13.events();
|
||||||
g13_table.set(
|
|
||||||
"set_color",
|
|
||||||
lua.create_function(move |_, (r, g, b)| _g13.set_lcd_color(r, g, b).into_lua_err())?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let _g13 = g13.clone();
|
let mut start = Instant::now();
|
||||||
let fn_clear = lua.create_function(move |_, on| {
|
|
||||||
let mut _g13 = _g13.clone();
|
|
||||||
use embedded_graphics::pixelcolor::BinaryColor;
|
|
||||||
use embedded_graphics::prelude::*;
|
|
||||||
let color = if on {
|
|
||||||
BinaryColor::On
|
|
||||||
} else {
|
|
||||||
BinaryColor::Off
|
|
||||||
};
|
|
||||||
_g13.clear(color).into_lua_err()
|
|
||||||
});
|
|
||||||
g13_table.set("clear", fn_clear?)?;
|
|
||||||
|
|
||||||
let mut _g13 = g13.clone();
|
|
||||||
let fn_set_pixel = lua.create_function_mut(move |_, (x, y, on)| {
|
|
||||||
use embedded_graphics::pixelcolor::BinaryColor;
|
|
||||||
use embedded_graphics::prelude::*;
|
|
||||||
let color = if on {
|
|
||||||
BinaryColor::On
|
|
||||||
} else {
|
|
||||||
BinaryColor::Off
|
|
||||||
};
|
|
||||||
Pixel(Point::new(x, y), color)
|
|
||||||
.draw(&mut _g13)
|
|
||||||
.into_lua_err()
|
|
||||||
});
|
|
||||||
g13_table.set("set_pixel", fn_set_pixel?)?;
|
|
||||||
|
|
||||||
g13_table.set("display_width", G13_LCD_COLUMNS)?;
|
|
||||||
g13_table.set("display_height", G13_LCD_ROWS)?;
|
|
||||||
|
|
||||||
globals.set("g13", g13_table)?;
|
|
||||||
|
|
||||||
// load all files from dir `./scripts` for now, user configurable later or ~/.config/g13-os/*.<lua[u]>
|
|
||||||
// for now, just main.luau
|
|
||||||
let main = fs::read_to_string("./scripts/main.luau")?;
|
|
||||||
|
|
||||||
lua.load(main).set_name("main.luau").exec()?;
|
|
||||||
|
|
||||||
if lua.load("setup ~= nil").eval()? {
|
|
||||||
lua.load("setup()").exec()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if lua.load("update ~= nil").eval()? {
|
|
||||||
let mut _g13 = g13.clone();
|
|
||||||
let mut delta: f32 = 0.0;
|
|
||||||
loop {
|
loop {
|
||||||
let start = Instant::now();
|
if let Ok(event) = events.try_recv() {
|
||||||
|
app.update(event); // update can happen A LOT more often
|
||||||
lua.load(format!("update({})", delta)).exec()?;
|
|
||||||
_g13.render()?;
|
|
||||||
|
|
||||||
let duration = Instant::now() - start;
|
|
||||||
|
|
||||||
// 30 fps lock
|
|
||||||
if duration < Duration::from_millis(33) {
|
|
||||||
sleep(Duration::from_millis(33) - duration);
|
|
||||||
}
|
|
||||||
delta = (Instant::now() - start).as_secs_f32();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
if (Instant::now() - start) >= Duration::from_millis(33) {
|
||||||
|
start = Instant::now();
|
||||||
|
g13.clear(BinaryColor::Off)?;
|
||||||
|
app.draw(&mut g13);
|
||||||
|
g13.flush()?; // 30 fps rendering
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
92
g13-os/src/mouse.rs
Normal file
92
g13-os/src/mouse.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use embedded_graphics::{Drawable, Pixel, pixelcolor::BinaryColor, prelude::Point};
|
||||||
|
use g13_driver::{G13, G13_LCD_COLUMNS, G13_LCD_ROWS, State};
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
const MOUSE_CURSOR: &[u8] = &[
|
||||||
|
2, 0, 0, 0, 0,
|
||||||
|
2, 2, 0, 0, 0,
|
||||||
|
2, 1, 2, 0, 0,
|
||||||
|
2, 1, 1, 2, 0,
|
||||||
|
2, 1, 2, 2, 2,
|
||||||
|
2, 2, 0, 0, 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct Mouse {
|
||||||
|
deadzone: i32,
|
||||||
|
speed: f32,
|
||||||
|
x: RefCell<f32>,
|
||||||
|
y: RefCell<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mouse {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mouse {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
deadzone: 40,
|
||||||
|
speed: 20.0,
|
||||||
|
x: RefCell::new(G13_LCD_COLUMNS as f32 / 2.0 - 2.0),
|
||||||
|
y: RefCell::new(G13_LCD_ROWS as f32 / 2.0 - 3.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self, state: State, delta: f32) {
|
||||||
|
let offset: i32 = 512 - self.deadzone;
|
||||||
|
let mut x = state.x;
|
||||||
|
let mut y = state.y;
|
||||||
|
|
||||||
|
x = if x.abs() < self.deadzone {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
x - self.deadzone * x.signum()
|
||||||
|
};
|
||||||
|
|
||||||
|
y = if y.abs() < self.deadzone {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
y - self.deadzone * y.signum()
|
||||||
|
};
|
||||||
|
|
||||||
|
let x = x as f32 / offset as f32;
|
||||||
|
let y = y as f32 / offset as f32;
|
||||||
|
|
||||||
|
let mut sx = self.x.borrow_mut();
|
||||||
|
let mut sy = self.y.borrow_mut();
|
||||||
|
|
||||||
|
*sx = (*sx + x * delta * self.speed * 1.2).clamp(0., (G13_LCD_COLUMNS - 1) as f32);
|
||||||
|
*sy = (*sy + y * delta * self.speed).clamp(0., (G13_LCD_ROWS - 1) as f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_x(&self) -> i32 {
|
||||||
|
*self.x.borrow() as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_y(&self) -> i32 {
|
||||||
|
*self.y.borrow() as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, g13: &mut G13) {
|
||||||
|
for (i, value) in MOUSE_CURSOR.iter().enumerate() {
|
||||||
|
let x = i as i32 % 5;
|
||||||
|
let y = i as i32 / 5;
|
||||||
|
|
||||||
|
let color = if *value == 1 {
|
||||||
|
BinaryColor::Off
|
||||||
|
} else if *value == 2 {
|
||||||
|
BinaryColor::On
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
Pixel(Point::new(self.get_x() + x, self.get_y() + y), color)
|
||||||
|
.draw(g13)
|
||||||
|
.expect("G13 to be Connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
231
g13-os/src/os.rs
Normal file
231
g13-os/src/os.rs
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
use std::{
|
||||||
|
fs::read_dir,
|
||||||
|
sync::{
|
||||||
|
Arc,
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
},
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
|
||||||
|
use embedded_graphics::{
|
||||||
|
Drawable,
|
||||||
|
mono_font::ascii::*,
|
||||||
|
pixelcolor::BinaryColor,
|
||||||
|
prelude::{DrawTarget, Point, Primitive, Size},
|
||||||
|
primitives::{Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle},
|
||||||
|
text::{Alignment, Baseline, Text, TextStyleBuilder},
|
||||||
|
};
|
||||||
|
use g13_driver::{G13, G13_LCD_COLUMNS, G13_LCD_ROWS};
|
||||||
|
use mlua::Lua;
|
||||||
|
use time::{OffsetDateTime, macros::offset};
|
||||||
|
|
||||||
|
use crate::mouse::Mouse;
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Button {
|
||||||
|
label: String,
|
||||||
|
action: Option<fn()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct G13Os {
|
||||||
|
g13: G13,
|
||||||
|
lua: Lua,
|
||||||
|
running: Arc<AtomicBool>,
|
||||||
|
apps: Vec<App>,
|
||||||
|
running_app: Option<App>,
|
||||||
|
buttons: [Option<Button>; 4],
|
||||||
|
mouse: Mouse,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl G13Os {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let g13 = G13::new().expect("G13 to be connected");
|
||||||
|
let lua = Lua::new();
|
||||||
|
let running = Arc::new(AtomicBool::new(false));
|
||||||
|
let mut apps = Vec::new();
|
||||||
|
|
||||||
|
let buttons = [
|
||||||
|
Some(Button {
|
||||||
|
label: "Settings".to_owned(),
|
||||||
|
action: None,
|
||||||
|
}),
|
||||||
|
// Some(Button {
|
||||||
|
// label: "Down".to_owned(),
|
||||||
|
// action: None,
|
||||||
|
// }),
|
||||||
|
None,
|
||||||
|
// Some(Button {
|
||||||
|
// label: "Up".to_owned(),
|
||||||
|
// action: None,
|
||||||
|
// }),
|
||||||
|
None,
|
||||||
|
Some(Button {
|
||||||
|
label: "00:00".to_owned(),
|
||||||
|
action: None,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
for entry in read_dir("./apps").unwrap() {
|
||||||
|
apps.push(App {
|
||||||
|
name: entry.unwrap().file_name().into_string().unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
g13,
|
||||||
|
lua,
|
||||||
|
running,
|
||||||
|
apps,
|
||||||
|
running_app: None,
|
||||||
|
buttons,
|
||||||
|
mouse: Mouse::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&self) {
|
||||||
|
let running = self.running.clone();
|
||||||
|
running.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
ctrlc::set_handler(move || running.store(false, Ordering::SeqCst))
|
||||||
|
.expect("Error setting Ctrl-C handler");
|
||||||
|
|
||||||
|
self.start_input_thread();
|
||||||
|
|
||||||
|
let mut g13 = self.g13.clone();
|
||||||
|
let mut delta = 0.0;
|
||||||
|
while self.running.load(Ordering::SeqCst) {
|
||||||
|
let start = Instant::now();
|
||||||
|
g13.clear(BinaryColor::Off).expect("G13 to be connected");
|
||||||
|
|
||||||
|
let mut show_mouse = false;
|
||||||
|
|
||||||
|
if self.running_app.is_none() {
|
||||||
|
self.render_menu();
|
||||||
|
show_mouse = true;
|
||||||
|
} else {
|
||||||
|
// render app
|
||||||
|
// if app.is_init == false
|
||||||
|
// app.is_init = true
|
||||||
|
// app.init()
|
||||||
|
// ..
|
||||||
|
// app.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
if show_mouse {
|
||||||
|
self.mouse.update(g13.state(), delta);
|
||||||
|
self.mouse.render(&mut g13);
|
||||||
|
}
|
||||||
|
|
||||||
|
g13.render().expect("G13 to be connected");
|
||||||
|
|
||||||
|
delta = (Instant::now() - start).as_secs_f32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_menu(&self) {
|
||||||
|
let mut g13 = self.g13.clone();
|
||||||
|
|
||||||
|
// todo: const
|
||||||
|
let button_count = 4;
|
||||||
|
let button_width = G13_LCD_COLUMNS / button_count;
|
||||||
|
|
||||||
|
let fill = PrimitiveStyleBuilder::new()
|
||||||
|
.fill_color(BinaryColor::On)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let stroke = PrimitiveStyleBuilder::new()
|
||||||
|
.stroke_color(BinaryColor::On)
|
||||||
|
.stroke_width(1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let character_style =
|
||||||
|
embedded_graphics::mono_font::MonoTextStyle::new(&FONT_4X6, BinaryColor::On);
|
||||||
|
let character_style_inv =
|
||||||
|
embedded_graphics::mono_font::MonoTextStyle::new(&FONT_4X6, BinaryColor::Off);
|
||||||
|
let textstyle = TextStyleBuilder::new()
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.baseline(Baseline::Top)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
for (i, btn) in self.buttons.iter().enumerate() {
|
||||||
|
let Some(btn) = btn else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let line_x = i as i32 * button_width;
|
||||||
|
|
||||||
|
Line::new(Point::new(line_x, 8), Point::new(line_x + button_width, 8))
|
||||||
|
.into_styled(stroke)
|
||||||
|
.draw(&mut g13)
|
||||||
|
.expect("G13 to be connected");
|
||||||
|
|
||||||
|
if i > 0 && i < 4 {
|
||||||
|
Line::new(Point::new(line_x, 0), Point::new(line_x, 8))
|
||||||
|
.into_styled(stroke)
|
||||||
|
.draw(&mut g13)
|
||||||
|
.expect("G13 to be connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
Line::new(
|
||||||
|
Point::new(line_x + button_width, 0),
|
||||||
|
Point::new(line_x + button_width, 8),
|
||||||
|
)
|
||||||
|
.into_styled(stroke)
|
||||||
|
.draw(&mut g13)
|
||||||
|
.expect("G13 to be connected");
|
||||||
|
|
||||||
|
let i = i as i32;
|
||||||
|
let text_x = ((i + 1) * button_width) - (button_width / 2);
|
||||||
|
|
||||||
|
let mut label = btn.label.clone();
|
||||||
|
if label.len() > 8 {
|
||||||
|
let _ = label.split_off(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if label == "00:00" {
|
||||||
|
let now = OffsetDateTime::now_utc().to_offset(offset!(+1)); // GMT+1
|
||||||
|
|
||||||
|
// Render
|
||||||
|
label = format!(
|
||||||
|
"{:0>2}:{:0>2}:{:0>2}",
|
||||||
|
now.hour(),
|
||||||
|
now.minute(),
|
||||||
|
now.second()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = if self.mouse.get_x() > i * button_width
|
||||||
|
&& self.mouse.get_x() < (i + 1) * button_width
|
||||||
|
&& self.mouse.get_y() < 8
|
||||||
|
{
|
||||||
|
Rectangle::new(
|
||||||
|
Point::new(i * button_width, 0),
|
||||||
|
Size::new(button_width as u32, 8),
|
||||||
|
)
|
||||||
|
.into_styled(fill)
|
||||||
|
.draw(&mut g13)
|
||||||
|
.expect("G13 to be Connected");
|
||||||
|
|
||||||
|
character_style_inv
|
||||||
|
} else {
|
||||||
|
character_style
|
||||||
|
};
|
||||||
|
Text::with_text_style(&label, Point::new(text_x, 1), style, textstyle)
|
||||||
|
.draw(&mut g13)
|
||||||
|
.expect("G13 to be connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_input_thread(&self) {
|
||||||
|
let g13 = self.g13.clone();
|
||||||
|
let running = self.running.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
while running.load(Ordering::SeqCst) {
|
||||||
|
let _ = g13.read();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,12 +8,3 @@ g13-driver.workspace = true
|
|||||||
input-linux = "0.7.1"
|
input-linux = "0.7.1"
|
||||||
embedded-graphics = "0.8.1"
|
embedded-graphics = "0.8.1"
|
||||||
time = { version = "0.3.47", features = ["formatting", "macros"] }
|
time = { version = "0.3.47", features = ["formatting", "macros"] }
|
||||||
tokio = { version = "1.49.0", features = [
|
|
||||||
"rt",
|
|
||||||
"rt-multi-thread",
|
|
||||||
"sync",
|
|
||||||
"macros",
|
|
||||||
"net",
|
|
||||||
"signal",
|
|
||||||
"time",
|
|
||||||
] }
|
|
||||||
|
|||||||
112
joystick/src/app.rs
Normal file
112
joystick/src/app.rs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
use embedded_graphics::Drawable;
|
||||||
|
use embedded_graphics::mono_font::MonoTextStyle;
|
||||||
|
use embedded_graphics::mono_font::ascii::*;
|
||||||
|
use embedded_graphics::pixelcolor::BinaryColor;
|
||||||
|
use embedded_graphics::prelude::Point;
|
||||||
|
use embedded_graphics::text::*;
|
||||||
|
use g13_driver::G13;
|
||||||
|
use g13_driver::G13_LCD_COLUMNS;
|
||||||
|
use g13_driver::G13_LCD_ROWS;
|
||||||
|
use g13_driver::G13Event;
|
||||||
|
use g13_driver::joystick::Axis;
|
||||||
|
use g13_driver::joystick::Button;
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
use time::macros::offset;
|
||||||
|
|
||||||
|
use crate::joystick::Joystick;
|
||||||
|
|
||||||
|
const CHARACTER_STYLE_TIME: MonoTextStyle<'static, BinaryColor> =
|
||||||
|
MonoTextStyle::new(&FONT_10X20, BinaryColor::On);
|
||||||
|
const CHARACTER_STYLE_DATE: MonoTextStyle<'static, BinaryColor> =
|
||||||
|
MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
|
||||||
|
const TEXTSTYLE: TextStyle = TextStyleBuilder::new()
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.baseline(embedded_graphics::text::Baseline::Middle)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
joystick: Joystick,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let joystick = Joystick::new("g13-joystick").expect("Joystick to be available");
|
||||||
|
Self { joystick }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, events: G13Event) {
|
||||||
|
match events {
|
||||||
|
G13Event::Axis(x, y) => {
|
||||||
|
let x = (x * 512.0) as i32;
|
||||||
|
let y = (y * 512.0) as i32;
|
||||||
|
|
||||||
|
self.joystick.move_axis(Axis::X, x).ok();
|
||||||
|
self.joystick.move_axis(Axis::Y, y).ok();
|
||||||
|
|
||||||
|
if x <= -500 {
|
||||||
|
self.joystick.button_press(Button::StickLeft, true).ok();
|
||||||
|
} else {
|
||||||
|
self.joystick.button_press(Button::StickLeft, false).ok();
|
||||||
|
}
|
||||||
|
if x >= 500 {
|
||||||
|
self.joystick.button_press(Button::StickRight, true).ok();
|
||||||
|
} else {
|
||||||
|
self.joystick.button_press(Button::StickRight, false).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
if y <= -500 {
|
||||||
|
self.joystick.button_press(Button::StickUp, true).ok();
|
||||||
|
} else {
|
||||||
|
self.joystick.button_press(Button::StickUp, false).ok();
|
||||||
|
}
|
||||||
|
if y >= 500 {
|
||||||
|
self.joystick.button_press(Button::StickDown, true).ok();
|
||||||
|
} else {
|
||||||
|
self.joystick.button_press(Button::StickDown, false).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
G13Event::Button(button, (_, value)) => {
|
||||||
|
self.joystick.button_press(button, value).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.joystick.synchronise().ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self, display: &mut G13) {
|
||||||
|
let now = OffsetDateTime::now_utc().to_offset(offset!(+1)); // GMT+1
|
||||||
|
|
||||||
|
let time = format!(
|
||||||
|
"{:0>2}:{:0>2}:{:0>2}",
|
||||||
|
now.hour(),
|
||||||
|
now.minute(),
|
||||||
|
now.second()
|
||||||
|
);
|
||||||
|
|
||||||
|
let date = format!("{}, {} {}", now.weekday(), now.day(), now.month());
|
||||||
|
|
||||||
|
Text::with_text_style(
|
||||||
|
&time,
|
||||||
|
Point::new(
|
||||||
|
(G13_LCD_COLUMNS as f64 / 2.0) as i32,
|
||||||
|
(G13_LCD_ROWS as f64 / 2.0) as i32 - 8,
|
||||||
|
),
|
||||||
|
CHARACTER_STYLE_TIME,
|
||||||
|
TEXTSTYLE,
|
||||||
|
)
|
||||||
|
.draw(display)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Text::with_text_style(
|
||||||
|
&date,
|
||||||
|
Point::new(
|
||||||
|
(G13_LCD_COLUMNS as f64 / 2.0) as i32,
|
||||||
|
(G13_LCD_ROWS as f64 / 2.0) as i32 + 8,
|
||||||
|
),
|
||||||
|
CHARACTER_STYLE_DATE,
|
||||||
|
TEXTSTYLE,
|
||||||
|
)
|
||||||
|
.draw(display)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -153,5 +153,9 @@ fn to_evdev_button(button: Button) -> input_linux::Key {
|
|||||||
Button::Stick1 => input_linux::Key::Unknown155,
|
Button::Stick1 => input_linux::Key::Unknown155,
|
||||||
Button::Stick2 => input_linux::Key::Unknown156,
|
Button::Stick2 => input_linux::Key::Unknown156,
|
||||||
Button::Stick3 => input_linux::Key::Unknown157,
|
Button::Stick3 => input_linux::Key::Unknown157,
|
||||||
|
Button::StickUp => input_linux::Key::Unknown158,
|
||||||
|
Button::StickDown => input_linux::Key::Unknown159,
|
||||||
|
Button::StickLeft => input_linux::Key::Unknown15A,
|
||||||
|
Button::StickRight => input_linux::Key::Unknown15B,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,135 +1,34 @@
|
|||||||
mod error;
|
pub(crate) mod app;
|
||||||
mod joystick;
|
pub(crate) mod error;
|
||||||
|
pub(crate) mod joystick;
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use embedded_graphics::{
|
use embedded_graphics::pixelcolor::BinaryColor;
|
||||||
mono_font::{MonoTextStyle, ascii::*},
|
use embedded_graphics::prelude::*;
|
||||||
pixelcolor::BinaryColor,
|
use g13_driver::G13;
|
||||||
prelude::*,
|
|
||||||
text::{Alignment, Text, TextStyleBuilder},
|
|
||||||
};
|
|
||||||
use time::{OffsetDateTime, macros::offset};
|
|
||||||
use tokio::time::Instant;
|
|
||||||
|
|
||||||
use g13_driver::{
|
use crate::app::App;
|
||||||
G13, G13_LCD_COLUMNS, G13_LCD_ROWS,
|
|
||||||
joystick::{Axis, Button},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::joystick::Joystick;
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut g13 = G13::new()?;
|
||||||
#[tokio::main]
|
let mut app = App::new();
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let g13 = G13::new()?;
|
|
||||||
let joystick = Joystick::new("g13-joystick")?;
|
|
||||||
|
|
||||||
g13.set_lcd_color(4, 8, 96)?;
|
g13.set_lcd_color(4, 8, 96)?;
|
||||||
|
|
||||||
let mut _g13 = g13.clone();
|
let events = g13.events();
|
||||||
tokio::spawn(async move {
|
|
||||||
let character_style_time = MonoTextStyle::new(&FONT_10X20, BinaryColor::On);
|
|
||||||
let character_style_date = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
|
|
||||||
let textstyle = TextStyleBuilder::new()
|
|
||||||
.alignment(Alignment::Center)
|
|
||||||
.baseline(embedded_graphics::text::Baseline::Middle)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
|
let mut start = Instant::now();
|
||||||
loop {
|
loop {
|
||||||
let start = Instant::now();
|
if let Ok(event) = events.try_recv() {
|
||||||
let now = OffsetDateTime::now_utc().to_offset(offset!(+1)); // GMT+1
|
app.update(event);
|
||||||
|
|
||||||
// Render
|
|
||||||
let time = format!(
|
|
||||||
"{:0>2}:{:0>2}:{:0>2}",
|
|
||||||
now.hour(),
|
|
||||||
now.minute(),
|
|
||||||
now.second()
|
|
||||||
);
|
|
||||||
|
|
||||||
let date = format!("{}, {} {}", now.weekday(), now.day(), now.month());
|
|
||||||
|
|
||||||
_g13.clear(BinaryColor::Off).unwrap();
|
|
||||||
Text::with_text_style(
|
|
||||||
&time,
|
|
||||||
Point::new(
|
|
||||||
(G13_LCD_COLUMNS as f64 / 2.0) as i32,
|
|
||||||
(G13_LCD_ROWS as f64 / 2.0) as i32 - 8,
|
|
||||||
),
|
|
||||||
character_style_time,
|
|
||||||
textstyle,
|
|
||||||
)
|
|
||||||
.draw(&mut _g13)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Text::with_text_style(
|
|
||||||
&date,
|
|
||||||
Point::new(
|
|
||||||
(G13_LCD_COLUMNS as f64 / 2.0) as i32,
|
|
||||||
(G13_LCD_ROWS as f64 / 2.0) as i32 + 8,
|
|
||||||
),
|
|
||||||
character_style_date,
|
|
||||||
textstyle,
|
|
||||||
)
|
|
||||||
.draw(&mut _g13)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
_g13.render().unwrap();
|
|
||||||
|
|
||||||
// Calculate delta time
|
|
||||||
let delta = Instant::now() - start;
|
|
||||||
tokio::time::sleep(Duration::from_millis(33) - delta).await;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
loop {
|
if (Instant::now() - start) >= Duration::from_millis(33) {
|
||||||
let state = g13.read()?;
|
start = Instant::now();
|
||||||
|
g13.clear(BinaryColor::Off)?;
|
||||||
joystick.move_axis(Axis::X, state.x)?;
|
app.draw(&mut g13);
|
||||||
joystick.move_axis(Axis::Y, state.y)?;
|
g13.flush()?;
|
||||||
|
}
|
||||||
joystick.button_press(Button::Action, state.buttons.action)?;
|
|
||||||
joystick.button_press(Button::Screen1, state.buttons.screen1)?;
|
|
||||||
joystick.button_press(Button::Screen2, state.buttons.screen2)?;
|
|
||||||
joystick.button_press(Button::Screen3, state.buttons.screen3)?;
|
|
||||||
joystick.button_press(Button::Screen4, state.buttons.screen4)?;
|
|
||||||
joystick.button_press(Button::Light, state.buttons.light)?;
|
|
||||||
|
|
||||||
joystick.button_press(Button::M1, state.buttons.m1)?;
|
|
||||||
joystick.button_press(Button::M2, state.buttons.m2)?;
|
|
||||||
joystick.button_press(Button::M3, state.buttons.m3)?;
|
|
||||||
joystick.button_press(Button::MR, state.buttons.mr)?;
|
|
||||||
|
|
||||||
joystick.button_press(Button::G1, state.buttons.g1)?;
|
|
||||||
joystick.button_press(Button::G2, state.buttons.g2)?;
|
|
||||||
joystick.button_press(Button::G3, state.buttons.g3)?;
|
|
||||||
joystick.button_press(Button::G4, state.buttons.g4)?;
|
|
||||||
joystick.button_press(Button::G5, state.buttons.g5)?;
|
|
||||||
joystick.button_press(Button::G6, state.buttons.g6)?;
|
|
||||||
joystick.button_press(Button::G7, state.buttons.g7)?;
|
|
||||||
|
|
||||||
joystick.button_press(Button::G8, state.buttons.g8)?;
|
|
||||||
joystick.button_press(Button::G9, state.buttons.g9)?;
|
|
||||||
joystick.button_press(Button::G10, state.buttons.g10)?;
|
|
||||||
joystick.button_press(Button::G11, state.buttons.g11)?;
|
|
||||||
joystick.button_press(Button::G12, state.buttons.g12)?;
|
|
||||||
joystick.button_press(Button::G13, state.buttons.g13)?;
|
|
||||||
joystick.button_press(Button::G14, state.buttons.g14)?;
|
|
||||||
|
|
||||||
joystick.button_press(Button::G15, state.buttons.g15)?;
|
|
||||||
joystick.button_press(Button::G16, state.buttons.g16)?;
|
|
||||||
joystick.button_press(Button::G17, state.buttons.g17)?;
|
|
||||||
joystick.button_press(Button::G18, state.buttons.g18)?;
|
|
||||||
joystick.button_press(Button::G19, state.buttons.g19)?;
|
|
||||||
|
|
||||||
joystick.button_press(Button::G20, state.buttons.g20)?;
|
|
||||||
joystick.button_press(Button::G21, state.buttons.g21)?;
|
|
||||||
joystick.button_press(Button::G22, state.buttons.g22)?;
|
|
||||||
|
|
||||||
joystick.button_press(Button::Stick1, state.buttons.stick1)?;
|
|
||||||
joystick.button_press(Button::Stick2, state.buttons.stick2)?;
|
|
||||||
joystick.button_press(Button::Stick3, state.buttons.stick3)?;
|
|
||||||
|
|
||||||
joystick.synchronise()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ fn save_buffer_to_disk(
|
|||||||
.draw(&mut g13.g13)
|
.draw(&mut g13.g13)
|
||||||
.expect("G13 to be connected");
|
.expect("G13 to be connected");
|
||||||
|
|
||||||
let _ = g13.g13.render();
|
let _ = g13.g13.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
name = "DVD"
|
|
||||||
authors = {"AviiNL"}
|
|
||||||
description = "Bouncing 'dvd' Logo"
|
|
||||||
|
|
||||||
local width = g13.display_width
|
|
||||||
local height = g13.display_height
|
|
||||||
|
|
||||||
local x = 22.0
|
|
||||||
local y = 6.0
|
|
||||||
|
|
||||||
local x_direction = 1
|
|
||||||
local y_direction = 1
|
|
||||||
|
|
||||||
local speed = 20
|
|
||||||
|
|
||||||
local size = 3
|
|
||||||
|
|
||||||
function update(delta: number)
|
|
||||||
local x = x + speed * delta * x_direction
|
|
||||||
local y = y + speed * delta * y_direction
|
|
||||||
|
|
||||||
if x <= size or x >= width - size then
|
|
||||||
x_direction = -x_direction
|
|
||||||
end
|
|
||||||
|
|
||||||
if y <= size or y >= height - size then
|
|
||||||
y_direction = -y_direction
|
|
||||||
end
|
|
||||||
|
|
||||||
local color_x = (x / width) * 255
|
|
||||||
local color_y = (y / height) * 255
|
|
||||||
local color_z = 255 - (color_x + color_y) / 2
|
|
||||||
|
|
||||||
g13.set_color(color_x/2, color_y/2, color_z/2)
|
|
||||||
|
|
||||||
g13.clear()
|
|
||||||
|
|
||||||
for sy = -size,size do
|
|
||||||
for sx = -size,size do
|
|
||||||
g13.set_pixel(x + sx, y + sy, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user