Merge branch 'tristan-wip' of https://gitea.clubconjure.com/Conjure/conjure-os into tristan-wip
This commit is contained in:
commit
552d30098f
4
app.go
4
app.go
@ -63,3 +63,7 @@ func (a *App) LoadGames() []models.Game {
|
||||
games = provider.GetConjureGameInfo()
|
||||
return games
|
||||
}
|
||||
|
||||
func (a *App) LoadImage(gameId string, imageSrc string) provider.FileBlob {
|
||||
return *provider.LoadImage(gameId, imageSrc)
|
||||
}
|
||||
|
||||
31
frontend/src/components/GameCard.vue
Normal file
31
frontend/src/components/GameCard.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div
|
||||
:key="game.Id"
|
||||
:class="[
|
||||
'transition-transform transform cursor-pointer rounded-lg overflow-hidden border-4',
|
||||
selected ? 'scale-110 border-blue-500' : 'scale-100 border-transparent'
|
||||
]"
|
||||
>
|
||||
<LocalImage
|
||||
:src="game.ThumbnailPath"
|
||||
class="h-32 w-48 object-cover"
|
||||
:alt="game.Game"
|
||||
:key="game.Id"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { models } from "../../wailsjs/go/models";
|
||||
import Game = models.Game;
|
||||
import LocalImage from "./LocalImage.vue";
|
||||
|
||||
defineProps<{
|
||||
game: Game
|
||||
selected: boolean
|
||||
}>()
|
||||
</script>
|
||||
@ -2,21 +2,12 @@
|
||||
<div class="relative h-[170px]">
|
||||
<Transition :name="`carousel-${direction}`" mode="out-in">
|
||||
<div :key="selectedTag" class="w-full py-4 px-6 flex overflow-x-auto space-x-4 items-end transition-inner">
|
||||
<div
|
||||
<GameCard
|
||||
v-for="game in games"
|
||||
:key="game.Id"
|
||||
:game="game"
|
||||
:selected="game.Id === selectedGame?.Id"
|
||||
@click="$emit('selectGame', game)"
|
||||
:class="[
|
||||
'transition-transform transform cursor-pointer rounded-lg overflow-hidden border-4',
|
||||
game.Id === selectedGame.Id ? 'scale-110 border-blue-500' : 'scale-100 border-transparent'
|
||||
]"
|
||||
>
|
||||
<img
|
||||
:src="game.ThumbnailPath"
|
||||
class="h-32 w-48 object-cover"
|
||||
:alt="game.Game"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
<!-- Give the whole carousel a key based on selectedTag to trigger transition -->
|
||||
<!-- <div :key="selectedTag" class="flex gap-4 w-full transition-inner">-->
|
||||
@ -36,6 +27,7 @@
|
||||
|
||||
import { models } from "../../wailsjs/go/models";
|
||||
import Game = models.Game;
|
||||
import GameCard from "./GameCard.vue";
|
||||
|
||||
defineProps<{
|
||||
games: Game[],
|
||||
|
||||
29
frontend/src/components/LocalImage.vue
Normal file
29
frontend/src/components/LocalImage.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<img
|
||||
:src="blobUrl"
|
||||
:alt="alt"
|
||||
>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { ImageManager } from "../utils/image-manager";
|
||||
|
||||
defineProps<{
|
||||
key: string
|
||||
src: string
|
||||
alt: string
|
||||
}>()
|
||||
|
||||
const blobUrl = ref<string | null>(null);
|
||||
|
||||
watch(() => props.imageUrl, async (newUrl) => {
|
||||
blobUrl.value = await ImageManager.getBlob(key, newUrl);
|
||||
}, { immediate: true });
|
||||
|
||||
console.log(src)
|
||||
</script>
|
||||
21
frontend/src/utils/image-manager.ts
Normal file
21
frontend/src/utils/image-manager.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import {LoadImage} from "../../wailsjs/go/main/App";
|
||||
import * as path from "node:path";
|
||||
|
||||
export class ImageManager {
|
||||
|
||||
static Dictionary: {[key: string]: string} = {}
|
||||
|
||||
public static async getImage(gameId: string, src: string): Promise<string> {
|
||||
const id = path.join(gameId, src);
|
||||
if (this.Dictionary[id])
|
||||
return this.Dictionary[id]
|
||||
|
||||
const fileBlob = await LoadImage(gameId, src);
|
||||
const bytes = new Uint8Array(fileBlob.Data);
|
||||
const blob = new Blob([bytes], {type: fileBlob.MimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
this.Dictionary[id] = url
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
3
frontend/wailsjs/go/main/App.d.ts
vendored
3
frontend/wailsjs/go/main/App.d.ts
vendored
@ -1,7 +1,10 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
import {models} from '../models';
|
||||
import {provider} from '../models';
|
||||
|
||||
export function LoadGames():Promise<Array<models.Game>>;
|
||||
|
||||
export function LoadImage(arg1:string,arg2:string):Promise<provider.FileBlob>;
|
||||
|
||||
export function StartGame(arg1:string):Promise<void>;
|
||||
|
||||
@ -6,6 +6,10 @@ export function LoadGames() {
|
||||
return window['go']['main']['App']['LoadGames']();
|
||||
}
|
||||
|
||||
export function LoadImage(arg1, arg2) {
|
||||
return window['go']['main']['App']['LoadImage'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function StartGame(arg1) {
|
||||
return window['go']['main']['App']['StartGame'](arg1);
|
||||
}
|
||||
|
||||
@ -45,3 +45,24 @@ export namespace models {
|
||||
|
||||
}
|
||||
|
||||
export namespace provider {
|
||||
|
||||
export class FileBlob {
|
||||
Name: string;
|
||||
MimeType: string;
|
||||
Data: number[];
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new FileBlob(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.Name = source["Name"];
|
||||
this.MimeType = source["MimeType"];
|
||||
this.Data = source["Data"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
0
frontend/wailsjs/runtime/package.json
Normal file → Executable file
0
frontend/wailsjs/runtime/package.json
Normal file → Executable file
0
frontend/wailsjs/runtime/runtime.d.ts
vendored
Normal file → Executable file
0
frontend/wailsjs/runtime/runtime.d.ts
vendored
Normal file → Executable file
0
frontend/wailsjs/runtime/runtime.js
Normal file → Executable file
0
frontend/wailsjs/runtime/runtime.js
Normal file → Executable file
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.22.0
|
||||
toolchain go1.23.5
|
||||
|
||||
require (
|
||||
github.com/karalabe/hid v1.0.0
|
||||
github.com/wailsapp/wails/v2 v2.9.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
2
go.sum
2
go.sum
@ -11,6 +11,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo=
|
||||
github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
package inputs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/karalabe/hid"
|
||||
)
|
||||
|
||||
type JoystickEvent struct {
|
||||
@ -21,29 +20,84 @@ const (
|
||||
)
|
||||
|
||||
func Start() {
|
||||
// Open the joystick device file
|
||||
file, err := os.Open("/dev/input/js0")
|
||||
if err != nil {
|
||||
fmt.Println("Error opening joystick:", err)
|
||||
return
|
||||
fmt.Println("Opening devices")
|
||||
const vendorID = 0x0079
|
||||
const productID = 0x0006
|
||||
|
||||
// Enumerate all matching devices
|
||||
devices := hid.Enumerate(vendorID, productID)
|
||||
if len(devices) == 0 {
|
||||
fmt.Printf("Device with VID:PID %04X:%04X not found", vendorID, productID)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Continuously read joystick events
|
||||
for _, d := range devices {
|
||||
fmt.Printf("Found: %s - VID:%04X PID:%04X\n", d.Product, d.VendorID, d.ProductID)
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
buf := make([]byte, 32) // Adjust size if needed
|
||||
for {
|
||||
var e JoystickEvent
|
||||
err := binary.Read(file, binary.LittleEndian, &e)
|
||||
_, err := device.Read(buf)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading joystick event:", err)
|
||||
fmt.Printf("Read error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle the event
|
||||
handleJoystickEvent(e)
|
||||
// For debugging: print raw data
|
||||
// fmt.Printf("Raw: % X\n", buf[:n])
|
||||
|
||||
// Sleep to avoid flooding output
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
// 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
|
||||
|
||||
// fmt.Printf("Joystick X: %d, Y: %d, Buttons: %08b\n", x, y, buttons)
|
||||
|
||||
if buttons != 0 {
|
||||
fmt.Printf("Button was pressed! %d\n", buttons)
|
||||
}
|
||||
|
||||
if x != 127 || y != 127 {
|
||||
fmt.Printf("Joystick moved! %d - %d\n", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Out")
|
||||
|
||||
// Open the joystick device file
|
||||
// file, err := os.Open("/dev/input/js0")
|
||||
// if err != nil {
|
||||
// fmt.Println("Error opening joystick:", err)
|
||||
// return
|
||||
// }
|
||||
// defer file.Close()
|
||||
|
||||
// // Continuously read joystick events
|
||||
// for {
|
||||
// var e JoystickEvent
|
||||
// err := binary.Read(file, binary.LittleEndian, &e)
|
||||
// if err != nil {
|
||||
// fmt.Println("Error reading joystick event:", err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// // Handle the event
|
||||
// handleJoystickEvent(e)
|
||||
|
||||
// // Sleep to avoid flooding output
|
||||
// time.Sleep(10 * time.Millisecond)
|
||||
// }
|
||||
}
|
||||
|
||||
// handleJoystickEvent processes joystick events.
|
||||
|
||||
53
lib/provider/file-provider.go
Normal file
53
lib/provider/file-provider.go
Normal file
@ -0,0 +1,53 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"conjure-os/lib/config"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func LoadImage(gameId string, imageSrc string) *FileBlob {
|
||||
imagePath := filepath.Join(config.GetDefaultConjureGamesDirectory(), gameId, imageSrc)
|
||||
blob, err := GetFileBlob(imagePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return blob
|
||||
}
|
||||
|
||||
type FileBlob struct {
|
||||
Name string
|
||||
MimeType string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func GetFileBlob(path string) (*FileBlob, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Try to guess MIME type from content
|
||||
mimeType := http.DetectContentType(data)
|
||||
|
||||
// Fallback to extension-based detection
|
||||
if mimeType == "application/octet-stream" {
|
||||
ext := filepath.Ext(path)
|
||||
if ext != "" {
|
||||
if t := mime.TypeByExtension(ext); t != "" {
|
||||
mimeType = t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name := filepath.Base(path)
|
||||
|
||||
return &FileBlob{
|
||||
Name: name,
|
||||
MimeType: mimeType,
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
@ -206,8 +206,6 @@ func GetConjureGameInfo() []models.Game {
|
||||
metadata, err := io.ReadAll(rc)
|
||||
game := parseGameInfo([]byte(escapeBackslashes(string(metadata))))
|
||||
fmt.Println(game.ThumbnailPath)
|
||||
game.ThumbnailPath = filepath.Join(conjBase, game.ThumbnailPath)
|
||||
game.ImagePath = filepath.Join(conjBase, game.ImagePath)
|
||||
games = append(games, game)
|
||||
check(err)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user