New SPI API supporting DMA
[betaflight.git] / src / main / cms / cms_menu_osd.c
blob177a765acfa081117ee17ac25d7cd69838e58f98
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <ctype.h>
26 #include "platform.h"
28 #if defined(USE_OSD) && defined(USE_CMS)
30 #include "build/version.h"
32 #include "cli/settings.h"
34 #include "cms/cms.h"
35 #include "cms/cms_types.h"
36 #include "cms/cms_menu_osd.h"
38 #include "common/utils.h"
40 #include "config/feature.h"
42 #include "drivers/display.h"
44 #include "io/displayport_max7456.h"
46 #include "osd/osd.h"
47 #include "osd/osd_elements.h"
49 #include "pg/pg.h"
50 #include "pg/pg_ids.h"
52 #include "rx/crsf.h"
54 #include "sensors/battery.h"
56 #ifdef USE_EXTENDED_CMS_MENUS
57 static uint16_t osdConfig_item_pos[OSD_ITEM_COUNT];
59 static const void *menuOsdActiveElemsOnEnter(displayPort_t *pDisp)
61 UNUSED(pDisp);
63 memcpy(&osdConfig_item_pos[0], &osdElementConfig()->item_pos[0], sizeof(uint16_t) * OSD_ITEM_COUNT);
64 return NULL;
67 static const void *menuOsdActiveElemsOnExit(displayPort_t *pDisp, const OSD_Entry *self)
69 UNUSED(pDisp);
70 UNUSED(self);
72 memcpy(&osdElementConfigMutable()->item_pos[0], &osdConfig_item_pos[0], sizeof(uint16_t) * OSD_ITEM_COUNT);
73 osdAnalyzeActiveElements();
74 return NULL;
77 const OSD_Entry menuOsdActiveElemsEntries[] =
79 {"--- ACTIV ELEM ---", OME_Label, NULL, NULL, 0},
80 {"RSSI", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_RSSI_VALUE], DYNAMIC},
81 #ifdef USE_RX_RSSI_DBM
82 {"RSSI DBM", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_RSSI_DBM_VALUE], DYNAMIC},
83 #endif
84 {"BATTERY VOLTAGE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_MAIN_BATT_VOLTAGE], DYNAMIC},
85 {"BATTERY USAGE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_MAIN_BATT_USAGE], DYNAMIC},
86 {"AVG CELL VOLTAGE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_AVG_CELL_VOLTAGE], DYNAMIC},
87 #ifdef USE_GPS
88 {"BATTERY EFFICIENCY", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_EFFICIENCY], DYNAMIC},
89 #endif // GPS
90 {"CROSSHAIRS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CROSSHAIRS], DYNAMIC},
91 {"HORIZON", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ARTIFICIAL_HORIZON], DYNAMIC},
92 {"HORIZON SIDEBARS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_HORIZON_SIDEBARS], DYNAMIC},
93 {"UP/DOWN REFERENCE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_UP_DOWN_REFERENCE], DYNAMIC},
94 {"TIMER 1", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ITEM_TIMER_1], DYNAMIC},
95 {"TIMER 2", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ITEM_TIMER_2], DYNAMIC},
96 {"REMAINING TIME ESTIMATE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_REMAINING_TIME_ESTIMATE], DYNAMIC},
97 #ifdef USE_RTC_TIME
98 {"RTC DATETIME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_RTC_DATETIME], DYNAMIC},
99 #endif
100 #ifdef USE_OSD_ADJUSTMENTS
101 {"ADJUSTMENT RANGE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ADJUSTMENT_RANGE], DYNAMIC},
102 #endif
103 #ifdef USE_ADC_INTERNAL
104 {"CORE TEMPERATURE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CORE_TEMPERATURE], DYNAMIC},
105 #endif
106 {"ANTI GRAVITY", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ANTI_GRAVITY], DYNAMIC},
107 {"FLY MODE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLYMODE], DYNAMIC},
108 {"NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CRAFT_NAME], DYNAMIC},
109 {"THROTTLE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_THROTTLE_POS], DYNAMIC},
110 #ifdef USE_VTX_CONTROL
111 {"VTX CHAN", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_VTX_CHANNEL], DYNAMIC},
112 #endif // VTX
113 {"CURRENT (A)", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CURRENT_DRAW], DYNAMIC},
114 {"USED MAH", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_MAH_DRAWN], DYNAMIC},
115 #ifdef USE_GPS
116 {"GPS SPEED", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_GPS_SPEED], DYNAMIC},
117 {"GPS SATS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_GPS_SATS], DYNAMIC},
118 {"GPS LAT", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_GPS_LAT], DYNAMIC},
119 {"GPS LON", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_GPS_LON], DYNAMIC},
120 {"HOME DIR", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_HOME_DIR], DYNAMIC},
121 {"HOME DIST", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_HOME_DIST], DYNAMIC},
122 {"FLIGHT DIST", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLIGHT_DIST], DYNAMIC},
123 #endif // GPS
124 {"COMPASS BAR", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_COMPASS_BAR], DYNAMIC},
125 #ifdef USE_ESC_SENSOR
126 {"ESC TEMPERATURE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ESC_TMP], DYNAMIC},
127 {"ESC RPM", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ESC_RPM], DYNAMIC},
128 #endif
129 {"ALTITUDE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ALTITUDE], DYNAMIC},
130 {"POWER", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_POWER], DYNAMIC},
131 {"ROLL PID", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ROLL_PIDS], DYNAMIC},
132 {"PITCH PID", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PITCH_PIDS], DYNAMIC},
133 {"YAW PID", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_YAW_PIDS], DYNAMIC},
134 {"PROFILES", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PIDRATE_PROFILE], DYNAMIC},
135 #ifdef USE_PROFILE_NAMES
136 {"PID PROFILE NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PID_PROFILE_NAME], DYNAMIC},
137 {"RATE PROFILE NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_RATE_PROFILE_NAME], DYNAMIC},
138 #endif
139 #ifdef USE_OSD_PROFILES
140 {"OSD PROFILE NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PROFILE_NAME], DYNAMIC},
141 #endif
142 {"DEBUG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_DEBUG], DYNAMIC},
143 {"WARNINGS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_WARNINGS], DYNAMIC},
144 {"DISARMED", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_DISARMED], DYNAMIC},
145 {"PIT ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PITCH_ANGLE], DYNAMIC},
146 {"ROL ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ROLL_ANGLE], DYNAMIC},
147 {"HEADING", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_HEADING], DYNAMIC},
148 #ifdef USE_VARIO
149 {"VARIO", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_VARIO], DYNAMIC},
150 #endif
151 {"G-FORCE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_G_FORCE], DYNAMIC},
152 {"MOTOR DIAGNOSTIC", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_MOTOR_DIAG], DYNAMIC},
153 #ifdef USE_BLACKBOX
154 {"LOG STATUS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_LOG_STATUS], DYNAMIC},
155 #endif
156 {"FLIP ARROW", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLIP_ARROW], DYNAMIC},
157 #ifdef USE_RX_LINK_QUALITY_INFO
158 {"LINK QUALITY", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_LINK_QUALITY], DYNAMIC},
159 #endif
160 #ifdef USE_OSD_STICK_OVERLAY
161 {"STICK OVERLAY LEFT", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_STICK_OVERLAY_LEFT], DYNAMIC},
162 {"STICK OVERLAY RIGHT",OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_STICK_OVERLAY_RIGHT], DYNAMIC},
163 #endif
164 {"DISPLAY NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_DISPLAY_NAME], DYNAMIC},
165 {"RC CHANNELS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_RC_CHANNELS], DYNAMIC},
166 {"CAMERA FRAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CAMERA_FRAME], DYNAMIC},
167 {"TOTAL FLIGHTS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_TOTAL_FLIGHTS], DYNAMIC},
168 {"BACK", OME_Back, NULL, NULL, 0},
169 {NULL, OME_END, NULL, NULL, 0}
172 static CMS_Menu menuOsdActiveElems = {
173 #ifdef CMS_MENU_DEBUG
174 .GUARD_text = "MENUOSDACT",
175 .GUARD_type = OME_MENU,
176 #endif
177 .onEnter = menuOsdActiveElemsOnEnter,
178 .onExit = menuOsdActiveElemsOnExit,
179 .onDisplayUpdate = NULL,
180 .entries = menuOsdActiveElemsEntries
183 static uint8_t osdConfig_rssi_alarm;
184 static uint16_t osdConfig_link_quality_alarm;
185 static int16_t osdConfig_rssi_dbm_alarm;
186 static uint16_t osdConfig_cap_alarm;
187 static uint16_t osdConfig_alt_alarm;
188 static uint16_t osdConfig_distance_alarm;
189 static uint8_t batteryConfig_vbatDurationForWarning;
190 static uint8_t batteryConfig_vbatDurationForCritical;
192 static const void *menuAlarmsOnEnter(displayPort_t *pDisp)
194 UNUSED(pDisp);
196 osdConfig_rssi_alarm = osdConfig()->rssi_alarm;
197 osdConfig_link_quality_alarm = osdConfig()->link_quality_alarm;
198 osdConfig_rssi_dbm_alarm = osdConfig()->rssi_dbm_alarm;
199 osdConfig_cap_alarm = osdConfig()->cap_alarm;
200 osdConfig_alt_alarm = osdConfig()->alt_alarm;
201 osdConfig_distance_alarm = osdConfig()->distance_alarm;
202 batteryConfig_vbatDurationForWarning = batteryConfig()->vbatDurationForWarning;
203 batteryConfig_vbatDurationForCritical = batteryConfig()->vbatDurationForCritical;
205 return NULL;
208 static const void *menuAlarmsOnExit(displayPort_t *pDisp, const OSD_Entry *self)
210 UNUSED(pDisp);
211 UNUSED(self);
213 osdConfigMutable()->rssi_alarm = osdConfig_rssi_alarm;
214 osdConfigMutable()->link_quality_alarm = osdConfig_link_quality_alarm;
215 osdConfigMutable()->rssi_dbm_alarm = osdConfig_rssi_dbm_alarm;
216 osdConfigMutable()->cap_alarm = osdConfig_cap_alarm;
217 osdConfigMutable()->alt_alarm = osdConfig_alt_alarm;
218 osdConfigMutable()->distance_alarm = osdConfig_distance_alarm;
219 batteryConfigMutable()->vbatDurationForWarning = batteryConfig_vbatDurationForWarning;
220 batteryConfigMutable()->vbatDurationForCritical = batteryConfig_vbatDurationForCritical;
222 return NULL;
225 const OSD_Entry menuAlarmsEntries[] =
227 {"--- ALARMS ---", OME_Label, NULL, NULL, 0},
228 {"RSSI", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_rssi_alarm, 5, 90, 5}, 0},
229 {"LINK QUALITY", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_link_quality_alarm, 5, 300, 5}, 0},
230 {"RSSI DBM", OME_INT16, NULL, &(OSD_INT16_t){&osdConfig_rssi_dbm_alarm, CRSF_RSSI_MIN, CRSF_SNR_MAX, 5}, 0},
231 {"MAIN BAT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_cap_alarm, 50, 30000, 50}, 0},
232 {"MAX ALT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_alt_alarm, 1, 200, 1}, 0},
233 {"MAX DISTANCE", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_distance_alarm, 0, UINT16_MAX, 10}, 0},
234 {"VBAT WARN DUR", OME_UINT8, NULL, &(OSD_UINT8_t){ &batteryConfig_vbatDurationForWarning, 0, 200, 1 }, 0 },
235 {"VBAT CRIT DUR", OME_UINT8, NULL, &(OSD_UINT8_t){ &batteryConfig_vbatDurationForCritical, 0, 200, 1 }, 0 },
236 {"BACK", OME_Back, NULL, NULL, 0},
237 {NULL, OME_END, NULL, NULL, 0}
240 static CMS_Menu menuAlarms = {
241 #ifdef CMS_MENU_DEBUG
242 .GUARD_text = "MENUALARMS",
243 .GUARD_type = OME_MENU,
244 #endif
245 .onEnter = menuAlarmsOnEnter,
246 .onExit = menuAlarmsOnExit,
247 .onDisplayUpdate = NULL,
248 .entries = menuAlarmsEntries,
251 osd_timer_source_e timerSource[OSD_TIMER_COUNT];
252 osd_timer_precision_e timerPrecision[OSD_TIMER_COUNT];
253 uint8_t timerAlarm[OSD_TIMER_COUNT];
255 static const void *menuTimersOnEnter(displayPort_t *pDisp)
257 UNUSED(pDisp);
259 for (int i = 0; i < OSD_TIMER_COUNT; i++) {
260 const uint16_t timer = osdConfig()->timers[i];
261 timerSource[i] = OSD_TIMER_SRC(timer);
262 timerPrecision[i] = OSD_TIMER_PRECISION(timer);
263 timerAlarm[i] = OSD_TIMER_ALARM(timer);
266 return NULL;
269 static const void *menuTimersOnExit(displayPort_t *pDisp, const OSD_Entry *self)
271 UNUSED(pDisp);
272 UNUSED(self);
274 for (int i = 0; i < OSD_TIMER_COUNT; i++) {
275 osdConfigMutable()->timers[i] = OSD_TIMER(timerSource[i], timerPrecision[i], timerAlarm[i]);
278 return NULL;
281 static const char * osdTimerPrecisionNames[] = {"SCND", "HDTH"};
283 const OSD_Entry menuTimersEntries[] =
285 {"--- TIMERS ---", OME_Label, NULL, NULL, 0},
286 {"1 SRC", OME_TAB, NULL, &(OSD_TAB_t){&timerSource[OSD_TIMER_1], OSD_TIMER_SRC_COUNT - 1, osdTimerSourceNames}, 0 },
287 {"1 PREC", OME_TAB, NULL, &(OSD_TAB_t){&timerPrecision[OSD_TIMER_1], OSD_TIMER_PREC_COUNT - 1, osdTimerPrecisionNames}, 0},
288 {"1 ALARM", OME_UINT8, NULL, &(OSD_UINT8_t){&timerAlarm[OSD_TIMER_1], 0, 0xFF, 1}, 0},
289 {"2 SRC", OME_TAB, NULL, &(OSD_TAB_t){&timerSource[OSD_TIMER_2], OSD_TIMER_SRC_COUNT - 1, osdTimerSourceNames}, 0 },
290 {"2 PREC", OME_TAB, NULL, &(OSD_TAB_t){&timerPrecision[OSD_TIMER_2], OSD_TIMER_PREC_COUNT - 1, osdTimerPrecisionNames}, 0},
291 {"2 ALARM", OME_UINT8, NULL, &(OSD_UINT8_t){&timerAlarm[OSD_TIMER_2], 0, 0xFF, 1}, 0},
292 {"BACK", OME_Back, NULL, NULL, 0},
293 {NULL, OME_END, NULL, NULL, 0}
296 static CMS_Menu menuTimers = {
297 #ifdef CMS_MENU_DEBUG
298 .GUARD_text = "MENUTIMERS",
299 .GUARD_type = OME_MENU,
300 #endif
301 .onEnter = menuTimersOnEnter,
302 .onExit = menuTimersOnExit,
303 .onDisplayUpdate = NULL,
304 .entries = menuTimersEntries,
306 #endif /* USE_EXTENDED_CMS_MENUS */
308 #ifdef USE_MAX7456
309 static bool displayPortProfileMax7456_invert;
310 static uint8_t displayPortProfileMax7456_blackBrightness;
311 static uint8_t displayPortProfileMax7456_whiteBrightness;
312 #endif
314 #ifdef USE_OSD_PROFILES
315 static uint8_t osdConfig_osdProfileIndex;
316 #endif
318 static displayPortBackground_e osdMenuBackgroundType;
320 static const void *cmsx_menuOsdOnEnter(displayPort_t *pDisp)
322 UNUSED(pDisp);
324 #ifdef USE_OSD_PROFILES
325 osdConfig_osdProfileIndex = osdConfig()->osdProfileIndex;
326 #endif
328 #ifdef USE_MAX7456
329 displayPortProfileMax7456_invert = displayPortProfileMax7456()->invert;
330 displayPortProfileMax7456_blackBrightness = displayPortProfileMax7456()->blackBrightness;
331 displayPortProfileMax7456_whiteBrightness = displayPortProfileMax7456()->whiteBrightness;
332 osdMenuBackgroundType = osdConfig()->cms_background_type;
333 #endif
335 return NULL;
338 static const void *cmsx_menuOsdOnExit(displayPort_t *pDisp, const OSD_Entry *self)
340 UNUSED(pDisp);
341 UNUSED(self);
343 #ifdef USE_OSD_PROFILES
344 changeOsdProfileIndex(osdConfig_osdProfileIndex);
345 #endif
347 return NULL;
350 #ifdef USE_MAX7456
351 static const void *cmsx_max7456Update(displayPort_t *pDisp, const void *self)
353 UNUSED(self);
355 displayPortProfileMax7456Mutable()->invert = displayPortProfileMax7456_invert;
356 displayPortProfileMax7456Mutable()->blackBrightness = displayPortProfileMax7456_blackBrightness;
357 displayPortProfileMax7456Mutable()->whiteBrightness = displayPortProfileMax7456_whiteBrightness;
359 displayClearScreen(pDisp);
361 return NULL;
363 #endif // USE_MAX7456
365 static const void *cmsx_osdBackgroundUpdate(displayPort_t *pDisp, const void *self)
367 UNUSED(self);
368 osdConfigMutable()->cms_background_type = osdMenuBackgroundType;
369 displaySetBackgroundType(pDisp, osdMenuBackgroundType);
370 return NULL;
373 const OSD_Entry cmsx_menuOsdEntries[] =
375 {"---OSD---", OME_Label, NULL, NULL, 0},
376 #ifdef USE_OSD_PROFILES
377 {"OSD PROFILE", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_osdProfileIndex, 1, 3, 1}, 0},
378 #endif
379 #ifdef USE_EXTENDED_CMS_MENUS
380 {"ACTIVE ELEM", OME_Submenu, cmsMenuChange, &menuOsdActiveElems, 0},
381 {"TIMERS", OME_Submenu, cmsMenuChange, &menuTimers, 0},
382 {"ALARMS", OME_Submenu, cmsMenuChange, &menuAlarms, 0},
383 #endif
384 #ifdef USE_MAX7456
385 {"INVERT", OME_Bool, cmsx_max7456Update, &displayPortProfileMax7456_invert, 0},
386 {"BRT BLACK", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_blackBrightness, 0, 3, 1}, 0},
387 {"BRT WHITE", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_whiteBrightness, 0, 3, 1}, 0},
388 #endif
389 {"BACKGROUND",OME_TAB, cmsx_osdBackgroundUpdate, &(OSD_TAB_t){&osdMenuBackgroundType, DISPLAY_BACKGROUND_COUNT - 1, lookupTableCMSMenuBackgroundType}, 0},
390 {"BACK", OME_Back, NULL, NULL, 0},
391 {NULL, OME_END, NULL, NULL, 0}
394 CMS_Menu cmsx_menuOsd = {
395 #ifdef CMS_MENU_DEBUG
396 .GUARD_text = "MENUOSD",
397 .GUARD_type = OME_MENU,
398 #endif
399 .onEnter = cmsx_menuOsdOnEnter,
400 .onExit = cmsx_menuOsdOnExit,
401 .onDisplayUpdate = NULL,
402 .entries = cmsx_menuOsdEntries
404 #endif // CMS