mirror of https://github.com/jetkvm/kvm.git
315 lines
7.5 KiB
C
315 lines
7.5 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/un.h>
|
|
#include <sys/socket.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <fcntl.h>
|
|
#include "frozen.h"
|
|
#include "video.h"
|
|
#include "screen.h"
|
|
#include "edid.h"
|
|
#include "ctrl.h"
|
|
#include <lvgl.h>
|
|
|
|
jetkvm_video_state_t state;
|
|
jetkvm_video_state_handler_t *video_state_handler = NULL;
|
|
|
|
void report_video_format(bool ready, const char *error, u_int16_t width, u_int16_t height, double frame_per_second)
|
|
{
|
|
state.ready = ready;
|
|
state.error = error;
|
|
state.width = width;
|
|
state.height = height;
|
|
state.frame_per_second = frame_per_second;
|
|
if (video_state_handler != NULL) {
|
|
(*video_state_handler)(&state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Convert a hexadecimal string to an array of uint8_t bytes
|
|
*
|
|
* @param hex_str The input hexadecimal string
|
|
* @param bytes The output byte array (must be pre-allocated)
|
|
* @param max_len The maximum number of bytes that can be stored in the output array
|
|
* @return int The number of bytes converted, or -1 on error
|
|
*/
|
|
int hex_to_bytes(const char *hex_str, uint8_t *bytes, size_t max_len)
|
|
{
|
|
size_t hex_len = strnlen(hex_str, 4096);
|
|
if (hex_len % 2 != 0 || hex_len / 2 > max_len)
|
|
{
|
|
return -1; // Invalid input length or insufficient output buffer
|
|
}
|
|
|
|
for (size_t i = 0; i < hex_len; i += 2)
|
|
{
|
|
char byte_str[3] = {hex_str[i], hex_str[i + 1], '\0'};
|
|
char *end_ptr;
|
|
long value = strtol(byte_str, &end_ptr, 16);
|
|
|
|
if (*end_ptr != '\0' || value < 0 || value > 255)
|
|
{
|
|
return -1; // Invalid hexadecimal value
|
|
}
|
|
|
|
bytes[i / 2] = (uint8_t)value;
|
|
}
|
|
|
|
return hex_len / 2;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert an array of uint8_t bytes to a hexadecimal string, user must free the returned string
|
|
*
|
|
* @param bytes The input byte array
|
|
* @param len The number of bytes in the input array
|
|
* @return char* The output hexadecimal string (dynamically allocated, must be freed by the caller), or NULL on error
|
|
*/
|
|
const char *bytes_to_hex(const uint8_t *bytes, size_t len)
|
|
{
|
|
if (bytes == NULL || len == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
char *hex_str = malloc(2 * len + 1); // Each byte becomes 2 hex chars, plus null terminator
|
|
if (hex_str == NULL)
|
|
{
|
|
return NULL; // Memory allocation failed
|
|
}
|
|
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
snprintf(hex_str + (2 * i), 3, "%02x", bytes[i]);
|
|
}
|
|
|
|
hex_str[2 * len] = '\0'; // Ensure null termination
|
|
return hex_str;
|
|
}
|
|
|
|
lv_obj_flag_t str_to_lv_obj_flag(const char *flag)
|
|
{
|
|
if (strcmp(flag, "LV_OBJ_FLAG_HIDDEN") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_HIDDEN;
|
|
}
|
|
else if (strcmp(flag, "LV_OBJ_FLAG_CLICKABLE") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_CLICKABLE;
|
|
}
|
|
else if (strcmp(flag, "LV_OBJ_FLAG_SCROLLABLE") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_SCROLLABLE;
|
|
}
|
|
else if (strcmp(flag, "LV_OBJ_FLAG_CLICK_FOCUSABLE") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_CLICK_FOCUSABLE;
|
|
}
|
|
else if (strcmp(flag, "LV_OBJ_FLAG_SCROLL_ON_FOCUS") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_SCROLL_ON_FOCUS;
|
|
}
|
|
else if (strcmp(flag, "LV_OBJ_FLAG_SCROLL_CHAIN") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_SCROLL_CHAIN;
|
|
}
|
|
else if (strcmp(flag, "LV_OBJ_FLAG_PRESS_LOCK") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_PRESS_LOCK;
|
|
}
|
|
else if (strcmp(flag, "LV_OBJ_FLAG_OVERFLOW_VISIBLE") == 0)
|
|
{
|
|
return LV_OBJ_FLAG_OVERFLOW_VISIBLE;
|
|
}
|
|
else
|
|
{
|
|
return 0; // Unknown flag
|
|
}
|
|
}
|
|
|
|
void jetkvm_ui_init() {
|
|
lvgl_init();
|
|
}
|
|
|
|
void jetkvm_ui_tick() {
|
|
lvgl_tick();
|
|
}
|
|
|
|
void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler) {
|
|
video_state_handler = handler;
|
|
}
|
|
|
|
void jetkvm_ui_set_rotation(u_int8_t rotation)
|
|
{
|
|
printf("setting rotation to %d\n", rotation);
|
|
if (rotation == 90)
|
|
{
|
|
lv_disp_set_rotation(NULL, LV_DISP_ROT_90);
|
|
} else if (rotation == 180) {
|
|
lv_disp_set_rotation(NULL, LV_DISP_ROT_180);
|
|
} else if (rotation == 270) {
|
|
lv_disp_set_rotation(NULL, LV_DISP_ROT_270);
|
|
} else {
|
|
lv_disp_set_rotation(NULL, LV_DISP_ROT_NONE);
|
|
}
|
|
}
|
|
|
|
const char *jetkvm_ui_get_current_screen() {
|
|
return ui_get_current_screen();
|
|
}
|
|
|
|
void jetkvm_ui_load_screen(const char *obj_name) {
|
|
lv_obj_t *obj = ui_get_obj(obj_name);
|
|
if (obj == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (lv_scr_act() != obj) {
|
|
lv_scr_load(obj);
|
|
}
|
|
}
|
|
|
|
int jetkvm_ui_set_text(const char *obj_name, const char *text) {
|
|
lv_obj_t *obj = ui_get_obj(obj_name);
|
|
if (obj == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(lv_label_get_text(obj), text) == 0) {
|
|
return 1;
|
|
}
|
|
|
|
lv_label_set_text(obj, text);
|
|
return 0;
|
|
}
|
|
|
|
void jetkvm_ui_set_image(const char *obj_name, const char *image_name) {
|
|
lv_obj_t *obj = ui_get_obj(obj_name);
|
|
if (obj == NULL) {
|
|
return;
|
|
}
|
|
lv_img_set_src(obj, image_name);
|
|
}
|
|
|
|
void jetkvm_ui_set_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;
|
|
}
|
|
lv_obj_clear_state(obj, LV_STATE_USER_1 | LV_STATE_USER_2);
|
|
lv_obj_add_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) {
|
|
return -1;
|
|
}
|
|
|
|
lv_obj_flag_t flag_val = str_to_lv_obj_flag(flag_name);
|
|
if (flag_val == 0)
|
|
{
|
|
return -2;
|
|
}
|
|
lv_obj_add_flag(obj, flag_val);
|
|
return 0;
|
|
}
|
|
|
|
int jetkvm_ui_clear_flag(const char *obj_name, const char *flag_name) {
|
|
lv_obj_t *obj = ui_get_obj(obj_name);
|
|
if (obj == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
lv_obj_flag_t flag_val = str_to_lv_obj_flag(flag_name);
|
|
if (flag_val == 0)
|
|
{
|
|
return -2;
|
|
}
|
|
lv_obj_clear_flag(obj, flag_val);
|
|
return 0;
|
|
}
|
|
|
|
void jetkvm_ui_fade_in(const char *obj_name, u_int32_t duration) {
|
|
lv_obj_t *obj = ui_get_obj(obj_name);
|
|
if (obj == NULL) {
|
|
return;
|
|
}
|
|
lv_obj_fade_in(obj, duration, 0);
|
|
}
|
|
|
|
void jetkvm_ui_fade_out(const char *obj_name, u_int32_t duration) {
|
|
lv_obj_t *obj = ui_get_obj(obj_name);
|
|
if (obj == NULL) {
|
|
return;
|
|
}
|
|
lv_obj_fade_out(obj, duration, 0);
|
|
}
|
|
|
|
void jetkvm_ui_set_opacity(const char *obj_name, u_int8_t opacity) {
|
|
lv_obj_t *obj = ui_get_obj(obj_name);
|
|
if (obj == NULL) {
|
|
return;
|
|
}
|
|
lv_obj_set_style_opa(obj, opacity, LV_PART_MAIN);
|
|
}
|
|
|
|
void jetkvm_video_start() {
|
|
video_start_streaming();
|
|
}
|
|
|
|
void jetkvm_video_stop() {
|
|
video_stop_streaming();
|
|
}
|
|
|
|
int jetkvm_video_set_quality_factor(float quality_factor) {
|
|
if (quality_factor < 0 || quality_factor > 1) {
|
|
return -1;
|
|
}
|
|
video_set_quality_factor(quality_factor);
|
|
return 0;
|
|
}
|
|
|
|
int jetkvm_video_set_edid(const char *edid_hex) {
|
|
uint8_t edid[256];
|
|
int edid_len = hex_to_bytes(edid_hex, edid, 256);
|
|
if (edid_len < 0) {
|
|
return -1;
|
|
}
|
|
return set_edid(edid, edid_len);
|
|
}
|
|
|
|
char *jetkvm_video_get_edid_hex() {
|
|
uint8_t edid[256];
|
|
int edid_len = get_edid(edid, 256);
|
|
if (edid_len < 0) {
|
|
return NULL;
|
|
}
|
|
return bytes_to_hex(edid, edid_len);
|
|
}
|
|
|
|
jetkvm_video_state_t *jetkvm_video_get_status() {
|
|
return &state;
|
|
}
|
|
|
|
int jetkvm_video_init() {
|
|
return video_init();
|
|
}
|
|
|
|
void jetkvm_video_shutdown() {
|
|
video_shutdown();
|
|
} |