mirror of https://github.com/jetkvm/kvm.git
Compare commits
3 Commits
83b7483cfd
...
938a94fcba
| Author | SHA1 | Date |
|---|---|---|
|
|
938a94fcba | |
|
|
01891e8aee | |
|
|
c6d4fffca4 |
|
|
@ -491,9 +491,9 @@ If you enable the [Sherlock](https://inlang.com/m/r7kp499g/app-inlang-ideExtensi
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Save the _en.json_ file and execute `npm run i18n` to resort the language files, validate the translations, and create the m-functions
|
4. Save the _en.json_ file and execute `npm run i18n` to resort the language files, validate the translations, and create the m-functions
|
||||||
5. Edit the _.tsx_ file and replace the string with the calls to the new m-function which will be the key-string you chose in snake-case. For example `This is a test` in _thing edit page_ turns into `m.thing_edit_this_is_a_test()`
|
5. Edit the _.tsx_ file and replace the string with the new m-function which will be the key-string you chose in snake-case. For example `This is a test` in _thing edit page_ turns into `m.thing_edit_this_is_a_test()`
|
||||||
- **Note** if the string has a replacement token, supply that to the m-function, for example for the literal `I will call you {name}`, use `m.profile_i_will_call_you({ name: edit.value })`
|
- **Note** if the string has a replacement token, supply that to the m-function, for example `m.profile_your_name({ name: edit.value })`
|
||||||
6. When all your strings are extracted, run `npm run i18n:machine-translate` to get a first-stab at the translations for the other supported languages. Make sure you use an LLM (you can use [aifiesta](https://chat.aifiesta.ai/chat/) to use multiple LLMs) or a [translator](https://translate.google.com) of some form to back-translate each **new** machine-generation in each _language_ to ensure those terms translate reasonably.
|
6. When all your strings are extracted, run `npm run i18n:machine-translate` to get a first-stab at the translations for the other supported languages. Make sure you use an LLM or [translator](https://translate.google.com) of some form to back-translate each **new** machine-generation in each _langauge_ to ensure those terms translate reasonably.
|
||||||
|
|
||||||
### Adding a new language
|
### Adding a new language
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,6 @@ type Config struct {
|
||||||
NetworkConfig *types.NetworkConfig `json:"network_config"`
|
NetworkConfig *types.NetworkConfig `json:"network_config"`
|
||||||
DefaultLogLevel string `json:"default_log_level"`
|
DefaultLogLevel string `json:"default_log_level"`
|
||||||
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
||||||
VideoQualityFactor float64 `json:"video_quality_factor"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetDisplayRotation() uint16 {
|
func (c *Config) GetDisplayRotation() uint16 {
|
||||||
|
|
|
||||||
|
|
@ -405,8 +405,8 @@ char *jetkvm_video_log_status() {
|
||||||
return (char *)videoc_log_status();
|
return (char *)videoc_log_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
int jetkvm_video_init(float factor) {
|
int jetkvm_video_init() {
|
||||||
return video_init(factor);
|
return video_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void jetkvm_video_shutdown() {
|
void jetkvm_video_shutdown() {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ const char *jetkvm_ui_get_lvgl_version();
|
||||||
|
|
||||||
const char *jetkvm_ui_event_code_to_name(int code);
|
const char *jetkvm_ui_event_code_to_name(int code);
|
||||||
|
|
||||||
int jetkvm_video_init(float quality_factor);
|
int jetkvm_video_init();
|
||||||
void jetkvm_video_shutdown();
|
void jetkvm_video_shutdown();
|
||||||
void jetkvm_video_start();
|
void jetkvm_video_start();
|
||||||
void jetkvm_video_stop();
|
void jetkvm_video_stop();
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@
|
||||||
|
|
||||||
#define VIDEO_DEV "/dev/video0"
|
#define VIDEO_DEV "/dev/video0"
|
||||||
#define SUB_DEV "/dev/v4l-subdev2"
|
#define SUB_DEV "/dev/v4l-subdev2"
|
||||||
#define SLEEP_MODE_FILE "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
|
|
||||||
|
|
||||||
#define RK_ALIGN(x, a) (((x) + (a)-1) & ~((a)-1))
|
#define RK_ALIGN(x, a) (((x) + (a)-1) & ~((a)-1))
|
||||||
#define RK_ALIGN_2(x) RK_ALIGN(x, 2)
|
#define RK_ALIGN_2(x) RK_ALIGN(x, 2)
|
||||||
|
|
@ -40,7 +39,6 @@ int sub_dev_fd = -1;
|
||||||
#define VENC_CHANNEL 0
|
#define VENC_CHANNEL 0
|
||||||
MB_POOL memPool = MB_INVALID_POOLID;
|
MB_POOL memPool = MB_INVALID_POOLID;
|
||||||
|
|
||||||
bool sleep_mode_available = false;
|
|
||||||
bool should_exit = false;
|
bool should_exit = false;
|
||||||
float quality_factor = 1.0f;
|
float quality_factor = 1.0f;
|
||||||
|
|
||||||
|
|
@ -53,45 +51,6 @@ RK_U64 get_us()
|
||||||
return (RK_U64)time.tv_sec * 1000000 + (RK_U64)time.tv_nsec / 1000; /* microseconds */
|
return (RK_U64)time.tv_sec * 1000000 + (RK_U64)time.tv_nsec / 1000; /* microseconds */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ensure_sleep_mode_disabled()
|
|
||||||
{
|
|
||||||
if (!sleep_mode_available)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd = open(SLEEP_MODE_FILE, O_RDWR);
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
log_error("Failed to open sleep mode file: %s", strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lseek(fd, 0, SEEK_SET);
|
|
||||||
char buffer[1];
|
|
||||||
read(fd, buffer, 1);
|
|
||||||
if (buffer[0] == '0') {
|
|
||||||
close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log_warn("HDMI sleep mode is not disabled, disabling it");
|
|
||||||
lseek(fd, 0, SEEK_SET);
|
|
||||||
write(fd, "0", 1);
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
usleep(1000); // give some time to the system to disable the sleep mode
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void detect_sleep_mode()
|
|
||||||
{
|
|
||||||
if (access(SLEEP_MODE_FILE, F_OK) != 0) {
|
|
||||||
sleep_mode_available = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sleep_mode_available = true;
|
|
||||||
ensure_sleep_mode_disabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculate_bitrate(float bitrate_factor, int width, int height)
|
double calculate_bitrate(float bitrate_factor, int width, int height)
|
||||||
{
|
{
|
||||||
const int32_t base_bitrate_high = 2000;
|
const int32_t base_bitrate_high = 2000;
|
||||||
|
|
@ -231,15 +190,8 @@ static int32_t buf_init()
|
||||||
|
|
||||||
pthread_t *format_thread = NULL;
|
pthread_t *format_thread = NULL;
|
||||||
|
|
||||||
int video_init(float factor)
|
int video_init()
|
||||||
{
|
{
|
||||||
detect_sleep_mode();
|
|
||||||
|
|
||||||
if (factor < 0 || factor > 1) {
|
|
||||||
factor = 1.0f;
|
|
||||||
}
|
|
||||||
quality_factor = factor;
|
|
||||||
|
|
||||||
if (RK_MPI_SYS_Init() != RK_SUCCESS)
|
if (RK_MPI_SYS_Init() != RK_SUCCESS)
|
||||||
{
|
{
|
||||||
log_error("RK_MPI_SYS_Init failed");
|
log_error("RK_MPI_SYS_Init failed");
|
||||||
|
|
@ -349,29 +301,11 @@ static void *venc_read_stream(void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t detected_width, detected_height;
|
uint32_t detected_width, detected_height;
|
||||||
bool detected_signal = false, streaming_flag = false, streaming_stopped = true;
|
bool detected_signal = false, streaming_flag = false;
|
||||||
|
|
||||||
pthread_t *streaming_thread = NULL;
|
pthread_t *streaming_thread = NULL;
|
||||||
pthread_mutex_t streaming_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t streaming_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
bool get_streaming_flag()
|
|
||||||
{
|
|
||||||
log_info("getting streaming flag");
|
|
||||||
pthread_mutex_lock(&streaming_mutex);
|
|
||||||
bool flag = streaming_flag;
|
|
||||||
pthread_mutex_unlock(&streaming_mutex);
|
|
||||||
return flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_streaming_flag(bool flag)
|
|
||||||
{
|
|
||||||
log_info("setting streaming flag to %d", flag);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&streaming_mutex);
|
|
||||||
streaming_flag = flag;
|
|
||||||
pthread_mutex_unlock(&streaming_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_buffer_to_file(const uint8_t *buffer, size_t length, const char *filename)
|
void write_buffer_to_file(const uint8_t *buffer, size_t length, const char *filename)
|
||||||
{
|
{
|
||||||
FILE *file = fopen(filename, "wb");
|
FILE *file = fopen(filename, "wb");
|
||||||
|
|
@ -385,8 +319,6 @@ void *run_video_stream(void *arg)
|
||||||
|
|
||||||
log_info("running video stream");
|
log_info("running video stream");
|
||||||
|
|
||||||
streaming_stopped = false;
|
|
||||||
|
|
||||||
while (streaming_flag)
|
while (streaming_flag)
|
||||||
{
|
{
|
||||||
if (detected_signal == false)
|
if (detected_signal == false)
|
||||||
|
|
@ -469,7 +401,7 @@ void *run_video_stream(void *arg)
|
||||||
{
|
{
|
||||||
log_error("get mb blk failed!");
|
log_error("get mb blk failed!");
|
||||||
close(video_dev_fd);
|
close(video_dev_fd);
|
||||||
return (void *)errno;
|
return ;
|
||||||
}
|
}
|
||||||
log_info("Got memory block for buffer %d", i);
|
log_info("Got memory block for buffer %d", i);
|
||||||
|
|
||||||
|
|
@ -606,18 +538,6 @@ void *run_video_stream(void *arg)
|
||||||
log_error("VIDIOC_STREAMOFF failed: %s", strerror(errno));
|
log_error("VIDIOC_STREAMOFF failed: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly free V4L2 buffer queue
|
|
||||||
struct v4l2_requestbuffers req_free;
|
|
||||||
memset(&req_free, 0, sizeof(req_free));
|
|
||||||
req_free.count = 0;
|
|
||||||
req_free.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
|
||||||
req_free.memory = V4L2_MEMORY_DMABUF;
|
|
||||||
|
|
||||||
if (ioctl(video_dev_fd, VIDIOC_REQBUFS, &req_free) < 0)
|
|
||||||
{
|
|
||||||
log_error("Failed to free V4L2 buffers: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
venc_stop();
|
venc_stop();
|
||||||
|
|
||||||
for (int i = 0; i < input_buffer_count; i++)
|
for (int i = 0; i < input_buffer_count; i++)
|
||||||
|
|
@ -633,9 +553,6 @@ void *run_video_stream(void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("video stream thread exiting");
|
log_info("video stream thread exiting");
|
||||||
|
|
||||||
streaming_stopped = true;
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -665,75 +582,56 @@ void video_shutdown()
|
||||||
log_info("Destroyed streaming mutex");
|
log_info("Destroyed streaming mutex");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void video_start_streaming()
|
void video_start_streaming()
|
||||||
{
|
{
|
||||||
log_info("starting video streaming");
|
pthread_mutex_lock(&streaming_mutex);
|
||||||
if (streaming_thread != NULL)
|
if (streaming_thread != NULL)
|
||||||
{
|
{
|
||||||
if (streaming_stopped == true) {
|
|
||||||
log_error("video streaming already stopped but streaming_thread is not NULL");
|
|
||||||
assert(streaming_stopped == true);
|
|
||||||
}
|
|
||||||
log_warn("video streaming already started");
|
log_warn("video streaming already started");
|
||||||
return;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_t *new_thread = malloc(sizeof(pthread_t));
|
pthread_t *new_thread = malloc(sizeof(pthread_t));
|
||||||
if (new_thread == NULL)
|
if (new_thread == NULL)
|
||||||
{
|
{
|
||||||
log_error("Failed to allocate memory for streaming thread");
|
log_error("Failed to allocate memory for streaming thread");
|
||||||
return;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_streaming_flag(true);
|
streaming_flag = true;
|
||||||
int result = pthread_create(new_thread, NULL, run_video_stream, NULL);
|
int result = pthread_create(new_thread, NULL, run_video_stream, NULL);
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
log_error("Failed to create streaming thread: %s", strerror(result));
|
log_error("Failed to create streaming thread: %s", strerror(result));
|
||||||
set_streaming_flag(false);
|
streaming_flag = false;
|
||||||
free(new_thread);
|
free(new_thread);
|
||||||
return;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only set streaming_thread after successful creation
|
// Only set streaming_thread after successful creation, and before unlocking the mutex
|
||||||
streaming_thread = new_thread;
|
streaming_thread = new_thread;
|
||||||
|
cleanup:
|
||||||
|
pthread_mutex_unlock(&streaming_mutex);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void video_stop_streaming()
|
void video_stop_streaming()
|
||||||
{
|
{
|
||||||
if (streaming_thread == NULL) {
|
pthread_mutex_lock(&streaming_mutex);
|
||||||
log_info("video streaming already stopped");
|
if (streaming_thread != NULL)
|
||||||
return;
|
{
|
||||||
}
|
streaming_flag = false;
|
||||||
|
|
||||||
log_info("stopping video streaming");
|
log_info("stopping video streaming");
|
||||||
set_streaming_flag(false);
|
// wait 100ms for the thread to exit
|
||||||
|
usleep(1000000);
|
||||||
log_info("waiting for video streaming thread to exit");
|
log_info("waiting for video streaming thread to exit");
|
||||||
int attempts = 0;
|
|
||||||
while (!streaming_stopped && attempts < 30) {
|
|
||||||
usleep(100000); // 100ms
|
|
||||||
attempts++;
|
|
||||||
}
|
|
||||||
if (!streaming_stopped) {
|
|
||||||
log_error("video streaming thread did not exit after 30s");
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_join(*streaming_thread, NULL);
|
pthread_join(*streaming_thread, NULL);
|
||||||
free(streaming_thread);
|
free(streaming_thread);
|
||||||
streaming_thread = NULL;
|
streaming_thread = NULL;
|
||||||
|
|
||||||
log_info("video streaming stopped");
|
log_info("video streaming stopped");
|
||||||
}
|
|
||||||
|
|
||||||
void video_restart_streaming()
|
|
||||||
{
|
|
||||||
if (get_streaming_flag() == true)
|
|
||||||
{
|
|
||||||
log_info("restarting video streaming");
|
|
||||||
video_stop_streaming();
|
|
||||||
}
|
}
|
||||||
video_start_streaming();
|
pthread_mutex_unlock(&streaming_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *run_detect_format(void *arg)
|
void *run_detect_format(void *arg)
|
||||||
|
|
@ -752,8 +650,6 @@ void *run_detect_format(void *arg)
|
||||||
|
|
||||||
while (!should_exit)
|
while (!should_exit)
|
||||||
{
|
{
|
||||||
ensure_sleep_mode_disabled();
|
|
||||||
|
|
||||||
memset(&dv_timings, 0, sizeof(dv_timings));
|
memset(&dv_timings, 0, sizeof(dv_timings));
|
||||||
if (ioctl(sub_dev_fd, VIDIOC_QUERY_DV_TIMINGS, &dv_timings) != 0)
|
if (ioctl(sub_dev_fd, VIDIOC_QUERY_DV_TIMINGS, &dv_timings) != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -793,17 +689,21 @@ void *run_detect_format(void *arg)
|
||||||
(dv_timings.bt.width + dv_timings.bt.hfrontporch + dv_timings.bt.hsync +
|
(dv_timings.bt.width + dv_timings.bt.hfrontporch + dv_timings.bt.hsync +
|
||||||
dv_timings.bt.hbackporch));
|
dv_timings.bt.hbackporch));
|
||||||
log_info("Frames per second: %.2f fps", frames_per_second);
|
log_info("Frames per second: %.2f fps", frames_per_second);
|
||||||
|
|
||||||
bool should_restart = dv_timings.bt.width != detected_width || dv_timings.bt.height != detected_height || !detected_signal;
|
|
||||||
|
|
||||||
detected_width = dv_timings.bt.width;
|
detected_width = dv_timings.bt.width;
|
||||||
detected_height = dv_timings.bt.height;
|
detected_height = dv_timings.bt.height;
|
||||||
detected_signal = true;
|
detected_signal = true;
|
||||||
video_report_format(true, NULL, detected_width, detected_height, frames_per_second);
|
video_report_format(true, NULL, detected_width, detected_height, frames_per_second);
|
||||||
|
pthread_mutex_lock(&streaming_mutex);
|
||||||
if (should_restart) {
|
if (streaming_flag == true)
|
||||||
log_info("restarting video streaming due to format change");
|
{
|
||||||
video_restart_streaming();
|
pthread_mutex_unlock(&streaming_mutex);
|
||||||
|
log_info("restarting on going video streaming");
|
||||||
|
video_stop_streaming();
|
||||||
|
video_start_streaming();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&streaming_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -831,7 +731,19 @@ void video_set_quality_factor(float factor)
|
||||||
quality_factor = factor;
|
quality_factor = factor;
|
||||||
|
|
||||||
// TODO: update venc bitrate without stopping streaming
|
// TODO: update venc bitrate without stopping streaming
|
||||||
video_restart_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() {
|
float video_get_quality_factor() {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
* @return int 0 on success, -1 on failure
|
* @return int 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int video_init(float quality_factor);
|
int video_init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Shutdown the video subsystem
|
* @brief Shutdown the video subsystem
|
||||||
|
|
|
||||||
|
|
@ -129,13 +129,11 @@ func uiTick() {
|
||||||
C.jetkvm_ui_tick()
|
C.jetkvm_ui_tick()
|
||||||
}
|
}
|
||||||
|
|
||||||
func videoInit(factor float64) error {
|
func videoInit() error {
|
||||||
cgoLock.Lock()
|
cgoLock.Lock()
|
||||||
defer cgoLock.Unlock()
|
defer cgoLock.Unlock()
|
||||||
|
|
||||||
factorC := C.float(factor)
|
ret := C.jetkvm_video_init()
|
||||||
|
|
||||||
ret := C.jetkvm_video_init(factorC)
|
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return fmt.Errorf("failed to initialize video: %d", ret)
|
return fmt.Errorf("failed to initialize video: %d", ret)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ type Native struct {
|
||||||
systemVersion *semver.Version
|
systemVersion *semver.Version
|
||||||
appVersion *semver.Version
|
appVersion *semver.Version
|
||||||
displayRotation uint16
|
displayRotation uint16
|
||||||
defaultQualityFactor float64
|
|
||||||
onVideoStateChange func(state VideoState)
|
onVideoStateChange func(state VideoState)
|
||||||
onVideoFrameReceived func(frame []byte, duration time.Duration)
|
onVideoFrameReceived func(frame []byte, duration time.Duration)
|
||||||
onIndevEvent func(event string)
|
onIndevEvent func(event string)
|
||||||
|
|
@ -23,14 +22,12 @@ type Native struct {
|
||||||
sleepModeSupported bool
|
sleepModeSupported bool
|
||||||
videoLock sync.Mutex
|
videoLock sync.Mutex
|
||||||
screenLock sync.Mutex
|
screenLock sync.Mutex
|
||||||
extraLock sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NativeOptions struct {
|
type NativeOptions struct {
|
||||||
SystemVersion *semver.Version
|
SystemVersion *semver.Version
|
||||||
AppVersion *semver.Version
|
AppVersion *semver.Version
|
||||||
DisplayRotation uint16
|
DisplayRotation uint16
|
||||||
DefaultQualityFactor float64
|
|
||||||
OnVideoStateChange func(state VideoState)
|
OnVideoStateChange func(state VideoState)
|
||||||
OnVideoFrameReceived func(frame []byte, duration time.Duration)
|
OnVideoFrameReceived func(frame []byte, duration time.Duration)
|
||||||
OnIndevEvent func(event string)
|
OnIndevEvent func(event string)
|
||||||
|
|
@ -68,11 +65,6 @@ func NewNative(opts NativeOptions) *Native {
|
||||||
|
|
||||||
sleepModeSupported := isSleepModeSupported()
|
sleepModeSupported := isSleepModeSupported()
|
||||||
|
|
||||||
defaultQualityFactor := opts.DefaultQualityFactor
|
|
||||||
if defaultQualityFactor < 0 || defaultQualityFactor > 1 {
|
|
||||||
defaultQualityFactor = 1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Native{
|
return &Native{
|
||||||
ready: make(chan struct{}),
|
ready: make(chan struct{}),
|
||||||
l: nativeLogger,
|
l: nativeLogger,
|
||||||
|
|
@ -80,7 +72,6 @@ func NewNative(opts NativeOptions) *Native {
|
||||||
systemVersion: opts.SystemVersion,
|
systemVersion: opts.SystemVersion,
|
||||||
appVersion: opts.AppVersion,
|
appVersion: opts.AppVersion,
|
||||||
displayRotation: opts.DisplayRotation,
|
displayRotation: opts.DisplayRotation,
|
||||||
defaultQualityFactor: defaultQualityFactor,
|
|
||||||
onVideoStateChange: onVideoStateChange,
|
onVideoStateChange: onVideoStateChange,
|
||||||
onVideoFrameReceived: onVideoFrameReceived,
|
onVideoFrameReceived: onVideoFrameReceived,
|
||||||
onIndevEvent: onIndevEvent,
|
onIndevEvent: onIndevEvent,
|
||||||
|
|
@ -106,7 +97,7 @@ func (n *Native) Start() {
|
||||||
n.initUI()
|
n.initUI()
|
||||||
go n.tickUI()
|
go n.tickUI()
|
||||||
|
|
||||||
if err := videoInit(n.defaultQualityFactor); err != nil {
|
if err := videoInit(); err != nil {
|
||||||
n.l.Error().Err(err).Msg("failed to initialize video")
|
n.l.Error().Err(err).Msg("failed to initialize video")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,11 @@
|
||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
|
const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
|
||||||
|
|
||||||
// DefaultEDID is the default EDID for the video stream.
|
|
||||||
const DefaultEDID = "00ffffffffffff0052620188008888881c150103800000780a0dc9a05747982712484c00000001010101010101010101010101010101023a801871382d40582c4500c48e2100001e011d007251d01e206e285500c48e2100001e000000fc00543734392d6648443732300a20000000fd00147801ff1d000a202020202020017b"
|
|
||||||
|
|
||||||
var extraLockTimeout = 5 * time.Second
|
|
||||||
|
|
||||||
// VideoState is the state of the video stream.
|
// VideoState is the state of the video stream.
|
||||||
type VideoState struct {
|
type VideoState struct {
|
||||||
Ready bool `json:"ready"`
|
Ready bool `json:"ready"`
|
||||||
|
|
@ -73,32 +66,12 @@ func (n *Native) VideoSleepModeSupported() bool {
|
||||||
return n.sleepModeSupported
|
return n.sleepModeSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// useExtraLock uses the extra lock to execute a function.
|
|
||||||
// if the lock is currently held by another goroutine, returns an error.
|
|
||||||
//
|
|
||||||
// it's used to ensure that only one change is made to the video stream at a time.
|
|
||||||
// as the change usually requires to restart video streaming
|
|
||||||
// TODO: check video streaming status instead of using a hardcoded timeout
|
|
||||||
func (n *Native) useExtraLock(fn func() error) error {
|
|
||||||
if !n.extraLock.TryLock() {
|
|
||||||
return fmt.Errorf("the previous change hasn't been completed yet")
|
|
||||||
}
|
|
||||||
err := fn()
|
|
||||||
if err == nil {
|
|
||||||
time.Sleep(extraLockTimeout)
|
|
||||||
}
|
|
||||||
n.extraLock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoSetQualityFactor sets the quality factor for the video stream.
|
// VideoSetQualityFactor sets the quality factor for the video stream.
|
||||||
func (n *Native) VideoSetQualityFactor(factor float64) error {
|
func (n *Native) VideoSetQualityFactor(factor float64) error {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
||||||
return n.useExtraLock(func() error {
|
|
||||||
return videoSetStreamQualityFactor(factor)
|
return videoSetStreamQualityFactor(factor)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoGetQualityFactor gets the quality factor for the video stream.
|
// VideoGetQualityFactor gets the quality factor for the video stream.
|
||||||
|
|
@ -114,13 +87,7 @@ func (n *Native) VideoSetEDID(edid string) error {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
||||||
if edid == "" {
|
|
||||||
edid = DefaultEDID
|
|
||||||
}
|
|
||||||
|
|
||||||
return n.useExtraLock(func() error {
|
|
||||||
return videoSetEDID(edid)
|
return videoSetEDID(edid)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoGetEDID gets the EDID for the video stream.
|
// VideoGetEDID gets the EDID for the video stream.
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,7 @@ func rpcGetEDID() (string, error) {
|
||||||
func rpcSetEDID(edid string) error {
|
func rpcSetEDID(edid string) error {
|
||||||
if edid == "" {
|
if edid == "" {
|
||||||
logger.Info().Msg("Restoring EDID to default")
|
logger.Info().Msg("Restoring EDID to default")
|
||||||
|
edid = "00ffffffffffff0052620188008888881c150103800000780a0dc9a05747982712484c00000001010101010101010101010101010101023a801871382d40582c4500c48e2100001e011d007251d01e206e285500c48e2100001e000000fc00543734392d6648443732300a20000000fd00147801ff1d000a202020202020017b"
|
||||||
} else {
|
} else {
|
||||||
logger.Info().Str("edid", edid).Msg("Setting EDID")
|
logger.Info().Str("edid", edid).Msg("Setting EDID")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
|
||||||
SystemVersion: systemVersion,
|
SystemVersion: systemVersion,
|
||||||
AppVersion: appVersion,
|
AppVersion: appVersion,
|
||||||
DisplayRotation: config.GetDisplayRotation(),
|
DisplayRotation: config.GetDisplayRotation(),
|
||||||
DefaultQualityFactor: config.VideoQualityFactor,
|
|
||||||
OnVideoStateChange: func(state native.VideoState) {
|
OnVideoStateChange: func(state native.VideoState) {
|
||||||
lastVideoState = state
|
lastVideoState = state
|
||||||
triggerVideoStateUpdate()
|
triggerVideoStateUpdate()
|
||||||
|
|
@ -59,13 +58,7 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
nativeInstance.Start()
|
nativeInstance.Start()
|
||||||
go func() {
|
|
||||||
if err := nativeInstance.VideoSetEDID(config.EdidString); err != nil {
|
|
||||||
nativeLogger.Warn().Err(err).Msg("error setting EDID")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if os.Getenv("JETKVM_CRASH_TESTING") == "1" {
|
if os.Getenv("JETKVM_CRASH_TESTING") == "1" {
|
||||||
nativeInstance.DoNotUseThisIsForCrashTestingOnly()
|
nativeInstance.DoNotUseThisIsForCrashTestingOnly()
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Spænding",
|
"dc_power_control_voltage": "Spænding",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Slet",
|
"delete": "Slet",
|
||||||
|
"deregister_from_cloud": "Afregistrering fra Cloud",
|
||||||
"deregister_cloud_devices": "Cloud-enheder",
|
"deregister_cloud_devices": "Cloud-enheder",
|
||||||
"deregister_description": "Dette vil fjerne enheden fra din cloud-konto og tilbagekalde fjernadgang til den. Bemærk venligst, at lokal adgang stadig vil være mulig.",
|
"deregister_description": "Dette vil fjerne enheden fra din cloud-konto og tilbagekalde fjernadgang til den. Bemærk venligst, at lokal adgang stadig vil være mulig.",
|
||||||
"deregister_error": "Der opstod en fejl {status} under afregistreringen af din enhed. Prøv igen.",
|
"deregister_error": "Der opstod en fejl {status} under afregistreringen af din enhed. Prøv igen.",
|
||||||
"deregister_from_cloud": "Afregistrering fra Cloud",
|
|
||||||
"deregister_headline": "Afregistrér {device} fra din cloud-konto",
|
"deregister_headline": "Afregistrér {device} fra din cloud-konto",
|
||||||
"detach": "Løsrive",
|
"detach": "Løsrive",
|
||||||
"dhcp_empty_lease_description": "Vi har endnu ikke modtaget nogen DHCP-leaseoplysninger fra enheden.",
|
"dhcp_empty_lease_description": "Vi har endnu ikke modtaget nogen DHCP-leaseoplysninger fra enheden.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Skærmretning",
|
"hardware_display_orientation_title": "Skærmretning",
|
||||||
"hardware_display_wake_up_note": "Skærmen vågner op, når forbindelsestilstanden ændres, eller når den berøres.",
|
"hardware_display_wake_up_note": "Skærmen vågner op, når forbindelsestilstanden ændres, eller når den berøres.",
|
||||||
"hardware_page_description": "Konfigurer skærmindstillinger og hardwareindstillinger for din JetKVM-enhed",
|
"hardware_page_description": "Konfigurer skærmindstillinger og hardwareindstillinger for din JetKVM-enhed",
|
||||||
"hardware_power_saving_description": "Reducer strømforbruget, når det ikke er i brug",
|
|
||||||
"hardware_power_saving_disabled": "Strømsparetilstand deaktiveret",
|
|
||||||
"hardware_power_saving_enabled": "Strømsparetilstand aktiveret",
|
|
||||||
"hardware_power_saving_failed_error": "Kunne ikke indstille strømsparetilstand: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Slå optagelse fra efter 90 sekunders inaktivitet",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "HDMI-dvaletilstand",
|
|
||||||
"hardware_power_saving_title": "Strømbesparelse",
|
|
||||||
"hardware_time_10_minutes": "10 minutter",
|
"hardware_time_10_minutes": "10 minutter",
|
||||||
"hardware_time_1_hour": "1 time",
|
"hardware_time_1_hour": "1 time",
|
||||||
"hardware_time_1_minute": "1 minut",
|
"hardware_time_1_minute": "1 minut",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Stromspannung",
|
"dc_power_control_voltage": "Stromspannung",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
|
"deregister_from_cloud": "Abmelden von der Cloud",
|
||||||
"deregister_cloud_devices": "Cloud-Geräte",
|
"deregister_cloud_devices": "Cloud-Geräte",
|
||||||
"deregister_description": "Dadurch wird das Gerät aus Ihrem Cloud-Konto entfernt und der Fernzugriff darauf widerrufen. Bitte beachten Sie, dass der lokale Zugriff weiterhin möglich ist.",
|
"deregister_description": "Dadurch wird das Gerät aus Ihrem Cloud-Konto entfernt und der Fernzugriff darauf widerrufen. Bitte beachten Sie, dass der lokale Zugriff weiterhin möglich ist.",
|
||||||
"deregister_error": "Beim Abmelden Ihres Geräts ist ein Fehler aufgetreten {status} . Bitte versuchen Sie es erneut.",
|
"deregister_error": "Beim Abmelden Ihres Geräts ist ein Fehler aufgetreten {status} . Bitte versuchen Sie es erneut.",
|
||||||
"deregister_from_cloud": "Abmelden von der Cloud",
|
|
||||||
"deregister_headline": "Melden Sie {device}",
|
"deregister_headline": "Melden Sie {device}",
|
||||||
"detach": "Abtrennen",
|
"detach": "Abtrennen",
|
||||||
"dhcp_empty_lease_description": "Wir haben noch keine DHCP-Lease-Informationen vom Gerät erhalten.",
|
"dhcp_empty_lease_description": "Wir haben noch keine DHCP-Lease-Informationen vom Gerät erhalten.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Anzeigeausrichtung",
|
"hardware_display_orientation_title": "Anzeigeausrichtung",
|
||||||
"hardware_display_wake_up_note": "Das Display wird aktiviert, wenn sich der Verbindungsstatus ändert oder wenn es berührt wird.",
|
"hardware_display_wake_up_note": "Das Display wird aktiviert, wenn sich der Verbindungsstatus ändert oder wenn es berührt wird.",
|
||||||
"hardware_page_description": "Konfigurieren Sie Anzeigeeinstellungen und Hardwareoptionen für Ihr JetKVM-Gerät",
|
"hardware_page_description": "Konfigurieren Sie Anzeigeeinstellungen und Hardwareoptionen für Ihr JetKVM-Gerät",
|
||||||
"hardware_power_saving_description": "Reduzieren Sie den Stromverbrauch bei Nichtgebrauch",
|
|
||||||
"hardware_power_saving_disabled": "Energiesparmodus deaktiviert",
|
|
||||||
"hardware_power_saving_enabled": "Energiesparmodus aktiviert",
|
|
||||||
"hardware_power_saving_failed_error": "Fehler beim Einstellen des Energiesparmodus: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Schalten Sie die Aufnahme nach 90 Sekunden Inaktivität aus",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "HDMI-Ruhemodus",
|
|
||||||
"hardware_power_saving_title": "Energiesparen",
|
|
||||||
"hardware_time_10_minutes": "10 Minuten",
|
"hardware_time_10_minutes": "10 Minuten",
|
||||||
"hardware_time_1_hour": "1 Stunde",
|
"hardware_time_1_hour": "1 Stunde",
|
||||||
"hardware_time_1_minute": "1 Minute",
|
"hardware_time_1_minute": "1 Minute",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Voltage",
|
"dc_power_control_voltage": "Voltage",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"deregister_from_cloud": "Deregister from Cloud",
|
||||||
"deregister_cloud_devices": "Cloud Devices",
|
"deregister_cloud_devices": "Cloud Devices",
|
||||||
"deregister_description": "This will remove the device from your cloud account and revoke remote access to it. Please note that local access will still be possible",
|
"deregister_description": "This will remove the device from your cloud account and revoke remote access to it. Please note that local access will still be possible",
|
||||||
"deregister_error": "There was an error {status} deregistering your device. Please try again.",
|
"deregister_error": "There was an error {status} deregistering your device. Please try again.",
|
||||||
"deregister_from_cloud": "Deregister from Cloud",
|
|
||||||
"deregister_headline": "Deregister {device} from your cloud account",
|
"deregister_headline": "Deregister {device} from your cloud account",
|
||||||
"detach": "Detach",
|
"detach": "Detach",
|
||||||
"dhcp_empty_lease_description": "We haven't received any DHCP lease information from the device yet.",
|
"dhcp_empty_lease_description": "We haven't received any DHCP lease information from the device yet.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Display Orientation",
|
"hardware_display_orientation_title": "Display Orientation",
|
||||||
"hardware_display_wake_up_note": "The display will wake up when the connection state changes, or when touched.",
|
"hardware_display_wake_up_note": "The display will wake up when the connection state changes, or when touched.",
|
||||||
"hardware_page_description": "Configure display settings and hardware options for your JetKVM device",
|
"hardware_page_description": "Configure display settings and hardware options for your JetKVM device",
|
||||||
"hardware_power_saving_description": "Reduce power consumption when not in use",
|
|
||||||
"hardware_power_saving_disabled": "Power saving mode disabled",
|
|
||||||
"hardware_power_saving_enabled": "Power saving mode enabled",
|
|
||||||
"hardware_power_saving_failed_error": "Failed to set power saving mode: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Turn off capture after 90 seconds of inactivity",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "HDMI Sleep Mode",
|
|
||||||
"hardware_power_saving_title": "Power Saving",
|
|
||||||
"hardware_time_10_minutes": "10 Minutes",
|
"hardware_time_10_minutes": "10 Minutes",
|
||||||
"hardware_time_1_hour": "1 Hour",
|
"hardware_time_1_hour": "1 Hour",
|
||||||
"hardware_time_1_minute": "1 Minute",
|
"hardware_time_1_minute": "1 Minute",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Voltaje",
|
"dc_power_control_voltage": "Voltaje",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Borrar",
|
"delete": "Borrar",
|
||||||
|
"deregister_from_cloud": "Darse de baja de la nube",
|
||||||
"deregister_cloud_devices": "Dispositivos en la nube",
|
"deregister_cloud_devices": "Dispositivos en la nube",
|
||||||
"deregister_description": "Esto eliminará el dispositivo de su cuenta en la nube y revocará el acceso remoto. Tenga en cuenta que el acceso local seguirá siendo posible.",
|
"deregister_description": "Esto eliminará el dispositivo de su cuenta en la nube y revocará el acceso remoto. Tenga en cuenta que el acceso local seguirá siendo posible.",
|
||||||
"deregister_error": "Se produjo un error {status} al cancelar el registro de su dispositivo. Inténtelo de nuevo.",
|
"deregister_error": "Se produjo un error {status} al cancelar el registro de su dispositivo. Inténtelo de nuevo.",
|
||||||
"deregister_from_cloud": "Darse de baja de la nube",
|
|
||||||
"deregister_headline": "Anular el registro de {device} en su cuenta en la nube",
|
"deregister_headline": "Anular el registro de {device} en su cuenta en la nube",
|
||||||
"detach": "Despegar",
|
"detach": "Despegar",
|
||||||
"dhcp_empty_lease_description": "Aún no hemos recibido ninguna información de concesión de DHCP del dispositivo.",
|
"dhcp_empty_lease_description": "Aún no hemos recibido ninguna información de concesión de DHCP del dispositivo.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Orientación de la pantalla",
|
"hardware_display_orientation_title": "Orientación de la pantalla",
|
||||||
"hardware_display_wake_up_note": "La pantalla se activará cuando cambie el estado de la conexión o cuando se toque.",
|
"hardware_display_wake_up_note": "La pantalla se activará cuando cambie el estado de la conexión o cuando se toque.",
|
||||||
"hardware_page_description": "Configure los ajustes de pantalla y las opciones de hardware para su dispositivo JetKVM",
|
"hardware_page_description": "Configure los ajustes de pantalla y las opciones de hardware para su dispositivo JetKVM",
|
||||||
"hardware_power_saving_description": "Reduce el consumo de energía cuando no esté en uso",
|
|
||||||
"hardware_power_saving_disabled": "Modo de ahorro de energía deshabilitado",
|
|
||||||
"hardware_power_saving_enabled": "Modo de ahorro de energía habilitado",
|
|
||||||
"hardware_power_saving_failed_error": "No se pudo establecer el modo de ahorro de energía: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Desactivar la captura después de 90 segundos de inactividad",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "Modo de suspensión HDMI",
|
|
||||||
"hardware_power_saving_title": "Ahorro de energía",
|
|
||||||
"hardware_time_10_minutes": "10 minutos",
|
"hardware_time_10_minutes": "10 minutos",
|
||||||
"hardware_time_1_hour": "1 hora",
|
"hardware_time_1_hour": "1 hora",
|
||||||
"hardware_time_1_minute": "1 minuto",
|
"hardware_time_1_minute": "1 minuto",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Tension",
|
"dc_power_control_voltage": "Tension",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
|
"deregister_from_cloud": "Se désinscrire du Cloud",
|
||||||
"deregister_cloud_devices": "Appareils Cloud",
|
"deregister_cloud_devices": "Appareils Cloud",
|
||||||
"deregister_description": "Cela supprimera l'appareil de votre compte cloud et révoquera l'accès à distance. Veuillez noter que l'accès local restera possible.",
|
"deregister_description": "Cela supprimera l'appareil de votre compte cloud et révoquera l'accès à distance. Veuillez noter que l'accès local restera possible.",
|
||||||
"deregister_error": "Une erreur {status} s'est produite lors de l'annulation de l'enregistrement de votre appareil. Veuillez réessayer.",
|
"deregister_error": "Une erreur {status} s'est produite lors de l'annulation de l'enregistrement de votre appareil. Veuillez réessayer.",
|
||||||
"deregister_from_cloud": "Se désinscrire du Cloud",
|
|
||||||
"deregister_headline": "Désinscrivez {device} de votre compte cloud",
|
"deregister_headline": "Désinscrivez {device} de votre compte cloud",
|
||||||
"detach": "Détacher",
|
"detach": "Détacher",
|
||||||
"dhcp_empty_lease_description": "Nous n'avons pas encore reçu d'informations sur le bail DHCP de l'appareil.",
|
"dhcp_empty_lease_description": "Nous n'avons pas encore reçu d'informations sur le bail DHCP de l'appareil.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Orientation de l'affichage",
|
"hardware_display_orientation_title": "Orientation de l'affichage",
|
||||||
"hardware_display_wake_up_note": "L'écran se réveille lorsque l'état de connexion change ou lorsqu'il est touché.",
|
"hardware_display_wake_up_note": "L'écran se réveille lorsque l'état de connexion change ou lorsqu'il est touché.",
|
||||||
"hardware_page_description": "Configurer les paramètres d'affichage et les options matérielles de votre périphérique JetKVM",
|
"hardware_page_description": "Configurer les paramètres d'affichage et les options matérielles de votre périphérique JetKVM",
|
||||||
"hardware_power_saving_description": "Réduisez la consommation d'énergie lorsque vous ne l'utilisez pas",
|
|
||||||
"hardware_power_saving_disabled": "Mode d'économie d'énergie désactivé",
|
|
||||||
"hardware_power_saving_enabled": "Mode d'économie d'énergie activé",
|
|
||||||
"hardware_power_saving_failed_error": "Échec de la définition du mode d'économie d'énergie : {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Désactiver la capture après 90 secondes d'inactivité",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "Mode veille HDMI",
|
|
||||||
"hardware_power_saving_title": "Économie d'énergie",
|
|
||||||
"hardware_time_10_minutes": "10 minutes",
|
"hardware_time_10_minutes": "10 minutes",
|
||||||
"hardware_time_1_hour": "1 heure",
|
"hardware_time_1_hour": "1 heure",
|
||||||
"hardware_time_1_minute": "1 minute",
|
"hardware_time_1_minute": "1 minute",
|
||||||
|
|
@ -588,7 +581,6 @@
|
||||||
"network_description": "Configurez vos paramètres réseau",
|
"network_description": "Configurez vos paramètres réseau",
|
||||||
"network_dhcp_client_description": "Configurer le client DHCP à utiliser",
|
"network_dhcp_client_description": "Configurer le client DHCP à utiliser",
|
||||||
"network_dhcp_client_jetkvm": "JetKVM interne",
|
"network_dhcp_client_jetkvm": "JetKVM interne",
|
||||||
"network_dhcp_client_title": "Client DHCP",
|
|
||||||
"network_dhcp_lease_renew_confirm": "Renouveler le bail",
|
"network_dhcp_lease_renew_confirm": "Renouveler le bail",
|
||||||
"network_dhcp_lease_renew_confirm_description": "Cette opération demandera une nouvelle adresse IP à votre serveur DHCP. Votre appareil pourrait perdre temporairement sa connectivité réseau pendant cette opération.",
|
"network_dhcp_lease_renew_confirm_description": "Cette opération demandera une nouvelle adresse IP à votre serveur DHCP. Votre appareil pourrait perdre temporairement sa connectivité réseau pendant cette opération.",
|
||||||
"network_dhcp_lease_renew_confirm_new_a": "Si vous recevez une nouvelle adresse IP",
|
"network_dhcp_lease_renew_confirm_new_a": "Si vous recevez une nouvelle adresse IP",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Voltaggio",
|
"dc_power_control_voltage": "Voltaggio",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Eliminare",
|
"delete": "Eliminare",
|
||||||
|
"deregister_from_cloud": "Annulla registrazione dal cloud",
|
||||||
"deregister_cloud_devices": "Dispositivi cloud",
|
"deregister_cloud_devices": "Dispositivi cloud",
|
||||||
"deregister_description": "Questo rimuoverà il dispositivo dal tuo account cloud e ne revocherà l'accesso remoto. Tieni presente che l'accesso locale sarà comunque possibile.",
|
"deregister_description": "Questo rimuoverà il dispositivo dal tuo account cloud e ne revocherà l'accesso remoto. Tieni presente che l'accesso locale sarà comunque possibile.",
|
||||||
"deregister_error": "Si è verificato un errore {status} durante l'annullamento della registrazione del dispositivo. Riprova.",
|
"deregister_error": "Si è verificato un errore {status} durante l'annullamento della registrazione del dispositivo. Riprova.",
|
||||||
"deregister_from_cloud": "Annulla registrazione dal cloud",
|
|
||||||
"deregister_headline": "Annulla la registrazione di {device} dal tuo account cloud",
|
"deregister_headline": "Annulla la registrazione di {device} dal tuo account cloud",
|
||||||
"detach": "Staccare",
|
"detach": "Staccare",
|
||||||
"dhcp_empty_lease_description": "Non abbiamo ancora ricevuto alcuna informazione di lease DHCP dal dispositivo.",
|
"dhcp_empty_lease_description": "Non abbiamo ancora ricevuto alcuna informazione di lease DHCP dal dispositivo.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Orientamento dello schermo",
|
"hardware_display_orientation_title": "Orientamento dello schermo",
|
||||||
"hardware_display_wake_up_note": "Il display si riattiverà quando cambia lo stato della connessione o quando viene toccato.",
|
"hardware_display_wake_up_note": "Il display si riattiverà quando cambia lo stato della connessione o quando viene toccato.",
|
||||||
"hardware_page_description": "Configura le impostazioni di visualizzazione e le opzioni hardware per il tuo dispositivo JetKVM",
|
"hardware_page_description": "Configura le impostazioni di visualizzazione e le opzioni hardware per il tuo dispositivo JetKVM",
|
||||||
"hardware_power_saving_description": "Ridurre il consumo energetico quando non in uso",
|
|
||||||
"hardware_power_saving_disabled": "Modalità di risparmio energetico disabilitata",
|
|
||||||
"hardware_power_saving_enabled": "Modalità di risparmio energetico abilitata",
|
|
||||||
"hardware_power_saving_failed_error": "Impossibile impostare la modalità di risparmio energetico: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Disattiva l'acquisizione dopo 90 secondi di inattività",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "Modalità sospensione HDMI",
|
|
||||||
"hardware_power_saving_title": "Risparmio energetico",
|
|
||||||
"hardware_time_10_minutes": "10 minuti",
|
"hardware_time_10_minutes": "10 minuti",
|
||||||
"hardware_time_1_hour": "1 ora",
|
"hardware_time_1_hour": "1 ora",
|
||||||
"hardware_time_1_minute": "1 minuto",
|
"hardware_time_1_minute": "1 minuto",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Spenning",
|
"dc_power_control_voltage": "Spenning",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Slett",
|
"delete": "Slett",
|
||||||
|
"deregister_from_cloud": "Avregistrer deg fra skyen",
|
||||||
"deregister_cloud_devices": "Skyenheter",
|
"deregister_cloud_devices": "Skyenheter",
|
||||||
"deregister_description": "Dette vil fjerne enheten fra skykontoen din og oppheve ekstern tilgang til den. Vær oppmerksom på at lokal tilgang fortsatt vil være mulig.",
|
"deregister_description": "Dette vil fjerne enheten fra skykontoen din og oppheve ekstern tilgang til den. Vær oppmerksom på at lokal tilgang fortsatt vil være mulig.",
|
||||||
"deregister_error": "Det oppsto en feil {status} enheten din skulle avregistreres. Prøv på nytt.",
|
"deregister_error": "Det oppsto en feil {status} enheten din skulle avregistreres. Prøv på nytt.",
|
||||||
"deregister_from_cloud": "Avregistrer deg fra skyen",
|
|
||||||
"deregister_headline": "Avregistrer {device} fra skykontoen din",
|
"deregister_headline": "Avregistrer {device} fra skykontoen din",
|
||||||
"detach": "Løsne",
|
"detach": "Løsne",
|
||||||
"dhcp_empty_lease_description": "Vi har ikke mottatt noen DHCP-leaseinformasjon fra enheten ennå.",
|
"dhcp_empty_lease_description": "Vi har ikke mottatt noen DHCP-leaseinformasjon fra enheten ennå.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Skjermretning",
|
"hardware_display_orientation_title": "Skjermretning",
|
||||||
"hardware_display_wake_up_note": "Skjermen vil våkne når tilkoblingsstatusen endres, eller når den berøres.",
|
"hardware_display_wake_up_note": "Skjermen vil våkne når tilkoblingsstatusen endres, eller når den berøres.",
|
||||||
"hardware_page_description": "Konfigurer skjerminnstillinger og maskinvarealternativer for JetKVM-enheten din",
|
"hardware_page_description": "Konfigurer skjerminnstillinger og maskinvarealternativer for JetKVM-enheten din",
|
||||||
"hardware_power_saving_description": "Reduser strømforbruket når det ikke er i bruk",
|
|
||||||
"hardware_power_saving_disabled": "Strømsparingsmodus deaktivert",
|
|
||||||
"hardware_power_saving_enabled": "Strømsparingsmodus aktivert",
|
|
||||||
"hardware_power_saving_failed_error": "Kunne ikke angi strømsparingsmodus: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Slå av opptak etter 90 sekunder med inaktivitet",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "HDMI-hvilemodus",
|
|
||||||
"hardware_power_saving_title": "Strømsparing",
|
|
||||||
"hardware_time_10_minutes": "10 minutter",
|
"hardware_time_10_minutes": "10 minutter",
|
||||||
"hardware_time_1_hour": "1 time",
|
"hardware_time_1_hour": "1 time",
|
||||||
"hardware_time_1_minute": "1 minutt",
|
"hardware_time_1_minute": "1 minutt",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "Spänning",
|
"dc_power_control_voltage": "Spänning",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "Radera",
|
"delete": "Radera",
|
||||||
|
"deregister_from_cloud": "Avregistrera dig från molnet",
|
||||||
"deregister_cloud_devices": "Molnenheter",
|
"deregister_cloud_devices": "Molnenheter",
|
||||||
"deregister_description": "Detta kommer att ta bort enheten från ditt molnkonto och återkalla fjärråtkomst till den. Observera att lokal åtkomst fortfarande kommer att vara möjlig.",
|
"deregister_description": "Detta kommer att ta bort enheten från ditt molnkonto och återkalla fjärråtkomst till den. Observera att lokal åtkomst fortfarande kommer att vara möjlig.",
|
||||||
"deregister_error": "Det uppstod ett fel {status} enheten avregistrerades. Försök igen.",
|
"deregister_error": "Det uppstod ett fel {status} enheten avregistrerades. Försök igen.",
|
||||||
"deregister_from_cloud": "Avregistrera dig från molnet",
|
|
||||||
"deregister_headline": "Avregistrera {device} från ditt molnkonto",
|
"deregister_headline": "Avregistrera {device} från ditt molnkonto",
|
||||||
"detach": "Lösgöra",
|
"detach": "Lösgöra",
|
||||||
"dhcp_empty_lease_description": "Vi har inte mottagit någon DHCP-leaseinformation från enheten ännu.",
|
"dhcp_empty_lease_description": "Vi har inte mottagit någon DHCP-leaseinformation från enheten ännu.",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "Skärmorientering",
|
"hardware_display_orientation_title": "Skärmorientering",
|
||||||
"hardware_display_wake_up_note": "Skärmen vaknar när anslutningsstatusen ändras eller när den berörs.",
|
"hardware_display_wake_up_note": "Skärmen vaknar när anslutningsstatusen ändras eller när den berörs.",
|
||||||
"hardware_page_description": "Konfigurera skärminställningar och maskinvarualternativ för din JetKVM-enhet",
|
"hardware_page_description": "Konfigurera skärminställningar och maskinvarualternativ för din JetKVM-enhet",
|
||||||
"hardware_power_saving_description": "Minska strömförbrukningen när den inte används",
|
|
||||||
"hardware_power_saving_disabled": "Energisparläge inaktiverat",
|
|
||||||
"hardware_power_saving_enabled": "Energisparläge aktiverat",
|
|
||||||
"hardware_power_saving_failed_error": "Misslyckades med att ställa in energisparläge: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "Stäng av inspelning efter 90 sekunders inaktivitet",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "HDMI-viloläge",
|
|
||||||
"hardware_power_saving_title": "Energisparande",
|
|
||||||
"hardware_time_10_minutes": "10 minuter",
|
"hardware_time_10_minutes": "10 minuter",
|
||||||
"hardware_time_1_hour": "1 timme",
|
"hardware_time_1_hour": "1 timme",
|
||||||
"hardware_time_1_minute": "1 minut",
|
"hardware_time_1_minute": "1 minut",
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@
|
||||||
"dc_power_control_voltage": "电压",
|
"dc_power_control_voltage": "电压",
|
||||||
"dc_power_control_voltage_unit": "V",
|
"dc_power_control_voltage_unit": "V",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
|
"deregister_from_cloud": "从云端注销",
|
||||||
"deregister_cloud_devices": "云设备",
|
"deregister_cloud_devices": "云设备",
|
||||||
"deregister_description": "这将从您的云帐户中移除该设备,并撤销其远程访问权限。请注意,您仍然可以进行本地访问",
|
"deregister_description": "这将从您的云帐户中移除该设备,并撤销其远程访问权限。请注意,您仍然可以进行本地访问",
|
||||||
"deregister_error": "注销您的设备时出现错误{status} 。请重试。",
|
"deregister_error": "注销您的设备时出现错误{status} 。请重试。",
|
||||||
"deregister_from_cloud": "从云端注销",
|
|
||||||
"deregister_headline": "从您的云帐户中取消注册{device}",
|
"deregister_headline": "从您的云帐户中取消注册{device}",
|
||||||
"detach": "分离",
|
"detach": "分离",
|
||||||
"dhcp_empty_lease_description": "我们尚未收到来自该设备的任何 DHCP 租约信息。",
|
"dhcp_empty_lease_description": "我们尚未收到来自该设备的任何 DHCP 租约信息。",
|
||||||
|
|
@ -298,13 +298,6 @@
|
||||||
"hardware_display_orientation_title": "显示方向",
|
"hardware_display_orientation_title": "显示方向",
|
||||||
"hardware_display_wake_up_note": "当连接状态改变或被触摸时,显示屏将会唤醒。",
|
"hardware_display_wake_up_note": "当连接状态改变或被触摸时,显示屏将会唤醒。",
|
||||||
"hardware_page_description": "为您的 JetKVM 设备配置显示设置和硬件选项",
|
"hardware_page_description": "为您的 JetKVM 设备配置显示设置和硬件选项",
|
||||||
"hardware_power_saving_description": "不使用时减少功耗",
|
|
||||||
"hardware_power_saving_disabled": "省电模式已禁用",
|
|
||||||
"hardware_power_saving_enabled": "启用省电模式",
|
|
||||||
"hardware_power_saving_failed_error": "无法设置省电模式: {error}",
|
|
||||||
"hardware_power_saving_hdmi_sleep_description": "90 秒不活动后关闭捕获",
|
|
||||||
"hardware_power_saving_hdmi_sleep_title": "HDMI睡眠模式",
|
|
||||||
"hardware_power_saving_title": "节能",
|
|
||||||
"hardware_time_10_minutes": "10分钟",
|
"hardware_time_10_minutes": "10分钟",
|
||||||
"hardware_time_1_hour": "1小时",
|
"hardware_time_1_hour": "1小时",
|
||||||
"hardware_time_1_minute": "1分钟",
|
"hardware_time_1_minute": "1分钟",
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"version": "2025.10.18.0100",
|
"version": "2025.10.15.1700",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"version": "2025.10.18.0100",
|
"version": "2025.10.15.1700",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.2.9",
|
"@headlessui/react": "^2.2.9",
|
||||||
"@headlessui/tailwindcss": "^0.2.2",
|
"@headlessui/tailwindcss": "^0.2.2",
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
"react-simple-keyboard": "^3.8.130",
|
"react-simple-keyboard": "^3.8.130",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
"recharts": "^3.3.0",
|
"recharts": "^3.2.1",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"usehooks-ts": "^3.1.1",
|
"usehooks-ts": "^3.1.1",
|
||||||
"validator": "^13.15.15",
|
"validator": "^13.15.15",
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.0",
|
"@eslint/compat": "^1.4.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.37.0",
|
||||||
"@inlang/cli": "^3.0.12",
|
"@inlang/cli": "^3.0.12",
|
||||||
"@inlang/paraglide-js": "^2.4.0",
|
"@inlang/paraglide-js": "^2.4.0",
|
||||||
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
"@typescript-eslint/parser": "^8.46.1",
|
"@typescript-eslint/parser": "^8.46.1",
|
||||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"eslint": "^9.38.0",
|
"eslint": "^9.37.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
"globals": "^16.4.0",
|
"globals": "^16.4.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.7.1",
|
"prettier-plugin-tailwindcss": "^0.7.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.1.10",
|
"vite": "^7.1.10",
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.3",
|
"@babel/generator": "^7.28.3",
|
||||||
|
|
@ -820,12 +821,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-array": {
|
"node_modules/@eslint/config-array": {
|
||||||
"version": "0.21.1",
|
"version": "0.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
|
||||||
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
|
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/object-schema": "^2.1.7",
|
"@eslint/object-schema": "^2.1.6",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"minimatch": "^3.1.2"
|
"minimatch": "^3.1.2"
|
||||||
},
|
},
|
||||||
|
|
@ -834,9 +835,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-helpers": {
|
"node_modules/@eslint/config-helpers": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
|
||||||
"integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==",
|
"integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/core": "^0.16.0"
|
"@eslint/core": "^0.16.0"
|
||||||
|
|
@ -893,9 +894,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.38.0",
|
"version": "9.37.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
|
||||||
"integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
|
"integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -905,9 +906,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/object-schema": {
|
"node_modules/@eslint/object-schema": {
|
||||||
"version": "2.1.7",
|
"version": "2.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
|
||||||
"integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
|
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -3970,24 +3971,25 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.38.0",
|
"version": "9.37.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
|
||||||
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
"@eslint/config-array": "^0.21.1",
|
"@eslint/config-array": "^0.21.0",
|
||||||
"@eslint/config-helpers": "^0.4.1",
|
"@eslint/config-helpers": "^0.4.0",
|
||||||
"@eslint/core": "^0.16.0",
|
"@eslint/core": "^0.16.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "9.38.0",
|
"@eslint/js": "9.37.0",
|
||||||
"@eslint/plugin-kit": "^0.4.0",
|
"@eslint/plugin-kit": "^0.4.0",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@humanwhocodes/retry": "^0.4.2",
|
"@humanwhocodes/retry": "^0.4.2",
|
||||||
"@types/estree": "^1.0.6",
|
"@types/estree": "^1.0.6",
|
||||||
|
"@types/json-schema": "^7.0.15",
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"cross-spawn": "^7.0.6",
|
"cross-spawn": "^7.0.6",
|
||||||
|
|
@ -6328,9 +6330,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier-plugin-tailwindcss": {
|
"node_modules/prettier-plugin-tailwindcss": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.0.tgz",
|
||||||
"integrity": "sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==",
|
"integrity": "sha512-zpRZhkfwq1cNmbKhmKzXKuKFdkgXZXlf6p+KttD75v6pGz1FxmcKMc4RKdw97GYBKBbout4113HSLaBJAomFDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -6612,9 +6614,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/recharts": {
|
"node_modules/recharts": {
|
||||||
"version": "3.3.0",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.2.1.tgz",
|
||||||
"integrity": "sha512-Vi0qmTB0iz1+/Cz9o5B7irVyUjX2ynvEgImbgMt/3sKRREcUM07QiYjS1QpAVrkmVlXqy5gykq4nGWMz9AS4Rg==",
|
"integrity": "sha512-0JKwHRiFZdmLq/6nmilxEZl3pqb4T+aKkOkOi/ZISRZwfBhVMgInxzlYU9D4KnCH3KINScLy68m/OvMXoYGZUw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "1.x.x || 2.x.x",
|
"@reduxjs/toolkit": "1.x.x || 2.x.x",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2025.10.18.0100",
|
"version": "2025.10.15.1700",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^22.20.0"
|
"node": "^22.20.0"
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
"react-simple-keyboard": "^3.8.130",
|
"react-simple-keyboard": "^3.8.130",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
"recharts": "^3.3.0",
|
"recharts": "^3.2.1",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"usehooks-ts": "^3.1.1",
|
"usehooks-ts": "^3.1.1",
|
||||||
"validator": "^13.15.15",
|
"validator": "^13.15.15",
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.0",
|
"@eslint/compat": "^1.4.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.37.0",
|
||||||
"@inlang/cli": "^3.0.12",
|
"@inlang/cli": "^3.0.12",
|
||||||
"@inlang/paraglide-js": "^2.4.0",
|
"@inlang/paraglide-js": "^2.4.0",
|
||||||
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
"@typescript-eslint/parser": "^8.46.1",
|
"@typescript-eslint/parser": "^8.46.1",
|
||||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"eslint": "^9.38.0",
|
"eslint": "^9.37.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
|
|
@ -91,7 +91,7 @@
|
||||||
"globals": "^16.4.0",
|
"globals": "^16.4.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.7.1",
|
"prettier-plugin-tailwindcss": "^0.7.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.1.10",
|
"vite": "^7.1.10",
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import { BacklightSettings, useSettingsStore } from "@hooks/stores";
|
import { BacklightSettings, useSettingsStore } from "@hooks/stores";
|
||||||
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
|
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
|
||||||
import { Checkbox } from "@components/Checkbox";
|
|
||||||
import { FeatureFlag } from "@components/FeatureFlag";
|
import { FeatureFlag } from "@components/FeatureFlag";
|
||||||
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
||||||
import { SettingsItem } from "@components/SettingsItem";
|
import { SettingsItem } from "@components/SettingsItem";
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
import { SettingsSectionHeader } from "@components/SettingsSectionHeader";
|
|
||||||
import { UsbDeviceSetting } from "@components/UsbDeviceSetting";
|
import { UsbDeviceSetting } from "@components/UsbDeviceSetting";
|
||||||
import { UsbInfoSetting } from "@components/UsbInfoSetting";
|
import { UsbInfoSetting } from "@components/UsbInfoSetting";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
|
|
@ -17,7 +15,6 @@ export default function SettingsHardwareRoute() {
|
||||||
const { send } = useJsonRpc();
|
const { send } = useJsonRpc();
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
const { displayRotation, setDisplayRotation } = useSettingsStore();
|
const { displayRotation, setDisplayRotation } = useSettingsStore();
|
||||||
const [powerSavingEnabled, setPowerSavingEnabled] = useState(false);
|
|
||||||
|
|
||||||
const handleDisplayRotationChange = (rotation: string) => {
|
const handleDisplayRotationChange = (rotation: string) => {
|
||||||
setDisplayRotation(rotation);
|
setDisplayRotation(rotation);
|
||||||
|
|
@ -76,19 +73,6 @@ export default function SettingsHardwareRoute() {
|
||||||
handleBacklightSettingsChange(settings);
|
handleBacklightSettingsChange(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePowerSavingChange = (enabled: boolean) => {
|
|
||||||
setPowerSavingEnabled(enabled);
|
|
||||||
const duration = enabled ? 90 : -1;
|
|
||||||
send("setVideoSleepMode", { duration }, (resp: JsonRpcResponse) => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
notifications.error(m.hardware_power_saving_failed_error({ error: resp.error.data ||m.unknown_error() }));
|
|
||||||
setPowerSavingEnabled(!enabled); // Attempt to revert on error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
notifications.success(enabled ? m.hardware_power_saving_enabled() : m.hardware_power_saving_disabled());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
send("getBacklightSettings", {}, (resp: JsonRpcResponse) => {
|
send("getBacklightSettings", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
|
|
@ -101,17 +85,6 @@ export default function SettingsHardwareRoute() {
|
||||||
});
|
});
|
||||||
}, [send, setBacklightSettings]);
|
}, [send, setBacklightSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
send("getVideoSleepMode", {}, (resp: JsonRpcResponse) => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
console.error("Failed to get power saving mode:", resp.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result = resp.result as { enabled: boolean; duration: number };
|
|
||||||
setPowerSavingEnabled(result.duration >= 0);
|
|
||||||
});
|
|
||||||
}, [send]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsPageHeader
|
<SettingsPageHeader
|
||||||
|
|
@ -151,7 +124,7 @@ export default function SettingsHardwareRoute() {
|
||||||
{ value: "64", label: m.hardware_display_brightness_high() },
|
{ value: "64", label: m.hardware_display_brightness_high() },
|
||||||
]}
|
]}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleBacklightMaxBrightnessChange(Number.parseInt(e.target.value));
|
handleBacklightMaxBrightnessChange(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
@ -174,7 +147,7 @@ export default function SettingsHardwareRoute() {
|
||||||
{ value: "3600", label: m.hardware_time_1_hour() },
|
{ value: "3600", label: m.hardware_time_1_hour() },
|
||||||
]}
|
]}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleBacklightDimAfterChange(Number.parseInt(e.target.value));
|
handleBacklightDimAfterChange(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
@ -194,7 +167,7 @@ export default function SettingsHardwareRoute() {
|
||||||
{ value: "3600", label: m.hardware_time_1_hour() },
|
{ value: "3600", label: m.hardware_time_1_hour() },
|
||||||
]}
|
]}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleBacklightOffAfterChange(Number.parseInt(e.target.value));
|
handleBacklightOffAfterChange(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
@ -205,26 +178,6 @@ export default function SettingsHardwareRoute() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FeatureFlag minAppVersion="0.4.9">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="h-px w-full bg-slate-800/10 dark:bg-slate-300/20" />
|
|
||||||
<SettingsSectionHeader
|
|
||||||
title={m.hardware_power_saving_title()}
|
|
||||||
description={m.hardware_power_saving_description()}
|
|
||||||
/>
|
|
||||||
<SettingsItem
|
|
||||||
badge={m.experimental()}
|
|
||||||
title={m.hardware_power_saving_hdmi_sleep_title()}
|
|
||||||
description={m.hardware_power_saving_hdmi_sleep_description()}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={powerSavingEnabled}
|
|
||||||
onChange={(e) => handlePowerSavingChange(e.target.checked)}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
</div>
|
|
||||||
</FeatureFlag>
|
|
||||||
|
|
||||||
<FeatureFlag minAppVersion="0.3.8">
|
<FeatureFlag minAppVersion="0.3.8">
|
||||||
<UsbDeviceSetting />
|
<UsbDeviceSetting />
|
||||||
</FeatureFlag>
|
</FeatureFlag>
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,7 @@ export default function SettingsNetworkRoute() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirty.ipv6_static?.gateway) {
|
if (dirty.ipv4_static?.gateway) {
|
||||||
changes.push({
|
changes.push({
|
||||||
label: m.network_ipv6_gateway(),
|
label: m.network_ipv6_gateway(),
|
||||||
from: initialSettingsRef.current?.ipv6_static?.gateway as string,
|
from: initialSettingsRef.current?.ipv6_static?.gateway as string,
|
||||||
|
|
@ -273,7 +273,7 @@ export default function SettingsNetworkRoute() {
|
||||||
changes.push({
|
changes.push({
|
||||||
label: m.network_ipv6_dns(),
|
label: m.network_ipv6_dns(),
|
||||||
from: initialSettingsRef.current?.ipv6_static?.dns.join(", ").toString() ?? "",
|
from: initialSettingsRef.current?.ipv6_static?.dns.join(", ").toString() ?? "",
|
||||||
to: data.ipv6_static?.dns.join(", ").toString() ?? "",
|
to: data.ipv4_static?.dns.join(", ").toString() ?? "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue