BetaFlight模块设计之二十:CMS菜单模块分析

基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。

CMS菜单模块

描述:将BetaFlight所有配置内容在不同的设备上进行CMS(Custom Menu System)菜单显示。

 ├──> 初始化
 │   ├──> [x]硬件初始化
 │   └──> [v]业务初始化cmsInit
 ├──> 任务
 │   ├──> [x]实时任务
 │   ├──> [x]事件任务
 │   └──> [v]时间任务[TASK_CMS] = DEFINE_TASK("CMS", NULL, NULL, cmsHandler, TASK_PERIOD_HZ(20), TASK_PRIORITY_LOW),
 ├──> 驱动
 │   ├──> [x]查询
 │   └──> [x]中断
 └──> 接口
     ├──> bool cmsDisplayPortRegister(displayPort_t *pDisplay);  //注册显示CMS菜单的设备,可以注册多个。
     ├──> void cmsMenuOpen(void); //打开菜单
     ├──> const void *cmsMenuChange(displayPort_t *pPort, const void *ptr); //进入子菜单
     ├──> const void *cmsMenuExit(displayPort_t *pPort, const void *ptr); //退出菜单
     ├──> void cmsAddMenuEntry(OSD_Entry *menuEntry, char *text, uint16_t flags, CMSEntryFuncPtr func, void *data);
     └──> void cmsSetExternKey(cms_key_e extKey);

注:多菜单显示设备支持,详见支持的设备类型。

typedef enum {
    DISPLAYPORT_DEVICE_TYPE_MAX7456 = 0,
    DISPLAYPORT_DEVICE_TYPE_OLED,
    DISPLAYPORT_DEVICE_TYPE_MSP,
    DISPLAYPORT_DEVICE_TYPE_FRSKYOSD,
    DISPLAYPORT_DEVICE_TYPE_CRSF,
    DISPLAYPORT_DEVICE_TYPE_HOTT,
    DISPLAYPORT_DEVICE_TYPE_SRXL,
} displayPortDeviceType_e;

CMS菜单按键控制

  • 打开菜单:IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH)
  • 进入菜单:IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) //菜单打开的情况下
  • 向上翻页: IS_HI(PITCH)
  • 向下翻页:IS_LO(PITCH)
  • 向左翻页:IS_LO(ROLL)
  • 向右翻页:IS_HI(ROLL)
  • 退出选项:IS_LO(YAW)
  • 保存菜单:IS_HI(YAW)

CMS菜单Elements

整个菜单从组成结构上由elements构成,分为两大类:CMS_Menu、OSD_Etnry。

CMS_Menu

typedef const void *(*CMSMenuFuncPtr)(displayPort_t *pDisp);
typedef const void *(*CMSMenuOnExitPtr)(displayPort_t *pDisp, const OSD_Entry *self);
typedef const void *(*CMSMenuOnDisplayUpdatePtr)(displayPort_t *pDisp, const OSD_Entry *selected);
typedef struct
{
#ifdef CMS_MENU_DEBUG
    // These two are debug aids for menu content creators.
    const char *GUARD_text;
    const OSD_MenuElement GUARD_type;
#endif
    const CMSMenuFuncPtr onEnter;
    const CMSMenuOnExitPtr onExit;
    const CMSMenuOnDisplayUpdatePtr onDisplayUpdate;
    const OSD_Entry *entries;
} CMS_Menu;

OSD_Etnry

typedef const void *(*CMSEntryFuncPtr)(displayPort_t *displayPort, const void *ptr);
typedef struct
{
    const char * text;
    // Logical OR of OSD_MenuElement and flags below
    uint16_t flags;
    CMSEntryFuncPtr func;
    void *data;
} __attribute__((packed)) OSD_Entry;

Element类型

各种类型对应的菜单操作详见cmsHandleKey函数。

typedef enum
{
    OME_Label,    //标记字符串
    OME_Back,     //返回前级菜单
    OME_OSD_Exit,
    OME_Submenu,  //子菜单类型
    OME_Funcall,  //执行函数
    OME_Bool,
    OME_INT8,     //OSD_INT8_t
    OME_UINT8,    //OSD_UINT8_t
    OME_UINT16,   //OSD_UINT16_t
    OME_INT16,    //OSD_INT16_t
    OME_UINT32,   //OSD_UINT32_t
    OME_INT32,    //OSD_INT32_t
    OME_String,   //OSD_String_t
    OME_FLOAT,    //OSD_FLOAT_t
#ifdef USE_OSD
    OME_VISIBLE,
#endif
    OME_TAB,      //OSD_TAB_t, 列表
    OME_END,

    // Debug aid
    OME_MENU,

    OME_MAX = OME_MENU
} OSD_MenuElement;

可调Element类型

typedef struct
{
    uint8_t *val;
    uint8_t min;
    uint8_t max;
    uint8_t step;
} OSD_UINT8_t;

typedef struct
{
    int8_t *val;
    int8_t min;
    int8_t max;
    int8_t step;
} OSD_INT8_t;

typedef struct
{
    int16_t *val;
    int16_t min;
    int16_t max;
    int16_t step;
} OSD_INT16_t;

typedef struct
{
    uint16_t *val;
    uint16_t min;
    uint16_t max;
    uint16_t step;
} OSD_UINT16_t;

typedef struct
{
    int32_t *val;
    int32_t min;
    int32_t max;
    int32_t step;
} OSD_INT32_t;

typedef struct
{
    uint32_t *val;
    uint32_t min;
    uint32_t max;
    uint32_t step;
} OSD_UINT32_t;

typedef struct
{
    uint8_t *val;
    uint8_t min;
    uint8_t max;
    uint8_t step;
    uint16_t multipler;
} OSD_FLOAT_t;

typedef struct
{
    uint8_t *val;
    uint8_t max;
    const char * const *names;
} OSD_TAB_t;

CMS菜单结构

第一层菜单(Top Menu)

CMS_Menu cmsx_menuMain = {
#ifdef CMS_MENU_DEBUG
    .GUARD_text = "MENUMAIN",
    .GUARD_type = OME_MENU,
#endif
    .onEnter = mainMenuOnEnter,
    .onExit = NULL,
    .onDisplayUpdate = NULL,
    .entries = menuMainEntries,
};
static const OSD_Entry menuMainEntries[] =
{
    {"-- MAIN --",  OME_Label, NULL, NULL},

    {"PROFILE",     OME_Submenu,  cmsMenuChange, &cmsx_menuImu},
    {"FEATURES",    OME_Submenu,  cmsMenuChange, &cmsx_menuFeatures},
#ifdef USE_OSD
    {"OSD",         OME_Submenu,  cmsMenuChange, &cmsx_menuOsd},
#endif
    {"FC&FIRMWARE", OME_Submenu,  cmsMenuChange, &cmsx_menuFirmware},
    {"MISC",        OME_Submenu,  cmsMenuChange, &cmsx_menuMisc},
    {"SAVE/EXIT",   OME_Funcall,  cmsx_SaveExitMenu, NULL},
    {NULL, OME_END, NULL, NULL},
};

第二层菜单(SubMenu)

IMU菜单

CMS_Menu cmsx_menuImu = {
#ifdef CMS_MENU_DEBUG
    .GUARD_text = "XIMU",
    .GUARD_type = OME_MENU,
#endif
    .onEnter = cmsx_menuImu_onEnter,
    .onExit = cmsx_menuImu_onExit,
    .onDisplayUpdate = NULL,
    .entries = cmsx_menuImuEntries,
};
static const OSD_Entry cmsx_menuImuEntries[] =
{
    { "-- PROFILE --", OME_Label, NULL, NULL},

    {"PID PROF",  OME_UINT8,   cmsx_profileIndexOnChange,     &(OSD_UINT8_t){ &tmpPidProfileIndex, 1, PID_PROFILE_COUNT, 1}},
    {"PID",       OME_Submenu, cmsMenuChange,                 &cmsx_menuPid},
#ifdef USE_SIMPLIFIED_TUNING
    {"SIMPLIFIED TUNING",   OME_Submenu, cmsMenuChange,                 &cmsx_menuSimplifiedTuning},
#endif
    {"MISC PP",   OME_Submenu, cmsMenuChange,                 &cmsx_menuProfileOther},
    {"FILT PP",   OME_Submenu, cmsMenuChange,                 &cmsx_menuFilterPerProfile},

    {"RATE PROF", OME_UINT8,   cmsx_rateProfileIndexOnChange, &(OSD_UINT8_t){ &tmpRateProfileIndex, 1, CONTROL_RATE_PROFILE_COUNT, 1}},
    {"RATE",      OME_Submenu, cmsMenuChange,                 &cmsx_menuRateProfile},

    {"FILT GLB",  OME_Submenu, cmsMenuChange,                 &cmsx_menuFilterGlobal},
#if  (defined(USE_DYN_NOTCH_FILTER) || defined(USE_DYN_LPF)) && defined(USE_EXTENDED_CMS_MENUS)
    {"DYN FILT",  OME_Submenu, cmsMenuChange,                 &cmsx_menuDynFilt},
#endif

#ifdef USE_EXTENDED_CMS_MENUS
    {"COPY PROF", OME_Submenu, cmsMenuChange,                 &cmsx_menuCopyProfile},
#endif /* USE_EXTENDED_CMS_MENUS */

    {"BACK", OME_Back, NULL, NULL},
    {NULL, OME_END, NULL, NULL}
};

FEATURES菜单

static CMS_Menu cmsx_menuFeatures = {
#ifdef CMS_MENU_DEBUG
    .GUARD_text = "MENUFEATURES",
    .GUARD_type = OME_MENU,
#endif
    .onEnter = NULL,
    .onExit = NULL,
    .onDisplayUpdate = NULL,
    .entries = menuFeaturesEntries,
};
static const OSD_Entry menuFeaturesEntries[] =
{
    {"--- FEATURES ---", OME_Label, NULL, NULL},

#if defined(USE_BLACKBOX)
    {"BLACKBOX", OME_Submenu, cmsMenuChange, &cmsx_menuBlackbox},
#endif
#if defined(USE_VTX_CONTROL)
#if defined(USE_VTX_RTC6705) || defined(USE_VTX_SMARTAUDIO) || defined(USE_VTX_TRAMP)
    {"VTX", OME_Funcall, cmsSelectVtx, NULL},
#endif
#endif // VTX_CONTROL
#ifdef USE_LED_STRIP
    {"LED STRIP", OME_Submenu, cmsMenuChange, &cmsx_menuLedstrip},
#endif // LED_STRIP
    {"POWER", OME_Submenu, cmsMenuChange, &cmsx_menuPower},
#ifdef USE_CMS_FAILSAFE_MENU
    {"FAILSAFE", OME_Submenu, cmsMenuChange, &cmsx_menuFailsafe},
#endif
#ifdef USE_PERSISTENT_STATS
    {"PERSISTENT STATS", OME_Submenu, cmsMenuChange, &cmsx_menuPersistentStats},
#endif
    {"BACK", OME_Back, NULL, NULL},
    {NULL, OME_END, NULL, NULL}
};

CMS菜单

CMS_Menu cmsx_menuOsd = {
#ifdef CMS_MENU_DEBUG
    .GUARD_text = "MENUOSD",
    .GUARD_type = OME_MENU,
#endif
    .onEnter = cmsx_menuOsdOnEnter,
    .onExit = cmsx_menuOsdOnExit,
    .onDisplayUpdate = NULL,
    .entries = cmsx_menuOsdEntries
};
const OSD_Entry cmsx_menuOsdEntries[] =
{
    {"---OSD---",   OME_Label,   NULL,          NULL},
#ifdef USE_OSD_PROFILES
    {"OSD PROFILE", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_osdProfileIndex, 1, 3, 1}},
#endif
#ifdef USE_EXTENDED_CMS_MENUS
    {"ACTIVE ELEM", OME_Submenu, cmsMenuChange, &menuOsdActiveElems},
    {"TIMERS",      OME_Submenu, cmsMenuChange, &menuTimers},
    {"ALARMS",      OME_Submenu, cmsMenuChange, &menuAlarms},
#endif
#ifdef USE_MAX7456
    {"INVERT",    OME_Bool,  cmsx_max7456Update, &displayPortProfileMax7456_invert},
    {"BRT BLACK", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_blackBrightness, 0, 3, 1}},
    {"BRT WHITE", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_whiteBrightness, 0, 3, 1}},
#endif
    {"BACKGROUND",OME_TAB,   cmsx_osdBackgroundUpdate, &(OSD_TAB_t){&osdMenuBackgroundType, DISPLAY_BACKGROUND_COUNT - 1, lookupTableCMSMenuBackgroundType}},
    {"BACK", OME_Back, NULL, NULL},
    {NULL,   OME_END,  NULL, NULL}
};

FIRMWARE菜单

CMS_Menu cmsx_menuFirmware = {
#ifdef CMS_MENU_DEBUG
    .GUARD_text = "MENUFIRMWARE",
    .GUARD_type = OME_MENU,
#endif
#if defined(USE_BOARD_INFO)
    .onEnter = cmsx_FirmwareInit,
#else
    .onEnter = NULL,
#endif
    .onExit = NULL,
    .onDisplayUpdate = NULL,
    .entries = menuFirmwareEntries
};
static const OSD_Entry menuFirmwareEntries[] = {
    { "--- INFO ---", OME_Label, NULL, NULL },
    { "FWID", OME_String, NULL, FC_FIRMWARE_IDENTIFIER },
    { "FWVER", OME_String, NULL, FC_VERSION_STRING },
    { "GITREV", OME_String, NULL, __REVISION__ },
    { "TARGET", OME_String, NULL, __TARGET__ },
#if defined(USE_BOARD_INFO)
    { "MFR", OME_String, NULL, manufacturerId },
    { "BOARD", OME_String, NULL, boardName },
#endif
    { "--- SETUP ---", OME_Label, NULL, NULL },
    { "CALIBRATE",     OME_Submenu, cmsMenuChange, &cmsx_menuCalibration},
    { "BACK", OME_Back, NULL, NULL },
    { NULL, OME_END, NULL, NULL}
};

MISC菜单

CMS_Menu cmsx_menuMisc = {
#ifdef CMS_MENU_DEBUG
    .GUARD_text = "XMISC",
    .GUARD_type = OME_MENU,
#endif
    .onEnter = cmsx_menuMiscOnEnter,
    .onExit = cmsx_menuMiscOnExit,
    .onDisplayUpdate = NULL,
    .entries = menuMiscEntries
};
static const OSD_Entry menuMiscEntries[]=
{
    { "-- MISC --", OME_Label, NULL, NULL },

    { "MIN THR",       OME_UINT16 | REBOOT_REQUIRED,  NULL,          &(OSD_UINT16_t){ &motorConfig_minthrottle,            1000, 2000, 1 } },
    { "DIGITAL IDLE",  OME_UINT8 | REBOOT_REQUIRED,   NULL,          &(OSD_UINT8_t) { &motorConfig_digitalIdleOffsetValue,    0,  200, 1 } },
    { "FPV CAM ANGLE", OME_UINT8,   NULL,          &(OSD_UINT8_t) { &rxConfig_fpvCamAngleDegrees,           0,   90, 1 } },
    { "RC PREV",       OME_Submenu, cmsMenuChange, &cmsx_menuRcPreview},

    { "BACK", OME_Back, NULL, NULL},
    { NULL, OME_END, NULL, NULL}
};

注:只要是OME_Submenu类型的OSD_Entry就一直能通过cmsMenuChange进入下级菜单。

CMS菜单代码

所有菜单相关代码在src/main/cms/路径下。

src/main/cms/
  ├── cms_types.h            //CMS菜单相关类型定义
  ├── cms.c                  //CMS菜单控制操作
  ├── cms.h                  //CMS菜单模块对外接口定义
  ├── cms_menu_blackbox.c
  ├── cms_menu_blackbox.h     
  ├── cms_menu_failsafe.c
  ├── cms_menu_failsafe.h
  ├── cms_menu_firmware.c
  ├── cms_menu_firmware.h
  ├── cms_menu_gps_rescue.c
  ├── cms_menu_gps_rescue.h
  ├── cms_menu_imu.c
  ├── cms_menu_imu.h
  ├── cms_menu_ledstrip.c
  ├── cms_menu_ledstrip.h
  ├── cms_menu_main.c
  ├── cms_menu_main.h
  ├── cms_menu_misc.c
  ├── cms_menu_misc.h
  ├── cms_menu_osd.c
  ├── cms_menu_osd.h
  ├── cms_menu_persistent_stats.c
  ├── cms_menu_persistent_stats.h
  ├── cms_menu_power.c
  ├── cms_menu_power.h
  ├── cms_menu_saveexit.c
  ├── cms_menu_saveexit.h
  ├── cms_menu_vtx_common.c
  ├── cms_menu_vtx_common.h
  ├── cms_menu_vtx_rtc6705.c
  ├── cms_menu_vtx_rtc6705.h
  ├── cms_menu_vtx_smartaudio.c
  ├── cms_menu_vtx_smartaudio.h
  ├── cms_menu_vtx_tramp.c
  └── cms_menu_vtx_tramp.h
  
  
  0 directories, 35 files

主要函数分析

cmsHandler

排除没有CMS菜单显示设备的情况。

cmsHandler
 └──> <cmsDeviceCount > 0>
     └──> cmsUpdate(currentTimeUs);

cmsUpdate

排除FC瘫痪模式、RunCam设置模式、USB连上电脑场景;并根据cmsInMenu是否已经打开菜单进行逻辑判断。

cmsUpdate
 ├──> <IS_RC_MODE_ACTIVE(BOXPARALYZE)>  //FC瘫痪模式
 │   └──> return
 ├──> <USE_RCDEVICE><rcdeviceInMenu> //已进入RunCam设置菜单
 │   └──> return
 ├──> <USE_USB_CDC_HID><cdcDeviceIsMayBeActive> //USB连上电脑(配置或者模拟器等使用方式无需CMS菜单)
 │   └──> return
 ├──> <!cmsInMenu> //Detect menu invocation
 │   └──> <IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) && !ARMING_FLAG(ARMED) && !IS_RC_MODE_ACTIVE(BOXSTICKCOMMANDDISABLE)>
 │       ├──> cmsMenuOpen
 │       └──> rcDelayMs = BUTTON_PAUSE;    // Tends to overshoot if BUTTON_TIME(500ms)
 ├──> <cmsInMenu>
 │   ├──> displayBeginTransaction(pCurrentDisplay, DISPLAY_TRANSACTION_OPT_RESET_DRAWING);
 │   ├──> rcDelayMs = cmsScanKeys(currentTimeMs, lastCalledMs, rcDelayMs);
 │   ├──> cmsDrawMenu(pCurrentDisplay, currentTimeUs);
 │   ├──> <currentTimeMs > lastCmsHeartBeatMs + 500>
 │   │   ├──> displayHeartbeat(pCurrentDisplay);
 │   │   └──> lastCmsHeartBeatMs = currentTimeMs;
 │   └──> displayCommitTransaction(pCurrentDisplay);
 └──> lastCalledMs = millis();

cmsScanKeys

在已经打开菜单场景下,对按键进行扫描并做简单按键延时、消抖处理。

注:对于Graupner HoTT protocol 特殊处理,采用externKey来做判断。

cmsScanKeys
 ├──> <externKey != CMS_KEY_NONE>
 │   ├──> rcDelayMs = cmsHandleKey(pCurrentDisplay, externKey);
 │   └──> externKey = CMS_KEY_NONE;
 ├──> <externKey == CMS_KEY_NONE>
 │   ├──> <IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) && !ARMING_FLAG(ARMED)>
 │   │   └──> key = CMS_KEY_MENU;
 │   ├──> <IS_HI(PITCH)>
 │   │   └──> key = CMS_KEY_UP;
 │   ├──> <IS_LO(PITCH)>
 │   │   └──> key = CMS_KEY_DOWN;
 │   ├──> <IS_LO(ROLL)>
 │   │   └──> key = CMS_KEY_LEFT;
 │   ├──> <IS_HI(ROLL)>
 │   │   └──> key = CMS_KEY_RIGHT;
 │   ├──> <IS_LO(YAW)>
 │   │   └──> key = CMS_KEY_ESC;
 │   ├──> <IS_HI(YAW)>
 │   │   └──> key = CMS_KEY_SAVEMENU;
 │   ├──> <key == CMS_KEY_NONE> // No 'key' pressed, reset repeat control
 │   │   └──> holdCount = 1;repeatCount = 1;repeatBase = 0;
 │   ├──> <key != CMS_KEY_NONE> // The 'key' is being pressed; keep counting
 │   │   └──> ++holdCount;
 │   ├──> <rcDelayMs > 0>
 │   │   └──> rcDelayMs -= (currentTimeMs - lastCalledMs);  //处理按键时长
 │   └──> <rcDelayMs <= 0 && key> //按键时长满足条件,并确实有按键按下(以最后时间检测到的按键为准)
 │       ├──> rcDelayMs = cmsHandleKeyWithRepeat(pCurrentDisplay, key, repeatCount);
 │       └──> [repeatCount] ajustments //根据CMS_KEY_LEFT、CMS_KEY_RIGHT键hold时长来提供repeatCount值,最多调整不超过5
 └──> return rcDelayMs;

cmsHandleKeyWithRepeat

主要处理CMS_KEY_LEFT、CMS_KEY_RIGHT键hold时长来提供多次连续按键事件。

cmsHandleKeyWithRepeat
 └──> <for (int i = 0 ; i < repeatCount ; i++)>
     └──> ret = cmsHandleKey(pDisplay, key); // return ret

cmsHandleKey

按键对CMS Elements的实际操作。

cmsHandleKey
 ├──> <!currentCtx.menu>
 │   └──> return BUTTON_TIME
 ├──> <key == CMS_KEY_MENU>
 │   ├──> cmsMenuOpen
 │   └──> return BUTTON_PAUSE;
 ├──> <key == CMS_KEY_ESC>
 │   ├──> <osdElementEditing>
 │   │   └──> osdElementEditing = false;
 │   ├──> <!osdElementEditing>
 │   │   └──> cmsMenuBack
 │   └──> return BUTTON_PAUSE;
 ├──> <key == CMS_KEY_SAVEMENU && !saveMenuInhibited>
 │   ├──> osdElementEditing = false;
 │   ├──> cmsMenuChange
 │   └──> return BUTTON_PAUSE;
 ├──> <(key == CMS_KEY_DOWN) && (!osdElementEditing)>
 │   ├──> <currentCtx.cursorRow < pageMaxRow>
 │   │   └──> currentCtx.cursorRow++;
 │   └──> <currentCtx.cursorRow >= pageMaxRow>
 │       ├──> cmsPageNext
 │       └──> currentCtx.cursorRow = 0;    // Goto top in any case
 ├──> <(key == CMS_KEY_UP) && (!osdElementEditing)>
 │   ├──> currentCtx.cursorRow--;
 │   ├──> <(rowIsSkippable(pageTop + currentCtx.cursorRow)) && currentCtx.cursorRow > 0>  // Skip non-title labels, strings and dynamic read-only entries
 │   │   └──> currentCtx.cursorRow--;
 │   └──> <currentCtx.cursorRow == -1 || ((pageTop + currentCtx.cursorRow)->flags & OSD_MENU_ELEMENT_MASK) == OME_Label>
 │       ├──> cmsPagePrev(pDisplay);
 │       └──> currentCtx.cursorRow = pageMaxRow;
 ├──> <(key == CMS_KEY_DOWN || key == CMS_KEY_UP) && (!osdElementEditing)>
 │   └──> return BUTTON_TIME
 ├──> p = pageTop + currentCtx.cursorRow;
 ├──> <p->flags & OSD_MENU_ELEMENT_MASK>
 │   ├──> <OME_Submenu> 
 │   │   └──> ... //Element类型操作, cmsMenuChange
 │   ├──> <OME_Funcall> 
 │   │   └──> ... //Element类型操作, cmsMenuBack/setRebootRequired
 │   ├──> <OME_OSD_Exit> 
 │   │   └──> ... //Element类型操作
 │   ├──> <OME_Back> 
 │   │   └──> ... //Element类型操作, cmsMenuBack 
 │   ├──> <OME_Bool> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_VISIBLE><USE_OSD> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_UINT8> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_TAB> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_INT8> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_UINT16> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_INT16> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_UINT32> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_INT32> 
 │   │   └──> ... //Element类型操作, setRebootRequired
 │   ├──> <OME_String> 
 │   │   └──> ... //Element类型操作
 │   ├──> <OME_Label> 
 │   │   └──> ... //Element类型操作
 │   ├──> <OME_END> 
 │   │   └──> ... //Element类型操作
 │   └──> <OME_MENU> // Shouldn't happen
 └──> return res

其他函数

关于细节函数的分析,有兴趣可以深入挖掘,如以下函数:

1. display相关函数

  • displayBeginTransaction
  • displayHeartbeat
  • displayCommitTransaction

2. cmsMenu相关函数

  • cmsMenuOpen
  • cmsMenuExit

3. cmsDrawMenu相关函数

  • cmsDrawMenuEntry
  • cmsDrawMenu
  • cmsDrawMenuItemValue

4. cmsPage相关函数

  • cmsPageSelect
  • cmsPageNext
  • cmsPagePrev
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值