Compare commits

...

31 Commits

Author SHA1 Message Date
Aveline ad229726fa
Merge 227409e2e8 into 703625d59a 2025-09-25 23:28:42 +00:00
Siyuan 227409e2e8 fix lint errors 2025-09-25 23:28:30 +00:00
Siyuan 8f8a743ee6 fix linter errors 2025-09-25 23:22:19 +00:00
Siyuan e3512cec36 update devcontainer.json 2025-09-25 22:52:10 +00:00
Siyuan Miao 882cd9ed19 entrypoint script 2025-09-25 22:37:42 +00:00
Siyuan Miao dde897699e cgo build script + ci helper 2025-09-25 22:29:32 +00:00
Siyuan Miao 4d274b3e3e exit on error in ci helper 2025-09-25 22:26:03 +00:00
Siyuan Miao 7d169e30d6 load docker image 2025-09-25 22:25:20 +00:00
Siyuan Miao e53eb320ee feat: add ci helper scripts 2025-09-25 22:21:33 +00:00
Siyuan Miao b0b1341508 build inside docker container 2025-09-25 21:31:47 +00:00
Siyuan Miao d0aa3f1386 set TERM to dumb if not set 2025-09-25 21:22:06 +00:00
Siyuan Miao a5cac6f51b update ci 2025-09-25 21:11:33 +00:00
Siyuan Miao 85b9955cad reduce initial build time 2025-09-25 21:04:22 +00:00
Siyuan Miao 3f16c28daf ignore build cache 2025-09-25 19:37:42 +00:00
Siyuan Miao 81063425ac add build cache to dev_deploy.sh 2025-09-25 19:35:26 +00:00
Aveline afec360f65
Merge branch 'dev' into feat/native-as-lib 2025-09-25 20:09:53 +02:00
Siyuan Miao 2a22f234f8 show video debugging info 2025-09-25 18:01:49 +00:00
Siyuan Miao 462613e129 makefile 2025-09-25 16:46:40 +00:00
Siyuan Miao 23a3aaa61d dd script to build inside docker 2025-09-25 16:27:01 +00:00
Siyuan Miao 358512fa83 cleanup 2025-09-25 15:21:08 +00:00
Siyuan Miao 70db172287 update state 2025-09-25 15:08:43 +00:00
Siyuan Miao 76c4144565 fix ipv6 address truncation 2025-09-25 13:05:50 +00:00
Siyuan Miao e9bcdc5f3f minor ui fixes 2025-09-25 13:00:29 +00:00
Siyuan Miao 028cb7ddd6 feat: add reset config and reboot buttons to UI 2025-09-25 12:35:03 +00:00
Siyuan Miao e930363b24 add locks and missing comments 2025-09-25 09:51:59 +00:00
Siyuan Miao f106d308a3 chore: use log_error in native library 2025-09-25 08:39:29 +00:00
Siyuan Miao b042adac67 fix: race condition in native library 2025-09-25 08:33:53 +00:00
Adam Shiervani 703625d59a
fix: faster paste speed (#824)
* fix: update delay handling in PasteModal component

- Changed default delay value to 20 and adjusted validation to allow values between 0 and 65534.
- Cleaned up code formatting for better readability.

* fix: formatting
2025-09-25 10:26:11 +02:00
Siyuan Miao e582486bec remove lldp 2025-09-24 21:05:05 +00:00
Marc Brooks cb24ef6d4f
Update UI dependencies (#819) 2025-09-24 12:16:02 +02:00
Siyuan Miao fe77acd5f0 chore: bump to 0.4.8 2025-09-22 12:51:19 +02:00
86 changed files with 6623 additions and 2064 deletions

View File

@ -35,4 +35,4 @@
]
}
}
}
}

View File

@ -1,8 +1,19 @@
#!/bin/bash
set -e
SUDO_PATH=$(which sudo)
function sudo() {
if [ "$UID" -eq 0 ]; then
"$@"
else
${SUDO_PATH} "$@"
fi
}
sudo apt-get update && sudo apt-get install -y --no-install-recommends \
set -ex
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update && \
sudo apt-get install -y --no-install-recommends \
build-essential \
device-tree-compiler \
gperf g++-multilib gcc-multilib \

View File

@ -16,13 +16,19 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up build dependencies
- name: Set up docker image context
run: |
apt-get update && apt-get install -y \
build-essential \
cmake \
libusb-1.0-0-dev \
libudev-dev
./scripts/ci_helper.sh prepare
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build docker image
uses: docker/build-push-action@v6
with:
context: ${{ env.DOCKER_BUILD_CONTEXT_DIR }}
file: ${{ env.DOCKER_BUILD_CONTEXT_DIR }}/Dockerfile.build
tags: ${{ env.DOCKER_BUILD_TAG }}
push: false
load: true
- name: Set up Cmake cache
uses: actions/cache@v4
with:
@ -37,21 +43,21 @@ jobs:
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: Set up Golang
uses: actions/setup-go@v5.5.0
uses: actions/setup-go@v6.0.0
with:
go-version: "1.24.4"
go-version: "^1.25.1"
- name: Build frontend
run: |
make frontend
- name: Build application
- name: Build application inside Docker container
run: |
make build_dev
./scripts/ci_helper.sh make build_dev
- name: Run tests
run: |
go test ./... -json > testreport.json
- name: Make test cases
run: |
make build_dev_test
./scripts/ci_helper.sh make build_dev_test
- name: Golang Test Report
uses: becheran/go-testreport@v0.3.2
with:

4
.gitignore vendored
View File

@ -3,6 +3,10 @@ static/*
.idea
.DS_Store
.cache
.vite
.pnpm-store
device-tests.tar.gz
node_modules

View File

@ -3,5 +3,6 @@
"cva",
"cx"
],
"cmake.sourceDirectory": "/Users/aveline/Projects/JetKVM/ymjk/internal/native/cgo"
"cmake.sourceDirectory": "/Users/aveline/Projects/JetKVM/ymjk/internal/native/cgo",
"git.ignoreLimitWarning": true
}

View File

@ -361,6 +361,17 @@ The application uses a JSON configuration file stored at `/userdata/kvm_config.j
3. **Add migration logic if needed for existing installations**
### LVGL Build
We modified the LVGL code a little bit to remove unused fonts and examples.
The patches are generated by
```bash
git diff --cached --diff-filter=d > ../internal/native/cgo/lvgl-minify.patch && \
git diff --name-only --diff-filter=d --cached > ../internal/native/cgo/lvgl-minify.del
```
---
**Happy coding!**

View File

@ -1,11 +1,11 @@
# syntax=docker/dockerfile:1
FROM golang:1.25.1-trixie
FROM --platform=${BUILDPLATFORM} golang:1.25.1-trixie AS builder
ENV GOTOOLCHAIN=local
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
ENV GOPATH=/go
ENV PATH=$GOPATH/bin:/usr/local/go/bin:$PATH
ADD .devcontainer/install-deps.sh /install-deps.sh
COPY install-deps.sh /install-deps.sh
RUN /install-deps.sh
# Create build directory
@ -16,4 +16,9 @@ COPY go.mod go.sum /build/
WORKDIR /build
RUN go mod download && go mod verify
RUN go mod download && go mod verify
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT [ "/entrypoint.sh" ]

View File

@ -2,16 +2,16 @@ BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
BUILDDATE := $(shell date -u +%FT%T%z)
BUILDTS := $(shell date -u +%s)
REVISION := $(shell git rev-parse HEAD)
VERSION_DEV := 0.4.8-dev$(shell date +%Y%m%d%H%M)
VERSION := 0.4.7
VERSION_DEV := 0.5.0-dev$(shell date +%Y%m%d%H%M)
VERSION := 0.4.9
PROMETHEUS_TAG := github.com/prometheus/common/version
KVM_PKG_NAME := github.com/jetkvm/kvm
BUILDKIT_FLAVOR := arm-rockchip830-linux-uclibcgnueabihf
BUILDKIT_PATH ?= /opt/jetkvm-native-buildkit
SKIP_NATIVE_IF_EXISTS ?= 1
SKIP_NATIVE_IF_EXISTS ?= 0
SKIP_UI_BUILD ?= 0
GO_BUILD_ARGS := -tags netgo -tags timetzdata
GO_RELEASE_BUILD_ARGS := -trimpath $(GO_BUILD_ARGS)
@ -45,10 +45,9 @@ build_native:
echo "libjknative.a already exists, skipping native build..."; \
else \
echo "Building native..."; \
cd internal/native/cgo && ./ui_index.gen.sh && \
CC="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-gcc" \
LD="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-ld" \
./build.sh; \
./scripts/build_cgo.sh; \
fi
build_dev: build_native
@ -88,22 +87,24 @@ build_dev_test: build_test2json build_gotestsum
tar czfv device-tests.tar.gz -C $(BIN_DIR)/tests .
frontend:
cd ui && npm ci && npm run build:device && \
find ../static/ \
-type f \
\( -name '*.js' \
-o -name '*.css' \
-o -name '*.html' \
-o -name '*.ico' \
-o -name '*.png' \
-o -name '*.jpg' \
-o -name '*.jpeg' \
-o -name '*.gif' \
-o -name '*.svg' \
-o -name '*.webp' \
-o -name '*.woff2' \
\) \
-exec sh -c 'gzip -9 -kfv {}' \;
@if [ "$(SKIP_UI_BUILD)" = "1" ] && [ -f "static/index.html" ]; then \
echo "Skipping frontend build..."; \
else \
cd ui && npm ci && npm run build:device && \
find ../static/ -type f \
\( -name '*.js' \
-o -name '*.css' \
-o -name '*.html' \
-o -name '*.ico' \
-o -name '*.png' \
-o -name '*.jpg' \
-o -name '*.jpeg' \
-o -name '*.gif' \
-o -name '*.svg' \
-o -name '*.webp' \
-o -name '*.woff2' \
\) -exec sh -c 'gzip -9 -kfv {}' \; ;\
fi
dev_release: frontend build_dev
@echo "Uploading release... $(VERSION_DEV)"
@ -111,7 +112,7 @@ dev_release: frontend build_dev
rclone copyto bin/jetkvm_app r2://jetkvm-update/app/$(VERSION_DEV)/jetkvm_app
rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION_DEV)/jetkvm_app.sha256
build_release: frontend
build_release: frontend build_native
@echo "Building release..."
$(GO_CMD) build \
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \

View File

@ -1,204 +0,0 @@
#!/usr/bin/env bash
#
# Exit immediately if a command exits with a non-zero status
set -e
C_RST="$(tput sgr0)"
C_ERR="$(tput setaf 1)"
C_OK="$(tput setaf 2)"
C_WARN="$(tput setaf 3)"
C_INFO="$(tput setaf 5)"
msg() { printf '%s%s%s\n' $2 "$1" $C_RST; }
msg_info() { msg "$1" $C_INFO; }
msg_ok() { msg "$1" $C_OK; }
msg_err() { msg "$1" $C_ERR; }
msg_warn() { msg "$1" $C_WARN; }
# Function to display help message
show_help() {
echo "Usage: $0 [options] -r <remote_ip>"
echo
echo "Required:"
echo " -r, --remote <remote_ip> Remote host IP address"
echo
echo "Optional:"
echo " -u, --user <remote_user> Remote username (default: root)"
echo " --run-go-tests Run go tests"
echo " --run-go-tests-only Run go tests and exit"
echo " --skip-ui-build Skip frontend/UI build"
echo " --skip-native-build Skip native build"
echo " -i, --install Build for release and install the app"
echo " --help Display this help message"
echo
echo "Example:"
echo " $0 -r 192.168.0.17"
echo " $0 -r 192.168.0.17 -u admin"
}
# Default values
REMOTE_USER="root"
REMOTE_PATH="/userdata/jetkvm/bin"
SKIP_UI_BUILD=false
SKIP_NATIVE_BUILD=0
RESET_USB_HID_DEVICE=false
LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}"
RUN_GO_TESTS=false
RUN_GO_TESTS_ONLY=false
INSTALL_APP=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-r|--remote)
REMOTE_HOST="$2"
shift 2
;;
-u|--user)
REMOTE_USER="$2"
shift 2
;;
--skip-ui-build)
SKIP_UI_BUILD=true
shift
;;
--skip-native-build)
SKIP_NATIVE_BUILD=1
shift
;;
--reset-usb-hid)
RESET_USB_HID_DEVICE=true
shift
;;
--run-go-tests)
RUN_GO_TESTS=true
shift
;;
--run-go-tests-only)
RUN_GO_TESTS_ONLY=true
RUN_GO_TESTS=true
shift
;;
-i|--install)
INSTALL_APP=true
shift
;;
--help)
show_help
exit 0
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Verify required parameters
if [ -z "$REMOTE_HOST" ]; then
msg_err "Error: Remote IP is a required parameter"
show_help
exit 1
fi
# Build the development version on the host
if [ "$SKIP_UI_BUILD" = false ]; then
msg_info "▶ Building frontend"
make frontend
fi
if [ "$RUN_GO_TESTS" = true ]; then
msg_info "▶ Building go tests"
make build_dev_test
msg_info "▶ Copying device-tests.tar.gz to remote host"
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz
msg_info "▶ Running go tests"
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF'
set -e
TMP_DIR=$(mktemp -d)
cd ${TMP_DIR}
tar zxf /tmp/device-tests.tar.gz
./gotestsum --format=testdox \
--jsonfile=/tmp/device-tests.json \
--post-run-command 'sh -c "echo $TESTS_FAILED > /tmp/device-tests.failed"' \
--raw-command -- ./run_all_tests -json
GOTESTSUM_EXIT_CODE=$?
if [ $GOTESTSUM_EXIT_CODE -ne 0 ]; then
echo "❌ Tests failed (exit code: $GOTESTSUM_EXIT_CODE)"
rm -rf ${TMP_DIR} /tmp/device-tests.tar.gz
exit 1
fi
TESTS_FAILED=$(cat /tmp/device-tests.failed)
if [ "$TESTS_FAILED" -ne 0 ]; then
echo "❌ Tests failed $TESTS_FAILED tests failed"
rm -rf ${TMP_DIR} /tmp/device-tests.tar.gz
exit 1
fi
echo "✅ Tests passed"
rm -rf ${TMP_DIR} /tmp/device-tests.tar.gz
EOF
if [ "$RUN_GO_TESTS_ONLY" = true ]; then
msg_info "▶ Go tests completed"
exit 0
fi
fi
if [ "$INSTALL_APP" = true ]
then
msg_info "▶ Building release binary"
make build_release SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD}
# Copy the binary to the remote host as if we were the OTA updater.
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
# Reboot the device, the new app will be deployed by the startup process.
ssh "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
else
msg_info "▶ Building development binary"
make build_dev SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD}
# Kill any existing instances of the application
ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
# Copy the binary to the remote host
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app
if [ "$RESET_USB_HID_DEVICE" = true ]; then
msg_info "▶ Resetting USB HID device"
msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed"
# Remove the old USB gadget configuration
ssh "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*"
ssh "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC"
fi
# Deploy and run the application on the remote host
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF
set -e
# Set the library path to include the directory where librockit.so is located
export LD_LIBRARY_PATH=/oem/usr/lib:\$LD_LIBRARY_PATH
# Kill any existing instances of the application
killall jetkvm_app || true
killall jetkvm_app_debug || true
# Navigate to the directory where the binary will be stored
cd "${REMOTE_PATH}"
# Make the new binary executable
chmod +x jetkvm_app_debug
# Run the application in the background
PION_LOG_TRACE=${LOG_TRACE_SCOPES} ./jetkvm_app_debug | tee -a /tmp/jetkvm_app_debug.log
EOF
fi
echo "Deployment complete."

1
dev_deploy.sh Symbolic link
View File

@ -0,0 +1 @@
scripts/dev_deploy.sh

View File

@ -23,7 +23,6 @@ var (
)
const (
touchscreenDevice string = "/dev/input/event1"
backlightControlClass string = "/sys/class/backlight/backlight/brightness"
)
@ -38,23 +37,25 @@ func updateDisplay() {
if usbState == "configured" {
nativeInstance.UpdateLabelIfChanged("usb_status_label", "Connected")
_, _ = nativeInstance.UIObjSetState("usb_status", "LV_STATE_DEFAULT")
_, _ = nativeInstance.UIObjAddState("usb_status_label", "LV_STATE_CHECKED")
} else {
nativeInstance.UpdateLabelIfChanged("usb_status_label", "Disconnected")
_, _ = nativeInstance.UIObjSetState("usb_status", "LV_STATE_DISABLED")
_, _ = nativeInstance.UIObjClearState("usb_status_label", "LV_STATE_CHECKED")
}
if lastVideoState.Ready {
nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Connected")
_, _ = nativeInstance.UIObjSetState("hdmi_status", "LV_STATE_DEFAULT")
_, _ = nativeInstance.UIObjAddState("hdmi_status_label", "LV_STATE_CHECKED")
} else {
nativeInstance.UpdateLabelIfChanged("hdmi_status_label", "Disconnected")
_, _ = nativeInstance.UIObjSetState("hdmi_status", "LV_STATE_DISABLED")
_, _ = nativeInstance.UIObjClearState("hdmi_status_label", "LV_STATE_CHECKED")
}
nativeInstance.UpdateLabelIfChanged("cloud_status_label", fmt.Sprintf("%d active", actionSessions))
if networkState.IsUp() {
nativeInstance.UISetVar("main_screen", "home_screen")
nativeInstance.SwitchToScreenIf("home_screen", []string{"no_network_screen", "boot_screen"})
} else {
nativeInstance.UISetVar("main_screen", "no_network_screen")
nativeInstance.SwitchToScreenIf("no_network_screen", []string{"home_screen", "boot_screen"})
}
@ -184,19 +185,19 @@ func updateStaticContents() {
nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkState.MACString())
// get cpu info
cpuInfo, err := os.ReadFile("/proc/cpuinfo")
// get the line starting with "Serial"
for _, line := range strings.Split(string(cpuInfo), "\n") {
if strings.HasPrefix(line, "Serial") {
serial := strings.SplitN(line, ":", 2)[1]
nativeInstance.UpdateLabelAndChangeVisibility("cpu_serial", strings.TrimSpace(serial))
break
if cpuInfo, err := os.ReadFile("/proc/cpuinfo"); err == nil {
// get the line starting with "Serial"
for line := range strings.SplitSeq(string(cpuInfo), "\n") {
if strings.HasPrefix(line, "Serial") {
serial := strings.SplitN(line, ":", 2)[1]
nativeInstance.UpdateLabelAndChangeVisibility("cpu_serial", strings.TrimSpace(serial))
break
}
}
}
// get kernel version
kernelVersion, err := os.ReadFile("/proc/version")
if err == nil {
if kernelVersion, err := os.ReadFile("/proc/version"); err == nil {
kernelVersion := strings.TrimPrefix(string(kernelVersion), "Linux version ")
kernelVersion = strings.SplitN(kernelVersion, " ", 2)[0]
nativeInstance.UpdateLabelAndChangeVisibility("kernel_version", kernelVersion)

View File

@ -1,84 +0,0 @@
package lldp
import (
"fmt"
"net"
"os"
"syscall"
"unsafe"
"github.com/google/gopacket/afpacket"
"golang.org/x/sys/unix"
)
const (
afPacketBufferSize = 2 // in MiB
afPacketSnaplen = 9216
)
func afPacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
frameSize int, blockSize int, numBlocks int, err error) {
if snaplen < pageSize {
frameSize = pageSize / (pageSize / snaplen)
} else {
frameSize = (snaplen/pageSize + 1) * pageSize
}
// 128 is the default from the gopacket library so just use that
blockSize = frameSize * 128
numBlocks = (targetSizeMb * 1024 * 1024) / blockSize
if numBlocks == 0 {
return 0, 0, 0, fmt.Errorf("interface buffersize is too small")
}
return frameSize, blockSize, numBlocks, nil
}
func afPacketNewTPacket(ifName string) (*afpacket.TPacket, error) {
szFrame, szBlock, numBlocks, err := afPacketComputeSize(
afPacketBufferSize,
afPacketSnaplen,
os.Getpagesize())
if err != nil {
return nil, err
}
return afpacket.NewTPacket(
afpacket.OptInterface(ifName),
afpacket.OptFrameSize(szFrame),
afpacket.OptBlockSize(szBlock),
afpacket.OptNumBlocks(numBlocks),
afpacket.OptAddVLANHeader(false),
afpacket.SocketRaw,
afpacket.TPacketVersion3)
}
type ifreq struct {
ifrName [IFNAMSIZ]byte
ifrHwaddr syscall.RawSockaddr
}
func addMulticastAddr(ifName string, addr net.HardwareAddr) error {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
if err != nil {
return err
}
defer syscall.Close(fd)
var name [IFNAMSIZ]byte
copy(name[:], []byte(ifName))
ifr := &ifreq{
ifrName: name,
ifrHwaddr: toRawSockaddr(addr),
}
_, _, ep := unix.Syscall(unix.SYS_IOCTL, uintptr(fd),
unix.SIOCADDMULTI, uintptr(unsafe.Pointer(ifr)))
if ep != 0 {
return syscall.Errno(ep)
}
return nil
}

View File

@ -1,15 +0,0 @@
//go:build arm && linux
package lldp
import (
"net"
"syscall"
)
func toRawSockaddr(mac net.HardwareAddr) (sockaddr syscall.RawSockaddr) {
for i, n := range mac {
sockaddr.Data[i] = uint8(n)
}
return
}

View File

@ -1,15 +0,0 @@
//go:build !arm && linux
package lldp
import (
"net"
"syscall"
)
func toRawSockaddr(mac net.HardwareAddr) (sockaddr syscall.RawSockaddr) {
for i, n := range mac {
sockaddr.Data[i] = int8(n)
}
return
}

View File

@ -1,106 +0,0 @@
package lldp
import (
"context"
"sync"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/afpacket"
"github.com/jellydator/ttlcache/v3"
"github.com/jetkvm/kvm/internal/logging"
"github.com/rs/zerolog"
)
var defaultLogger = logging.GetSubsystemLogger("lldp")
type LLDP struct {
l *zerolog.Logger
tPacket *afpacket.TPacket
pktSource *gopacket.PacketSource
rxCtx context.Context
rxCancel context.CancelFunc
rxLock sync.Mutex
enableRx bool
enableTx bool
packets chan gopacket.Packet
interfaceName string
stop chan struct{}
neighbors *ttlcache.Cache[string, Neighbor]
}
type LLDPOptions struct {
InterfaceName string
EnableRx bool
EnableTx bool
Logger *zerolog.Logger
}
func NewLLDP(opts *LLDPOptions) *LLDP {
if opts.Logger == nil {
opts.Logger = defaultLogger
}
if opts.InterfaceName == "" {
opts.Logger.Fatal().Msg("InterfaceName is required")
}
return &LLDP{
interfaceName: opts.InterfaceName,
enableRx: opts.EnableRx,
enableTx: opts.EnableTx,
l: opts.Logger,
neighbors: ttlcache.New(ttlcache.WithTTL[string, Neighbor](1 * time.Hour)),
}
}
func (l *LLDP) Start() error {
l.rxLock.Lock()
defer l.rxLock.Unlock()
if l.rxCtx != nil {
l.l.Info().Msg("LLDP already started")
return nil
}
l.rxCtx, l.rxCancel = context.WithCancel(context.Background())
if l.enableRx {
l.l.Info().Msg("setting up AF_PACKET")
if err := l.setUpCapture(); err != nil {
l.l.Error().Err(err).Msg("unable to set up AF_PACKET")
return err
}
if err := l.startCapture(); err != nil {
l.l.Error().Err(err).Msg("unable to start capture")
return err
}
}
go l.neighbors.Start()
return nil
}
func (l *LLDP) Stop() error {
l.rxLock.Lock()
defer l.rxLock.Unlock()
if l.rxCancel != nil {
l.rxCancel()
l.rxCancel = nil
l.rxCtx = nil
}
if l.enableRx {
_ = l.shutdownCapture()
}
l.neighbors.Stop()
l.neighbors.DeleteAll()
return nil
}

View File

@ -1,57 +0,0 @@
package lldp
import "time"
type Neighbor struct {
Mac string `json:"mac"`
Source string `json:"source"`
ChassisID string `json:"chassis_id"`
PortID string `json:"port_id"`
PortDescription string `json:"port_description"`
SystemName string `json:"system_name"`
SystemDescription string `json:"system_description"`
TTL uint16 `json:"ttl"`
ManagementAddress string `json:"management_address"`
Values map[string]string `json:"values"`
}
func (l *LLDP) addNeighbor(mac string, neighbor Neighbor, ttl time.Duration) {
logger := l.l.With().
Str("mac", mac).
Interface("neighbor", neighbor).
Logger()
current_neigh := l.neighbors.Get(mac)
if current_neigh != nil {
current_source := current_neigh.Value().Source
if current_source == "lldp" && neighbor.Source != "lldp" {
logger.Info().Msg("skip updating neighbor, as LLDP has higher priority")
return
}
}
logger.Info().Msg("adding neighbor")
l.neighbors.Set(mac, neighbor, ttl)
}
func (l *LLDP) deleteNeighbor(mac string) {
logger := l.l.With().
Str("mac", mac).
Logger()
logger.Info().Msg("deleting neighbor")
l.neighbors.Delete(mac)
}
func (l *LLDP) GetNeighbors() []Neighbor {
items := l.neighbors.Items()
neighbors := make([]Neighbor, 0, len(items))
for _, item := range items {
neighbors = append(neighbors, item.Value())
}
l.l.Info().Interface("neighbors", neighbors).Msg("neighbors")
return neighbors
}

View File

@ -1,264 +0,0 @@
package lldp
import (
"fmt"
"net"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/rs/zerolog"
"golang.org/x/net/bpf"
)
const IFNAMSIZ = 16
var (
lldpDefaultTTL = 120 * time.Second
cdpDefaultTTL = 180 * time.Second
)
// from lldpd
// https://github.com/lldpd/lldpd/blob/9034c9332cca0c8b1a20e1287f0e5fed81f7eb2a/src/daemon/lldpd.h#L246
//
//nolint:govet
var bpfFilter = []bpf.RawInstruction{
{0x30, 0, 0, 0x00000000}, {0x54, 0, 0, 0x00000001}, {0x15, 0, 16, 0x00000001},
{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x000088cc},
{0x20, 0, 0, 0x00000002}, {0x15, 2, 0, 0xc200000e},
{0x15, 1, 0, 0xc2000003}, {0x15, 0, 2, 0xc2000000},
{0x28, 0, 0, 0x00000000}, {0x15, 12, 13, 0x00000180},
{0x20, 0, 0, 0x00000002}, {0x15, 0, 2, 0x52cccccc},
{0x28, 0, 0, 0x00000000}, {0x15, 8, 9, 0x000001e0},
{0x15, 1, 0, 0x0ccccccc}, {0x15, 0, 2, 0x81000100},
{0x28, 0, 0, 0x00000000}, {0x15, 4, 5, 0x00000100},
{0x20, 0, 0, 0x00000002}, {0x15, 0, 3, 0x2b000000},
{0x28, 0, 0, 0x00000000}, {0x15, 0, 1, 0x000000e0},
{0x6, 0, 0, 0x00040000},
{0x6, 0, 0, 0x00000000},
}
var multicastAddrs = []string{
// LLDP
"01:80:C2:00:00:00",
"01:80:C2:00:00:03",
"01:80:C2:00:00:0E",
// CDP
"01:00:0C:CC:CC:CC",
}
func (l *LLDP) setUpCapture() error {
logger := l.l.With().Str("interface", l.interfaceName).Logger()
tPacket, err := afPacketNewTPacket(l.interfaceName)
if err != nil {
return err
}
logger.Info().Msg("created TPacket")
// set up multicast addresses
// otherwise the kernel might discard the packets
// another workaround would be to enable promiscuous mode but that's too tricky
for _, mac := range multicastAddrs {
hwAddr, err := net.ParseMAC(mac)
if err != nil {
logger.Error().Msgf("unable to parse MAC address %s: %s", mac, err)
continue
}
if err := addMulticastAddr(l.interfaceName, hwAddr); err != nil {
logger.Error().Msgf("unable to add multicast address %s: %s", mac, err)
continue
}
logger.Info().
Interface("hwaddr", hwAddr).
Msgf("added multicast address")
}
if err = tPacket.SetBPF(bpfFilter); err != nil {
logger.Error().Msgf("unable to set BPF filter: %s", err)
tPacket.Close()
return err
}
logger.Info().Msg("BPF filter set")
l.pktSource = gopacket.NewPacketSource(tPacket, layers.LayerTypeEthernet)
l.tPacket = tPacket
return nil
}
func (l *LLDP) startCapture() error {
logger := l.l.With().Str("interface", l.interfaceName).Logger()
if l.tPacket == nil {
return fmt.Errorf("AFPacket not initialized")
}
if l.pktSource == nil {
return fmt.Errorf("packet source not initialized")
}
go func() {
logger.Info().Msg("starting capture LLDP ethernet frames")
for {
select {
case <-l.rxCtx.Done():
logger.Info().Msg("shutting down LLDP capture")
return
case packet := <-l.pktSource.Packets():
if err := l.handlePacket(packet, &logger); err != nil {
logger.Error().Msgf("error handling packet: %s", err)
}
}
}
}()
return nil
}
func (l *LLDP) handlePacket(packet gopacket.Packet, logger *zerolog.Logger) error {
linkLayer := packet.LinkLayer()
if linkLayer == nil {
return fmt.Errorf("no link layer")
}
srcMac := linkLayer.LinkFlow().Src().String()
dstMac := linkLayer.LinkFlow().Dst().String()
logger.Trace().
Str("src_mac", srcMac).
Str("dst_mac", dstMac).
Int("length", len(packet.Data())).
Hex("data", packet.Data()).
Msg("received packet")
lldpRaw := packet.Layer(layers.LayerTypeLinkLayerDiscovery)
if lldpRaw != nil {
logger.Trace().Msgf("Found LLDP Frame")
lldpInfo := packet.Layer(layers.LayerTypeLinkLayerDiscoveryInfo)
if lldpInfo == nil {
return fmt.Errorf("no LLDP info layer")
}
return l.handlePacketLLDP(
srcMac,
lldpRaw.(*layers.LinkLayerDiscovery),
lldpInfo.(*layers.LinkLayerDiscoveryInfo),
)
}
cdpRaw := packet.Layer(layers.LayerTypeCiscoDiscovery)
if cdpRaw != nil {
logger.Trace().Msgf("Found CDP Frame")
cdpInfo := packet.Layer(layers.LayerTypeCiscoDiscoveryInfo)
if cdpInfo == nil {
return fmt.Errorf("no CDP info layer")
}
return l.handlePacketCDP(
srcMac,
cdpRaw.(*layers.CiscoDiscovery),
cdpInfo.(*layers.CiscoDiscoveryInfo),
)
}
return nil
}
func (l *LLDP) handlePacketLLDP(mac string, raw *layers.LinkLayerDiscovery, info *layers.LinkLayerDiscoveryInfo) error {
n := &Neighbor{
Values: make(map[string]string),
Source: "lldp",
Mac: mac,
}
gotEnd := false
ttl := lldpDefaultTTL
for _, v := range raw.Values {
switch v.Type {
case layers.LLDPTLVEnd:
gotEnd = true
case layers.LLDPTLVChassisID:
n.ChassisID = string(raw.ChassisID.ID)
n.Values["chassis_id"] = n.ChassisID
case layers.LLDPTLVPortID:
n.PortID = string(raw.PortID.ID)
n.Values["port_id"] = n.PortID
case layers.LLDPTLVPortDescription:
n.PortDescription = info.PortDescription
n.Values["port_description"] = n.PortDescription
case layers.LLDPTLVSysName:
n.SystemName = info.SysName
n.Values["system_name"] = n.SystemName
case layers.LLDPTLVSysDescription:
n.SystemDescription = info.SysDescription
n.Values["system_description"] = n.SystemDescription
case layers.LLDPTLVMgmtAddress:
// n.ManagementAddress = info.MgmtAddress.Address
case layers.LLDPTLVTTL:
n.TTL = uint16(raw.TTL)
ttl = time.Duration(n.TTL) * time.Second
n.Values["ttl"] = fmt.Sprintf("%d", n.TTL)
case layers.LLDPTLVOrgSpecific:
for _, org := range info.OrgTLVs {
n.Values[fmt.Sprintf("org_specific_%d", org.OUI)] = string(org.Info)
}
}
}
if gotEnd || ttl < 1*time.Second {
l.deleteNeighbor(mac)
} else {
l.addNeighbor(mac, *n, ttl)
}
return nil
}
func (l *LLDP) handlePacketCDP(mac string, raw *layers.CiscoDiscovery, info *layers.CiscoDiscoveryInfo) error {
// TODO: implement full CDP parsing
n := &Neighbor{
Values: make(map[string]string),
Source: "cdp",
Mac: mac,
}
ttl := cdpDefaultTTL
n.ChassisID = info.DeviceID
n.PortID = info.PortID
n.SystemName = info.SysName
n.SystemDescription = info.Platform
n.TTL = uint16(raw.TTL)
if n.TTL > 1 {
ttl = time.Duration(n.TTL) * time.Second
}
if len(info.MgmtAddresses) > 0 {
n.ManagementAddress = string(info.MgmtAddresses[0])
}
l.addNeighbor(mac, *n, ttl)
return nil
}
func (l *LLDP) shutdownCapture() error {
if l.tPacket != nil {
l.l.Info().Msg("closing TPacket")
l.tPacket.Close()
l.tPacket = nil
}
if l.pktSource != nil {
l.l.Info().Msg("closing packet source")
l.pktSource = nil
}
return nil
}

View File

@ -35,6 +35,7 @@ FetchContent_Declare(
GIT_TAG v9.3.0
GIT_SHALLOW 1
UPDATE_DISCONNECTED 1
PATCH_COMMAND patch -p1 -f < ${CMAKE_CURRENT_SOURCE_DIR}/lvgl-minify.patch && cat ${CMAKE_CURRENT_SOURCE_DIR}/lvgl-minify.del | xargs rm -v
)
FetchContent_MakeAvailable(lvgl)

View File

@ -18,7 +18,7 @@
jetkvm_video_state_t state;
jetkvm_video_state_handler_t *video_state_handler = NULL;
jetkvm_rpc_handler_t *rpc_handler = NULL;
jetkvm_video_handler_t *video_handler = NULL;
@ -34,6 +34,16 @@ void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler) {
lvgl_set_indev_handler(handler);
}
void jetkvm_set_rpc_handler(jetkvm_rpc_handler_t *handler) {
rpc_handler = handler;
}
void jetkvm_call_rpc_handler(const char *method, const char *params) {
if (rpc_handler != NULL) {
(*rpc_handler)(method, params);
}
}
const char *jetkvm_ui_event_code_to_name(int code) {
lv_event_code_t cCode = (lv_event_code_t)code;
return lv_event_code_get_name(code);
@ -237,38 +247,52 @@ void jetkvm_ui_set_image(const char *obj_name, const char *image_name) {
lv_img_set_src(obj, image_name);
}
void jetkvm_ui_set_state(const char *obj_name, const char *state_name) {
lv_state_t str_to_lv_state(const char *state_name) {
if (strcmp(state_name, "LV_STATE_USER_1") == 0) {
return LV_STATE_USER_1;
}
else if (strcmp(state_name, "LV_STATE_USER_2") == 0) {
return LV_STATE_USER_2;
}
else if (strcmp(state_name, "LV_STATE_USER_3") == 0) {
return LV_STATE_USER_3;
}
else if (strcmp(state_name, "LV_STATE_USER_4") == 0) {
return LV_STATE_USER_4;
}
else if (strcmp(state_name, "LV_STATE_DISABLED") == 0) {
return LV_STATE_DISABLED;
}
else if (strcmp(state_name, "LV_STATE_DEFAULT") == 0) {
return LV_STATE_DEFAULT;
}
else if (strcmp(state_name, "LV_STATE_CHECKED") == 0) {
return LV_STATE_CHECKED;
}
else if (strcmp(state_name, "LV_STATE_FOCUSED") == 0) {
return LV_STATE_FOCUSED;
}
return LV_STATE_DEFAULT;
}
void jetkvm_ui_add_state(const char *obj_name, const char *state_name) {
lv_obj_t *obj = ui_get_obj(obj_name);
if (obj == NULL) {
return;
}
lv_obj_add_state(obj, LV_STATE_USER_1);
lv_state_t state_val = LV_STATE_DEFAULT;
if (strcmp(state_name, "LV_STATE_USER_1") == 0)
{
state_val = LV_STATE_USER_1;
}
else if (strcmp(state_name, "LV_STATE_USER_2") == 0)
{
state_val = LV_STATE_USER_2;
}
else if (strcmp(state_name, "LV_STATE_USER_3") == 0)
{
state_val = LV_STATE_USER_3;
}
else if (strcmp(state_name, "LV_STATE_USER_4") == 0)
{
state_val = LV_STATE_USER_4;
}
else if (strcmp(state_name, "LV_STATE_DISABLED") == 0)
{
state_val = LV_STATE_DISABLED;
}
// TODO: use LV_STATE_USER_* once eez supports it
lv_obj_clear_state(obj, LV_STATE_USER_1 | LV_STATE_USER_2 | LV_STATE_USER_3 | LV_STATE_USER_4 | LV_STATE_DISABLED);
lv_state_t state_val = str_to_lv_state(state_name);
lv_obj_add_state(obj, state_val);
}
void jetkvm_ui_clear_state(const char *obj_name, const char *state_name) {
lv_obj_t *obj = ui_get_obj(obj_name);
if (obj == NULL) {
return;
}
lv_state_t state_val = str_to_lv_state(state_name);
lv_obj_clear_state(obj, state_val);
}
int jetkvm_ui_add_flag(const char *obj_name, const char *flag_name) {
lv_obj_t *obj = ui_get_obj(obj_name);
if (obj == NULL) {
@ -369,6 +393,10 @@ jetkvm_video_state_t *jetkvm_video_get_status() {
return &state;
}
char *jetkvm_video_log_status() {
return videoc_log_status();
}
int jetkvm_video_init() {
return video_init();
}

View File

@ -16,12 +16,15 @@ typedef struct
typedef void (jetkvm_video_state_handler_t)(jetkvm_video_state_t *state);
typedef void (jetkvm_log_handler_t)(int level, const char *filename, const char *funcname, int line, const char *message);
typedef void (jetkvm_rpc_handler_t)(const char *method, const char *params);
typedef void (jetkvm_video_handler_t)(const uint8_t *frame, ssize_t len);
typedef void (jetkvm_indev_handler_t)(int code);
void jetkvm_set_log_handler(jetkvm_log_handler_t *handler);
void jetkvm_set_video_handler(jetkvm_video_handler_t *handler);
void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler);
void jetkvm_set_rpc_handler(jetkvm_rpc_handler_t *handler);
void jetkvm_call_rpc_handler(const char *method, const char *params);
void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler);
void jetkvm_ui_set_var(const char *name, const char *value);
@ -36,7 +39,8 @@ const char *jetkvm_ui_get_current_screen();
void jetkvm_ui_load_screen(const char *obj_name);
int jetkvm_ui_set_text(const char *obj_name, const char *text);
void jetkvm_ui_set_image(const char *obj_name, const char *image_name);
void jetkvm_ui_set_state(const char *obj_name, const char *state_name);
void jetkvm_ui_add_state(const char *obj_name, const char *state_name);
void jetkvm_ui_clear_state(const char *obj_name, const char *state_name);
void jetkvm_ui_fade_in(const char *obj_name, u_int32_t duration);
void jetkvm_ui_fade_out(const char *obj_name, u_int32_t duration);
void jetkvm_ui_set_opacity(const char *obj_name, u_int8_t opacity);
@ -55,6 +59,7 @@ int jetkvm_video_set_quality_factor(float quality_factor);
float jetkvm_video_get_quality_factor();
int jetkvm_video_set_edid(const char *edid_hex);
char *jetkvm_video_get_edid_hex();
char *jetkvm_video_log_status();
jetkvm_video_state_t *jetkvm_video_get_status();
void video_report_format(bool ready, const char *error, u_int16_t width, u_int16_t height, double frame_per_second);

View File

@ -36,7 +36,7 @@ int get_edid(uint8_t *edid, size_t max_size)
fd = open(V4L_SUBDEV, O_RDWR);
if (fd < 0)
{
perror("Failed to open device");
log_error("Failed to open device");
return -1;
}
@ -48,7 +48,7 @@ int get_edid(uint8_t *edid, size_t max_size)
if (ioctl(fd, VIDIOC_G_EDID, &v4l2_edid) < 0)
{
perror("Failed to get EDID");
log_error("Failed to get EDID");
close(fd);
return -1;
}
@ -90,7 +90,7 @@ int set_edid(uint8_t *edid, size_t size)
fd = open(V4L_SUBDEV, O_RDWR);
if (fd < 0)
{
perror("Failed to open device");
log_error("Failed to open device");
return -1;
}
@ -104,7 +104,7 @@ int set_edid(uint8_t *edid, size_t size)
if (ioctl(fd, VIDIOC_S_EDID, &v4l2_edid) < 0)
{
perror("Failed to set EDID");
log_error("Failed to set EDID");
close(fd);
return -1;
}
@ -123,13 +123,13 @@ const char *videoc_log_status()
fd = open(V4L_SUBDEV, O_RDWR);
if (fd < 0)
{
perror("Failed to open device");
log_error("Failed to open device");
return NULL;
}
if (ioctl(fd, VIDIOC_LOG_STATUS) == -1)
{
perror("VIDIOC_LOG_STATUS failed");
log_error("VIDIOC_LOG_STATUS failed");
close(fd);
return NULL;
}
@ -166,14 +166,14 @@ const char *videoc_log_status()
buffer = strdup(p);
if (buffer == NULL)
{
perror("Failed to allocate memory for status");
log_error("Failed to allocate memory for status");
return NULL;
}
return buffer;
}
else
{
log_error("Failed to read kernel log\n");
log_error("Failed to read kernel log");
return NULL;
}

View File

@ -2,8 +2,25 @@
#define LOG_HANDLER_H
typedef void (jetkvm_log_handler_t)(int level, const char *filename, const char *funcname, const int line, const char *message);
/**
* @brief Log a message
*
* @param level The level of the message
* @param filename The filename of the message
* @param funcname The function name of the message
* @param line The line number of the message
* @param message The message to log
* @return void
*/
void log_message(int level, const char *filename, const char *funcname, const int line, const char *message);
/**
* @brief Set the log handler
*
* @param handler The handler to set
* @return void
*/
void log_set_handler(jetkvm_log_handler_t *handler);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,251 @@
diff --git a/env_support/cmake/os_desktop.cmake b/env_support/cmake/os_desktop.cmake
index 5c6b05ed8..a18044d64 100644
--- a/env_support/cmake/os_desktop.cmake
+++ b/env_support/cmake/os_desktop.cmake
@@ -60,8 +60,6 @@ get_directory_property(HAS_PARENT_SCOPE PARENT_DIRECTORY)
# Set sources used for LVGL components
file(GLOB_RECURSE SOURCES ${LVGL_ROOT_DIR}/src/*.c
${LVGL_ROOT_DIR}/src/*.S)
-file(GLOB_RECURSE EXAMPLE_SOURCES ${LVGL_ROOT_DIR}/examples/*.c)
-file(GLOB_RECURSE DEMO_SOURCES ${LVGL_ROOT_DIR}/demos/*.c)
file(GLOB_RECURSE THORVG_SOURCES ${LVGL_ROOT_DIR}/src/libs/thorvg/*.cpp
${LVGL_ROOT_DIR}/src/others/vg_lite_tvg/*.cpp)
@@ -229,51 +227,6 @@ if(NOT (CMAKE_C_COMPILER_ID STREQUAL "MSVC"))
PROPERTIES COMPILE_FLAGS -Wunused-parameter)
endif()
-# Build LVGL example library
-if(CONFIG_LV_BUILD_EXAMPLES)
-
- message(STATUS "Enabling the building of examples")
-
- add_library(lvgl_examples ${EXAMPLE_SOURCES})
- add_library(lvgl::examples ALIAS lvgl_examples)
- target_include_directories(lvgl_examples SYSTEM PUBLIC ${LVGL_ROOT_DIR}/examples)
- set_target_properties(lvgl_examples PROPERTIES COMPILE_DEFINITIONS "${COMP_DEF}")
-
- # This tells cmake to link lvgl with lvgl_examples
- # PUBLIC allows code linking with LVGL to also use the library
- # The linker will resolve all dependencies when dynamic linking
- target_link_libraries(lvgl PUBLIC lvgl_examples)
-
- # During static linking, we need to create a cyclic dependency as the examples also needs lvgl
- if (NOT BUILD_SHARED_LIBS)
- target_link_libraries(lvgl_examples PRIVATE lvgl)
- endif()
-
-endif()
-
-# Build LVGL demos library
-if(CONFIG_LV_BUILD_DEMOS)
-
- message(STATUS "Enabling the building of demos")
-
- add_library(lvgl_demos ${DEMO_SOURCES})
- add_library(lvgl::demos ALIAS lvgl_demos)
- target_include_directories(lvgl_demos SYSTEM PUBLIC ${LVGL_ROOT_DIR}/demos)
- set_target_properties(lvgl_demos PROPERTIES COMPILE_DEFINITIONS "${COMP_DEF}")
-
- # This tells cmake to link lvgl with lvgl_examples
- # PUBLIC allows code linking with LVGL to also use the library
- # The linker will resolve all dependencies when dynamic linking
- target_link_libraries(lvgl PUBLIC lvgl_demos)
-
- # During static linking, we need to create a cyclic dependency as the demos also needs lvgl
- if (NOT BUILD_SHARED_LIBS)
- # If static linking - demos depends on fonts defined in lvgl
- # During dynamic linking, the linker is able to resolve everything
- target_link_libraries(lvgl_demos PRIVATE lvgl)
- endif()
-
-endif()
############################## INSTALLATION ######################################
@@ -373,58 +326,6 @@ if(CONFIG_LV_USE_THORVG_INTERNAL)
endif()
-if(CONFIG_LV_BUILD_DEMOS)
-
- install(
- DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/demos"
- DESTINATION "${INC_INSTALL_DIR}"
- FILES_MATCHING
- PATTERN "*.h")
-
- set_target_properties(
- lvgl_demos
- PROPERTIES OUTPUT_NAME lvgl_demos
- VERSION ${LVGL_VERSION}
- SOVERSION ${LVGL_SOVERSION}
- ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
- LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
- PUBLIC_HEADER "${LVGL_PUBLIC_HEADERS}")
-
- install(
- TARGETS lvgl_demos
- ARCHIVE DESTINATION "${LIB_INSTALL_DIR}"
- LIBRARY DESTINATION "${LIB_INSTALL_DIR}"
- RUNTIME DESTINATION "${RUNTIME_INSTALL_DIR}"
- PUBLIC_HEADER DESTINATION "${INC_INSTALL_DIR}")
-
-endif()
-
-if(CONFIG_LV_BUILD_EXAMPLES)
-
- install(
- DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/examples"
- DESTINATION "${INC_INSTALL_DIR}"
- FILES_MATCHING
- PATTERN "*.h")
-
- set_target_properties(
- lvgl_examples
- PROPERTIES OUTPUT_NAME lvgl_examples
- VERSION ${LVGL_VERSION}
- SOVERSION ${LVGL_SOVERSION}
- ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
- LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib"
- PUBLIC_HEADER "${LVGL_PUBLIC_HEADERS}")
-
- install(
- TARGETS lvgl_examples
- ARCHIVE DESTINATION "${LIB_INSTALL_DIR}"
- LIBRARY DESTINATION "${LIB_INSTALL_DIR}"
- RUNTIME DESTINATION "${RUNTIME_INSTALL_DIR}"
- PUBLIC_HEADER DESTINATION "${INC_INSTALL_DIR}")
-
-endif()
-
############################## SET GLOBAL VARIABLES ######################################
if (HAS_PARENT_SCOPE)
diff --git a/src/font/lv_font.h b/src/font/lv_font.h
index 180891832..b66c30eaa 100644
--- a/src/font/lv_font.h
+++ b/src/font/lv_font.h
@@ -230,122 +230,10 @@ bool lv_font_has_static_bitmap(const lv_font_t * font);
#define LV_FONT_DECLARE(font_name) LV_ATTRIBUTE_EXTERN_DATA extern const lv_font_t font_name;
-#if LV_FONT_MONTSERRAT_8
-LV_FONT_DECLARE(lv_font_montserrat_8)
-#endif
-
-#if LV_FONT_MONTSERRAT_10
-LV_FONT_DECLARE(lv_font_montserrat_10)
-#endif
-
-#if LV_FONT_MONTSERRAT_12
-LV_FONT_DECLARE(lv_font_montserrat_12)
-#endif
-
#if LV_FONT_MONTSERRAT_14
LV_FONT_DECLARE(lv_font_montserrat_14)
#endif
-#if LV_FONT_MONTSERRAT_16
-LV_FONT_DECLARE(lv_font_montserrat_16)
-#endif
-
-#if LV_FONT_MONTSERRAT_18
-LV_FONT_DECLARE(lv_font_montserrat_18)
-#endif
-
-#if LV_FONT_MONTSERRAT_20
-LV_FONT_DECLARE(lv_font_montserrat_20)
-#endif
-
-#if LV_FONT_MONTSERRAT_22
-LV_FONT_DECLARE(lv_font_montserrat_22)
-#endif
-
-#if LV_FONT_MONTSERRAT_24
-LV_FONT_DECLARE(lv_font_montserrat_24)
-#endif
-
-#if LV_FONT_MONTSERRAT_26
-LV_FONT_DECLARE(lv_font_montserrat_26)
-#endif
-
-#if LV_FONT_MONTSERRAT_28
-LV_FONT_DECLARE(lv_font_montserrat_28)
-#endif
-
-#if LV_FONT_MONTSERRAT_30
-LV_FONT_DECLARE(lv_font_montserrat_30)
-#endif
-
-#if LV_FONT_MONTSERRAT_32
-LV_FONT_DECLARE(lv_font_montserrat_32)
-#endif
-
-#if LV_FONT_MONTSERRAT_34
-LV_FONT_DECLARE(lv_font_montserrat_34)
-#endif
-
-#if LV_FONT_MONTSERRAT_36
-LV_FONT_DECLARE(lv_font_montserrat_36)
-#endif
-
-#if LV_FONT_MONTSERRAT_38
-LV_FONT_DECLARE(lv_font_montserrat_38)
-#endif
-
-#if LV_FONT_MONTSERRAT_40
-LV_FONT_DECLARE(lv_font_montserrat_40)
-#endif
-
-#if LV_FONT_MONTSERRAT_42
-LV_FONT_DECLARE(lv_font_montserrat_42)
-#endif
-
-#if LV_FONT_MONTSERRAT_44
-LV_FONT_DECLARE(lv_font_montserrat_44)
-#endif
-
-#if LV_FONT_MONTSERRAT_46
-LV_FONT_DECLARE(lv_font_montserrat_46)
-#endif
-
-#if LV_FONT_MONTSERRAT_48
-LV_FONT_DECLARE(lv_font_montserrat_48)
-#endif
-
-#if LV_FONT_MONTSERRAT_28_COMPRESSED
-LV_FONT_DECLARE(lv_font_montserrat_28_compressed)
-#endif
-
-#if LV_FONT_DEJAVU_16_PERSIAN_HEBREW
-LV_FONT_DECLARE(lv_font_dejavu_16_persian_hebrew)
-#endif
-
-#if LV_FONT_SIMSUN_14_CJK
-LV_FONT_DECLARE(lv_font_simsun_14_cjk)
-#endif
-
-#if LV_FONT_SIMSUN_16_CJK
-LV_FONT_DECLARE(lv_font_simsun_16_cjk)
-#endif
-
-#if LV_FONT_SOURCE_HAN_SANS_SC_14_CJK
-LV_FONT_DECLARE(lv_font_source_han_sans_sc_14_cjk)
-#endif
-
-#if LV_FONT_SOURCE_HAN_SANS_SC_16_CJK
-LV_FONT_DECLARE(lv_font_source_han_sans_sc_16_cjk)
-#endif
-
-#if LV_FONT_UNSCII_8
-LV_FONT_DECLARE(lv_font_unscii_8)
-#endif
-
-#if LV_FONT_UNSCII_16
-LV_FONT_DECLARE(lv_font_unscii_16)
-#endif
-
/*Declare the custom (user defined) fonts*/
#ifdef LV_FONT_CUSTOM_DECLARE
LV_FONT_CUSTOM_DECLARE

View File

@ -1,207 +0,0 @@
diff --git a/env_support/cmake/custom.cmake b/env_support/cmake/custom.cmake
index 7da68124b..1fbe2d3de 100644
--- a/env_support/cmake/custom.cmake
+++ b/env_support/cmake/custom.cmake
@@ -15,8 +15,6 @@ get_filename_component(LV_CONF_DIR ${LV_CONF_PATH} DIRECTORY)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
file(GLOB_RECURSE SOURCES ${LVGL_ROOT_DIR}/src/*.c)
-file(GLOB_RECURSE EXAMPLE_SOURCES ${LVGL_ROOT_DIR}/examples/*.c)
-file(GLOB_RECURSE DEMO_SOURCES ${LVGL_ROOT_DIR}/demos/*.c)
if (BUILD_SHARED_LIBS)
add_library(lvgl SHARED ${SOURCES})
@@ -25,10 +23,6 @@ else()
endif()
add_library(lvgl::lvgl ALIAS lvgl)
-add_library(lvgl_examples STATIC ${EXAMPLE_SOURCES})
-add_library(lvgl::examples ALIAS lvgl_examples)
-add_library(lvgl_demos STATIC ${DEMO_SOURCES})
-add_library(lvgl::demos ALIAS lvgl_demos)
target_compile_definitions(
lvgl PUBLIC $<$<BOOL:${LV_LVGL_H_INCLUDE_SIMPLE}>:LV_LVGL_H_INCLUDE_SIMPLE>
@@ -37,15 +31,6 @@ target_compile_definitions(
# Include root and optional parent path of LV_CONF_PATH
target_include_directories(lvgl SYSTEM PUBLIC ${LVGL_ROOT_DIR} ${LV_CONF_DIR})
-# Include /examples folder
-target_include_directories(lvgl_examples SYSTEM
- PUBLIC ${LVGL_ROOT_DIR}/examples)
-target_include_directories(lvgl_demos SYSTEM
- PUBLIC ${LVGL_ROOT_DIR}/demos)
-
-target_link_libraries(lvgl_examples PUBLIC lvgl)
-target_link_libraries(lvgl_demos PUBLIC lvgl)
-
# Lbrary and headers can be installed to system using make install
file(GLOB LVGL_PUBLIC_HEADERS "${CMAKE_SOURCE_DIR}/lv_conf.h"
"${CMAKE_SOURCE_DIR}/lvgl.h")
diff --git a/lvgl.mk b/lvgl.mk
index 0ea126daa..300fb6cbe 100644
--- a/lvgl.mk
+++ b/lvgl.mk
@@ -1,5 +1,3 @@
-include $(LVGL_DIR)/$(LVGL_DIR_NAME)/demos/lv_demos.mk
-include $(LVGL_DIR)/$(LVGL_DIR_NAME)/examples/lv_examples.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core/lv_core.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/lv_draw.mk
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra/lv_extra.mk
diff --git a/src/font/lv_font.h b/src/font/lv_font.h
index e3b670c87..4cceffc45 100644
--- a/src/font/lv_font.h
+++ b/src/font/lv_font.h
@@ -132,114 +132,10 @@ static inline lv_coord_t lv_font_get_line_height(const lv_font_t * font_p)
#define LV_FONT_DECLARE(font_name) extern const lv_font_t font_name;
-#if LV_FONT_MONTSERRAT_8
-LV_FONT_DECLARE(lv_font_montserrat_8)
-#endif
-
-#if LV_FONT_MONTSERRAT_10
-LV_FONT_DECLARE(lv_font_montserrat_10)
-#endif
-
-#if LV_FONT_MONTSERRAT_12
-LV_FONT_DECLARE(lv_font_montserrat_12)
-#endif
-
#if LV_FONT_MONTSERRAT_14
LV_FONT_DECLARE(lv_font_montserrat_14)
#endif
-#if LV_FONT_MONTSERRAT_16
-LV_FONT_DECLARE(lv_font_montserrat_16)
-#endif
-
-#if LV_FONT_MONTSERRAT_18
-LV_FONT_DECLARE(lv_font_montserrat_18)
-#endif
-
-#if LV_FONT_MONTSERRAT_20
-LV_FONT_DECLARE(lv_font_montserrat_20)
-#endif
-
-#if LV_FONT_MONTSERRAT_22
-LV_FONT_DECLARE(lv_font_montserrat_22)
-#endif
-
-#if LV_FONT_MONTSERRAT_24
-LV_FONT_DECLARE(lv_font_montserrat_24)
-#endif
-
-#if LV_FONT_MONTSERRAT_26
-LV_FONT_DECLARE(lv_font_montserrat_26)
-#endif
-
-#if LV_FONT_MONTSERRAT_28
-LV_FONT_DECLARE(lv_font_montserrat_28)
-#endif
-
-#if LV_FONT_MONTSERRAT_30
-LV_FONT_DECLARE(lv_font_montserrat_30)
-#endif
-
-#if LV_FONT_MONTSERRAT_32
-LV_FONT_DECLARE(lv_font_montserrat_32)
-#endif
-
-#if LV_FONT_MONTSERRAT_34
-LV_FONT_DECLARE(lv_font_montserrat_34)
-#endif
-
-#if LV_FONT_MONTSERRAT_36
-LV_FONT_DECLARE(lv_font_montserrat_36)
-#endif
-
-#if LV_FONT_MONTSERRAT_38
-LV_FONT_DECLARE(lv_font_montserrat_38)
-#endif
-
-#if LV_FONT_MONTSERRAT_40
-LV_FONT_DECLARE(lv_font_montserrat_40)
-#endif
-
-#if LV_FONT_MONTSERRAT_42
-LV_FONT_DECLARE(lv_font_montserrat_42)
-#endif
-
-#if LV_FONT_MONTSERRAT_44
-LV_FONT_DECLARE(lv_font_montserrat_44)
-#endif
-
-#if LV_FONT_MONTSERRAT_46
-LV_FONT_DECLARE(lv_font_montserrat_46)
-#endif
-
-#if LV_FONT_MONTSERRAT_48
-LV_FONT_DECLARE(lv_font_montserrat_48)
-#endif
-
-#if LV_FONT_MONTSERRAT_12_SUBPX
-LV_FONT_DECLARE(lv_font_montserrat_12_subpx)
-#endif
-
-#if LV_FONT_MONTSERRAT_28_COMPRESSED
-LV_FONT_DECLARE(lv_font_montserrat_28_compressed)
-#endif
-
-#if LV_FONT_DEJAVU_16_PERSIAN_HEBREW
-LV_FONT_DECLARE(lv_font_dejavu_16_persian_hebrew)
-#endif
-
-#if LV_FONT_SIMSUN_16_CJK
-LV_FONT_DECLARE(lv_font_simsun_16_cjk)
-#endif
-
-#if LV_FONT_UNSCII_8
-LV_FONT_DECLARE(lv_font_unscii_8)
-#endif
-
-#if LV_FONT_UNSCII_16
-LV_FONT_DECLARE(lv_font_unscii_16)
-#endif
-
/*Declare the custom (user defined) fonts*/
#ifdef LV_FONT_CUSTOM_DECLARE
LV_FONT_CUSTOM_DECLARE
diff --git a/src/font/lv_font.mk b/src/font/lv_font.mk
index 2201b73f2..7b2707da4 100644
--- a/src/font/lv_font.mk
+++ b/src/font/lv_font.mk
@@ -2,33 +2,7 @@ CSRCS += lv_font.c
CSRCS += lv_font_fmt_txt.c
CSRCS += lv_font_loader.c
-CSRCS += lv_font_dejavu_16_persian_hebrew.c
-CSRCS += lv_font_montserrat_8.c
-CSRCS += lv_font_montserrat_10.c
-CSRCS += lv_font_montserrat_12.c
-CSRCS += lv_font_montserrat_12_subpx.c
CSRCS += lv_font_montserrat_14.c
-CSRCS += lv_font_montserrat_16.c
-CSRCS += lv_font_montserrat_18.c
-CSRCS += lv_font_montserrat_20.c
-CSRCS += lv_font_montserrat_22.c
-CSRCS += lv_font_montserrat_24.c
-CSRCS += lv_font_montserrat_26.c
-CSRCS += lv_font_montserrat_28.c
-CSRCS += lv_font_montserrat_28_compressed.c
-CSRCS += lv_font_montserrat_30.c
-CSRCS += lv_font_montserrat_32.c
-CSRCS += lv_font_montserrat_34.c
-CSRCS += lv_font_montserrat_36.c
-CSRCS += lv_font_montserrat_38.c
-CSRCS += lv_font_montserrat_40.c
-CSRCS += lv_font_montserrat_42.c
-CSRCS += lv_font_montserrat_44.c
-CSRCS += lv_font_montserrat_46.c
-CSRCS += lv_font_montserrat_48.c
-CSRCS += lv_font_simsun_16_cjk.c
-CSRCS += lv_font_unscii_8.c
-CSRCS += lv_font_unscii_16.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/font
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/font

View File

@ -9,6 +9,7 @@
// #include "st7789/lcd.h"
#include "ui/ui.h"
#include "ui_index.h"
#include "ctrl.h"
#define DISP_BUF_SIZE (300 * 240 * 2)
static lv_color_t buf[DISP_BUF_SIZE];
@ -32,8 +33,6 @@ void lvgl_init(u_int16_t rotation) {
/*LittlevGL init*/
lv_init();
/*Linux frame buffer device init*/
/*Linux frame buffer device init*/
lv_display_t *disp = lv_linux_fbdev_create();
// lv_display_set_physical_resolution(disp, 240, 300);
@ -42,29 +41,6 @@ void lvgl_init(u_int16_t rotation) {
lvgl_set_rotation(disp, rotation);
// lv_display_t *disp = lv_st7789_create(LCD_H_RES, LCD_V_RES, LV_LCD_FLAG_NONE, lcd_send_cmd, lcd_send_color);
// lv_display_set_resolution(disp, 240, 300);
// lv_display_set_rotation(disp, LV_DISP_ROTATION_270);
// lv_color_t * buf1 = NULL;
// lv_color_t * buf2 = NULL;
// uint32_t buf_size = LCD_H_RES * LCD_V_RES / 10 * lv_color_format_get_size(lv_display_get_color_format(disp));
// buf1 = lv_malloc(buf_size);
// if(buf1 == NULL) {
// log_error("display draw buffer malloc failed");
// return;
// }
// buf2 = lv_malloc(buf_size);
// if(buf2 == NULL) {
// log_error("display buffer malloc failed");
// lv_free(buf1);
// return;
// }
// lv_display_set_buffers(disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
/* Linux input device init */
lv_indev_t *mouse = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event1");
lv_indev_set_group(mouse, lv_group_get_default());
@ -76,10 +52,9 @@ void lvgl_init(u_int16_t rotation) {
ui_init();
ui_set_rpc_handler((jetkvm_rpc_handler_t *)jetkvm_call_rpc_handler);
log_info("ui initalized");
// lv_label_set_text(ui_Boot_Screen_Version, "");
// lv_label_set_text(ui_Home_Content_Ip, "...");
// lv_label_set_text(ui_Home_Header_Cloud_Status_Label, "0 active");
}
void lvgl_tick(void) {
@ -188,7 +163,7 @@ lv_img_dsc_t *ui_get_image(const char *name) {
void ui_set_text(const char *name, const char *text) {
lv_obj_t *obj = ui_get_obj(name);
if(obj == NULL) {
log_error("ui_set_text %s %s, obj not found\n", name, text);
log_error("ui_set_text %s %s, obj not found", name, text);
return;
}
lv_label_set_text(obj, text);

View File

@ -12,10 +12,37 @@ void lvgl_tick(void);
void lvgl_set_rotation(lv_display_t *disp, u_int16_t rotation);
/**
* @brief Set the text of an object
*
* @param name The name of the object
* @param text The text to set
* @return void
*/
void ui_set_text(const char *name, const char *text);
/**
* @brief Get the object with the given name
*
* @param name The name of the object
* @return lv_obj_t* The object with the given name
*/
lv_obj_t *ui_get_obj(const char *name);
/**
* @brief Get the style with the given name
*
* @param name The name of the style
* @return lv_style_t* The style with the given name
*/
lv_style_t *ui_get_style(const char *name);
/**
* @brief Get the image with the given name
*
* @param name The name of the image
* @return lv_img_dsc_t* The image with the given name
*/
lv_img_dsc_t *ui_get_image(const char *name);
#endif // SCREEN_H

View File

@ -268,14 +268,14 @@ static void *venc_read_stream(void *arg)
stFrame.pstPack = malloc(sizeof(VENC_PACK_S));
while (venc_running)
{
// printf("RK_MPI_VENC_GetStream\n");
log_trace("RK_MPI_VENC_GetStream");
s32Ret = RK_MPI_VENC_GetStream(VENC_CHANNEL, &stFrame, 200); // blocks max 200ms
if (s32Ret == RK_SUCCESS)
{
RK_U64 nowUs = get_us();
// printf("chn:0, loopCount:%d enc->seq:%d wd:%d pts=%llu delay=%lldus\n",
// loopCount, stFrame.u32Seq, stFrame.pstPack->u32Len,
// stFrame.pstPack->u64PTS, nowUs - stFrame.pstPack->u64PTS);
log_trace("chn:0, loopCount:%d enc->seq:%d wd:%d pts=%llu delay=%lldus",
loopCount, stFrame.u32Seq, stFrame.pstPack->u32Len,
stFrame.pstPack->u64PTS, nowUs - stFrame.pstPack->u64PTS);
pData = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
video_send_frame(pData, (ssize_t)stFrame.pstPack->u32Len);
s32Ret = RK_MPI_VENC_ReleaseStream(VENC_CHANNEL, &stFrame);
@ -304,6 +304,7 @@ uint32_t detected_width, detected_height;
bool detected_signal = false, streaming_flag = false;
pthread_t *streaming_thread = NULL;
pthread_mutex_t streaming_mutex = PTHREAD_MUTEX_INITIALIZER;
void write_buffer_to_file(const uint8_t *buffer, size_t length, const char *filename)
{
@ -322,7 +323,7 @@ void *run_video_stream(void *arg)
{
if (detected_signal == false)
{
usleep(100000);
usleep(10000); // Reduced to 10ms for better responsiveness to streaming_flag changes
continue;
}
@ -347,7 +348,7 @@ void *run_video_stream(void *arg)
if (ioctl(video_dev_fd, VIDIOC_S_FMT, &fmt) < 0)
{
perror("Set format fail");
log_error("Set format fail: %s", strerror(errno));
usleep(100000); // Sleep for 100 milliseconds
close(video_dev_fd);
continue;
@ -362,7 +363,8 @@ void *run_video_stream(void *arg)
if (ioctl(video_dev_fd, VIDIOC_REQBUFS, &req) < 0)
{
perror("VIDIOC_REQBUFS failed");
log_error("VIDIOC_REQBUFS failed: %s", strerror(errno));
close(video_dev_fd);
return errno;
}
log_info("VIDIOC_REQBUFS successful");
@ -384,32 +386,35 @@ void *run_video_stream(void *arg)
if (-1 == ioctl(video_dev_fd, VIDIOC_QUERYBUF, &buf))
{
perror("VIDIOC_QUERYBUF failed");
log_error("VIDIOC_QUERYBUF failed: %s", strerror(errno));
req.count = i;
close(video_dev_fd);
return errno;
}
printf("VIDIOC_QUERYBUF successful for buffer %d\n", i);
log_info("VIDIOC_QUERYBUF successful for buffer %d", i);
printf("plane: length = %d\n", planes_buffer->length);
printf("plane: offset = %d\n", planes_buffer->m.mem_offset);
log_info("plane: length = %d", planes_buffer->length);
log_info("plane: offset = %d", planes_buffer->m.mem_offset);
MB_BLK blk = RK_MPI_MB_GetMB(memPool, (planes_buffer)->length, RK_TRUE);
if (blk == NULL)
{
RK_LOGE("get mb blk failed!");
log_error("get mb blk failed!");
close(video_dev_fd);
return -1;
}
printf("Got memory block for buffer %d\n", i);
log_info("Got memory block for buffer %d", i);
buffers[i].mb_blk = blk;
RK_S32 buf_fd = (RK_MPI_MB_Handle2Fd(blk));
if (buf_fd < 0)
{
RK_LOGE("RK_MPI_MB_Handle2Fd failed!");
log_error("RK_MPI_MB_Handle2Fd failed!");
close(video_dev_fd);
return -1;
}
printf("Converted memory block to file descriptor for buffer %d\n", i);
log_info("Converted memory block to file descriptor for buffer %d", i);
planes_buffer->m.fd = buf_fd;
}
@ -424,15 +429,16 @@ void *run_video_stream(void *arg)
buf.m.planes = &buffers[i].plane_buffer;
if (ioctl(video_dev_fd, VIDIOC_QBUF, &buf) < 0)
{
perror("VIDIOC_QBUF failed");
log_error("VIDIOC_QBUF failed: %s", strerror(errno));
close(video_dev_fd);
return errno;
}
printf("VIDIOC_QBUF successful for buffer %d\n", i);
log_info("VIDIOC_QBUF successful for buffer %d", i);
}
if (ioctl(video_dev_fd, VIDIOC_STREAMON, &type) < 0)
{
perror("VIDIOC_STREAMON failed");
log_error("VIDIOC_STREAMON failed: %s", strerror(errno));
goto cleanup;
}
@ -463,7 +469,7 @@ void *run_video_stream(void *arg)
r = select(video_dev_fd + 1, &fds, NULL, NULL, &tv);
if (r == 0)
{
log_info("select timeout \n");
log_info("select timeout");
break;
}
if (r == -1)
@ -472,7 +478,7 @@ void *run_video_stream(void *arg)
{
continue;
}
perror("select in video streaming");
log_error("select in video streaming");
break;
}
memset(&buf, 0, sizeof(buf));
@ -482,10 +488,10 @@ void *run_video_stream(void *arg)
buf.length = 1;
if (ioctl(video_dev_fd, VIDIOC_DQBUF, &buf) < 0)
{
perror("VIDIOC_DQBUF failed");
log_error("VIDIOC_DQBUF failed: %s", strerror(errno));
break;
}
// printf("got frame, bytesused = %d\n", tmp_plane.bytesused);
log_trace("got frame, bytesused = %d", tmp_plane.bytesused);
memset(&stFrame, 0, sizeof(VIDEO_FRAME_INFO_S));
MB_BLK blk = RK_NULL;
blk = RK_MPI_MMZ_Fd2Handle(tmp_plane.m.fd);
@ -503,26 +509,16 @@ void *run_video_stream(void *arg)
stFrame.stVFrame.u32FrameFlag |= 0;
stFrame.stVFrame.enCompressMode = COMPRESS_MODE_NONE;
bool retried = false;
// if (num == 100) {
// RK_VOID *pData = RK_MPI_MB_Handle2VirAddr(stFrame.stVFrame.pMbBlk);
// if (pData) {
// size_t frameSize = tmp_plane.bytesused; // Use the actual size reported by the driver
// write_buffer_to_file(pData, frameSize, "/userdata/banana.raw");
// printf("Frame 100 written to /userdata/banana.raw\n");
// } else {
// printf("Failed to get virtual address for frame 100\n");
// }
// }
retry_send_frame:
if (RK_MPI_VENC_SendFrame(VENC_CHANNEL, &stFrame, 2000) != RK_SUCCESS)
{
if (retried == true)
{
RK_LOGE("RK_MPI_VENC_SendFrame retry failed");
log_error("RK_MPI_VENC_SendFrame retry failed");
}
else
{
RK_LOGE("RK_MPI_VENC_SendFrame failed,retrying");
log_error("RK_MPI_VENC_SendFrame failed,retrying");
retried = true;
usleep(1000llu);
goto retry_send_frame;
@ -532,12 +528,13 @@ void *run_video_stream(void *arg)
num++;
if (ioctl(video_dev_fd, VIDIOC_QBUF, &buf) < 0)
printf("failture VIDIOC_QBUF\n");
log_error("failure VIDIOC_QBUF: %s", strerror(errno));
}
cleanup:
log_info("cleaning up video capture device %s", VIDEO_DEV);
if (ioctl(video_dev_fd, VIDIOC_STREAMOFF, &type) < 0)
{
perror("VIDIOC_STREAMOFF failed");
log_error("VIDIOC_STREAMOFF failed: %s", strerror(errno));
}
venc_stop();
@ -550,9 +547,11 @@ void *run_video_stream(void *arg)
}
}
log_info("closing video capture device %s", VIDEO_DEV);
close(video_dev_fd);
}
log_info("video stream thread exiting");
return NULL;
}
@ -560,65 +559,78 @@ void video_shutdown()
{
if (should_exit == true)
{
printf("shutting down in progress already\n");
log_info("shutting down in progress already");
return;
}
video_stop_streaming();
// if (buffers != NULL) {
// for (int i = 0; i < input_buffer_count; i++) {
// if ((buffers + i)->mb_blk != NULL) {
// RK_MPI_MB_ReleaseMB((buffers + i)->mb_blk);
// }
// free((buffers + i)->planes_buffer);
// }
// free(buffers);
// }
should_exit = true;
if (sub_dev_fd > 0)
{
shutdown(sub_dev_fd, SHUT_RDWR);
// close(sub_dev_fd);
printf("Closed sub_dev_fd\n");
log_info("Closed sub_dev_fd");
}
if (memPool != MB_INVALID_POOLID)
{
RK_MPI_MB_DestroyPool(memPool);
}
printf("Destroyed memory pool\n");
// if (format_thread != NULL) {
// pthread_join(*format_thread, NULL);
// free(format_thread);
// format_thread = NULL;
// }
// printf("Joined format detection thread\n");
log_info("Destroyed memory pool");
pthread_mutex_destroy(&streaming_mutex);
log_info("Destroyed streaming mutex");
}
// TODO: mutex?
void video_start_streaming()
{
pthread_mutex_lock(&streaming_mutex);
if (streaming_thread != NULL)
{
log_info("video streaming already started");
return;
log_warn("video streaming already started");
goto cleanup;
}
streaming_thread = malloc(sizeof(pthread_t));
assert(streaming_thread != NULL);
pthread_t *new_thread = malloc(sizeof(pthread_t));
if (new_thread == NULL)
{
log_error("Failed to allocate memory for streaming thread");
goto cleanup;
}
streaming_flag = true;
pthread_create(streaming_thread, NULL, run_video_stream, NULL);
int result = pthread_create(new_thread, NULL, run_video_stream, NULL);
if (result != 0)
{
log_error("Failed to create streaming thread: %s", strerror(result));
streaming_flag = false;
free(new_thread);
goto cleanup;
}
// Only set streaming_thread after successful creation, and before unlocking the mutex
streaming_thread = new_thread;
cleanup:
pthread_mutex_unlock(&streaming_mutex);
return;
}
void video_stop_streaming()
{
pthread_mutex_lock(&streaming_mutex);
if (streaming_thread != NULL)
{
streaming_flag = false;
log_info("stopping video streaming");
// wait 100ms for the thread to exit
usleep(1000000);
log_info("waiting for video streaming thread to exit");
pthread_join(*streaming_thread, NULL);
free(streaming_thread);
streaming_thread = NULL;
log_info("video streaming stopped");
}
pthread_mutex_unlock(&streaming_mutex);
}
void *run_detect_format(void *arg)
@ -632,7 +644,6 @@ void *run_detect_format(void *arg)
if (ioctl(sub_dev_fd, VIDIOC_SUBSCRIBE_EVENT, &sub) == -1)
{
log_error("cannot subscribe to event");
perror("Cannot subscribe to event");
goto exit;
}
@ -657,12 +668,12 @@ void *run_detect_format(void *arg)
else if (errno == ERANGE)
{
// Timings were found, but they are out of range of the hardware capabilities.
printf("HDMI status: out of range\n");
log_warn("HDMI status: out of range");
video_report_format(false, "out_of_range", 0, 0, 0);
}
else
{
perror("error VIDIOC_QUERY_DV_TIMINGS");
log_error("error VIDIOC_QUERY_DV_TIMINGS: %s", strerror(errno));
sleep(1);
continue;
}
@ -681,19 +692,24 @@ void *run_detect_format(void *arg)
detected_height = dv_timings.bt.height;
detected_signal = true;
video_report_format(true, NULL, detected_width, detected_height, frames_per_second);
pthread_mutex_lock(&streaming_mutex);
if (streaming_flag == true)
{
pthread_mutex_unlock(&streaming_mutex);
log_info("restarting on going video streaming");
video_stop_streaming();
video_start_streaming();
}
else
{
pthread_mutex_unlock(&streaming_mutex);
}
}
memset(&ev, 0, sizeof(ev));
if (ioctl(sub_dev_fd, VIDIOC_DQEVENT, &ev) != 0)
{
log_error("failed to VIDIOC_DQEVENT");
perror("failed to VIDIOC_DQEVENT");
log_error("failed to VIDIOC_DQEVENT: %s", strerror(errno));
break;
}
log_info("New event of type %u", ev.type);
@ -715,12 +731,18 @@ void video_set_quality_factor(float factor)
// TODO: update venc bitrate without stopping streaming
pthread_mutex_lock(&streaming_mutex);
if (streaming_flag == true)
{
pthread_mutex_unlock(&streaming_mutex);
log_info("restarting on going video streaming due to quality factor change");
video_stop_streaming();
video_start_streaming();
}
else
{
pthread_mutex_unlock(&streaming_mutex);
}
}
float video_get_quality_factor() {

View File

@ -1,13 +1,48 @@
#ifndef VIDEO_DAEMON_VIDEO_H
#define VIDEO_DAEMON_VIDEO_H
/**
* @brief Initialize the video subsystem
*
* @return int 0 on success, -1 on failure
*/
int video_init();
/**
* @brief Shutdown the video subsystem
*/
void video_shutdown();
/**
* @brief Run the detect format thread
*
* @param arg The argument to pass to the thread
* @return void* The result of the thread
*/
void *run_detect_format(void *arg);
/**
* @brief Start the video streaming
*/
void video_start_streaming();
/**
* @brief Stop the video streaming
*/
void video_stop_streaming();
/**
* @brief Set the quality factor of the video
*
* @param factor The quality factor to set
*/
void video_set_quality_factor(float factor);
/**
* @brief Get the quality factor of the video
*
* @return float The quality factor of the video
*/
float video_get_quality_factor();
#endif //VIDEO_DAEMON_VIDEO_H

View File

@ -4,6 +4,7 @@ package native
import (
"fmt"
"sync"
"unsafe"
"github.com/rs/zerolog"
@ -37,9 +38,16 @@ extern void jetkvm_go_indev_handler(int code);
static inline void jetkvm_cgo_setup_indev_handler() {
jetkvm_set_indev_handler(&jetkvm_go_indev_handler);
}
extern void jetkvm_go_rpc_handler(cchar_t *method, cchar_t *params);
static inline void jetkvm_cgo_setup_rpc_handler() {
jetkvm_set_rpc_handler(&jetkvm_go_rpc_handler);
}
*/
import "C"
var cgoLock sync.Mutex
//export jetkvm_go_video_state_handler
func jetkvm_go_video_state_handler(state *C.jetkvm_video_state_t) {
videoState := VideoState{
@ -75,6 +83,11 @@ func jetkvm_go_indev_handler(code C.int) {
indevEventChan <- int(code)
}
//export jetkvm_go_rpc_handler
func jetkvm_go_rpc_handler(method *C.cchar_t, params *C.cchar_t) {
rpcEventChan <- C.GoString(method)
}
var eventCodeToNameMap = map[int]string{}
func uiEventCodeToName(code int) string {
@ -90,13 +103,20 @@ func uiEventCodeToName(code int) string {
}
func setUpNativeHandlers() {
cgoLock.Lock()
defer cgoLock.Unlock()
C.jetkvm_cgo_setup_log_handler()
C.jetkvm_cgo_setup_video_state_handler()
C.jetkvm_cgo_setup_video_handler()
C.jetkvm_cgo_setup_indev_handler()
C.jetkvm_cgo_setup_rpc_handler()
}
func uiInit(rotation uint16) {
cgoLock.Lock()
defer cgoLock.Unlock()
cRotation := C.u_int16_t(rotation)
defer C.free(unsafe.Pointer(&cRotation))
@ -104,10 +124,16 @@ func uiInit(rotation uint16) {
}
func uiTick() {
cgoLock.Lock()
defer cgoLock.Unlock()
C.jetkvm_ui_tick()
}
func videoInit() error {
cgoLock.Lock()
defer cgoLock.Unlock()
ret := C.jetkvm_video_init()
if ret != 0 {
return fmt.Errorf("failed to initialize video: %d", ret)
@ -116,18 +142,40 @@ func videoInit() error {
}
func videoShutdown() {
cgoLock.Lock()
defer cgoLock.Unlock()
C.jetkvm_video_shutdown()
}
func videoStart() {
cgoLock.Lock()
defer cgoLock.Unlock()
C.jetkvm_video_start()
}
func videoStop() {
cgoLock.Lock()
defer cgoLock.Unlock()
C.jetkvm_video_stop()
}
func videoLogStatus() string {
cgoLock.Lock()
defer cgoLock.Unlock()
logStatus := C.jetkvm_video_log_status()
defer C.free(unsafe.Pointer(logStatus))
return C.GoString(logStatus)
}
func uiSetVar(name string, value string) {
cgoLock.Lock()
defer cgoLock.Unlock()
nameCStr := C.CString(name)
defer C.free(unsafe.Pointer(nameCStr))
@ -138,6 +186,9 @@ func uiSetVar(name string, value string) {
}
func uiGetVar(name string) string {
cgoLock.Lock()
defer cgoLock.Unlock()
nameCStr := C.CString(name)
defer C.free(unsafe.Pointer(nameCStr))
@ -145,31 +196,58 @@ func uiGetVar(name string) string {
}
func uiSwitchToScreen(screen string) {
cgoLock.Lock()
defer cgoLock.Unlock()
screenCStr := C.CString(screen)
defer C.free(unsafe.Pointer(screenCStr))
C.jetkvm_ui_load_screen(screenCStr)
}
func uiGetCurrentScreen() string {
cgoLock.Lock()
defer cgoLock.Unlock()
screenCStr := C.jetkvm_ui_get_current_screen()
return C.GoString(screenCStr)
}
func uiObjSetState(objName string, state string) (bool, error) {
func uiObjAddState(objName string, state string) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
stateCStr := C.CString(state)
defer C.free(unsafe.Pointer(stateCStr))
C.jetkvm_ui_set_state(objNameCStr, stateCStr)
C.jetkvm_ui_add_state(objNameCStr, stateCStr)
return true, nil
}
func uiObjClearState(objName string, state string) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
stateCStr := C.CString(state)
defer C.free(unsafe.Pointer(stateCStr))
C.jetkvm_ui_clear_state(objNameCStr, stateCStr)
return true, nil
}
func uiGetLVGLVersion() string {
cgoLock.Lock()
defer cgoLock.Unlock()
return C.GoString(C.jetkvm_ui_get_lvgl_version())
}
// TODO: use Enum instead of string but it's not a hot path and performance is not a concern now
func uiObjAddFlag(objName string, flag string) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
@ -179,6 +257,9 @@ func uiObjAddFlag(objName string, flag string) (bool, error) {
}
func uiObjClearFlag(objName string, flag string) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
@ -196,6 +277,9 @@ func uiObjShow(objName string) (bool, error) {
}
func uiObjSetOpacity(objName string, opacity int) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
@ -204,6 +288,9 @@ func uiObjSetOpacity(objName string, opacity int) (bool, error) {
}
func uiObjFadeIn(objName string, duration uint32) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
@ -213,6 +300,9 @@ func uiObjFadeIn(objName string, duration uint32) (bool, error) {
}
func uiObjFadeOut(objName string, duration uint32) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
@ -222,6 +312,9 @@ func uiObjFadeOut(objName string, duration uint32) (bool, error) {
}
func uiLabelSetText(objName string, text string) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
@ -236,6 +329,9 @@ func uiLabelSetText(objName string, text string) (bool, error) {
}
func uiImgSetSrc(objName string, src string) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
@ -248,6 +344,9 @@ func uiImgSetSrc(objName string, src string) (bool, error) {
}
func uiDispSetRotation(rotation uint16) (bool, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
nativeLogger.Info().Uint16("rotation", rotation).Msg("setting rotation")
cRotation := C.u_int16_t(rotation)
@ -258,21 +357,33 @@ func uiDispSetRotation(rotation uint16) (bool, error) {
}
func videoGetStreamQualityFactor() (float64, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
factor := C.jetkvm_video_get_quality_factor()
return float64(factor), nil
}
func videoSetStreamQualityFactor(factor float64) error {
cgoLock.Lock()
defer cgoLock.Unlock()
C.jetkvm_video_set_quality_factor(C.float(factor))
return nil
}
func videoGetEDID() (string, error) {
cgoLock.Lock()
defer cgoLock.Unlock()
edidCStr := C.jetkvm_video_get_edid_hex()
return C.GoString(edidCStr), nil
}
func videoSetEDID(edid string) error {
cgoLock.Lock()
defer cgoLock.Unlock()
edidCStr := C.CString(edid)
defer C.free(unsafe.Pointer(edidCStr))
C.jetkvm_video_set_edid(edidCStr)

View File

@ -28,7 +28,12 @@ func uiGetCurrentScreen() string {
return ""
}
func uiObjSetState(objName string, state string) (bool, error) {
func uiObjAddState(objName string, state string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjClearState(objName string, state string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
@ -103,6 +108,11 @@ func videoSetStreamQualityFactor(factor float64) error {
return nil
}
func videoLogStatus() string {
panicPlatformNotSupported()
return ""
}
func videoGetEDID() (string, error) {
panicPlatformNotSupported()
return "", nil

View File

@ -11,6 +11,7 @@ var (
videoStateChan chan VideoState = make(chan VideoState)
logChan chan nativeLogMessage = make(chan nativeLogMessage)
indevEventChan chan int = make(chan int)
rpcEventChan chan string = make(chan string)
)
func (n *Native) handleVideoFrameChan() {
@ -70,3 +71,10 @@ func (n *Native) handleIndevEventChan() {
n.onIndevEvent(name)
}
}
func (n *Native) handleRpcEventChan() {
for {
event := <-rpcEventChan
n.onRpcEvent(event)
}
}

View File

@ -37,9 +37,24 @@ func (n *Native) UIObjShow(objName string) (bool, error) {
return uiObjShow(objName)
}
// UIObjSetState clears the state then adds the new state
func (n *Native) UIObjSetState(objName string, state string) (bool, error) {
return uiObjSetState(objName, state)
// UISetVar sets the variable
func (n *Native) UISetVar(name string, value string) {
uiSetVar(name, value)
}
// UIGetVar gets the variable
func (n *Native) UIGetVar(name string) string {
return uiGetVar(name)
}
// UIObjAddState adds the state to the object
func (n *Native) UIObjAddState(objName string, state string) (bool, error) {
return uiObjAddState(objName, state)
}
// UIObjClearState clears the state from the object
func (n *Native) UIObjClearState(objName string, state string) (bool, error) {
return uiObjClearState(objName, state)
}
// UIObjAddFlag adds the flag to the object

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
{
"navigation": {
"selectedUserPageObject": "[jetkvm.eez-project]:/userPages/5",
"selectedActionObject": "[jetkvm.eez-project]:/actions/0",
"selectedUserPageObject": "[jetkvm.eez-project]:/userPages/8",
"selectedActionObject": "[jetkvm.eez-project]:/actions/12",
"selectedGlobalVariableObject": "[jetkvm.eez-project]:/variables/globalVariables/1",
"selectedStyleObject": "[jetkvm.eez-project]:/lvglStyles/styles/0",
"selectedStyleObject": "[jetkvm.eez-project]:/lvglStyles/styles/8",
"selectedThemeObject": "[jetkvm.eez-project]:/themes/0",
"selectedFontObject": "[jetkvm.eez-project]:/fonts/4",
"selectedBitmapObject": "[jetkvm.eez-project]:/bitmaps/11",
"selectedBitmapObject": "[jetkvm.eez-project]:/bitmaps/9",
"subnavigationSelectedItems": {
"variables-tab/sub-navigation/selected-item": "Global"
}
@ -30,7 +30,7 @@
{
"type": "border",
"selected": 2,
"size": 113.5,
"size": 271.5,
"location": "right",
"children": [
{
@ -77,7 +77,7 @@
},
{
"type": "border",
"selected": 1,
"selected": 0,
"location": "bottom",
"children": [
{
@ -188,38 +188,48 @@
"enableClose": false,
"icon": "svg:variable"
}
],
"active": true
]
}
]
},
{
"type": "tabset",
"id": "EDITORS",
"weight": 49.31058517127421,
"selected": 4,
"weight": 52.479136828866125,
"selected": 5,
"enableDeleteWhenEmpty": false,
"enableClose": false,
"children": [
{
"type": "tab",
"id": "#aec56ae8-5b75-4a2f-ad81-f0d7d683c77a",
"name": "FontBook24",
"id": "#2b774476-9ef3-4363-83f8-8b478f163b02",
"name": "MenuAdvancedScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/fonts/4",
"objectPath": "[jetkvm.eez-project]:/userPages/4",
"permanent": false
},
"icon": "material:font_download"
"icon": "svg:page"
},
{
"type": "tab",
"id": "#7bbd8382-ea41-467d-8ad3-4312a2d47266",
"name": "ResetConfigScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/8",
"permanent": true
},
"icon": "svg:page"
},
{
"type": "tab",
"id": "#c8dece00-e490-46b8-8a14-5dcfa8bbce36",
"name": "AboutScreen",
"name": "StatusScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/5",
"permanent": false
"objectPath": "[jetkvm.eez-project]:/userPages/7",
"permanent": true
},
"icon": "svg:page"
},
@ -237,7 +247,7 @@
{
"type": "tab",
"id": "#f5a057a5-977c-46be-8702-5447d603a34f",
"name": "MenuScreen",
"name": "HomeScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/2",
@ -257,12 +267,13 @@
},
"icon": "material:settings"
}
]
],
"active": true
},
{
"type": "row",
"id": "#ee319cf9-6145-49e4-b40e-1d999be897c8",
"weight": 24.95234430353635,
"weight": 21.78379264594443,
"children": [
{
"type": "tabset",
@ -1065,8 +1076,7 @@
"0": {
"0": {
"0": {
"1": {},
"$selected": true
"1": {}
}
}
}
@ -1086,11 +1096,58 @@
}
},
"[jetkvm.eez-project]:/userPages/1[flow-state]": {
"selection": {
"0": {
"0": {
"0": {},
"1": {},
"$selected": true
}
}
},
"transform": {
"translate": {
"x": -180,
"y": -207
},
"scale": 1
},
"timeline": {
"isEditorActive": false,
"position": 0,
"secondToPx": 200,
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/lvglStyles/styles[tree-state]": {
"0": {},
"1": {},
"2": {},
"3": {},
"4": {
"$selected": true
},
"5": {
"$selected": true
},
"6": {
"$selected": true
},
"7": {
"$selected": true
},
"8": {
"$selected": true
}
},
"[jetkvm.eez-project]:/userPages/2[flow-state]": {
"selection": {
"0": {
"0": {
"0": {
"0": {},
"0": {
"$selected": true
},
"1": {
"0": {}
}
@ -1102,9 +1159,7 @@
"3": {
"0": {
"0": {},
"1": {
"$selected": true
}
"1": {}
},
"1": {
"0": {},
@ -1128,12 +1183,7 @@
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/lvglStyles/styles[tree-state]": {
"0": {
"$selected": true
}
},
"[jetkvm.eez-project]:/userPages/2[flow-state]": {
"[jetkvm.eez-project]:/userPages/3[flow-state]": {
"selection": {
"0": {
"0": {
@ -1142,13 +1192,14 @@
},
"1": {
"0": {
"0": {},
"0": {
"$selected": true
},
"1": {},
"2": {},
"3": {},
"4": {
"0": {},
"$selected": true
"0": {}
}
}
}
@ -1169,19 +1220,15 @@
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/userPages/3[flow-state]": {
"[jetkvm.eez-project]:/userPages/4[flow-state]": {
"selection": {
"0": {
"0": {
"0": {
"0": {},
"$selected": true
"0": {}
},
"1": {
"0": {
"0": {},
"1": {},
"2": {},
"3": {}
}
}
@ -1190,41 +1237,8 @@
},
"transform": {
"translate": {
"x": -150,
"y": -120
},
"scale": 1
},
"timeline": {
"isEditorActive": false,
"position": 0,
"secondToPx": 200,
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/userPages/4[flow-state]": {
"selection": {
"0": {
"0": {
"0": {
"0": {
"$selected": true
}
},
"1": {
"0": {
"0": {},
"1": {},
"2": {}
}
}
}
}
},
"transform": {
"translate": {
"x": -176,
"y": -127
"x": -181,
"y": -256.3828125
},
"scale": 1
},
@ -1245,19 +1259,8 @@
"1": {
"0": {
"0": {},
"1": {
"1": {}
},
"2": {},
"3": {},
"4": {},
"5": {
"1": {
"$selected": true
}
},
"6": {},
"7": {}
"1": {},
"2": {}
}
}
}
@ -1300,11 +1303,11 @@
"0": {},
"1": {
"0": {
"0": {},
"0": {
"1": {}
},
"2": {
"1": {
"$selected": true
}
"1": {}
}
}
}
@ -1313,7 +1316,7 @@
},
"transform": {
"translate": {
"x": -138,
"x": -10.425644531250029,
"y": -122
},
"scale": 1
@ -1324,6 +1327,67 @@
"secondToPx": 200,
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/userPages/7[flow-state]": {
"selection": {
"0": {
"0": {
"0": {},
"1": {},
"$selected": true
}
}
},
"transform": {
"translate": {
"x": -180,
"y": -207
},
"scale": 1
},
"timeline": {
"isEditorActive": false,
"position": 0,
"secondToPx": 200,
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/userPages/8[flow-state]": {
"selection": {
"0": {
"0": {
"0": {
"0": {}
},
"1": {
"0": {
"0": {
"0": {
"$selected": true
}
},
"1": {},
"2": {
"0": {}
}
}
}
}
}
},
"transform": {
"translate": {
"x": -194,
"y": -37
},
"scale": 1
},
"timeline": {
"isEditorActive": false,
"position": 0,
"secondToPx": 200,
"scrollLeft": 0
}
}
},
"activeOutputSection": 0,
@ -1339,10 +1403,10 @@
"logsPanelFilter": "all",
"selectedStylePropertyName": "",
"lvglPart": "MAIN",
"lvglState": "DISABLED",
"lvglState": "DEFAULT",
"lvglExpandedPropertiesGroup": [
"MARGIN",
"TEXT"
"POSITION AND SIZE",
"LAYOUT"
],
"showInactiveFlowsInDebugger": true,
"globalFlowZoom": true,

View File

@ -1,5 +1,8 @@
#include "actions.h"
#include "screens.h"
#include <stdio.h>
#include "ui.h"
#include "vars.h"
int handle_gesture_screen_switch(lv_event_t *e, lv_dir_t direction, int screenId) {
lv_event_code_t event_code = lv_event_get_code(e);
@ -15,6 +18,15 @@ int handle_gesture_screen_switch(lv_event_t *e, lv_dir_t direction, int screenId
return 1;
}
void handle_gesture_main_screen_switch(lv_event_t *e, lv_dir_t direction) {
const char *main_screen = get_var_main_screen();
if (strcmp(main_screen, "home_screen") == 0) {
loadScreen(SCREEN_ID_HOME_SCREEN);
} else if (strcmp(main_screen, "no_network_screen") == 0) {
loadScreen(SCREEN_ID_NO_NETWORK_SCREEN);
}
}
void action_switch_to_menu(lv_event_t *e) {
loadScreen(SCREEN_ID_MENU_SCREEN);
}
@ -31,8 +43,16 @@ void action_switch_to_about(lv_event_t *e) {
loadScreen(SCREEN_ID_ABOUT_SCREEN);
}
void action_switch_to_reset_config(lv_event_t *e) {
loadScreen(SCREEN_ID_RESET_CONFIG_SCREEN);
}
void action_switch_to_reboot(lv_event_t *e) {
loadScreen(SCREEN_ID_REBOOT_SCREEN);
}
void action_menu_screen_gesture(lv_event_t * e) {
handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_HOME_SCREEN);
handle_gesture_main_screen_switch(e, LV_DIR_RIGHT);
}
void action_menu_advanced_screen_gesture(lv_event_t * e) {
@ -40,7 +60,7 @@ void action_menu_advanced_screen_gesture(lv_event_t * e) {
}
void action_reset_config_screen_gesture(lv_event_t * e) {
handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_ADVANCED_SCREEN);
handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);
}
void action_home_screen_gesture(lv_event_t * e) {
@ -49,4 +69,72 @@ void action_home_screen_gesture(lv_event_t * e) {
void action_about_screen_gesture(lv_event_t * e) {
handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);
}
// user_data doesn't seem to be working, so we use a global variable here
static uint32_t t_reset_config;
static uint32_t t_reboot;
static bool b_reboot = false;
static bool b_reset_config = false;
const int RESET_CONFIG_HOLD_TIME = 10;
const int REBOOT_HOLD_TIME = 5;
void action_reset_config(lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t *obj = lv_event_get_target(e);
if (event_code == LV_EVENT_PRESSED) {
t_reset_config = lv_tick_get();
}
else if (event_code == LV_EVENT_PRESSING) {
int remaining_time = RESET_CONFIG_HOLD_TIME * 1000 - lv_tick_elaps(t_reset_config);
if (remaining_time <= 0) {
lv_obj_add_flag(objects.reset_config_button, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(objects.reset_config_spinner, LV_OBJ_FLAG_HIDDEN);
ui_call_rpc_handler("resetConfig", NULL);
b_reset_config = true;
} else {
b_reset_config = false;
char buf[100];
int remaining_time_seconds = remaining_time / 1000;
if (remaining_time_seconds <= 1) {
remaining_time_seconds = 1;
}
sprintf(buf, "Press and hold for %d seconds", remaining_time_seconds);
lv_label_set_text(objects.reset_config_label, buf);
}
} else if (event_code == LV_EVENT_RELEASED) {
if (!b_reset_config) {
lv_label_set_text(objects.reset_config_label, "Press and hold for 10 seconds");
}
}
}
void action_reboot(lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t *obj = lv_event_get_target(e);
if (event_code == LV_EVENT_PRESSED) {
t_reboot = lv_tick_get();
}
else if (event_code == LV_EVENT_PRESSING) {
int remaining_time = REBOOT_HOLD_TIME * 1000 - lv_tick_elaps(t_reboot);
if (remaining_time <= 0) {
ui_call_rpc_handler("reboot", NULL);
b_reboot = false;
} else {
b_reboot = false;
char buf[100];
int remaining_time_seconds = remaining_time / 1000;
if (remaining_time_seconds <= 1) {
remaining_time_seconds = 1;
}
sprintf(buf, "Press and hold for %d seconds", remaining_time_seconds);
lv_label_set_text(objects.reboot_label, buf);
}
} else if (event_code == LV_EVENT_RELEASED) {
if (!b_reboot) {
lv_label_set_text(objects.reboot_label, "Press and hold for 5 seconds");
}
}
}

View File

@ -11,6 +11,7 @@ extern int handle_gesture_screen_switch(lv_event_t *e, lv_dir_t direction, int s
extern void action_switch_to_menu(lv_event_t * e);
extern void action_switch_to_advanced_menu(lv_event_t * e);
extern void action_switch_to_reset_config(lv_event_t * e);
extern void action_switch_to_about(lv_event_t * e);
extern void action_menu_screen_gesture(lv_event_t * e);
extern void action_home_screen_gesture(lv_event_t * e);
@ -20,6 +21,9 @@ extern void action_about_screen_gesture(lv_event_t * e);
extern void action_switch_to_status(lv_event_t * e);
extern void action_common_click_event(lv_event_t * e);
extern void action_handle_common_press_event(lv_event_t * e);
extern void action_reset_config(lv_event_t * e);
extern void action_reboot(lv_event_t * e);
extern void action_switch_to_reboot(lv_event_t * e);
#ifdef __cplusplus

View File

@ -325,10 +325,11 @@ void create_screen_home_screen() {
lv_obj_t *obj = lv_label_create(parent_obj);
objects.home_info_ipv6_addr = obj;
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_size(obj, LV_PCT(98), 17);
lv_label_set_long_mode(obj, LV_LABEL_LONG_DOT);
add_style_label_font16(obj);
lv_label_set_text(obj, "fe80::ffff:ffff:ffff:ffff:ffff:ffff");
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "fe80::ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
}
{
// HomeInfoMACAddr
@ -431,10 +432,11 @@ void create_screen_home_screen() {
objects.usb_status_label = obj;
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
add_style_label_font16(obj);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DISABLED);
lv_label_set_text(obj, "Connected");
lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_CHECKED);
lv_label_set_text(obj, "Unknown");
}
}
}
@ -493,11 +495,11 @@ void create_screen_home_screen() {
objects.hdmi_status_label = obj;
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_add_state(obj, LV_STATE_DISABLED);
lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
add_style_label_font16(obj);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DISABLED);
lv_label_set_text(obj, "Disconnected");
lv_obj_set_style_text_color(obj, lv_color_hex(0xff22c55e), LV_PART_MAIN | LV_STATE_CHECKED);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Unknown");
}
}
}
@ -829,6 +831,8 @@ void create_screen_menu_advanced_screen() {
objects.menu_btn_advanced_developer_mode = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), 50);
lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_PRESSED, (void *)0);
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
add_style_menu_button(obj);
{
lv_obj_t *parent_obj = obj;
@ -847,6 +851,7 @@ void create_screen_menu_advanced_screen() {
objects.menu_btn_advanced_usb_emulation = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), 50);
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
add_style_menu_button(obj);
{
lv_obj_t *parent_obj = obj;
@ -865,6 +870,7 @@ void create_screen_menu_advanced_screen() {
objects.menu_btn_advanced_reboot = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), 50);
lv_obj_add_event_cb(obj, action_switch_to_reboot, LV_EVENT_PRESSED, (void *)0);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SNAPPABLE);
add_style_menu_button(obj);
{
@ -884,6 +890,7 @@ void create_screen_menu_advanced_screen() {
objects.menu_btn_advanced_reset_config = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), 50);
lv_obj_add_event_cb(obj, action_switch_to_reset_config, LV_EVENT_PRESSED, (void *)0);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SNAPPABLE);
add_style_menu_button(obj);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT);
@ -1719,6 +1726,403 @@ void create_screen_status_screen() {
void tick_screen_status_screen() {
}
void create_screen_reset_config_screen() {
lv_obj_t *obj = lv_obj_create(0);
objects.reset_config_screen = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 300, 240);
lv_obj_add_event_cb(obj, action_about_screen_gesture, LV_EVENT_GESTURE, (void *)0);
add_style_flex_screen_menu(obj);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_obj_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_start(obj);
{
lv_obj_t *parent_obj = obj;
{
// ResetConfigHeader
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reset_config_header = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
add_style_flow_row_space_between(obj);
lv_obj_set_style_pad_right(obj, 4, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_button_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 32, 32);
lv_obj_add_event_cb(obj, action_switch_to_menu, LV_EVENT_CLICKED, (void *)0);
add_style_back_button(obj);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_image_create(parent_obj);
lv_obj_set_pos(obj, -1, 2);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_back_caret);
}
}
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
add_style_header_link(obj);
lv_label_set_text(obj, "Reset Config");
}
}
}
{
// ResetConfigContainer
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reset_config_container = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80));
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_AUTO);
lv_obj_set_scroll_dir(obj, LV_DIR_VER);
lv_obj_set_scroll_snap_x(obj, LV_SCROLL_SNAP_START);
add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 4, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_obj_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
// ResetConfigLabelContainer
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reset_config_label_container = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
// ResetConfigLabel
lv_obj_t *obj = lv_label_create(parent_obj);
objects.reset_config_label = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
add_style_info_content_label(obj);
lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Press and hold for\n10 seconds");
}
}
}
{
// ResetConfigSpinner
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reset_config_spinner = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE|LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj);
lv_obj_set_style_flex_main_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_flex_cross_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_flex_track_place(obj, LV_FLEX_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_spinner_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 80, 80);
lv_spinner_set_anim_params(obj, 1000, 60);
}
}
}
{
// ResetConfigButton
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reset_config_button = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_button_create(parent_obj);
objects.obj0 = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), 50);
lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_PRESSED, (void *)0);
lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_PRESSING, (void *)0);
lv_obj_add_event_cb(obj, action_reset_config, LV_EVENT_RELEASED, (void *)0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 13, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_label_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Reset configuration");
}
}
}
}
}
}
}
}
}
}
}
}
tick_screen_reset_config_screen();
}
void tick_screen_reset_config_screen() {
}
void create_screen_reboot_screen() {
lv_obj_t *obj = lv_obj_create(0);
objects.reboot_screen = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 300, 240);
lv_obj_add_event_cb(obj, action_about_screen_gesture, LV_EVENT_GESTURE, (void *)0);
add_style_flex_screen_menu(obj);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_obj_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_start(obj);
{
lv_obj_t *parent_obj = obj;
{
// RebootHeader
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reboot_header = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
add_style_flow_row_space_between(obj);
lv_obj_set_style_pad_right(obj, 4, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_button_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, 32, 32);
lv_obj_add_event_cb(obj, action_switch_to_menu, LV_EVENT_CLICKED, (void *)0);
add_style_back_button(obj);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_image_create(parent_obj);
lv_obj_set_pos(obj, -1, 2);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_image_set_src(obj, &img_back_caret);
}
}
}
{
lv_obj_t *obj = lv_label_create(parent_obj);
lv_obj_set_pos(obj, LV_PCT(0), LV_PCT(0));
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
add_style_header_link(obj);
lv_label_set_text(obj, "Reboot Device");
}
}
}
{
// RebootContainer
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reboot_container = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(80));
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_AUTO);
lv_obj_set_scroll_dir(obj, LV_DIR_VER);
lv_obj_set_scroll_snap_x(obj, LV_SCROLL_SNAP_START);
add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 4, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_obj_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
// RebootLabelContainer
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reboot_label_container = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj);
lv_obj_set_style_pad_right(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 10, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
// RebootLabel
lv_obj_t *obj = lv_label_create(parent_obj);
objects.reboot_label = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
add_style_info_content_label(obj);
lv_obj_set_style_text_font(obj, &ui_font_font_book20, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Press and hold for\n5 seconds");
}
}
}
{
// RebootConfigButton
lv_obj_t *obj = lv_obj_create(parent_obj);
objects.reboot_config_button = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_left(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
add_style_flex_column_start(obj);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_button_create(parent_obj);
objects.obj1 = obj;
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_PCT(100), 50);
lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSED, (void *)0);
lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_PRESSING, (void *)0);
lv_obj_add_event_cb(obj, action_reboot, LV_EVENT_RELEASED, (void *)0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xffdc2626), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(obj, 13, LV_PART_MAIN | LV_STATE_DEFAULT);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_label_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(obj, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_label_set_text(obj, "Hold to reboot");
}
}
}
}
}
}
}
}
}
}
}
}
tick_screen_reboot_screen();
}
void tick_screen_reboot_screen() {
}
typedef void (*tick_screen_func_t)();
@ -1731,6 +2135,8 @@ tick_screen_func_t tick_screen_funcs[] = {
tick_screen_menu_network_screen,
tick_screen_about_screen,
tick_screen_status_screen,
tick_screen_reset_config_screen,
tick_screen_reboot_screen,
};
void tick_screen(int screen_index) {
tick_screen_funcs[screen_index]();
@ -1752,4 +2158,6 @@ void create_screens() {
create_screen_menu_network_screen();
create_screen_about_screen();
create_screen_status_screen();
create_screen_reset_config_screen();
create_screen_reboot_screen();
}

View File

@ -16,6 +16,8 @@ typedef struct _objects_t {
lv_obj_t *menu_network_screen;
lv_obj_t *about_screen;
lv_obj_t *status_screen;
lv_obj_t *reset_config_screen;
lv_obj_t *reboot_screen;
lv_obj_t *boot_logo;
lv_obj_t *boot_screen_version;
lv_obj_t *no_network_header_container;
@ -83,6 +85,19 @@ typedef struct _objects_t {
lv_obj_t *app_version_1;
lv_obj_t *cloud_domain_container;
lv_obj_t *cloud_domain;
lv_obj_t *reset_config_header;
lv_obj_t *reset_config_container;
lv_obj_t *reset_config_label_container;
lv_obj_t *reset_config_label;
lv_obj_t *reset_config_spinner;
lv_obj_t *reset_config_button;
lv_obj_t *obj0;
lv_obj_t *reboot_header;
lv_obj_t *reboot_container;
lv_obj_t *reboot_label_container;
lv_obj_t *reboot_label;
lv_obj_t *reboot_config_button;
lv_obj_t *obj1;
} objects_t;
extern objects_t objects;
@ -96,6 +111,8 @@ enum ScreensEnum {
SCREEN_ID_MENU_NETWORK_SCREEN = 6,
SCREEN_ID_ABOUT_SCREEN = 7,
SCREEN_ID_STATUS_SCREEN = 8,
SCREEN_ID_RESET_CONFIG_SCREEN = 9,
SCREEN_ID_REBOOT_SCREEN = 10,
};
void create_screen_boot_screen();
@ -121,6 +138,12 @@ void tick_screen_about_screen();
void create_screen_status_screen();
void tick_screen_status_screen();
void create_screen_reset_config_screen();
void tick_screen_reset_config_screen();
void create_screen_reboot_screen();
void tick_screen_reboot_screen();
void tick_screen_by_id(enum ScreensEnum screenId);
void tick_screen(int screen_index);

View File

@ -14,6 +14,19 @@
jetkvm_rpc_handler_t *ui_rpc_handler = NULL;
void ui_set_rpc_handler(jetkvm_rpc_handler_t *handler) {
ui_rpc_handler = handler;
}
void ui_call_rpc_handler(const char *method, const char *params) {
if (ui_rpc_handler != NULL) {
(*ui_rpc_handler)(method, params);
}
}
#if defined(EEZ_FOR_LVGL)
void ui_init() {

View File

@ -3,6 +3,13 @@
#include <lvgl.h>
typedef void (jetkvm_rpc_handler_t)(const char *method, const char *params);
void ui_set_rpc_handler(jetkvm_rpc_handler_t *handler);
void ui_call_rpc_handler(const char *method, const char *params);
#if defined(EEZ_FOR_LVGL)

View File

@ -5,6 +5,7 @@
char app_version[100] = { 0 };
char system_version[100] = { 0 };
char lvgl_version[32] = { 0 };
char main_screen[32] = "home_screen";
const char *get_var_app_version() {
return app_version;
@ -36,4 +37,13 @@ void set_var_system_version(const char *value) {
system_version[sizeof(system_version) / sizeof(char) - 1] = 0;
}
void set_var_lvgl_version(const char *value) {}
void set_var_lvgl_version(const char *value) {}
void set_var_main_screen(const char *value) {
strncpy(main_screen, value, sizeof(main_screen) / sizeof(char));
main_screen[sizeof(main_screen) / sizeof(char) - 1] = 0;
}
const char *get_var_main_screen() {
return main_screen;
}

View File

@ -17,7 +17,8 @@ extern "C" {
enum FlowGlobalVariables {
FLOW_GLOBAL_VARIABLE_APP_VERSION = 0,
FLOW_GLOBAL_VARIABLE_SYSTEM_VERSION = 1,
FLOW_GLOBAL_VARIABLE_LVGL_VERSION = 2
FLOW_GLOBAL_VARIABLE_LVGL_VERSION = 2,
FLOW_GLOBAL_VARIABLE_MAIN_SCREEN = 3
};
// Native global variables
@ -28,6 +29,8 @@ extern const char *get_var_system_version();
extern void set_var_system_version(const char *value);
extern const char *get_var_lvgl_version();
extern void set_var_lvgl_version(const char *value);
extern const char *get_var_main_screen();
extern void set_var_main_screen(const char *value);
#ifdef __cplusplus

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,7 @@
package native
import (
"sync"
"time"
"github.com/Masterminds/semver/v3"
@ -17,6 +18,9 @@ type Native struct {
onVideoStateChange func(state VideoState)
onVideoFrameReceived func(frame []byte, duration time.Duration)
onIndevEvent func(event string)
onRpcEvent func(event string)
videoLock sync.Mutex
screenLock sync.Mutex
}
type NativeOptions struct {
@ -26,20 +30,21 @@ type NativeOptions struct {
OnVideoStateChange func(state VideoState)
OnVideoFrameReceived func(frame []byte, duration time.Duration)
OnIndevEvent func(event string)
OnRpcEvent func(event string)
}
func NewNative(opts NativeOptions) *Native {
onVideoStateChange := opts.OnVideoStateChange
if onVideoStateChange == nil {
onVideoStateChange = func(state VideoState) {
nativeLogger.Info().Msg("video state changed")
nativeLogger.Info().Interface("state", state).Msg("video state changed")
}
}
onVideoFrameReceived := opts.OnVideoFrameReceived
if onVideoFrameReceived == nil {
onVideoFrameReceived = func(frame []byte, duration time.Duration) {
nativeLogger.Info().Msg("video frame received")
nativeLogger.Info().Interface("frame", frame).Dur("duration", duration).Msg("video frame received")
}
}
@ -50,6 +55,13 @@ func NewNative(opts NativeOptions) *Native {
}
}
onRpcEvent := opts.OnRpcEvent
if onRpcEvent == nil {
onRpcEvent = func(event string) {
nativeLogger.Info().Str("event", event).Msg("rpc event")
}
}
return &Native{
ready: make(chan struct{}),
l: nativeLogger,
@ -57,9 +69,12 @@ func NewNative(opts NativeOptions) *Native {
systemVersion: opts.SystemVersion,
appVersion: opts.AppVersion,
displayRotation: opts.DisplayRotation,
onVideoStateChange: opts.OnVideoStateChange,
onVideoFrameReceived: opts.OnVideoFrameReceived,
onIndevEvent: opts.OnIndevEvent,
onVideoStateChange: onVideoStateChange,
onVideoFrameReceived: onVideoFrameReceived,
onIndevEvent: onIndevEvent,
onRpcEvent: onRpcEvent,
videoLock: sync.Mutex{},
screenLock: sync.Mutex{},
}
}
@ -73,11 +88,14 @@ func (n *Native) Start() {
go n.handleVideoStateChan()
go n.handleVideoFrameChan()
go n.handleIndevEventChan()
go n.handleRpcEventChan()
n.initUI()
go n.tickUI()
videoInit()
if err := videoInit(); err != nil {
n.l.Error().Err(err).Msg("failed to initialize video")
}
close(n.ready)
}

View File

@ -9,27 +9,52 @@ type VideoState struct {
}
func (n *Native) VideoSetQualityFactor(factor float64) error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
return videoSetStreamQualityFactor(factor)
}
func (n *Native) VideoGetQualityFactor() (float64, error) {
n.videoLock.Lock()
defer n.videoLock.Unlock()
return videoGetStreamQualityFactor()
}
func (n *Native) VideoSetEDID(edid string) error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
return videoSetEDID(edid)
}
func (n *Native) VideoGetEDID() (string, error) {
n.videoLock.Lock()
defer n.videoLock.Unlock()
return videoGetEDID()
}
func (n *Native) VideoLogStatus() (string, error) {
n.videoLock.Lock()
defer n.videoLock.Unlock()
return videoLogStatus(), nil
}
func (n *Native) VideoStop() error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
videoStop()
return nil
}
func (n *Native) VideoStart() error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
videoStart()
return nil
}

View File

@ -1,46 +0,0 @@
package network
import (
"errors"
"github.com/jetkvm/kvm/internal/lldp"
)
func (s *NetworkInterfaceState) shouldStartLLDP() bool {
if s.lldp == nil {
s.l.Trace().Msg("LLDP not initialized")
return false
}
s.l.Trace().Msgf("LLDP mode: %s", s.config.LLDPMode.String)
return s.config.LLDPMode.String != "disabled"
}
func (s *NetworkInterfaceState) startLLDP() {
if !s.shouldStartLLDP() || s.lldp == nil {
return
}
s.l.Trace().Msg("starting LLDP")
if err := s.lldp.Start(); err != nil {
s.l.Error().Err(err).Msg("unable to start LLDP")
}
}
func (s *NetworkInterfaceState) stopLLDP() {
if s.lldp == nil {
return
}
s.l.Trace().Msg("stopping LLDP")
if err := s.lldp.Stop(); err != nil {
s.l.Error().Err(err).Msg("unable to stop LLDP")
}
}
func (s *NetworkInterfaceState) GetLLDPNeighbors() ([]lldp.Neighbor, error) {
if s.lldp == nil {
return nil, errors.New("lldp not initialized")
}
return s.lldp.GetNeighbors(), nil
}

View File

@ -6,7 +6,6 @@ import (
"sync"
"github.com/jetkvm/kvm/internal/confparser"
"github.com/jetkvm/kvm/internal/lldp"
"github.com/jetkvm/kvm/internal/logging"
"github.com/jetkvm/kvm/internal/udhcpc"
"github.com/rs/zerolog"
@ -31,8 +30,6 @@ type NetworkInterfaceState struct {
config *NetworkConfig
dhcpClient *udhcpc.DHCPClient
lldp *lldp.LLDP
defaultHostname string
currentHostname string
currentFqdn string
@ -101,24 +98,7 @@ func NewNetworkInterfaceState(opts *NetworkInterfaceOptions) (*NetworkInterfaceS
},
})
// create the lldp service
lldpClient := lldp.NewLLDP(&lldp.LLDPOptions{
InterfaceName: opts.InterfaceName,
EnableRx: true,
EnableTx: true,
Logger: l,
})
// create the lldp service
lldpClient = lldp.NewLLDP(&lldp.LLDPOptions{
InterfaceName: opts.InterfaceName,
EnableRx: true,
EnableTx: true,
Logger: l,
})
s.dhcpClient = dhcpClient
s.lldp = lldpClient
return s, nil
}
@ -386,18 +366,12 @@ func (s *NetworkInterfaceState) updateNtpServersFromLease(lease *udhcpc.Lease) e
}
func (s *NetworkInterfaceState) handleInitialCheck() {
if s.IsUp() {
s.startLLDP()
}
// if s.IsUp() {}
s.onInitialCheck(s)
}
func (s *NetworkInterfaceState) handleStateChange() {
if s.IsUp() {
s.startLLDP()
} else {
s.stopLLDP()
}
// if s.IsUp() {} else {}
s.onStateChange(s)
}

View File

@ -251,6 +251,10 @@ func rpcSetEDID(edid string) error {
return nil
}
func rpcGetVideoLogStatus() (string, error) {
return nativeInstance.VideoLogStatus()
}
func rpcGetDevChannelState() (bool, error) {
return config.IncludePreRelease, nil
}
@ -1207,6 +1211,7 @@ var rpcHandlers = map[string]RPCHandler{
"setAutoUpdateState": {Func: rpcSetAutoUpdateState, Params: []string{"enabled"}},
"getEDID": {Func: rpcGetEDID},
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
"getVideoLogStatus": {Func: rpcGetVideoLogStatus},
"getDevChannelState": {Func: rpcGetDevChannelState},
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
"getLocalVersion": {Func: rpcGetLocalVersion},
@ -1259,5 +1264,4 @@ var rpcHandlers = map[string]RPCHandler{
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
"getLocalLoopbackOnly": {Func: rpcGetLocalLoopbackOnly},
"setLocalLoopbackOnly": {Func: rpcSetLocalLoopbackOnly, Params: []string{"enabled"}},
"getLLDPNeighbors": {Func: rpcGetLLDPNeighbors},
}

View File

@ -24,6 +24,21 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
nativeLogger.Trace().Str("event", event).Msg("indev event received")
wakeDisplay(false, "indev_event")
},
OnRpcEvent: func(event string) {
nativeLogger.Trace().Str("event", event).Msg("rpc event received")
switch event {
case "resetConfig":
err := rpcResetConfig()
if err != nil {
nativeLogger.Warn().Err(err).Msg("error resetting config")
}
_ = rpcReboot(true)
case "reboot":
_ = rpcReboot(true)
default:
nativeLogger.Warn().Str("event", event).Msg("unknown rpc event received")
}
},
OnVideoFrameReceived: func(frame []byte, duration time.Duration) {
if currentSession != nil {
err := currentSession.VideoTrack.WriteSample(media.Sample{Data: frame, Duration: duration})

View File

@ -3,7 +3,6 @@ package kvm
import (
"fmt"
"github.com/jetkvm/kvm/internal/lldp"
"github.com/jetkvm/kvm/internal/network"
"github.com/jetkvm/kvm/internal/udhcpc"
)
@ -124,7 +123,3 @@ func rpcSetNetworkSettings(settings network.RpcNetworkSettings) (*network.RpcNet
func rpcRenewDHCPLease() error {
return networkState.RpcRenewDHCPLease()
}
func rpcGetLLDPNeighbors() ([]lldp.Neighbor, error) {
return networkState.GetLLDPNeighbors()
}

View File

@ -1,21 +1,11 @@
#!/bin/bash
set -e
C_RST="$(tput sgr0)"
C_ERR="$(tput setaf 1)"
C_OK="$(tput setaf 2)"
C_WARN="$(tput setaf 3)"
C_INFO="$(tput setaf 5)"
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
source ${SCRIPT_PATH}/build_utils.sh
msg() { printf '%s%s%s\n' $2 "$1" $C_RST; }
msg_info() { msg "$1" $C_INFO; }
msg_ok() { msg "$1" $C_OK; }
msg_err() { msg "$1" $C_ERR; }
msg_warn() { msg "$1" $C_WARN; }
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
BUILD_DIR=${SCRIPT_DIR}/build
CGO_PATH=$(realpath "${SCRIPT_PATH}/../internal/native/cgo")
BUILD_DIR=${CGO_PATH}/build
CMAKE_TOOLCHAIN_FILE=/opt/jetkvm-native-buildkit/rv1106-jetkvm-v2.cmake
CLEAN_ALL=${CLEAN_ALL:-0}
@ -25,7 +15,7 @@ if [ "$CLEAN_ALL" -eq 1 ]; then
fi
TMP_DIR=$(mktemp -d)
pushd "${SCRIPT_DIR}" > /dev/null
pushd "${CGO_PATH}" > /dev/null
msg_info "▶ Generating UI index"
./ui_index.gen.sh
@ -37,7 +27,7 @@ VERBOSE=1 cmake -B "${BUILD_DIR}" \
-DCMAKE_CROSSCOMPILING=1 \
-DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE \
-DLV_BUILD_USE_KCONFIG=ON \
-DLV_BUILD_DEFCONFIG_PATH=${SCRIPT_DIR}/lvgl_defconfig \
-DLV_BUILD_DEFCONFIG_PATH=${CGO_PATH}/lvgl_defconfig \
-DCONFIG_LV_BUILD_EXAMPLES=OFF \
-DCONFIG_LV_BUILD_DEMOS=OFF \
-DSKIP_GLIBC_NAMES=ON \
@ -46,8 +36,8 @@ VERBOSE=1 cmake -B "${BUILD_DIR}" \
msg_info "▶ Copying built library and header files"
cmake --build "${BUILD_DIR}" --target install
cp -r "${TMP_DIR}/include" ../
cp -r "${TMP_DIR}/lib" ../
cp -r "${TMP_DIR}/include" "${CGO_PATH}"
cp -r "${TMP_DIR}/lib" "${CGO_PATH}"
rm -rf "${TMP_DIR}"
popd > /dev/null

99
scripts/build_utils.sh Normal file
View File

@ -0,0 +1,99 @@
#!/bin/bash
# check if TERM is set
# though it's not the actual way to detect if TTY is available, it's a good enough approximation for our use case
HAS_TTY=true
if [ -z "$TERM" ] || [ "$TERM" = "dumb" ]; then
HAS_TTY=false
fi
# default colors
C_RST=$(echo -e "\e[0m")
C_ERR=$(echo -e "\e[31m")
C_OK=$(echo -e "\e[32m")
C_WARN=$(echo -e "\e[33m")
C_INFO=$(echo -e "\e[35m")
# if TTY is available, use colors
if [ "$HAS_TTY" = true ]; then
C_RST="$(tput sgr0)"
C_ERR="$(tput setaf 1)"
C_OK="$(tput setaf 2)"
C_WARN="$(tput setaf 3)"
C_INFO="$(tput setaf 5)"
fi
msg() { printf '%s%s%s\n' $2 "$1" $C_RST; }
msg_info() { msg "$1" $C_INFO; }
msg_ok() { msg "$1" $C_OK; }
msg_err() { msg "$1" $C_ERR; }
msg_warn() { msg "$1" $C_WARN; }
DOCKER_BUILD_TAG=${DOCKER_BUILD_TAG:-ghcr.io/jetkvm/buildkit:latest}
DOCKER_BUILD_DEBUG=${DOCKER_BUILD_DEBUG:-false}
DOCKER_BUILD_CONTEXT_DIR=${DOCKER_BUILD_CONTEXT_DIR:-$(mktemp -d)}
DOCKER_GO_CACHE_DIR=${DOCKER_GO_CACHE_DIR:-$(pwd)/.cache}
BUILD_IN_DOCKER=${BUILD_IN_DOCKER:-false}
function prepare_docker_build_context() {
msg_info "▶ Preparing docker build context ..."
cp .devcontainer/install-deps.sh \
go.mod \
go.sum \
Dockerfile.build \
"${DOCKER_BUILD_CONTEXT_DIR}"
cat > "${DOCKER_BUILD_CONTEXT_DIR}/entrypoint.sh" << 'EOF'
#!/bin/bash
git config --global --add safe.directory /build
exec $@
EOF
chmod +x "${DOCKER_BUILD_CONTEXT_DIR}/entrypoint.sh"
}
function build_docker_image() {
if [ "$JETKVM_INSIDE_DOCKER" = 1 ]; then
msg_err "Error: already running inside Docker"
exit
fi
BUILD_ARGS="--build-arg BUILDPLATFORM=linux/amd64"
if [ "$DOCKER_BUILD_DEBUG" = true ]; then
BUILD_ARGS="$BUILD_ARGS --progress=plain --no-cache"
fi
msg_info "Checking if Docker is available ..."
if ! command -v docker &> /dev/null; then
msg_err "Error: Docker is not installed"
exit 1
fi
prepare_docker_build_context
pushd "${DOCKER_BUILD_CONTEXT_DIR}" > /dev/null
msg_info "▶ Building docker image ..."
docker build $BUILD_ARGS -t ${DOCKER_BUILD_TAG} -f Dockerfile.build .
popd > /dev/null
}
function do_make() {
DOCKER_BUILD_ARGS="--rm"
if [ "$HAS_TTY" = true ]; then
DOCKER_BUILD_ARGS="$DOCKER_BUILD_ARGS --interactive --tty"
fi
if [ "$BUILD_IN_DOCKER" = true ]; then
msg_info "▶ Building the project in Docker ..."
set -x
docker run \
--env JETKVM_INSIDE_DOCKER=1 \
-v "$(pwd):/build" \
-v "${DOCKER_GO_CACHE_DIR}:/root/.cache/go-build" \
${DOCKER_BUILD_TAG} make "$@"
set +x
else
msg_info "▶ Building the project in host ..."
set -x
make "$@"
set +x
fi
}

28
scripts/ci_helper.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
source ${SCRIPT_PATH}/build_utils.sh
set -e
# check if GITHUB_ENV is set
if [ -z "$GITHUB_ENV" ]; then
echo "GITHUB_ENV is not set"
exit 1
fi
if [ "$1" = "prepare" ]; then
prepare_docker_build_context
echo "DOCKER_BUILD_CONTEXT_DIR=$DOCKER_BUILD_CONTEXT_DIR" >> $GITHUB_ENV
echo "DOCKER_BUILD_TAG=$DOCKER_BUILD_TAG" >> $GITHUB_ENV
elif [ "$1" = "make" ]; then
BUILD_IN_DOCKER=true
# check if GO is available
if ! command -v go &> /dev/null; then
msg_info "Go is not available, will using default cache directory"
else
DOCKER_GO_CACHE_DIR=$(go env GOCACHE)
fi
do_make "${@:2}"
fi

218
scripts/dev_deploy.sh Executable file
View File

@ -0,0 +1,218 @@
#!/usr/bin/env bash
#
# Exit immediately if a command exits with a non-zero status
set -e
# Function to display help message
show_help() {
echo "Usage: $0 [options] -r <remote_ip>"
echo
echo "Required:"
echo " -r, --remote <remote_ip> Remote host IP address"
echo
echo "Optional:"
echo " -u, --user <remote_user> Remote username (default: root)"
echo " --run-go-tests Run go tests"
echo " --run-go-tests-only Run go tests and exit"
echo " --skip-ui-build Skip frontend/UI build"
echo " --skip-native-build Skip native build"
echo " -i, --install Build for release and install the app"
echo " --help Display this help message"
echo
echo "Example:"
echo " $0 -r 192.168.0.17"
echo " $0 -r 192.168.0.17 -u admin"
}
# Default values
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
REMOTE_USER="root"
REMOTE_PATH="/userdata/jetkvm/bin"
SKIP_UI_BUILD=false
SKIP_UI_BUILD_RELEASE=0
SKIP_NATIVE_BUILD=0
RESET_USB_HID_DEVICE=false
LOG_TRACE_SCOPES="${LOG_TRACE_SCOPES:-jetkvm,cloud,websocket,native,jsonrpc}"
RUN_GO_TESTS=false
RUN_GO_TESTS_ONLY=false
INSTALL_APP=false
BUILD_IN_DOCKER=false
DOCKER_BUILD_DEBUG=false
DOCKER_BUILD_TAG=ghcr.io/jetkvm/buildkit:latest
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-r|--remote)
REMOTE_HOST="$2"
shift 2
;;
-u|--user)
REMOTE_USER="$2"
shift 2
;;
--skip-ui-build)
SKIP_UI_BUILD=true
shift
;;
--skip-native-build)
SKIP_NATIVE_BUILD=1
shift
;;
--reset-usb-hid)
RESET_USB_HID_DEVICE=true
shift
;;
--build-in-docker)
BUILD_IN_DOCKER=true
shift
;;
--docker-build-debug)
DOCKER_BUILD_DEBUG=true
shift
;;
--run-go-tests)
RUN_GO_TESTS=true
shift
;;
--run-go-tests-only)
RUN_GO_TESTS_ONLY=true
RUN_GO_TESTS=true
shift
;;
-i|--install)
INSTALL_APP=true
shift
;;
--help)
show_help
exit 0
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
source ${SCRIPT_PATH}/build_utils.sh
# Verify required parameters
if [ -z "$REMOTE_HOST" ]; then
msg_err "Error: Remote IP is a required parameter"
show_help
exit 1
fi
# check if the current CPU architecture is x86_64
if [ "$(uname -m)" != "x86_64" ]; then
msg_warn "Warning: This script is only supported on x86_64 architecture"
BUILD_IN_DOCKER=true
fi
if [ "$BUILD_IN_DOCKER" = true ]; then
build_docker_image
fi
# Build the development version on the host
# When using `make build_release`, the frontend will be built regardless of the `SKIP_UI_BUILD` flag
if [[ "$SKIP_UI_BUILD" = false && "$JETKVM_INSIDE_DOCKER" != 1 ]]; then
msg_info "▶ Building frontend"
make frontend SKIP_UI_BUILD=0
SKIP_UI_BUILD_RELEASE=1
fi
if [ "$RUN_GO_TESTS" = true ]; then
msg_info "▶ Building go tests"
make build_dev_test
msg_info "▶ Copying device-tests.tar.gz to remote host"
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz
msg_info "▶ Running go tests"
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF'
set -e
TMP_DIR=$(mktemp -d)
cd ${TMP_DIR}
tar zxf /tmp/device-tests.tar.gz
./gotestsum --format=testdox \
--jsonfile=/tmp/device-tests.json \
--post-run-command 'sh -c "echo $TESTS_FAILED > /tmp/device-tests.failed"' \
--raw-command -- ./run_all_tests -json
GOTESTSUM_EXIT_CODE=$?
if [ $GOTESTSUM_EXIT_CODE -ne 0 ]; then
echo "❌ Tests failed (exit code: $GOTESTSUM_EXIT_CODE)"
rm -rf ${TMP_DIR} /tmp/device-tests.tar.gz
exit 1
fi
TESTS_FAILED=$(cat /tmp/device-tests.failed)
if [ "$TESTS_FAILED" -ne 0 ]; then
echo "❌ Tests failed $TESTS_FAILED tests failed"
rm -rf ${TMP_DIR} /tmp/device-tests.tar.gz
exit 1
fi
echo "✅ Tests passed"
rm -rf ${TMP_DIR} /tmp/device-tests.tar.gz
EOF
if [ "$RUN_GO_TESTS_ONLY" = true ]; then
msg_info "▶ Go tests completed"
exit 0
fi
fi
if [ "$INSTALL_APP" = true ]
then
msg_info "▶ Building release binary"
do_make build_release SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD} SKIP_UI_BUILD=${SKIP_UI_BUILD_RELEASE}
# Copy the binary to the remote host as if we were the OTA updater.
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
# Reboot the device, the new app will be deployed by the startup process.
ssh "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
else
msg_info "▶ Building development binary"
do_make build_dev SKIP_NATIVE_IF_EXISTS=${SKIP_NATIVE_BUILD} SKIP_UI_BUILD=${SKIP_UI_BUILD_RELEASE}
# Kill any existing instances of the application
ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
# Copy the binary to the remote host
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app
if [ "$RESET_USB_HID_DEVICE" = true ]; then
msg_info "▶ Resetting USB HID device"
msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed"
# Remove the old USB gadget configuration
ssh "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*"
ssh "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC"
fi
# Deploy and run the application on the remote host
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF
set -e
# Set the library path to include the directory where librockit.so is located
export LD_LIBRARY_PATH=/oem/usr/lib:\$LD_LIBRARY_PATH
# Kill any existing instances of the application
killall jetkvm_app || true
killall jetkvm_app_debug || true
# Navigate to the directory where the binary will be stored
cd "${REMOTE_PATH}"
# Make the new binary executable
chmod +x jetkvm_app_debug
# Run the application in the background
PION_LOG_TRACE=${LOG_TRACE_SCOPES} ./jetkvm_app_debug | tee -a /tmp/jetkvm_app_debug.log
EOF
fi
echo "Deployment complete."

View File

@ -3,18 +3,8 @@
# Exit immediately if a command exits with a non-zero status
set -e
C_RST="$(tput sgr0)"
C_ERR="$(tput setaf 1)"
C_OK="$(tput setaf 2)"
C_WARN="$(tput setaf 3)"
C_INFO="$(tput setaf 5)"
msg() { printf '%s%s%s\n' $2 "$1" $C_RST; }
msg_info() { msg "$1" $C_INFO; }
msg_ok() { msg "$1" $C_OK; }
msg_err() { msg "$1" $C_ERR; }
msg_warn() { msg "$1" $C_WARN; }
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
source ${SCRIPT_PATH}/build_utils.sh
# Get the latest release information
msg_info "Getting latest release information ..."

View File

@ -3,7 +3,6 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- These are the fonts used in the app -->
<link
rel="preload"
href="./public/fonts/CircularXXWeb-Medium.woff2"
@ -32,6 +31,13 @@
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="./public/fonts/CircularXXWeb-Bold.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<title>JetKVM</title>
<link rel="stylesheet" href="./public/fonts/fonts.css" />
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />

1033
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "kvm-ui",
"private": true,
"version": "2025.09.03.2100",
"version": "2025.09.23.0000",
"type": "module",
"engines": {
"node": "^22.15.0"
@ -19,7 +19,7 @@
"preview": "vite preview"
},
"dependencies": {
"@headlessui/react": "^2.2.7",
"@headlessui/react": "^2.2.8",
"@headlessui/tailwindcss": "^0.2.2",
"@heroicons/react": "^2.2.0",
"@vitejs/plugin-basic-ssl": "^2.1.0",
@ -33,7 +33,7 @@
"dayjs": "^1.11.18",
"eslint-import-resolver-alias": "^1.1.2",
"focus-trap-react": "^11.0.4",
"framer-motion": "^12.23.12",
"framer-motion": "^12.23.18",
"lodash.throttle": "^4.1.1",
"mini-svg-data-uri": "^1.4.4",
"react": "^19.1.1",
@ -41,45 +41,45 @@
"react-dom": "^19.1.1",
"react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0",
"react-router": "^7.8.2",
"react-simple-keyboard": "^3.8.119",
"react-router": "^7.9.1",
"react-simple-keyboard": "^3.8.122",
"react-use-websocket": "^4.13.0",
"react-xtermjs": "^1.0.10",
"recharts": "^3.1.2",
"recharts": "^3.2.1",
"tailwind-merge": "^3.3.1",
"usehooks-ts": "^3.1.1",
"validator": "^13.15.15",
"zustand": "^4.5.2"
},
"devDependencies": {
"@eslint/compat": "^1.3.2",
"@eslint/compat": "^1.4.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.34.0",
"@eslint/js": "^9.36.0",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/postcss": "^4.1.12",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.12",
"@types/react": "^19.1.12",
"@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/typography": "^0.5.18",
"@tailwindcss/vite": "^4.1.13",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"@types/semver": "^7.7.1",
"@types/validator": "^13.15.3",
"@typescript-eslint/eslint-plugin": "^8.42.0",
"@typescript-eslint/parser": "^8.42.0",
"@vitejs/plugin-react-swc": "^4.0.1",
"@typescript-eslint/eslint-plugin": "^8.44.1",
"@typescript-eslint/parser": "^8.44.1",
"@vitejs/plugin-react-swc": "^4.1.0",
"autoprefixer": "^10.4.21",
"eslint": "^9.34.0",
"eslint": "^9.36.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"eslint-plugin-react-refresh": "^0.4.21",
"globals": "^16.4.0",
"postcss": "^8.5.6",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4.1.12",
"tailwindcss": "^4.1.13",
"typescript": "^5.9.2",
"vite": "^7.1.5",
"vite": "^7.1.7",
"vite-tsconfig-paths": "^5.1.4"
}
}

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/></svg><style>@media (prefers-color-scheme:light){:root{filter:none}}@media (prefers-color-scheme:dark){:root{filter:none}}</style></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56Zm4.403-1.468c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56Zm4.403-1.468c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/></svg><style>@media (prefers-color-scheme:light){:root{filter:none}}@media (prefers-color-scheme:dark){:root{filter:none}}</style></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56Zm4.403-1.468c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56Zm4.403-1.468c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 736 B

View File

@ -1,15 +1 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M2 7C2 5.89543 2.89543 5 4 5H20C21.1046 5 22 5.89543 22 7V19C22 20.1046 21.1046 21 20 21H4C2.89543 21 2 20.1046 2 19V7Z"
fill="transparent" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M20 7H4V19H20V7ZM4 5C2.89543 5 2 5.89543 2 7V19C2 20.1046 2.89543 21 4 21H20C21.1046 21 22 20.1046 22 19V7C22 5.89543 21.1046 5 20 5H4Z"
fill="currentColor" />
<path d="M12 3H24V15H12V3Z" fill="transparent" />
<path
d="M14 6C14 5.44772 14.4477 5 15 5H21C21.5523 5 22 5.44772 22 6V12C22 12.5523 21.5523 13 21 13H15C14.4477 13 14 12.5523 14 12V6Z"
fill="transparent" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M16 7V11H20V7H16ZM15 5C14.4477 5 14 5.44772 14 6V12C14 12.5523 14.4477 13 15 13H21C21.5523 13 22 12.5523 22 12V6C22 5.44772 21.5523 5 21 5H15Z"
fill="currentColor" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="transparent" d="M2 7a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7Z"/><path fill="currentColor" fill-rule="evenodd" d="M20 7H4v12h16V7ZM4 5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2H4Z" clip-rule="evenodd"/><path fill="transparent" d="M12 3h12v12H12V3Z"/><path fill="transparent" d="M14 6a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-6a1 1 0 0 1-1-1V6Z"/><path fill="currentColor" fill-rule="evenodd" d="M16 7v4h4V7h-4Zm-1-2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-6Z" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 991 B

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,12 +1 @@
<svg width="89" height="24" viewBox="0 0 89 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="6" fill="#1D4ED8"/>
<path d="M0 6C0 2.68629 2.68629 0 6 0H18C21.3137 0 24 2.68629 24 6V18C24 21.3137 21.3137 24 18 24H6C2.68629 24 0 21.3137 0 18V6Z" fill="#1D4ED8"/>
<path d="M13.8854 12.0001C13.8854 13.0465 13.037 13.8949 11.9906 13.8949C10.9441 13.8949 10.0957 13.0465 10.0957 12.0001C10.0957 10.9536 10.9441 10.1052 11.9906 10.1052C13.037 10.1052 13.8854 10.9536 13.8854 12.0001Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.58968 9.36279C8.08026 9.54475 8.33045 10.09 8.14849 10.5805C7.9844 11.0229 7.89433 11.5023 7.89433 12.0048C7.89433 14.2684 9.73148 16.1051 11.9998 16.1051C14.268 16.1051 16.1052 14.2684 16.1052 12.0048C16.1052 11.5023 16.0151 11.0229 15.851 10.5805C15.6691 10.09 15.9192 9.54475 16.4098 9.36279C16.9004 9.18083 17.4456 9.43101 17.6276 9.92159C17.8687 10.5717 18 11.274 18 12.0048C18 15.3167 15.3127 17.9999 11.9998 17.9999C8.68682 17.9999 5.99951 15.3167 5.99951 12.0048C5.99951 11.274 6.13081 10.5717 6.37194 9.92159C6.5539 9.43101 7.09911 9.18083 7.58968 9.36279Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9927 7.89481C11.3649 7.89481 10.7726 8.03489 10.243 8.28454C9.7697 8.50765 9.20516 8.30483 8.98206 7.83154C8.75895 7.35825 8.96177 6.79371 9.43506 6.57061C10.2121 6.20432 11.0799 6 11.9927 6C12.9056 6 13.7733 6.20432 14.5504 6.57061C15.0237 6.79371 15.2265 7.35825 15.0034 7.83154C14.7803 8.30483 14.2157 8.50765 13.7424 8.28454C13.2128 8.03489 12.6205 7.89481 11.9927 7.89481Z" fill="white"/>
<path d="M0 6C0 2.68629 2.68629 0 6 0H18C21.3137 0 24 2.68629 24 6V18C24 21.3137 21.3137 24 18 24H6C2.68629 24 0 21.3137 0 18V6Z" fill="#1D4ED8"/>
<path d="M13.8854 12.0001C13.8854 13.0465 13.037 13.8949 11.9906 13.8949C10.9441 13.8949 10.0957 13.0465 10.0957 12.0001C10.0957 10.9536 10.9441 10.1052 11.9906 10.1052C13.037 10.1052 13.8854 10.9536 13.8854 12.0001Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.58968 9.36279C8.08026 9.54475 8.33045 10.09 8.14849 10.5805C7.9844 11.0229 7.89433 11.5023 7.89433 12.0048C7.89433 14.2684 9.73148 16.1051 11.9998 16.1051C14.268 16.1051 16.1052 14.2684 16.1052 12.0048C16.1052 11.5023 16.0151 11.0229 15.851 10.5805C15.6691 10.09 15.9192 9.54475 16.4098 9.36279C16.9004 9.18083 17.4456 9.43101 17.6276 9.92159C17.8687 10.5717 18 11.274 18 12.0048C18 15.3167 15.3127 17.9999 11.9998 17.9999C8.68682 17.9999 5.99951 15.3167 5.99951 12.0048C5.99951 11.274 6.13081 10.5717 6.37194 9.92159C6.5539 9.43101 7.09911 9.18083 7.58968 9.36279Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9927 7.89481C11.3649 7.89481 10.7726 8.03489 10.243 8.28454C9.7697 8.50765 9.20516 8.30483 8.98206 7.83154C8.75895 7.35825 8.96177 6.79371 9.43506 6.57061C10.2121 6.20432 11.0799 6 11.9927 6C12.9056 6 13.7733 6.20432 14.5504 6.57061C15.0237 6.79371 15.2265 7.35825 15.0034 7.83154C14.7803 8.30483 14.2157 8.50765 13.7424 8.28454C13.2128 8.03489 12.6205 7.89481 11.9927 7.89481Z" fill="white"/>
<path d="M28.32 13.84L30.192 13.456V14.512C30.192 15.184 30.3573 15.6747 30.688 15.984C31.0187 16.2827 31.4347 16.432 31.936 16.432C32.448 16.432 32.8533 16.272 33.152 15.952C33.4507 15.6213 33.6 15.168 33.6 14.592V6.656H35.52V14.544C35.52 15.0453 35.4347 15.52 35.264 15.968C35.0933 16.416 34.8533 16.8107 34.544 17.152C34.2347 17.4827 33.8613 17.7493 33.424 17.952C32.9867 18.144 32.496 18.24 31.952 18.24C31.3973 18.24 30.896 18.1547 30.448 17.984C30 17.8027 29.616 17.552 29.296 17.232C28.9867 16.912 28.7467 16.528 28.576 16.08C28.4053 15.632 28.32 15.1307 28.32 14.576V13.84ZM42.9919 13.248C42.9812 13.024 42.9332 12.8107 42.8479 12.608C42.7732 12.3947 42.6559 12.208 42.4959 12.048C42.3359 11.888 42.1385 11.76 41.9039 11.664C41.6692 11.568 41.3919 11.52 41.0719 11.52C40.7839 11.52 40.5225 11.5733 40.2879 11.68C40.0639 11.776 39.8719 11.9093 39.7119 12.08C39.5519 12.24 39.4239 12.4267 39.3279 12.64C39.2319 12.8427 39.1785 13.0453 39.1679 13.248H42.9919ZM44.7679 15.776C44.6612 16.1173 44.5065 16.4373 44.3039 16.736C44.1012 17.0347 43.8505 17.296 43.5519 17.52C43.2532 17.744 42.9119 17.92 42.5279 18.048C42.1439 18.176 41.7172 18.24 41.2479 18.24C40.7145 18.24 40.2079 18.1493 39.7279 17.968C39.2479 17.776 38.8265 17.504 38.4639 17.152C38.1012 16.7893 37.8079 16.352 37.5839 15.84C37.3705 15.3173 37.2639 14.7253 37.2639 14.064C37.2639 13.4453 37.3652 12.8853 37.5679 12.384C37.7812 11.8827 38.0639 11.456 38.4159 11.104C38.7679 10.7413 39.1732 10.464 39.6319 10.272C40.0905 10.0693 40.5652 9.968 41.0559 9.968C41.6532 9.968 42.1865 10.064 42.6559 10.256C43.1359 10.448 43.5359 10.72 43.8559 11.072C44.1865 11.424 44.4372 11.8507 44.6079 12.352C44.7785 12.8427 44.8639 13.3973 44.8639 14.016C44.8639 14.1653 44.8585 14.2987 44.8479 14.416C44.8372 14.5227 44.8265 14.5867 44.8159 14.608H39.1199C39.1305 14.9067 39.1945 15.1787 39.3119 15.424C39.4292 15.6693 39.5839 15.8827 39.7759 16.064C39.9679 16.2453 40.1865 16.3893 40.4319 16.496C40.6879 16.592 40.9599 16.64 41.2479 16.64C41.8132 16.64 42.2452 16.512 42.5439 16.256C42.8532 15.9893 43.0719 15.664 43.1999 15.28L44.7679 15.776ZM48.9895 10.208H50.6055V11.856H48.9895V15.472C48.9895 15.8133 49.0695 16.064 49.2295 16.224C49.3895 16.3733 49.6402 16.448 49.9815 16.448C50.1095 16.448 50.2375 16.4427 50.3655 16.432C50.4935 16.4107 50.5788 16.3947 50.6215 16.384V17.92C50.5682 17.9413 50.4508 17.9733 50.2695 18.016C50.0882 18.0693 49.8268 18.096 49.4855 18.096C48.7602 18.096 48.1895 17.8933 47.7735 17.488C47.3575 17.0827 47.1495 16.512 47.1495 15.776V11.856H45.7095V10.208H46.1095C46.5255 10.208 46.8295 10.0907 47.0215 9.856C47.2135 9.62133 47.3095 9.33333 47.3095 8.992V7.824H48.9895V10.208ZM56.0653 13.088L54.5613 14.736V18H52.6413V6.656H54.5613V12.096L59.4413 6.656H61.9693L57.3773 11.664L62.0173 18H59.6013L56.0653 13.088ZM70.6701 6.656H72.7021L68.4141 18H66.4621L62.2381 6.656H64.3181L67.4861 15.488L70.6701 6.656ZM84.7954 18V9.648L81.2594 18H79.5954L76.0914 9.68V18H74.2194V6.656H76.7794L80.4594 15.312L84.0914 6.656H86.6994V18H84.7954Z" fill="#1D4ED8"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="89" height="24" fill="none"><rect width="24" height="24" fill="#1D4ED8" rx="6"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="m28.32 13.84 1.872-.384v1.056c0 .672.165 1.163.496 1.472.33.299.747.448 1.248.448.512 0 .917-.16 1.216-.48.299-.33.448-.784.448-1.36V6.656h1.92v7.888c0 .501-.085.976-.256 1.424-.17.448-.41.843-.72 1.184a3.57 3.57 0 0 1-1.12.8 3.625 3.625 0 0 1-1.472.288 4.196 4.196 0 0 1-1.504-.256 3.424 3.424 0 0 1-1.152-.752c-.31-.32-.55-.704-.72-1.152a4.196 4.196 0 0 1-.256-1.504v-.736Zm14.672-.592a1.866 1.866 0 0 0-.144-.64 1.469 1.469 0 0 0-.352-.56 1.776 1.776 0 0 0-.592-.384 2.19 2.19 0 0 0-.832-.144c-.288 0-.55.053-.784.16-.224.096-.416.23-.576.4-.16.16-.288.347-.384.56a1.61 1.61 0 0 0-.16.608h3.824Zm1.776 2.528a3.633 3.633 0 0 1-.464.96c-.203.299-.454.56-.752.784-.299.224-.64.4-1.024.528s-.81.192-1.28.192c-.533 0-1.04-.09-1.52-.272a3.846 3.846 0 0 1-1.264-.816 4.23 4.23 0 0 1-.88-1.312c-.214-.523-.32-1.115-.32-1.776 0-.619.101-1.179.304-1.68.213-.501.496-.928.848-1.28.352-.363.757-.64 1.216-.832a3.487 3.487 0 0 1 1.424-.304c.597 0 1.13.096 1.6.288.48.192.88.464 1.2.816.33.352.581.779.752 1.28.17.49.256 1.045.256 1.664 0 .15-.005.283-.016.4a.814.814 0 0 1-.032.192H39.12c.01.299.075.57.192.816.117.245.272.459.464.64s.41.325.656.432c.256.096.528.144.816.144.565 0 .997-.128 1.296-.384.31-.267.528-.592.656-.976l1.568.496Zm4.221-5.568h1.617v1.648h-1.617v3.616c0 .341.08.592.24.752.16.15.411.224.752.224.128 0 .256-.005.384-.016.128-.021.214-.037.256-.048v1.536c-.053.021-.17.053-.352.096-.18.053-.442.08-.783.08-.726 0-1.296-.203-1.712-.608-.416-.405-.624-.976-.624-1.712v-3.92h-1.44v-1.648h.4c.416 0 .72-.117.912-.352a1.32 1.32 0 0 0 .288-.864V7.824h1.68v2.384Zm7.076 2.88-1.504 1.648V18h-1.92V6.656h1.92v5.44l4.88-5.44h2.528l-4.592 5.008L62.017 18h-2.416l-3.536-4.912ZM70.67 6.656h2.032L68.414 18h-1.952L62.238 6.656h2.08l3.168 8.832 3.184-8.832ZM84.795 18V9.648L81.26 18h-1.664l-3.504-8.32V18H74.22V6.656h2.56l3.68 8.656 3.632-8.656H86.7V18h-1.904Z"/></svg>

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,30 +1 @@
<svg viewBox="0 0 89 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="6" fill="#1D4ED8" />
<path
d="M0 6C0 2.68629 2.68629 0 6 0H18C21.3137 0 24 2.68629 24 6V18C24 21.3137 21.3137 24 18 24H6C2.68629 24 0 21.3137 0 18V6Z"
fill="#1D4ED8" />
<path
d="M13.8854 12.0001C13.8854 13.0465 13.037 13.8949 11.9906 13.8949C10.9441 13.8949 10.0957 13.0465 10.0957 12.0001C10.0957 10.9536 10.9441 10.1052 11.9906 10.1052C13.037 10.1052 13.8854 10.9536 13.8854 12.0001Z"
fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M7.58968 9.36279C8.08026 9.54475 8.33045 10.09 8.14849 10.5805C7.9844 11.0229 7.89433 11.5023 7.89433 12.0048C7.89433 14.2684 9.73148 16.1051 11.9998 16.1051C14.268 16.1051 16.1052 14.2684 16.1052 12.0048C16.1052 11.5023 16.0151 11.0229 15.851 10.5805C15.6691 10.09 15.9192 9.54475 16.4098 9.36279C16.9004 9.18083 17.4456 9.43101 17.6276 9.92159C17.8687 10.5717 18 11.274 18 12.0048C18 15.3167 15.3127 17.9999 11.9998 17.9999C8.68682 17.9999 5.99951 15.3167 5.99951 12.0048C5.99951 11.274 6.13081 10.5717 6.37194 9.92159C6.5539 9.43101 7.09911 9.18083 7.58968 9.36279Z"
fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.9927 7.89481C11.3649 7.89481 10.7726 8.03489 10.243 8.28454C9.7697 8.50765 9.20516 8.30483 8.98206 7.83154C8.75895 7.35825 8.96177 6.79371 9.43506 6.57061C10.2121 6.20432 11.0799 6 11.9927 6C12.9056 6 13.7733 6.20432 14.5504 6.57061C15.0237 6.79371 15.2265 7.35825 15.0034 7.83154C14.7803 8.30483 14.2157 8.50765 13.7424 8.28454C13.2128 8.03489 12.6205 7.89481 11.9927 7.89481Z"
fill="white" />
<path
d="M0 6C0 2.68629 2.68629 0 6 0H18C21.3137 0 24 2.68629 24 6V18C24 21.3137 21.3137 24 18 24H6C2.68629 24 0 21.3137 0 18V6Z"
fill="#1D4ED8" />
<path
d="M13.8854 12.0001C13.8854 13.0465 13.037 13.8949 11.9906 13.8949C10.9441 13.8949 10.0957 13.0465 10.0957 12.0001C10.0957 10.9536 10.9441 10.1052 11.9906 10.1052C13.037 10.1052 13.8854 10.9536 13.8854 12.0001Z"
fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M7.58968 9.36279C8.08026 9.54475 8.33045 10.09 8.14849 10.5805C7.9844 11.0229 7.89433 11.5023 7.89433 12.0048C7.89433 14.2684 9.73148 16.1051 11.9998 16.1051C14.268 16.1051 16.1052 14.2684 16.1052 12.0048C16.1052 11.5023 16.0151 11.0229 15.851 10.5805C15.6691 10.09 15.9192 9.54475 16.4098 9.36279C16.9004 9.18083 17.4456 9.43101 17.6276 9.92159C17.8687 10.5717 18 11.274 18 12.0048C18 15.3167 15.3127 17.9999 11.9998 17.9999C8.68682 17.9999 5.99951 15.3167 5.99951 12.0048C5.99951 11.274 6.13081 10.5717 6.37194 9.92159C6.5539 9.43101 7.09911 9.18083 7.58968 9.36279Z"
fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.9927 7.89481C11.3649 7.89481 10.7726 8.03489 10.243 8.28454C9.7697 8.50765 9.20516 8.30483 8.98206 7.83154C8.75895 7.35825 8.96177 6.79371 9.43506 6.57061C10.2121 6.20432 11.0799 6 11.9927 6C12.9056 6 13.7733 6.20432 14.5504 6.57061C15.0237 6.79371 15.2265 7.35825 15.0034 7.83154C14.7803 8.30483 14.2157 8.50765 13.7424 8.28454C13.2128 8.03489 12.6205 7.89481 11.9927 7.89481Z"
fill="white" />
<path
d="M28.32 13.84L30.192 13.456V14.512C30.192 15.184 30.3573 15.6747 30.688 15.984C31.0187 16.2827 31.4347 16.432 31.936 16.432C32.448 16.432 32.8533 16.272 33.152 15.952C33.4507 15.6213 33.6 15.168 33.6 14.592V6.656H35.52V14.544C35.52 15.0453 35.4347 15.52 35.264 15.968C35.0933 16.416 34.8533 16.8107 34.544 17.152C34.2347 17.4827 33.8613 17.7493 33.424 17.952C32.9867 18.144 32.496 18.24 31.952 18.24C31.3973 18.24 30.896 18.1547 30.448 17.984C30 17.8027 29.616 17.552 29.296 17.232C28.9867 16.912 28.7467 16.528 28.576 16.08C28.4053 15.632 28.32 15.1307 28.32 14.576V13.84ZM42.9919 13.248C42.9812 13.024 42.9332 12.8107 42.8479 12.608C42.7732 12.3947 42.6559 12.208 42.4959 12.048C42.3359 11.888 42.1385 11.76 41.9039 11.664C41.6692 11.568 41.3919 11.52 41.0719 11.52C40.7839 11.52 40.5225 11.5733 40.2879 11.68C40.0639 11.776 39.8719 11.9093 39.7119 12.08C39.5519 12.24 39.4239 12.4267 39.3279 12.64C39.2319 12.8427 39.1785 13.0453 39.1679 13.248H42.9919ZM44.7679 15.776C44.6612 16.1173 44.5065 16.4373 44.3039 16.736C44.1012 17.0347 43.8505 17.296 43.5519 17.52C43.2532 17.744 42.9119 17.92 42.5279 18.048C42.1439 18.176 41.7172 18.24 41.2479 18.24C40.7145 18.24 40.2079 18.1493 39.7279 17.968C39.2479 17.776 38.8265 17.504 38.4639 17.152C38.1012 16.7893 37.8079 16.352 37.5839 15.84C37.3705 15.3173 37.2639 14.7253 37.2639 14.064C37.2639 13.4453 37.3652 12.8853 37.5679 12.384C37.7812 11.8827 38.0639 11.456 38.4159 11.104C38.7679 10.7413 39.1732 10.464 39.6319 10.272C40.0905 10.0693 40.5652 9.968 41.0559 9.968C41.6532 9.968 42.1865 10.064 42.6559 10.256C43.1359 10.448 43.5359 10.72 43.8559 11.072C44.1865 11.424 44.4372 11.8507 44.6079 12.352C44.7785 12.8427 44.8639 13.3973 44.8639 14.016C44.8639 14.1653 44.8585 14.2987 44.8479 14.416C44.8372 14.5227 44.8265 14.5867 44.8159 14.608H39.1199C39.1305 14.9067 39.1945 15.1787 39.3119 15.424C39.4292 15.6693 39.5839 15.8827 39.7759 16.064C39.9679 16.2453 40.1865 16.3893 40.4319 16.496C40.6879 16.592 40.9599 16.64 41.2479 16.64C41.8132 16.64 42.2452 16.512 42.5439 16.256C42.8532 15.9893 43.0719 15.664 43.1999 15.28L44.7679 15.776ZM48.9895 10.208H50.6055V11.856H48.9895V15.472C48.9895 15.8133 49.0695 16.064 49.2295 16.224C49.3895 16.3733 49.6402 16.448 49.9815 16.448C50.1095 16.448 50.2375 16.4427 50.3655 16.432C50.4935 16.4107 50.5788 16.3947 50.6215 16.384V17.92C50.5682 17.9413 50.4508 17.9733 50.2695 18.016C50.0882 18.0693 49.8268 18.096 49.4855 18.096C48.7602 18.096 48.1895 17.8933 47.7735 17.488C47.3575 17.0827 47.1495 16.512 47.1495 15.776V11.856H45.7095V10.208H46.1095C46.5255 10.208 46.8295 10.0907 47.0215 9.856C47.2135 9.62133 47.3095 9.33333 47.3095 8.992V7.824H48.9895V10.208ZM56.0653 13.088L54.5613 14.736V18H52.6413V6.656H54.5613V12.096L59.4413 6.656H61.9693L57.3773 11.664L62.0173 18H59.6013L56.0653 13.088ZM70.6701 6.656H72.7021L68.4141 18H66.4621L62.2381 6.656H64.3181L67.4861 15.488L70.6701 6.656ZM84.7954 18V9.648L81.2594 18H79.5954L76.0914 9.68V18H74.2194V6.656H76.7794L80.4594 15.312L84.0914 6.656H86.6994V18H84.7954Z"
fill="white" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 89 24"><rect width="24" height="24" fill="#1D4ED8" rx="6"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#1D4ED8" d="M0 6a6 6 0 0 1 6-6h12a6 6 0 0 1 6 6v12a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6Z"/><path fill="#fff" d="M13.885 12a1.895 1.895 0 1 1-3.79 0 1.895 1.895 0 0 1 3.79 0Z"/><path fill="#fff" fill-rule="evenodd" d="M7.59 9.363c.49.182.74.727.558 1.218A4.103 4.103 0 0 0 12 16.105a4.103 4.103 0 0 0 3.852-5.526.947.947 0 0 1 1.777-.658A5.998 5.998 0 0 1 12 18a5.998 5.998 0 0 1-5.628-8.078.947.947 0 0 1 1.218-.56ZM11.993 7.895c-.628 0-1.22.14-1.75.39a.947.947 0 1 1-.808-1.714A5.985 5.985 0 0 1 11.993 6c.913 0 1.78.204 2.557.57a.947.947 0 1 1-.808 1.715 4.09 4.09 0 0 0-1.75-.39Z" clip-rule="evenodd"/><path fill="#fff" d="m28.32 13.84 1.872-.384v1.056c0 .672.165 1.163.496 1.472.33.299.747.448 1.248.448.512 0 .917-.16 1.216-.48.299-.33.448-.784.448-1.36V6.656h1.92v7.888c0 .501-.085.976-.256 1.424-.17.448-.41.843-.72 1.184a3.57 3.57 0 0 1-1.12.8 3.625 3.625 0 0 1-1.472.288 4.196 4.196 0 0 1-1.504-.256 3.424 3.424 0 0 1-1.152-.752c-.31-.32-.55-.704-.72-1.152a4.196 4.196 0 0 1-.256-1.504v-.736Zm14.672-.592a1.866 1.866 0 0 0-.144-.64 1.469 1.469 0 0 0-.352-.56 1.776 1.776 0 0 0-.592-.384 2.19 2.19 0 0 0-.832-.144c-.288 0-.55.053-.784.16-.224.096-.416.23-.576.4-.16.16-.288.347-.384.56a1.61 1.61 0 0 0-.16.608h3.824Zm1.776 2.528a3.633 3.633 0 0 1-.464.96c-.203.299-.454.56-.752.784-.299.224-.64.4-1.024.528s-.81.192-1.28.192c-.533 0-1.04-.09-1.52-.272a3.846 3.846 0 0 1-1.264-.816 4.23 4.23 0 0 1-.88-1.312c-.214-.523-.32-1.115-.32-1.776 0-.619.101-1.179.304-1.68.213-.501.496-.928.848-1.28.352-.363.757-.64 1.216-.832a3.487 3.487 0 0 1 1.424-.304c.597 0 1.13.096 1.6.288.48.192.88.464 1.2.816.33.352.581.779.752 1.28.17.49.256 1.045.256 1.664 0 .15-.005.283-.016.4a.814.814 0 0 1-.032.192H39.12c.01.299.075.57.192.816.117.245.272.459.464.64s.41.325.656.432c.256.096.528.144.816.144.565 0 .997-.128 1.296-.384.31-.267.528-.592.656-.976l1.568.496Zm4.221-5.568h1.617v1.648h-1.617v3.616c0 .341.08.592.24.752.16.15.411.224.752.224.128 0 .256-.005.384-.016.128-.021.214-.037.256-.048v1.536c-.053.021-.17.053-.352.096-.18.053-.442.08-.783.08-.726 0-1.296-.203-1.712-.608-.416-.405-.624-.976-.624-1.712v-3.92h-1.44v-1.648h.4c.416 0 .72-.117.912-.352a1.32 1.32 0 0 0 .288-.864V7.824h1.68v2.384Zm7.076 2.88-1.504 1.648V18h-1.92V6.656h1.92v5.44l4.88-5.44h2.528l-4.592 5.008L62.017 18h-2.416l-3.536-4.912ZM70.67 6.656h2.032L68.414 18h-1.952L62.238 6.656h2.08l3.168 8.832 3.184-8.832ZM84.795 18V9.648L81.26 18h-1.664l-3.504-8.32V18H74.22V6.656h2.56l3.68 8.656 3.632-8.656H86.7V18h-1.904Z"/></svg>

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,11 +1 @@
<svg viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3161_210)">
<path d="M11.5 1V7.60833H17.3333C17.3333 4.20833 14.7917 1.40833 11.5 1ZM4 12.6083C4 16.2917 6.98333 19.275 10.6667 19.275C14.35 19.275 17.3333 16.2917 17.3333 12.6083V9.275H4V12.6083ZM9.83333 1C6.54167 1.40833 4 4.20833 4 7.60833H9.83333V1Z"
fill="black"/>
</g>
<defs>
<clipPath id="clip0_3161_210">
<rect width="20" height="20" fill="white" transform="translate(0.5)"/>
</clipPath>
</defs>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 21 20"><g clip-path="url(#a)"><path fill="#000" d="M11.5 1v6.608h5.833c0-3.4-2.541-6.2-5.833-6.608ZM4 12.608a6.665 6.665 0 0 0 6.667 6.667 6.665 6.665 0 0 0 6.666-6.667V9.275H4v3.333ZM9.833 1A6.657 6.657 0 0 0 4 7.608h5.833V1Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M.5 0h20v20H.5z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,11 +1 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3161_200)">
<path d="M7.49967 9.36667V6.25C7.49967 5.69747 7.71917 5.16756 8.10987 4.77686C8.50057 4.38616 9.03047 4.16667 9.58301 4.16667C10.1355 4.16667 10.6654 4.38616 11.0561 4.77686C11.4468 5.16756 11.6663 5.69747 11.6663 6.25V9.36667C12.6747 8.69167 13.333 7.55 13.333 6.25C13.333 4.175 11.658 2.5 9.58301 2.5C7.50801 2.5 5.83301 4.175 5.83301 6.25C5.83301 7.55 6.49134 8.69167 7.49967 9.36667ZM15.6997 13.225L11.9163 11.3417C11.7747 11.2833 11.6247 11.25 11.4663 11.25H10.833V6.25C10.833 5.55833 10.2747 5 9.58301 5C8.89134 5 8.33301 5.55833 8.33301 6.25V15.2C5.33301 14.5667 5.38301 14.575 5.27467 14.575C5.01634 14.575 4.78301 14.6833 4.61634 14.85L3.95801 15.5167L8.07467 19.6333C8.29967 19.8583 8.61634 20 8.95801 20H14.6163C15.2413 20 15.7247 19.5417 15.8163 18.9333L16.4413 14.5417C16.4497 14.4833 16.458 14.425 16.458 14.375C16.458 13.8583 16.1413 13.4083 15.6997 13.225Z"
fill="black"/>
</g>
<defs>
<clipPath id="clip0_3161_200">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><g clip-path="url(#a)"><path fill="#000" d="M7.5 9.367V6.25a2.083 2.083 0 0 1 4.166 0v3.117a3.738 3.738 0 0 0 1.667-3.117 3.745 3.745 0 0 0-3.75-3.75 3.745 3.745 0 0 0-3.75 3.75c0 1.3.658 2.442 1.667 3.117Zm8.2 3.858-3.784-1.883a1.172 1.172 0 0 0-.45-.092h-.633v-5c0-.692-.558-1.25-1.25-1.25s-1.25.558-1.25 1.25v8.95c-3-.633-2.95-.625-3.058-.625a.932.932 0 0 0-.659.275l-.658.667 4.117 4.116c.225.225.541.367.883.367h5.658c.625 0 1.109-.458 1.2-1.067l.625-4.391c.009-.059.017-.117.017-.167 0-.517-.317-.967-.758-1.15Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h20v20H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

After

Width:  |  Height:  |  Size: 857 B

View File

@ -207,8 +207,8 @@ export function MacroStepCard({
)}
<div className="relative w-full">
<Combobox
onChange={(value: { value: string; label: string }) => {
onKeySelect(value);
onChange={(value) => {
onKeySelect({ value: value as string | null });
onKeyQueryChange('');
}}
displayValue={() => keyQuery}

View File

@ -17,6 +17,7 @@ import { TextAreaWithLabel } from "@components/TextArea";
// uint32 max value / 4
const pasteMaxLength = 1073741824;
const defaultDelay = 20;
export default function PasteModal() {
const TextAreaRef = useRef<HTMLTextAreaElement>(null);
@ -27,10 +28,10 @@ export default function PasteModal() {
const { executeMacro, cancelExecuteMacro } = useKeyboard();
const [invalidChars, setInvalidChars] = useState<string[]>([]);
const [delayValue, setDelayValue] = useState(100);
const [delayValue, setDelayValue] = useState(defaultDelay);
const delay = useMemo(() => {
if (delayValue < 50 || delayValue > 65534) {
return 100;
if (delayValue < 0 || delayValue > 65534) {
return defaultDelay;
}
return delayValue;
}, [delayValue]);
@ -40,7 +41,7 @@ export default function PasteModal() {
const delayClassName = useMemo(() => debugMode ? "" : "hidden", [debugMode]);
const { setKeyboardLayout } = useSettingsStore();
const { selectedKeyboard } = useKeyboardLayout();
const { selectedKeyboard } = useKeyboardLayout();
useEffect(() => {
send("getKeyboardLayout", {}, (resp: JsonRpcResponse) => {
@ -136,7 +137,8 @@ export default function PasteModal() {
<div
className="w-full"
onKeyUp={e => e.stopPropagation()}
onKeyDown={e => e.stopPropagation()} onKeyDownCapture={e => e.stopPropagation()}
onKeyDown={e => e.stopPropagation()}
onKeyDownCapture={e => e.stopPropagation()}
onKeyUpCapture={e => e.stopPropagation()}
>
<TextAreaWithLabel

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { Button } from "@/components/Button";
import { TextAreaWithLabel } from "@/components/TextArea";
@ -52,7 +52,7 @@ export default function SettingsVideoRoute() {
const [customEdidValue, setCustomEdidValue] = useState<string | null>(null);
const [edid, setEdid] = useState<string | null>(null);
const [edidLoading, setEdidLoading] = useState(false);
const { debugMode } = useSettingsStore();
// Video enhancement settings from store
const {
videoSaturation,
@ -132,6 +132,26 @@ export default function SettingsVideoRoute() {
});
};
const [debugInfo, setDebugInfo] = useState<string | null>(null);
const [debugInfoLoading, setDebugInfoLoading] = useState(false);
const getDebugInfo = useCallback(() => {
setDebugInfoLoading(true);
send("getVideoLogStatus", {}, (resp: JsonRpcResponse) => {
if ("error" in resp) {
notifications.error(`Failed to get debug info: ${resp.error.data || "Unknown error"}`);
setDebugInfoLoading(false);
return;
}
const data = resp.result as string;
setDebugInfo(data
.split("\n")
.map(line => line.trim().replace(/^\[\s*\d+\.\d+\]\s*/, ""))
.join("\n")
);
setDebugInfoLoading(false);
});
}, [send]);
return (
<div className="space-y-3">
<div className="space-y-4">
@ -279,6 +299,30 @@ export default function SettingsVideoRoute() {
)}
</Fieldset>
</div>
{debugMode && (
<div className="space-y-4">
<SettingsItem
title="Debugging Info"
description="Debugging information for video"
>
<Button size="SM" theme="primary" text="Get Debugging Info"
loading={debugInfoLoading}
disabled={debugInfoLoading}
onClick={() => {
getDebugInfo();
}} />
</SettingsItem>
{debugInfo && (
<div className="font-mono bg-gray-100 dark:bg-gray-800 p-2 rounded-md text-xs max-h-64 overflow-y-auto">
<pre className="whitespace-pre-wrap">
{debugInfo}
</pre>
</div>
)}
</div>
)}
</div>
</div>
</div>

View File

@ -1,13 +1,8 @@
package kvm
import "github.com/jetkvm/kvm/internal/native"
// max frame size for 1080p video, specified in mpp venc setting
const maxFrameSize = 1920 * 1080 / 2
func writeCtrlAction(action string) error {
return nil
}
import (
"github.com/jetkvm/kvm/internal/native"
)
var lastVideoState native.VideoState
@ -15,19 +10,9 @@ func triggerVideoStateUpdate() {
go func() {
writeJSONRPCEvent("videoInputState", lastVideoState, currentSession)
}()
}
// func HandleVideoStateMessage(event CtrlResponse) {
// videoState := VideoInputState{}
// err := json.Unmarshal(event.Data, &videoState)
// if err != nil {
// logger.Warn().Err(err).Msg("Error parsing video state json")
// return
// }
// lastVideoState = videoState
// triggerVideoStateUpdate()
// requestDisplayUpdate(true)
// }
nativeLogger.Info().Interface("state", lastVideoState).Msg("video state updated")
}
func rpcGetVideoState() (native.VideoState, error) {
return lastVideoState, nil