다른 UI Framework 지원¶
Oasis는 Qt, LVGL과 같은 다른 UI Framework과 같이 사용할 수 있습니다.
LVGL¶
LVGL을 Oasis와 동시에 사용하려면 아래와 같은 순서로 진행합니다.
oasis::ui::setup호출 시hide-unmanaged-ui-layer패러미터 키 값으로 LVGL이 사용하는 HLAYER로 설정합니다. 예로 "1:2:0" 과 같습니다.oasis::ui::getDisplayHandle를 호출하여 Display 핸들을 얻습니다.lv_scr_setup호출 시 매개변수로 Display 핸들, 스크린 크기, Layer ID를 지정합니다. HLAYER는 (채널, 레이어 ID, 0) 으로 구성되어 있습니다.
위 2와 3번 과정은 LVGL 버전에 따라 다를 수 있습니다.
아래는 KL730 칩셋에서 lvgl 6.1.2과 lv_drivers 6.1.1를 응용 프로그램의 메인 UI 프레임워크로 사용할 경우 예입니다.
Oasis UI를 초기화 합니다. LVGL 그래픽은 Layer 2에서 처리하고, Oasis에서 Video YUV 영상은 Layer 0에서, Oasis UI는 Layer 1에서 사용하도록 설정합니다. hide-unmanaged-ui-layer에 LVGL에서 사용할 HLAYER를 지정합니다.
static int32_t oasis_setup()
{
int32_t err;
oasis::key_value_map_t parameters;
parameters["offs-disable"] = "1";
//parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
if(oasis::initialize(parameters) < 0) {
DLOG(DLOG_ERROR, "Oasis init failed\n");
return -1;
}
////////////////////////////////////////////////////////////////////////////////////////////
// display setup
parameters.clear();
parameters["memory-type"] = "ion"; //cma
parameters["screen-width"] = "480";
parameters["screen-height"] = "320";
display::setup(parameters);
//////////////////////////////////////////////////////////////////////////////////////////
// ui setup
parameters.clear();
parameters["system-font-size"] = "11";
parameters["system-font-path"] = "/mnt/sd/NanumGothicCoding.ttf";
parameters["display-rotation"] = "0";
parameters["enable-touch"] = "1";
parameters["touch-cal-data-path"] = "/mnt/sd/cal.dat";
parameters["vi0-hlayer"] = "1:0:0"; // VMF_VDISP_L0
parameters["fb0-hlayer"] = "1:1:0"; // VMF_VDISP_L1
parameters["hide-unmanaged-ui-layer"] = "1:2:0"; // VMF_VDISP_L2
err = ui::setup(parameters);
if (err < 0) {
DLOG(DLOG_ERROR, "Oasis ui::setup failed\n");
oasis::finalize();
return -1;
}
return 0;
}
LVGL 라이브러리를 초기화합니다.
static int32_t lvgl_setup(void *disp_handle)
{
static int32_t pool_size = 32;
static int32_t gui_width = 480;
static int32_t gui_height = 320;
static int32_t gui_x = 0;
static int32_t gui_y = 0;
unsigned int disp_buf_size;
lv_disp_buf_t disp_buf;
lv_disp_drv_t disp_drv;
lv_indev_drv_t indev_drv;
lv_indev_t *my_indev;
lv_init(pool_size);
lv_scr_setup(disp_handle, gui_width, gui_height, gui_x, gui_y, VMF_VDISP_L2);
if(v4l2dev_init()) {
fprintf(stderr, "v4l2dev_init failed!\n");
lv_release();
return -1;
}
/*Init display buffer, allocate buffer for screen-flushing*/
disp_buf_size = 10 * lv_scr_get_hor_res();
lvgl_buf = (lv_color_t*)malloc(disp_buf_size * sizeof *lvgl_buf);
/*Initialize a descriptor for the buffer*/
lv_disp_buf_init(&disp_buf, lvgl_buf, NULL, disp_buf_size);
/*Initialize and register a display driver*/
lv_disp_drv_init(&disp_drv);
disp_drv.buffer = &disp_buf;
disp_drv.flush_cb = v4l2dev_flush;
lv_disp_drv_register(&disp_drv);
return 0;
}
위 코드에서 disp_handle은 oasis::ui::getDisplayHandle 를 호출하여 얻어옵니다.
void *disp_handle = ui::getDisplayHandle();
LVGL 초기화 다음에 LVGL 라이브러리를 이용하여 UI를 구성합니다. 아래는 화면에 세로 방향으로 4개 버튼을 생성하는 예입니다.
static void lvgl_draw_screen(void)
{
lv_obj_t *scr = NULL;
static lv_style_t button_style_rel;
static lv_style_t button_style_pre;
uint32_t btn_w = lv_scr_get_hor_res() / 2;
uint32_t btn_h = lv_scr_get_ver_res() / 8;
static const char *btn_txt[4] = {
"page1", "page2", "page3", "page4"
};
lv_point_t positons[4];
/*Init screen*/
scr = lv_disp_get_scr_act(NULL);
lv_obj_clean(scr);
lv_obj_set_style(scr, &lv_style_transp);
/*Init button styles*/
lv_style_copy(&button_style_rel, &lv_style_btn_rel);
lv_style_copy(&button_style_pre, &lv_style_btn_pr);
button_style_rel.body.opa = LV_OPA_TRANSP;
button_style_rel.body.border.opa = LV_OPA_TRANSP;
button_style_rel.text.color = LV_COLOR_WHITE;
button_style_pre.body.opa = LV_OPA_TRANSP;
button_style_pre.body.border.opa = LV_OPA_TRANSP;
button_style_pre.text.color = LV_COLOR_YELLOW;
for (int i = 0; i < 4; i++) {
lv_obj_t *btn;
char name[32];
lv_obj_t *label;
uint32_t offset = btn_h * 2 + i * btn_h;
lv_area_t coords;
btn = lv_btn_create(scr, NULL);
lv_btn_set_style(btn, LV_BTN_STYLE_REL, &button_style_rel);
lv_btn_set_style(btn, LV_BTN_STYLE_PR, &button_style_pre);
lv_obj_set_size(btn, btn_w, btn_h);
lv_obj_align(btn, NULL, LV_ALIGN_IN_TOP_MID, 0, offset);
lv_obj_get_coords(btn, &coords);
positons[i].x = (coords.x1 + coords.x2) >> 1;
positons[i].y = (coords.y1 + coords.y2) >> 1;
strncpy(name, btn_txt[i], 31);
label = lv_label_create(btn, NULL);
lv_label_set_text(label, name);
}
}
Oasis UI를 사용할 경우, 아래 코드 예 처럼 Oasis UI API를 이용합니다. 아래 예는 화면 중앙에 "Hello, World!" 라벨을 표시합니다.
static void oasis_draw_screen()
{
ui::ScreenRef screen = ui::getDefaultScreen();
int32_t screen_width = ui::screenWidth(screen);
int32_t screen_height = ui::screenHeight(screen);
ui::WindowRef window = ui::createWindow("Hello");
ui::BoxRef vbox = ui::createBox(ui::kOrientationVertical, false, 6);
ui::setVAlign(vbox, ui::kAlignCenter);
ui::LabelRef label = ui::createLabelWithText("Hello, World!");
ui::setVAlign(label, ui::kAlignCenter);
ui::setHAlign(label, ui::kAlignCenter);
ui::setLabelHorzJustification(label, ui::kJustificationCenter);
ui::setLabelVertJustification(label, ui::kJustificationCenter);
ui::setLabelFontPointSize(label, 16);
ui::boxPackStart(vbox, label, true, true, 0);
ui::containerAdd(window, vbox);
ui::screenAdd(screen, window);
ui::setVisible(window, true);
}
Note
LVGL 을 메인 쓰레드가 아닌 별도 쓰레드를 생성하여 사용합니다.
종료시에 LVGL을 해제합니다.
static void lvgl_cleanup()
{
if(lvgl_buf) {
free(lvgl_buf);
lvgl_buf = nullptr;
}
v4l2dev_release();
lv_release();
}
LVGL 해제 후 Oasis도 해제합니다.
static void oasis_cleanup()
{
oasis::ui::cleanup();
oasis::finalize();
}
전체 예제코드는 아래와 같습니다.
#include "OasisAPI.h"
#include "OasisLog.h"
#include "OasisUI.h"
#include "OasisUtil.h"
#include "OasisDisplay.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <thread>
#include <mutex>
#include <memory>
#include <condition_variable>
#include "lvgl/lvgl.h"
#include "lvgl/custom/lv_custom.h"
#include "lv_drivers/display/v4l2dev.h"
#undef DLOG_TAG
#define DLOG_TAG "Hello-LVGL"
using namespace oasis;
static bool got_interrupt = false;
static lv_color_t *lvgl_buf = nullptr;
#include <signal.h>
void cancel_handler(int sig)
{
got_interrupt = true;
}
static void lvgl_draw_screen(void)
{
lv_obj_t *scr = NULL;
static lv_style_t button_style_rel;
static lv_style_t button_style_pre;
uint32_t btn_w = lv_scr_get_hor_res() / 2;
uint32_t btn_h = lv_scr_get_ver_res() / 8;
static const char *btn_txt[4] = {
"page1", "page2", "page3", "page4"
};
lv_point_t positons[4];
/*Init screen*/
scr = lv_disp_get_scr_act(NULL);
lv_obj_clean(scr);
lv_obj_set_style(scr, &lv_style_transp);
/*Init button styles*/
lv_style_copy(&button_style_rel, &lv_style_btn_rel);
lv_style_copy(&button_style_pre, &lv_style_btn_pr);
button_style_rel.body.opa = LV_OPA_TRANSP;
button_style_rel.body.border.opa = LV_OPA_TRANSP;
button_style_rel.text.color = LV_COLOR_WHITE;
button_style_pre.body.opa = LV_OPA_TRANSP;
button_style_pre.body.border.opa = LV_OPA_TRANSP;
button_style_pre.text.color = LV_COLOR_YELLOW;
for (int i = 0; i < 4; i++) {
lv_obj_t *btn;
char name[32];
lv_obj_t *label;
uint32_t offset = btn_h * 2 + i * btn_h;
lv_area_t coords;
btn = lv_btn_create(scr, NULL);
lv_btn_set_style(btn, LV_BTN_STYLE_REL, &button_style_rel);
lv_btn_set_style(btn, LV_BTN_STYLE_PR, &button_style_pre);
lv_obj_set_size(btn, btn_w, btn_h);
lv_obj_align(btn, NULL, LV_ALIGN_IN_TOP_MID, 0, offset);
lv_obj_get_coords(btn, &coords);
positons[i].x = (coords.x1 + coords.x2) >> 1;
positons[i].y = (coords.y1 + coords.y2) >> 1;
strncpy(name, btn_txt[i], 31);
label = lv_label_create(btn, NULL);
lv_label_set_text(label, name);
}
}
static int32_t lvgl_setup(void *disp_handle)
{
static int32_t pool_size = 32;
static int32_t gui_width = 480;
static int32_t gui_height = 320;
static int32_t gui_x = 0;
static int32_t gui_y = 0;
unsigned int disp_buf_size;
lv_disp_buf_t disp_buf;
lv_disp_drv_t disp_drv;
lv_indev_drv_t indev_drv;
lv_indev_t *my_indev;
lv_init(pool_size);
lv_scr_setup(disp_handle, gui_width, gui_height, gui_x, gui_y, VMF_VDISP_L2);
if(v4l2dev_init()) {
fprintf(stderr, "v4l2dev_init failed!\n");
lv_release();
return -1;
}
/*Init display buffer, allocate buffer for screen-flushing*/
disp_buf_size = 10 * lv_scr_get_hor_res();
lvgl_buf = (lv_color_t*)malloc(disp_buf_size * sizeof *lvgl_buf);
/*Initialize a descriptor for the buffer*/
lv_disp_buf_init(&disp_buf, lvgl_buf, NULL, disp_buf_size);
/*Initialize and register a display driver*/
lv_disp_drv_init(&disp_drv);
disp_drv.buffer = &disp_buf;
disp_drv.flush_cb = v4l2dev_flush;
lv_disp_drv_register(&disp_drv);
return 0;
}
static void lvgl_cleanup()
{
if(lvgl_buf) {
free(lvgl_buf);
lvgl_buf = nullptr;
}
v4l2dev_release();
lv_release();
}
static void oasis_draw_screen()
{
ui::ScreenRef screen = ui::getDefaultScreen();
int32_t screen_width = ui::screenWidth(screen);
int32_t screen_height = ui::screenHeight(screen);
ui::WindowRef window = ui::createWindow("Hello");
ui::BoxRef vbox = ui::createBox(ui::kOrientationVertical, false, 6);
ui::setVAlign(vbox, ui::kAlignCenter);
ui::LabelRef label = ui::createLabelWithText("Hello, World!");
ui::setVAlign(label, ui::kAlignCenter);
ui::setHAlign(label, ui::kAlignCenter);
ui::setLabelHorzJustification(label, ui::kJustificationCenter);
ui::setLabelVertJustification(label, ui::kJustificationCenter);
ui::setLabelFontPointSize(label, 16);
ui::boxPackStart(vbox, label, true, true, 0);
ui::containerAdd(window, vbox);
ui::screenAdd(screen, window);
ui::setVisible(window, true);
}
static int32_t oasis_setup()
{
int32_t err;
oasis::key_value_map_t parameters;
parameters["offs-disable"] = "1";
//parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
if(oasis::initialize(parameters) < 0) {
DLOG(DLOG_ERROR, "Oasis init failed\n");
return -1;
}
////////////////////////////////////////////////////////////////////////////////////////////
// display setup
parameters.clear();
parameters["memory-type"] = "ion"; //cma
parameters["screen-width"] = "480";
parameters["screen-height"] = "320";
display::setup(parameters);
//////////////////////////////////////////////////////////////////////////////////////////
// ui setup
parameters.clear();
parameters["system-font-size"] = "11";
parameters["system-font-path"] = "/mnt/sd/NanumGothicCoding.ttf";
parameters["display-rotation"] = "0";
parameters["enable-touch"] = "1";
parameters["touch-cal-data-path"] = "/mnt/sd/cal.dat";
parameters["vi0-hlayer"] = "1:0:0"; // VMF_VDISP_L0
parameters["fb0-hlayer"] = "1:1:0"; // VMF_VDISP_L1
parameters["hide-unmanaged-ui-layer"] = "1:2:0"; // VMF_VDISP_L2
err = ui::setup(parameters);
if (err < 0) {
DLOG(DLOG_ERROR, "Oasis ui::setup failed\n");
oasis::finalize();
return -1;
}
return 0;
}
static void oasis_cleanup()
{
oasis::ui::cleanup();
oasis::finalize();
}
int main(int argc, char *argv[])
{
int32_t err;
std::thread t;
signal(SIGINT, cancel_handler);
if(oasis_setup() < 0) {
fprintf(stderr, "Oasis setup failed\n");
return -1;
}
oasis_draw_screen();
t = std::thread([&]() {
void *disp_handle = ui::getDisplayHandle();
if(!disp_handle) {
fprintf(stderr, "display handle not found!\n");
got_interrupt = true;
return;
}
if(lvgl_setup(disp_handle) < 0) {
got_interrupt = true;
return;
}
lvgl_draw_screen();
do {
try {
lv_task_handler();
} catch(...) {
fprintf(stderr, "lvgl got exception!\n");
got_interrupt = true;
break;
}
usleep(10000);
} while(got_interrupt == false);
lvgl_cleanup();
});
fprintf(stdout, "press ctrl+c to exit\n");
do {
usleep(10000);
} while(got_interrupt == false);
t.join();
done:
oasis_cleanup();
return 0;
}
아래는 실행 화면 입니다.
