文章目录
- 0. 控制台输出信息等级设置
- 0.1 设置log level = 4 无法正常启动
- 1.宏观启动流程
- 1.1 控制台入口函数finsh_thread_entry()执行《startup.sh》
- 1.2 《startup.sh》启动桌面GUI模块
- 1.2.1 《startup.sh》加载 desktop.mod
- 1.2.2 desktop.mod加载 init.axf
- 1.2.3 init.axf 介绍
- 1.2.3.1 INIT_MOpen()调用application_init_process()启动消息死循环
- 1.2.3.2 application_init_process()调用gscene_bgd_init()设置背景图片
- 1.2.3.3 application_init_process()调用_process_init()
- 2. GUI桌面app加载
- 2.1 app_root.axf 介绍
- 2.2 主函数 app_root_start()调用app_root_wincreate()创建根窗口
- 2.2 主函数 _home_on_create()调用init_multi_screen_res()初始化图片、文字资源
- 2.3 dsk_theme_open() 通过ID获取图片数据
- 3.绘制主界面
- 3.1 draw_multi_screen_icon_coor_rect()函数调用表格
在阅读Melis4.0GUI加载流程,顺手做笔记,供自己查阅。
0. 控制台输出信息等级设置
为了方便跟踪代码执行流程,可以参考控制台的输出信息。控制台输出调试信息有6个等级,分别为0-5:
#define OPTION_LOG_LEVEL_CLOSE 0
#define OPTION_LOG_LEVEL_LOG 1
#define OPTION_LOG_LEVEL_ERROR 2
#define OPTION_LOG_LEVEL_WARNING 3
#define OPTION_LOG_LEVEL_INFO 4
#define OPTION_LOG_LEVEL_MESSAGE 5
在GUI源码中,有很多INFO级别的调试信息,可以打开观察学习:
0.1 设置log level = 4 无法正常启动
设置log level = 4,测试了一下,显示开机界面后就死机了,一直打印下面的信息,等了几分钟都没有反应:
[INF]: [riscv_cpu_handle_exception:0271]: ecall SYSCALL_FROM_S
只好设置为log level = 3 。可以正常使用,但是界面切换比 log level = 2会慢一点。
1.宏观启动流程
Melis启动顺序的介绍在《Melis4.0 RTOS_方案快速开发指南》,这里先上一张该文档的截图:
1.1 控制台入口函数finsh_thread_entry()执行《startup.sh》
这个函数在文件 《ekernel\subsys\finsh_cli\shell_entry.c》 中,在进入cli(command line interface)之前,会执行《startup.sh》文件。
1.2 《startup.sh》启动桌面GUI模块
1.2.1 《startup.sh》加载 desktop.mod
该文件的路径为 《projects\d1s-mq\data\UDISK\startup.sh》 ,内容如下:
echo "Execute startup script begin.............."
#insmod d:\mod\display.mod
insmod d:\mod\orange.mod
insmod d:\apps\desktop.mod
echo "...............Execute startup script end"
desktop.mod就是桌面GUI的入口app。它主要由《D1s-Melis\livedesk\beetles\mod_desktop\》目录下的文件编译链接产生。该目录下的Makefile文件:
ccflags-y += -DEPDK_DEBUG_LEVEL=EPDK_DEBUG_LEVEL_LOG_ALL \
-DUSED_BY_DESKTOP \
$(SOLUTION_INCLUDE)
usrlibs-y += -L$(srctree)/${elibrary-libs}/ \
--start-group \
-lsyscall -lminic \
-llzma -lcharset \
-lmediainfo \
--end-group
#-lmediainfo
MOD_NAME := desktop
SUF_NAME := mod
PRJ_PATH := $(MELIS_BASE)/projects/${TARGET_BOARD}/data/UDISK/apps/
TEMP_PATH := $(MELIS_BASE)/emodules/bin
$(MOD_NAME)-objs += desktop_api.o
$(MOD_NAME)-objs += mod_desktop.o
$(MOD_NAME)-objs += magic.o
$(MOD_NAME)-objs += built-in.o
$(MOD_NAME)-objs += engine/
$(MOD_NAME)-objs += framework/
$(MOD_NAME)-objs += functions/
$(MOD_NAME)-objs += msg_srv/
$(MOD_NAME)-objs += util/
include $(MELIS_BASE)/scripts/Makefile.mods
1.2.2 desktop.mod加载 init.axf
mod_desktop.c 中加载GUI的函数 DESKTOP_MOpen()代码,其中的 init.axf 就是完成GUI准备工作可执行文件 :
__mp *DESKTOP_MOpen(__u32 mid, __u32 mod)
{
__log("DESKTOP_MOpen.");
int ret = -1, value = 0;
desktp_data.mid = mid;
ret = esCFG_GetKeyValue("card_product", "card_product_used", (int32_t *)&value, 1);
if (ret == -1 || value == 0)
{
desktp_data.init_id = esMODS_MInstall(BEETLES_APP_ROOT"apps\\init.axf", 0);
} else if (value) {
desktp_data.init_id = esMODS_MInstall(BEETLES_APP_ROOT"apps\\init_sd_product.axf", 0);
}
desktp_data.init_mp = esMODS_MOpen(desktp_data.init_id, 0);
return (__mp *)&desktp_data;
}
1.2.3 init.axf 介绍
请注意,这里有2个C文件名很相似:init_mod.c 和 mod_init.c 。
init.axf由 《livedesk\beetles\init》 目录下的源码编译链接产生。看看该目录下的Makefile部分内容:
主要函数INIT_MOpen()在文件 《livedesk\beetles\init\mod_init.c》 中:
__mp *INIT_MOpen(__u32 mid, __u32 mod)
{
__inf("----------------------INIT_MOpen --------------------------\n");
msg_emit_init();
init_data.mid = mid;
init_data.init_tid = esKRNL_TCreate(application_init_process, NULL, 0x10000, KRNL_priolevel3);
esKRNL_TaskNameSet(init_data.init_tid, "initprocess");
if (init_data.init_tid == 0)
{
__err(" application_init_process main thread create error\n");
return NULL;
}
return (__mp *)&init_data;
}
1.2.3.1 INIT_MOpen()调用application_init_process()启动消息死循环
application_init_process()在文件 《livedesk\beetles\init\init_mod.c》 中:
void application_init_process(void *arg)
{
.....
__log("%s %d before init_mainwin_create", __FUNCTION__, __LINE__);
init_mainwin = init_mainwin_create();
.....
_process_init();
__log("%s %d before GUI_GetMessageEx", __FUNCTION__, __LINE__);
/* message loop*/
while (GUI_GetMessageEx(&msg, init_mainwin)) // 消息循环
{
#ifdef __MSG
if (msg.id != GUI_MSG_TIMER)
{
__msg("msg.h_deswin=%x, msg.id=%d, msg.dwAddData1=%d,msg.dwAddData2=%d", msg.h_deswin, msg.id, msg.dwAddData1, msg.dwAddData2);
}
#endif
ret = GUI_DispatchMessage(&msg); // 分发消息到回调
if (msg.p_arg) // 同步消息回应
{
GUI_SetSyncMsgRetVal(&msg, ret);
GUI_PostSyncSem(&msg);
}
}
__log("%s %d want to shutdown_logo_show", __FUNCTION__, __LINE__);// add by hwd00001
.....
}
上面的代码一直停留在消息循环中,正常情况不会退出循环。我在消息循环后面加了打印log信息,一直没有看到log输出。
1.2.3.2 application_init_process()调用gscene_bgd_init()设置背景图片
逐级调用函数 | 所在文件 |
---|---|
application_init_process() | livedesk\beetles\init\init_mod.c |
gscene_bgd_init() | livedesk\beetles\init\background\gscene_backgrd.c |
gscene_bgd_set_status_show() | livedesk\beetles\init\background\gscene_backgrd.c |
gscene_bgd_update_filename() | livedesk\beetles\init\background\gscene_backgrd.c |
gscene_bgd_get_default_file() | livedesk\beetles\init\background\gscene_backgrd.c |
get_logo_mode() | livedesk\beetles\init\background\fb_lib\backlayer_lib.c |
这里重点分析一下get_logo_mode():
uint32_t get_logo_mode(void)
{
if (esCFG_GetKeyValue("backlayer", "backlayer_mode", &bk_mode, 1) != EPDK_OK)
{
bk_mode = 0;
__err("get_logo_mode err!");
}
return bk_mode;
}
esCFG_GetKeyValue()函数是从 sys_config_nor.fex 中读取键值,这里是 backlayer_mode=0:
gscene_bgd_get_default_file()于是获取的图片名称为 res\bg_default0.jpg ,对应的SDK目录下的 《projects\d1s-mq\data\UDISK\res\bg_default0.jpg》 ,只要更换这个图片,使用相同的名称,就可以改变背景图片。
sys_config.fex 的读写可参考官方文档《Melis4.0_RTOS_配置(sys_config)_开发指南.pdf》
gscene_bgd_get_default_file()部分源码:
static int32_t gscene_bgd_get_default_file(char *filename)
{
int32_t index;
char path[BG_MAX_CHAR_LEN] = {0};
index = gscene_bgd_get_default_bg_index();
if (get_logo_mode() == JPG_MODE)
{
eLIBs_sprintf(path, "%sbg_default%d.jpg", BG_DEFAULT_PATH, index);
}
else
{
eLIBs_sprintf(path, "%sbg_default%d.bgd", BG_DEFAULT_PATH, index);
}
......
}
1.2.3.3 application_init_process()调用_process_init()
application_init_process()和_process_init()位于同一个C文件。
static void _process_init(void)
{
H_WIN scene_adjust;
int32_t flag = 2;
char load_para[1024] = {0};
_framework_init();
#if CONFIG_SUPPORT_TOUCHPANEL
open("/dev/tpadc_rtp", O_WRONLY);
#endif
eLIBs_memcpy(load_para, &flag, 4);
__msg("********_process_init***********");
activity_set_load_para("root", "", load_para, sizeof(load_para));
activity_load_app("application://app_root");
}
到此处,加载 app_root.axf,启动GUI桌面程序。
2. GUI桌面app加载
2.1 app_root.axf 介绍
app_root.axf由 《livedesk\beetles\sun20iw1_app\apps》 目录下的源码编译链接产生。看看该文件下的Makefile部分内容:
2.2 主函数 app_root_start()调用app_root_wincreate()创建根窗口
app_root_start()在文件 《livedesk\beetles\sun20iw1_app\apps\app_root\app_root.c》 中:
/**********************************************************************************************************************
插件接口实现
**********************************************************************************************************************/
static int32_t app_root_start(Activity *activity)
{
__inf("**************app_root plugin start!**************** \n");
__inf("****************************\n");
app_root_init_res();
root_activity = activity;
happ_root_manwin = app_root_wincreate(activity);
if (NULL == happ_root_manwin)
{
__inf(" app_root_wincreate fail! \n");
return EPDK_FAIL;
}
else
{
GUI_WinSetFocusChild(happ_root_manwin);
__inf(" app_root_wincreate success! \n");
return EPDK_OK;
}
}
E:\C_code\D1s-Melis\livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c
逐级调用函数 | 所在文件 |
---|---|
app_root_start() | livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c |
app_root_wincreate() | livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c |
app_root_win_proc() | livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c |
app_home_create() | livedesk\beetles\sun20iw1_app\apps\multi_screen_home\app_multi_screen_home.c |
_home_on_create() | livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c |
desktop_scene_create() | livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c |
2.2 主函数 _home_on_create()调用init_multi_screen_res()初始化图片、文字资源
跟踪到这里,虽然还不知道如何显示界面的图片,不过已经明白如何替换图片了。
init_multi_screen_res()初始化主界面的8个图片(每个图片分焦点和非焦点2种),8个字符串。
static const __s32 multi_screen_uf_icon[HOME_UNFOCUS_ICON_NUM] =
{
//固定应用
ID_HOME_NEW_EXPLORER_UF_BMP,
ID_HOME_NEW_MOVIE_UF_BMP,
ID_HOME_NEW_MUSIC_UF_BMP,
ID_HOME_NEW_PICTURE_UF_BMP,
ID_HOME_NEW_EBOOK_UF_BMP,
ID_HOME_NEW_FM_UF_BMP,
ID_HOME_NEW_RECORD_UF_BMP,
ID_HOME_NEW_SETTING_UF_BMP,
};
static const __s32 multi_screen_f_icon[HOME_FOCUS_ICON_NUM] =
{
//固定应用选中
ID_HOME_NEW_EXPLORER_FC_BMP,
ID_HOME_NEW_MOVIE_FC_BMP,
ID_HOME_NEW_MUSIC_FC_BMP,
ID_HOME_NEW_PICTURE_FC_BMP,
ID_HOME_NEW_EBOOK_FC_BMP,
ID_HOME_NEW_FM_FC_BMP,
ID_HOME_NEW_RECORD_FC_BMP,
ID_HOME_NEW_SETTING_FC_BMP
};
static const int32_t multi_screen_string[MULTI_SCREEN_STRING_MAX] =
{
STRING_HOME_EXPLORER,
STRING_HOME_MOVIE,
STRING_HOME_MUSIC,
STRING_HOME_PHOTO,
STRING_HOME_EBOOK,
STRING_HOME_FM,
STRING_HOME_RECORD,
STRING_HOME_SETTING
};
int32_t init_multi_screen_res(pmulti_screen_ui_t pui, __s16 current_focus)
{
uint32_t i;
//初始化unfocus ICON 资源
for (i = HOME_UNFOCUS_ICON_START; i < HOME_UNFOCUS_ICON_NUM; i++)
{
if (multi_screen_uf_icon[i])
{
pui->bmp_uf[i] = dsk_theme_open(multi_screen_uf_icon[i]);
if (pui->bmp_uf[i] == NULL)
{
__wrn("init_multi_screen_res() index:%d open fail\n", i);
}
}
else
{
pui->bmp_uf[i] = NULL;
}
}
/*focus icon*/
pui->bmp_f[current_focus] = dsk_theme_open(multi_screen_f_icon[current_focus]);
//初始化String 资源
for (i = 0; i < MULTI_SCREEN_STRING_MAX; i++)
{
dsk_langres_get_menu_text(multi_screen_string[i], pui->lang[i], GUI_NAME_MAX);
}
return EPDK_OK;
}
以图中文件管理图标为例,说说我的理解。
文件管理的非焦点图标ID为 ID_HOME_NEW_EXPLORER_UF_BMP ,焦点图标为 ID_HOME_NEW_EXPLORER_FC_BMP,对应的资源文件:
详细的可以阅读《livedesk\beetles\sun20iw1_app\res\theme》目录下的文件,我现在只是一知半解。
《facebuildercmd》生成 theme.h 和 theme.bin,h文件供源码使用,bin文件则嵌入到image中,在运行中使用。
2.3 dsk_theme_open() 通过ID获取图片数据
dsk_theme_open()部分源码:
int32_t init_multi_screen_res(pmulti_screen_ui_t pui, __s16 current_focus)
{
......
pui->bmp_uf[i] = dsk_theme_open(multi_screen_uf_icon[i]);
......
/*focus icon*/
pui->bmp_f[current_focus] = dsk_theme_open(multi_screen_f_icon[current_focus]);
......
}
dsk_theme_open()函数就是根据 theme.h 范围内的ID,到 theme.bin找到相应的图片数据。
3.绘制主界面
3.1 draw_multi_screen_icon_coor_rect()函数调用表格
逐级调用函数 | 所在文件 |
---|---|
desktop_scene_create() | livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c |
desktop_scene_frm_manager_proc() | livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c |
desktop_scene_view_show_all() | livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c |
draw_multi_screen_icon_coor_rect() | livedesk\beetles\sun20iw1_app\apps\multi_screen_home\ui\multi_screen_ui.c |
draw_multi_screen_icon_coor_rect()用来绘制界面图标,其中 ==uf_rect_800_480[8]==是指定图标的区域:
draw_multi_screen_icon_coor_rect(self->pui, HOME_UNFOCUS_ICON_START + i,
EPDK_FALSE, &uf_rect_800_480[i]);
《livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.h》部分内容:
static GUI_RECT string_rect_800_480[MULTI_SCREEN_STRING_MAX] =
{
{16 + 45, 80 + 100, 16 + 180 - 45, 80 + 142},
{16 + 180 + 16 + 45, 80 + 100, 16 + 180 + 16 + 180 - 45, 80 + 142},
{16 + 180 + 16 + 180 + 16 + 45, 80 + 100, 16 + 180 + 16 + 180 + 16 + 180 - 45, 80 + 142},
{16 + 180 + 16 + 180 + 16 + 180 + 16 + 45, 80 + 100, 16 + 180 + 16 + 180 + 16 + 180 + 16 + 180 - 45, 80 + 142},
{16 + 45, 80 + 50 + 100 + 142, 16 + 180 - 45, 80 + 142 + 50 + 142},
{16 + 180 + 16 + 45, 80 + 50 + 100 + 142, 16 + 180 + 16 + 180 - 45, 80 + 142 + 50 + 142},
{16 + 180 + 16 + 180 + 16 + 45, 80 + 50 + 100 + 142, 16 + 180 + 16 + 180 + 16 + 180 - 45, 80 + 142 + 50 + 142},
{16 + 180 + 16 + 180 + 16 + 180 + 16 + 45, 80 + 50 + 100 + 142, 16 + 180 + 16 + 180 + 16 + 180 + 16 + 180 - 45, 80 + 142 + 50 + 142},
};