diff --git a/app.go b/app.go index 0eb4ea0..cc04e72 100644 --- a/app.go +++ b/app.go @@ -6,12 +6,19 @@ import ( "conjure-os/lib/provider" "context" "fmt" - "github.com/wailsapp/wails/v2/pkg/runtime" "os" "os/exec" + "time" + + "github.com/wailsapp/wails/v2/pkg/runtime" ) -var games []models.Game +var ( + games []models.Game + lastEmitTimestamp = time.Now().Add(-10 * time.Second) + emitInterval = 300 * time.Millisecond + gameIsOpen = false +) // App struct type App struct { @@ -31,8 +38,27 @@ func (a *App) startup(ctx context.Context) { provider.Update() } -func (a *App) onControllerChange(data any) { - runtime.EventsEmit(a.ctx, "controller_change", data) +func (a *App) onControllerChange(state inputs.ControllerState) { + now := time.Now() + + if now.Sub(lastEmitTimestamp) >= emitInterval && !gameIsOpen { + if state.Buttons != 0 { + for _, button := range inputs.ConjureControllerButtons { + if state.Buttons&(1< + + + diff --git a/frontend/src/models/controller-state.ts b/frontend/src/models/controller-state.ts new file mode 100644 index 0000000..441d3f4 --- /dev/null +++ b/frontend/src/models/controller-state.ts @@ -0,0 +1,7 @@ +export interface ControllerState { + joystick: { + x: number, + y: number, + }, + buttons: number +} \ No newline at end of file diff --git a/frontend/src/stores/app-store.ts b/frontend/src/stores/app-store.ts index 18715bc..896d6f4 100644 --- a/frontend/src/stores/app-store.ts +++ b/frontend/src/stores/app-store.ts @@ -11,7 +11,8 @@ export const useAppStore = defineStore('app', { transitionDirection: 'down' as 'up' | 'down', selectedGame: null as Game | null, selectedGameIndex: 0, - qrLink: '' as string + qrLink: '' as string, + gameIsStarting: false as boolean }), getters: { filteredGames(state): Game[] { @@ -50,9 +51,11 @@ export const useAppStore = defineStore('app', { showQr(link: string) { this.qrLink = link; }, - startSelectedGame() { + async startSelectedGame() { if (this.selectedGame) { - StartGame(this.selectedGame.Id).then() + this.gameIsStarting = true; + await StartGame(this.selectedGame.Id); + this.gameIsStarting = false; } else { console.log("No game selected") diff --git a/frontend/src/utils/key-contexts/key-context.ts b/frontend/src/utils/key-contexts/key-context.ts index 7a4aa32..1eaffdc 100644 --- a/frontend/src/utils/key-contexts/key-context.ts +++ b/frontend/src/utils/key-contexts/key-context.ts @@ -1,3 +1,4 @@ +import { ControllerState } from "../../models/controller-state"; import { useAppStore } from "../../stores/app-store"; export abstract class KeyContext { @@ -30,6 +31,31 @@ export abstract class KeyContext { } } + public handleState(state: ControllerState) { + if (state.joystick.x === 0) { + this.onKeyLeft() + } + else if (state.joystick.x === 255) { + this.onKeyRight() + } + + if (state.joystick.y === 0) { + this.onKeyUp() + } + else if (state.joystick.y === 255) { + this.onKeyDown() + } + + if ((state.buttons & 0x02) !== 0) { + this.onEnter() + } + + // TODO should be 0x01 when the power button will work + if ((state.buttons & 0x04) !== 0) { + this.onEscape() + } + } + protected onKeyRight(): void { console.log('onKeyRight'); } diff --git a/frontend/src/utils/keyboard-manager.ts b/frontend/src/utils/keyboard-manager.ts index 810b137..faf9cb7 100644 --- a/frontend/src/utils/keyboard-manager.ts +++ b/frontend/src/utils/keyboard-manager.ts @@ -1,3 +1,4 @@ +import { ControllerState } from "../models/controller-state"; import { CarouselKeyContext } from "./key-contexts/carousel-key-context"; import { KeyContext } from "./key-contexts/key-context"; import { SidebarKeyContext } from "./key-contexts/sidebar-key-context"; @@ -14,4 +15,8 @@ export class KeyboardManager { static handle(event: KeyboardEvent) { this.current?.handleKey(event); } + + static handleState(controllerState: ControllerState) { + this.current?.handleState(controllerState); + } } \ No newline at end of file diff --git a/frontend/src/utils/use-keyboard-navigation.ts b/frontend/src/utils/use-keyboard-navigation.ts index fe7f3ae..666d59f 100644 --- a/frontend/src/utils/use-keyboard-navigation.ts +++ b/frontend/src/utils/use-keyboard-navigation.ts @@ -4,7 +4,7 @@ import { EventsOn } from "../../wailsjs/runtime"; export function useKeyboardNavigation(): void { onMounted(() => { - EventsOn("controller_change", (data) => console.log(data)); + EventsOn("controller_change", KeyboardManager.handleState.bind(KeyboardManager)); window.addEventListener('keydown', KeyboardManager.handle.bind(KeyboardManager)); }); diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/frontend/wailsjs/runtime/runtime.d.ts index 94778df..4445dac 100644 --- a/frontend/wailsjs/runtime/runtime.d.ts +++ b/frontend/wailsjs/runtime/runtime.d.ts @@ -134,7 +134,7 @@ export function WindowIsFullscreen(): Promise; // [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize) // Sets the width and height of the window. -export function WindowSetSize(width: number, height: number): Promise; +export function WindowSetSize(width: number, height: number): void; // [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize) // Gets the width and height of the window. diff --git a/go.mod b/go.mod index a42c190..aa26443 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.5 require ( github.com/karalabe/hid v1.0.0 - github.com/wailsapp/wails/v2 v2.9.2 + github.com/wailsapp/wails/v2 v2.10.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 9690a5f..58e26dc 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyT github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v2 v2.9.2 h1:Xb5YRTos1w5N7DTMyYegWaGukCP2fIaX9WF21kPPF2k= -github.com/wailsapp/wails/v2 v2.9.2/go.mod h1:uehvlCwJSFcBq7rMCGfk4rxca67QQGsbg5Nm4m9UnBs= +github.com/wailsapp/wails/v2 v2.10.1 h1:QWHvWMXII2nI/nXz77gpPG8P3ehl6zKe+u4su5BWIns= +github.com/wailsapp/wails/v2 v2.10.1/go.mod h1:zrebnFV6MQf9kx8HI4iAv63vsR5v67oS7GTEZ7Pz1TY= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= diff --git a/lib/inputs/joystick.go b/lib/inputs/joystick.go index d73c8d4..375e3dc 100644 --- a/lib/inputs/joystick.go +++ b/lib/inputs/joystick.go @@ -23,52 +23,60 @@ type Controller struct { Device *hid.Device } -func (c *Controller) ReadState() error { - buf := make([]byte, 64) +func (c *Controller) ReadState(buf []byte) (*ControllerState, error) { _, err := c.Device.Read(buf) + if err != nil { - return err + return nil, err } - fmt.Printf("Joystick: X=%d Y=%d Buttons=%08b\n", buf[0], buf[1], buf[6]) - return nil + x := buf[0] // Horizontal axis (0–255) + y := buf[1] // Vertical axis (0–255) + buttons := buf[6] // Buttons as bitfield + state := ControllerState{ + Joystick: Vec2B{x, y}, + Buttons: buttons, + } + + return &state, nil } type Vec2B struct { - X, Y byte + X byte `json:"x"` + Y byte `json:"y"` } type ControllerState struct { - joystick Vec2B - buttons byte + Joystick Vec2B `json:"joystick"` + Buttons byte `json:"buttons"` } type ConjureControllerButton int const ( - ButtonA ConjureControllerButton = iota //0 - ButtonB - ButtonC + ButtonPower ConjureControllerButton = iota //0 + ButtonStart Button1 Button2 Button3 - ButtonStart - ButtonPower // 7 + ButtonA + ButtonB + ButtonC // 7 ) var ConjureControllerButtons = []ConjureControllerButton{ - ButtonA, - ButtonB, - ButtonC, + ButtonStart, + ButtonPower, Button1, Button2, Button3, - ButtonStart, - ButtonPower, + ButtonA, + ButtonB, + ButtonC, } func (s ConjureControllerButton) String() string { @@ -94,7 +102,7 @@ func (s ConjureControllerButton) String() string { } } -func Start(onStateChange func(any)) { +func Start(onStateChange func(ControllerState)) { fmt.Println("Opening devices") const vendorID = 0x0079 const productID = 0x0006 @@ -111,50 +119,34 @@ func Start(onStateChange func(any)) { } // Open the first matching device - device, err := devices[0].Open() - if err != nil { - fmt.Printf("Failed to open device: %v", err) - return - } - defer device.Close() - fmt.Println("Reading data... Press Ctrl+C to exit") + for i, deviceDetected := range devices { - buf := make([]byte, 32) // Adjust size if needed - for { - _, err := device.Read(buf) + fmt.Printf("device %d detected\n", i) + device, err := deviceDetected.Open() if err != nil { - fmt.Printf("Read error: %v", err) + fmt.Printf("Failed to open device: %v", err) return } + defer device.Close() - // For debugging: print raw data - // fmt.Printf("Raw: % X\n", buf[:n]) + controller := Controller{Device: device} - // Example: decode joystick + button state - x := buf[0] // Horizontal axis (0–255) - y := buf[1] // Vertical axis (0–255) - buttons := buf[6] // Buttons as bitfield - state := ControllerState{ - joystick: Vec2B{x, y}, - buttons: buttons, - } + fmt.Println("Reading data... Press Ctrl+C to exit") - // fmt.Printf("Joystick X: %d, Y: %d, Buttons: %08b\n", x, y, buttons) + buf := make([]byte, 32) // Adjust size if needed + for { + state, err := controller.ReadState(buf) - if buttons != 0 { - for _, button := range ConjureControllerButtons { - if buttons&(1<