mirror of https://github.com/jetkvm/kvm.git
Compare commits
5 Commits
cd6ffb87d2
...
def37141c8
Author | SHA1 | Date |
---|---|---|
|
def37141c8 | |
|
63b3ef0151 | |
|
69168ff062 | |
|
dd1189e8ad | |
|
c380c702c7 |
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "go.sum"
|
||||
- "go.mod"
|
||||
- "**.go"
|
||||
- ".github/workflows/golangci-lint.yml"
|
||||
- ".golangci.yml"
|
||||
pull_request:
|
||||
|
||||
permissions: # added using https://github.com/step-security/secure-repo
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
|
||||
with:
|
||||
args: --verbose
|
||||
version: v1.62.0
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
linters:
|
||||
enable:
|
||||
# - goimports
|
||||
# - misspell
|
||||
# - revive
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test.go
|
||||
linters:
|
||||
- errcheck
|
2
cloud.go
2
cloud.go
|
@ -130,7 +130,7 @@ func runWebsocketClient() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.CloseNow()
|
||||
defer c.CloseNow() //nolint:errcheck
|
||||
logger.Infof("WS connected to %v", wsURL.String())
|
||||
runCtx, cancelRun := context.WithCancel(context.Background())
|
||||
defer cancelRun()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"kvm"
|
||||
"github.com/jetkvm/kvm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
4
hw.go
4
hw.go
|
@ -14,7 +14,7 @@ func extractSerialNumber() (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
r, err := regexp.Compile("Serial\\s*:\\s*(\\S+)")
|
||||
r, err := regexp.Compile(`Serial\s*:\s*(\S+)`)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to compile regex: %w", err)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func extractSerialNumber() (string, error) {
|
|||
return matches[1], nil
|
||||
}
|
||||
|
||||
func readOtpEntropy() ([]byte, error) {
|
||||
func readOtpEntropy() ([]byte, error) { //nolint:unused
|
||||
content, err := os.ReadFile("/sys/bus/nvmem/devices/rockchip-otp0/nvmem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
2
main.go
2
main.go
|
@ -48,7 +48,7 @@ func Main() {
|
|||
time.Sleep(15 * time.Minute)
|
||||
for {
|
||||
logger.Debugf("UPDATING - Auto update enabled: %v", config.AutoUpdateEnabled)
|
||||
if config.AutoUpdateEnabled == false {
|
||||
if !config.AutoUpdateEnabled {
|
||||
return
|
||||
}
|
||||
if currentSession != nil {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"kvm/resource"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -14,6 +13,8 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/resource"
|
||||
|
||||
"github.com/pion/webrtc/v4/pkg/media"
|
||||
)
|
||||
|
||||
|
@ -91,8 +92,8 @@ func WriteCtrlMessage(message []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var nativeCtrlSocketListener net.Listener
|
||||
var nativeVideoSocketListener net.Listener
|
||||
var nativeCtrlSocketListener net.Listener //nolint:unused
|
||||
var nativeVideoSocketListener net.Listener //nolint:unused
|
||||
|
||||
var ctrlClientConnected = make(chan struct{})
|
||||
|
||||
|
@ -192,7 +193,7 @@ func handleVideoClient(conn net.Conn) {
|
|||
for {
|
||||
n, err := conn.Read(inboundPacket)
|
||||
if err != nil {
|
||||
log.Println("error during read: %s", err)
|
||||
log.Printf("error during read: %s\n", err)
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
|
|
|
@ -28,7 +28,7 @@ type LocalIpInfo struct {
|
|||
func checkNetworkState() {
|
||||
iface, err := netlink.LinkByName("eth0")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to get eth0 interface: %v\n", err)
|
||||
fmt.Printf("failed to get eth0 interface: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ func checkNetworkState() {
|
|||
|
||||
addrs, err := netlink.AddrList(iface, nl.FAMILY_ALL)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to get addresses for eth0: %v\n", err)
|
||||
fmt.Printf("failed to get addresses for eth0: %s\n", err)
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
|
@ -98,7 +98,7 @@ func init() {
|
|||
done := make(chan struct{})
|
||||
|
||||
if err := netlink.LinkSubscribe(updates, done); err != nil {
|
||||
fmt.Println("failed to subscribe to link updates: %v", err)
|
||||
fmt.Printf("failed to subscribe to link updates: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,6 @@ func init() {
|
|||
fmt.Println("Starting mDNS server")
|
||||
err := startMDNS()
|
||||
if err != nil {
|
||||
fmt.Println("failed to run mDNS: %v", err)
|
||||
fmt.Printf("failed to run mDNS: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
|
2
ntp.go
2
ntp.go
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/beevik/ntp"
|
||||
)
|
||||
|
||||
var timeSynced = false
|
||||
var timeSynced = false //nolint:unused
|
||||
|
||||
func TimeSyncLoop() {
|
||||
for {
|
||||
|
|
|
@ -49,7 +49,7 @@ func (w *WebRTCDiskReader) Read(ctx context.Context, offset int64, size int64) (
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := make([]byte, 0)
|
||||
var buf []byte
|
||||
for {
|
||||
select {
|
||||
case data := <-diskReadChan:
|
||||
|
|
|
@ -55,12 +55,14 @@ func handleTerminalChannel(d *webrtc.DataChannel) {
|
|||
var size TerminalSize
|
||||
err := json.Unmarshal([]byte(msg.Data), &size)
|
||||
if err == nil {
|
||||
pty.Setsize(ptmx, &pty.Winsize{
|
||||
err = pty.Setsize(ptmx, &pty.Winsize{
|
||||
Rows: uint16(size.Rows),
|
||||
Cols: uint16(size.Cols),
|
||||
})
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
logger.Errorf("Failed to parse terminal size: %v", err)
|
||||
}
|
||||
_, err := ptmx.Write(msg.Data)
|
||||
|
@ -74,7 +76,7 @@ func handleTerminalChannel(d *webrtc.DataChannel) {
|
|||
ptmx.Close()
|
||||
}
|
||||
if cmd != nil && cmd.Process != nil {
|
||||
cmd.Process.Kill()
|
||||
_ = cmd.Process.Kill()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"dev": "vite dev --mode=development",
|
||||
"build": "npm run build:prod",
|
||||
"build:device": "tsc && vite build --mode=device --emptyOutDir",
|
||||
"dev:device": "vite dev --mode=device",
|
||||
"build:prod": "tsc && vite build --mode=production",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
||||
},
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
useMountMediaStore,
|
||||
useUiStore,
|
||||
useSettingsStore,
|
||||
useVideoStore,
|
||||
} from "@/hooks/stores";
|
||||
import { MdOutlineContentPasteGo } from "react-icons/md";
|
||||
import Container from "@components/Container";
|
||||
|
@ -33,6 +34,7 @@ export default function Actionbar({
|
|||
state => state.remoteVirtualMediaState,
|
||||
);
|
||||
const developerMode = useSettingsStore(state => state.developerMode);
|
||||
const hdmiState = useVideoStore(state => state.hdmiState);
|
||||
|
||||
// This is the only way to get a reliable state change for the popover
|
||||
// at time of writing this there is no mount, or unmount event for the popover
|
||||
|
@ -247,6 +249,7 @@ export default function Actionbar({
|
|||
size="XS"
|
||||
theme="light"
|
||||
text="Fullscreen"
|
||||
disabled={hdmiState !== 'ready'}
|
||||
LeadingIcon={LuMaximize}
|
||||
onClick={() => requestFullscreen()}
|
||||
/>
|
||||
|
|
|
@ -30,6 +30,8 @@ export default function WebRTCVideo() {
|
|||
const {
|
||||
setClientSize: setVideoClientSize,
|
||||
setSize: setVideoSize,
|
||||
width: videoWidth,
|
||||
height: videoHeight,
|
||||
clientWidth: videoClientWidth,
|
||||
clientHeight: videoClientHeight,
|
||||
} = useVideoStore();
|
||||
|
@ -102,20 +104,43 @@ export default function WebRTCVideo() {
|
|||
const mouseMoveHandler = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (!videoClientWidth || !videoClientHeight) return;
|
||||
const { buttons } = e;
|
||||
// Get the aspect ratios of the video element and the video stream
|
||||
const videoElementAspectRatio = videoClientWidth / videoClientHeight;
|
||||
const videoStreamAspectRatio = videoWidth / videoHeight;
|
||||
|
||||
// Clamp mouse position within the video boundaries
|
||||
const currMouseX = Math.min(Math.max(1, e.offsetX), videoClientWidth);
|
||||
const currMouseY = Math.min(Math.max(1, e.offsetY), videoClientHeight);
|
||||
// Calculate the effective video display area
|
||||
let effectiveWidth = videoClientWidth;
|
||||
let effectiveHeight = videoClientHeight;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
// Normalize mouse position to 0-32767 range (HID absolute coordinate system)
|
||||
const x = Math.round((currMouseX / videoClientWidth) * 32767);
|
||||
const y = Math.round((currMouseY / videoClientHeight) * 32767);
|
||||
if (videoElementAspectRatio > videoStreamAspectRatio) {
|
||||
// Pillarboxing: black bars on the left and right
|
||||
effectiveWidth = videoClientHeight * videoStreamAspectRatio;
|
||||
offsetX = (videoClientWidth - effectiveWidth) / 2;
|
||||
} else if (videoElementAspectRatio < videoStreamAspectRatio) {
|
||||
// Letterboxing: black bars on the top and bottom
|
||||
effectiveHeight = videoClientWidth / videoStreamAspectRatio;
|
||||
offsetY = (videoClientHeight - effectiveHeight) / 2;
|
||||
}
|
||||
|
||||
// Clamp mouse position within the effective video boundaries
|
||||
const clampedX = Math.min(Math.max(offsetX, e.offsetX), offsetX + effectiveWidth);
|
||||
const clampedY = Math.min(Math.max(offsetY, e.offsetY), offsetY + effectiveHeight);
|
||||
|
||||
// Map clamped mouse position to the video stream's coordinate system
|
||||
const relativeX = (clampedX - offsetX) / effectiveWidth;
|
||||
const relativeY = (clampedY - offsetY) / effectiveHeight;
|
||||
|
||||
// Convert to HID absolute coordinate system (0-32767 range)
|
||||
const x = Math.round(relativeX * 32767);
|
||||
const y = Math.round(relativeY * 32767);
|
||||
|
||||
// Send mouse movement
|
||||
const { buttons } = e;
|
||||
sendMouseMovement(x, y, buttons);
|
||||
},
|
||||
[sendMouseMovement, videoClientHeight, videoClientWidth],
|
||||
[sendMouseMovement, videoClientHeight, videoClientWidth, videoWidth, videoHeight],
|
||||
);
|
||||
|
||||
const mouseWheelHandler = useCallback(
|
||||
|
|
|
@ -2,13 +2,31 @@ import { defineConfig } from "vite";
|
|||
import react from "@vitejs/plugin-react-swc";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
declare const process: {
|
||||
env: {
|
||||
JETKVM_PROXY_URL: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default defineConfig(({ mode, command }) => {
|
||||
const isCloud = mode === "production";
|
||||
const onDevice = mode === "device";
|
||||
const { JETKVM_PROXY_URL } = process.env;
|
||||
|
||||
return {
|
||||
plugins: [tsconfigPaths(), react()],
|
||||
build: { outDir: isCloud ? "dist" : "../static" },
|
||||
server: { host: "0.0.0.0" },
|
||||
base: onDevice ? "/static" : "/",
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
proxy: JETKVM_PROXY_URL ? {
|
||||
'/me': JETKVM_PROXY_URL,
|
||||
'/device': JETKVM_PROXY_URL,
|
||||
'/webrtc': JETKVM_PROXY_URL,
|
||||
'/auth': JETKVM_PROXY_URL,
|
||||
'/storage': JETKVM_PROXY_URL,
|
||||
'/cloud': JETKVM_PROXY_URL,
|
||||
} : undefined
|
||||
},
|
||||
base: onDevice && command === 'build' ? "/static" : "/",
|
||||
};
|
||||
});
|
||||
|
|
6
usb.go
6
usb.go
|
@ -132,7 +132,7 @@ func writeGadgetConfig() error {
|
|||
}
|
||||
err = writeGadgetAttrs(hid0Path, [][]string{
|
||||
{"protocol", "1"},
|
||||
{"subclass", "0"},
|
||||
{"subclass", "1"},
|
||||
{"report_length", "8"},
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -152,7 +152,7 @@ func writeGadgetConfig() error {
|
|||
}
|
||||
err = writeGadgetAttrs(hid1Path, [][]string{
|
||||
{"protocol", "2"},
|
||||
{"subclass", "0"},
|
||||
{"subclass", "1"},
|
||||
{"report_length", "6"},
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -215,7 +215,7 @@ func writeGadgetConfig() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func rebindUsb() error {
|
||||
func rebindUsb() error { //nolint:unused
|
||||
err := os.WriteFile("/sys/bus/platform/drivers/dwc3/unbind", []byte(udc), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"kvm/resource"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -16,12 +15,12 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/resource"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/psanford/httpreadat"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/psanford/httpreadat"
|
||||
)
|
||||
|
||||
const massStorageName = "mass_storage.usb0"
|
||||
|
@ -60,11 +59,11 @@ func onDiskMessage(msg webrtc.DataChannelMessage) {
|
|||
func mountImage(imagePath string) error {
|
||||
err := setMassStorageImage("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Remove Mass Storage Image Error", err)
|
||||
return fmt.Errorf("Remove Mass Storage Image Error: %w", err)
|
||||
}
|
||||
err = setMassStorageImage(imagePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Set Mass Storage Image Error", err)
|
||||
return fmt.Errorf("Set Mass Storage Image Error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue