diff --git a/radio/src/fonts/extra_16px.png b/radio/src/fonts/extra_16px.png index 8c40ecca6..ae21c8403 100644 Binary files a/radio/src/fonts/extra_16px.png and b/radio/src/fonts/extra_16px.png differ diff --git a/radio/src/fonts/extra_17px.png b/radio/src/fonts/extra_17px.png index d4ad17eab..ae21c8403 100644 Binary files a/radio/src/fonts/extra_17px.png and b/radio/src/fonts/extra_17px.png differ diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 73e7c8718..143b3cf30 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -335,7 +335,7 @@ class InputLineButton : public CommonInputOrMixButton { { const ExpoData & line = g_model.expoData[index]; if (line.swtch || line.curve.value != 0 || line.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT); + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); } } @@ -353,13 +353,13 @@ class InputLineButton : public CommonInputOrMixButton { drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); if (line.name[0]) { - dc->drawMask(146, 2 + FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name)); } // second line ... if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, mixerSetupSwitchIcon, DEFAULT_COLOR); + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchIcon, DEFAULT_COLOR); drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); } diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 7bbb7a650..08dc1d107 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -233,7 +233,7 @@ class MixLineButton : public CommonInputOrMixButton { { const MixData & mix = g_model.mixData[index]; if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT); + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); } } @@ -251,13 +251,13 @@ class MixLineButton : public CommonInputOrMixButton { drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); if (line.name[0]) { - dc->drawMask(146, 2 + FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name)); } // second line ... if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, mixerSetupSwitchIcon, DEFAULT_COLOR); + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchIcon, DEFAULT_COLOR); drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); } @@ -521,4 +521,4 @@ bool swapMixes(uint8_t &idx, uint8_t up) idx = tgt_idx; return true; -} \ No newline at end of file +} diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 47a735b17..aa381ad59 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -114,7 +114,7 @@ class OutputLineButton : public Button { output(output) { if (output->revert || output->curve || output->name[0]) { - setHeight(height() + PAGE_LINE_HEIGHT); + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); } } @@ -125,7 +125,7 @@ class OutputLineButton : public Button { dc->drawNumber(68, FIELD_PADDING_TOP, output->max + 1000, PREC1); dc->drawNumber(132, FIELD_PADDING_TOP, output->offset, PREC1); dc->drawNumber(226, FIELD_PADDING_TOP, PPM_CENTER + output->ppmCenter, RIGHT); - dc->drawText(228, FIELD_PADDING_TOP, output->symetrical ? "=" : "\206"); + dc->drawText(228, FIELD_PADDING_TOP, output->symetrical ? "=" : "\210"); // second line if (output->revert) { @@ -136,7 +136,7 @@ class OutputLineButton : public Button { dc->drawText(88, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, getCurveString(output->curve)); } if (output->name[0]) { - dc->drawMask(146, 2 + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); + dc->drawMask(146, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); dc->drawSizedText(166, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, output->name, sizeof(output->name)); } diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 633142518..a7092095a 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -149,7 +149,7 @@ class SpecialFunctionEditPage : public Page { [=](std::string newValue) { strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); SET_DIRTY(); - }); + }, true); // strip extension grid.nextLine(); break; @@ -272,7 +272,7 @@ class SpecialFunctionButton : public Button { uint8_t func = CFN_FUNC(cfn); if (!cfn->isEmpty() && (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT))) { - setHeight(height() + 20); + setHeight(line2 + PAGE_LINE_HEIGHT); } } diff --git a/radio/src/gui/colorlcd/view_main.cpp b/radio/src/gui/colorlcd/view_main.cpp index 287e6439e..b9c1721e4 100644 --- a/radio/src/gui/colorlcd/view_main.cpp +++ b/radio/src/gui/colorlcd/view_main.cpp @@ -141,7 +141,8 @@ void ViewMain::updateTopbarVisibility() int leftScroll = getScrollPositionX() % pageWidth; if (leftScroll == 0) { setTopbarVisible(hasTopbar(g_model.view)); - customScreens[g_model.view]->adjustLayout(); + if (customScreens[g_model.view]) + customScreens[g_model.view]->adjustLayout(); } else { int leftIdx = getScrollPositionX() / pageWidth; diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index ed2146e13..356f0a43f 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -789,6 +789,258 @@ static int luaRGB(lua_State *L) return 1; } +static int luaLcdDrawCircle(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x = luaL_checkunsigned(L, 1); + coord_t y = luaL_checkunsigned(L, 2); + coord_t r = luaL_checkunsigned(L, 3); + LcdFlags flags = luaL_checkunsigned(L, 4); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + luaLcdBuffer->drawCircle(x, y, r, flags); + + return 0; +} + +static int luaLcdDrawFilledCircle(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x = luaL_checkunsigned(L, 1); + coord_t y = luaL_checkunsigned(L, 2); + coord_t r = luaL_checkunsigned(L, 3); + LcdFlags flags = luaL_checkunsigned(L, 4); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + luaLcdBuffer->drawFilledCircle(x, y, r, flags); + + return 0; +} + +static int luaLcdDrawTriangle(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x1 = luaL_checkunsigned(L, 1); + coord_t y1 = luaL_checkunsigned(L, 2); + coord_t x2 = luaL_checkunsigned(L, 3); + coord_t y2 = luaL_checkunsigned(L, 4); + coord_t x3 = luaL_checkunsigned(L, 5); + coord_t y3 = luaL_checkunsigned(L, 6); + LcdFlags flags = luaL_checkunsigned(L, 7); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + luaLcdBuffer->drawLine(x1, y1, x2, y2, SOLID, flags); + luaLcdBuffer->drawLine(x2, y2, x3, y3, SOLID, flags); + luaLcdBuffer->drawLine(x3, y3, x1, y1, SOLID, flags); + + return 0; +} + +static int luaLcdDrawFilledTriangle(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x1 = luaL_checkunsigned(L, 1); + coord_t y1 = luaL_checkunsigned(L, 2); + coord_t x2 = luaL_checkunsigned(L, 3); + coord_t y2 = luaL_checkunsigned(L, 4); + coord_t x3 = luaL_checkunsigned(L, 5); + coord_t y3 = luaL_checkunsigned(L, 6); + LcdFlags flags = luaL_checkunsigned(L, 7); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + luaLcdBuffer->drawFilledTriangle(x1, y1, x2, y2, x3, y3, flags); + + return 0; +} + +static int luaLcdDrawArc(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x = luaL_checkunsigned(L, 1); + coord_t y = luaL_checkunsigned(L, 2); + coord_t r = luaL_checkunsigned(L, 3); + int start = luaL_checkunsigned(L, 4); + int end = luaL_checkunsigned(L, 5); + LcdFlags flags = luaL_checkunsigned(L, 6); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + if (r > 0) + luaLcdBuffer->drawAnnulusSector(x, y, r - 1, r, start, end, flags); + + return 0; +} + +static int luaLcdDrawPie(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x = luaL_checkunsigned(L, 1); + coord_t y = luaL_checkunsigned(L, 2); + coord_t r = luaL_checkunsigned(L, 3); + int start = luaL_checkunsigned(L, 4); + int end = luaL_checkunsigned(L, 5); + LcdFlags flags = luaL_checkunsigned(L, 6); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + if (r > 0) + luaLcdBuffer->drawAnnulusSector(x, y, 0, r, start, end, flags); + + return 0; +} + +static int luaLcdDrawAnnulus(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x = luaL_checkunsigned(L, 1); + coord_t y = luaL_checkunsigned(L, 2); + coord_t r1 = luaL_checkunsigned(L, 3); + coord_t r2 = luaL_checkunsigned(L, 4); + int start = luaL_checkunsigned(L, 5); + int end = luaL_checkunsigned(L, 6); + LcdFlags flags = luaL_checkunsigned(L, 7); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + luaLcdBuffer->drawAnnulusSector(x, y, r1, r2, start, end, flags); + + return 0; +} + +static int luaLcdDrawLineWithClipping(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + coord_t x1 = luaL_checkunsigned(L, 1); + coord_t y1 = luaL_checkunsigned(L, 2); + coord_t x2 = luaL_checkunsigned(L, 3); + coord_t y2 = luaL_checkunsigned(L, 4); + coord_t xmin = luaL_checkunsigned(L, 5); + coord_t xmax = luaL_checkunsigned(L, 6); + coord_t ymin = luaL_checkunsigned(L, 7); + coord_t ymax = luaL_checkunsigned(L, 8); + uint8_t pat = luaL_checkunsigned(L, 9); + LcdFlags flags = luaL_checkunsigned(L, 10); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + // backup clipping rect + coord_t dc_xmin; coord_t dc_xmax; coord_t dc_ymin; coord_t dc_ymax; + luaLcdBuffer->getClippingRect(dc_xmin, dc_xmax, dc_ymin, dc_ymax); + + // resticts given clipping rect by drawing clipping rect + if (dc_xmin > xmin) xmin = dc_xmin; + if (dc_xmax < xmax) xmax = dc_xmax; + if (dc_ymin > ymin) ymin = dc_ymin; + if (dc_ymax < ymax) ymax = dc_ymax; + + luaLcdBuffer->setClippingRect(xmin, xmax, ymin, ymax); + luaLcdBuffer->drawLine(x1, y1, x2, y2, pat, flags); + + // restore original clipping rect + luaLcdBuffer->setClippingRect(dc_xmin, dc_xmax, dc_ymin, dc_ymax); + + return 0; +} + +static void drawHudRectangle(BitmapBuffer * dc, float pitch, float roll, coord_t xmin, coord_t xmax, coord_t ymin, coord_t ymax, LcdFlags flags) +{ + constexpr float GRADTORAD = 0.017453293f; + + float dx = sinf(GRADTORAD*roll) * pitch; + float dy = cosf(GRADTORAD*roll) * pitch * 1.85f; + float angle = tanf(-GRADTORAD*roll); + float ox = 0.5f * (xmin + xmax) + dx; + float oy = 0.5f * (ymin + ymax) + dy; + coord_t ywidth = (ymax - ymin); + + if (roll == 0.0f) { // prevent divide by zero + dc->drawSolidFilledRect( + xmin, max(ymin, ymin + (ywidth/2 + (coord_t)dy)), + xmax - xmin, min(ywidth, ywidth/2 - (coord_t)dy + (dy != 0.0f ? 1 : 0)), flags); + } + else if (fabs(roll) >= 180.0f) { + dc->drawSolidFilledRect(xmin, ymin, xmax - xmin, min(ywidth, ywidth/2 + (coord_t)fabsf(dy)), flags); + } + else { + bool inverted = (fabsf(roll) > 90.0f); + bool fillNeeded = false; + coord_t ybot = (inverted) ? 0 : LCD_H; + + if (roll > 0.0f) { + for (coord_t s = 0; s < ywidth; s++) { + coord_t yy = ymin + s; + coord_t xx = ox + ((float)yy - oy) / angle; // + 0.5f; rounding not needed + if (xx >= xmin && xx <= xmax) { + dc->drawSolidHorizontalLine(xx, yy, xmax - xx + 1, flags); + } + else if (xx < xmin) { + ybot = (inverted) ? max(yy, ybot) + 1 : min(yy, ybot); + fillNeeded = true; + } + } + } + else { + for (coord_t s = 0; s < ywidth; s++) { + coord_t yy = ymin + s; + coord_t xx = ox + ((float)yy - oy) / angle; // + 0.5f; rounding not needed + if (xx >= xmin && xx <= xmax) { + dc->drawSolidHorizontalLine(xmin, yy, xx - xmin, flags); + } + else if (xx > xmax) { + ybot = (inverted) ? max(yy, ybot) + 1 : min(yy, ybot); + fillNeeded = true; + } + } + } + + if (fillNeeded) { + coord_t ytop = (inverted) ? ymin : ybot; + coord_t height = (inverted) ? ybot - ymin : ymax - ybot; + dc->drawSolidFilledRect(xmin, ytop, xmax - xmin, height, flags); + } + } +} + +static int luaLcdDrawHudRectangle(lua_State *L) +{ + if (!luaLcdAllowed || !luaLcdBuffer) + return 0; + + float pitch = luaL_checknumber(L, 1); + float roll = luaL_checknumber(L, 2); + coord_t xmin = luaL_checkunsigned(L, 3); + coord_t xmax = luaL_checkunsigned(L, 4); + coord_t ymin = luaL_checkunsigned(L, 5); + coord_t ymax = luaL_checkunsigned(L, 6); + LcdFlags flags = luaL_checkunsigned(L, 7); + + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); + + drawHudRectangle(luaLcdBuffer, pitch, roll, xmin, xmax, ymin, ymax, flags); + + return 0; +} + const luaL_Reg lcdLib[] = { { "refresh", luaLcdRefresh }, { "clear", luaLcdClear }, @@ -808,5 +1060,14 @@ const luaL_Reg lcdLib[] = { { "setColor", luaLcdSetColor }, { "getColor", luaLcdGetColor }, { "RGB", luaRGB }, + { "drawCircle", luaLcdDrawCircle }, + { "drawFilledCircle", luaLcdDrawFilledCircle }, + { "drawTriangle", luaLcdDrawTriangle }, + { "drawFilledTriangle", luaLcdDrawFilledTriangle }, + { "drawArc", luaLcdDrawArc }, + { "drawPie", luaLcdDrawPie }, + { "drawAnnulus", luaLcdDrawAnnulus }, + { "drawLineWithClipping", luaLcdDrawLineWithClipping }, + { "drawHudRectangle", luaLcdDrawHudRectangle }, { NULL, NULL } /* sentinel */ }; diff --git a/radio/src/storage/conversions/conversions_219_220.cpp b/radio/src/storage/conversions/conversions_219_220.cpp index 387c392b8..8743b2889 100644 --- a/radio/src/storage/conversions/conversions_219_220.cpp +++ b/radio/src/storage/conversions/conversions_219_220.cpp @@ -84,7 +84,7 @@ typedef TimerData TimerData_v220; static void convertToStr(char* str, size_t len) { - for (int c = 0; c < len; c++) { + for (size_t c = 0; c < len; c++) { str[c] = zchar2char(str[c]); } // Trim string @@ -197,7 +197,7 @@ void convertModelData_219_to_220(ModelData &model) memset(newModel.screenData, 0, sizeof(newModel.screenData) + sizeof(newModel.topbarData)); - //TODO: set defaults as with new model + loadDefaultLayout(); #endif free(oldModelAllocated); } diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 77b0e421f..0d5b13c57 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 77b0e421fc5790e777930aef1f7224155916629c +Subproject commit 0d5b13c578e5893b98a54490815e3d3b5a3d6ff6 diff --git a/radio/src/timers.cpp b/radio/src/timers.cpp index 7fbe4bb5f..afda7f0ef 100644 --- a/radio/src/timers.cpp +++ b/radio/src/timers.cpp @@ -74,10 +74,14 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) for (uint8_t i=0; istate == TMR_OFF) && (timerMode != TMRMODE_THR_START)) { + if ((timerState->state == TMR_OFF) + && (timerMode != TMRMODE_THR_START) + && (timerMode != TMRMODE_START)) { + timerState->state = TMR_RUNNING; timerState->cnt = 0; timerState->sum = 0; @@ -92,44 +96,52 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) if (timerState->val == TIMER_MAX) break; if (timerState->val == TIMER_MIN) break; - timerState->val_10ms -= 100 ; + timerState->val_10ms -= 100; tmrval_t newTimerVal = timerState->val; if (timerStart) newTimerVal = timerStart - newTimerVal; - if (timerMode == TMRMODE_ON) { - newTimerVal++; - } - else if (timerMode == TMRMODE_THR) { - if (throttle) newTimerVal++; - } - else if (timerMode == TMRMODE_THR_REL) { - if ((timerState->sum/timerState->cnt) >= 128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) - newTimerVal++; // add second used of throttle - timerState->sum -= 128*timerState->cnt; - } - timerState->cnt = 0; - } - else if (timerMode == TMRMODE_THR_START) { - // we can't rely on (throttle || newTimerVal > 0) as a detection if timer should be running - // because having persistent timer brakes this rule - if ((throttle > THR_TRG_TRESHOLD) && timerState->state == TMR_OFF) { + if (timerMode == TMRMODE_START) { + // Start timer based on switch + if (getSwitch(timerSwtch) && timerState->state == TMR_OFF) { timerState->state = TMR_RUNNING; // start timer running timerState->cnt = 0; timerState->sum = 0; - // TRACE("Timer[%d] THr triggered", i); } - if (timerState->state != TMR_OFF) newTimerVal++; - } - else { - if (timerMode > 0) timerMode -= (TMRMODE_COUNT-1); - if (getSwitch(timerMode)) { + if (timerState->state != TMR_OFF) { newTimerVal++; + } + } else if (getSwitch(timerSwtch)) { + + // Modes conditional on switch at any time + if (timerMode == TMRMODE_ON) { + newTimerVal++; + } else if (timerMode == TMRMODE_THR) { + if (throttle) newTimerVal++; + } else if (timerMode == TMRMODE_THR_REL) { + // throttle was normalized to 0 to 128 value + // (throttle/64*2 (because - range is added as well) + if ((timerState->sum / timerState->cnt) >= 128) { + newTimerVal++; // add second used of throttle + timerState->sum -= 128 * timerState->cnt; + } + timerState->cnt = 0; + } else if (timerMode == TMRMODE_THR_START) { + // we can't rely on (throttle || newTimerVal > 0) as a detection if + // timer should be running because having persistent timer brakes + // this rule + if ((throttle > THR_TRG_TRESHOLD) && timerState->state == TMR_OFF) { + timerState->state = TMR_RUNNING; // start timer running + timerState->cnt = 0; + timerState->sum = 0; + // TRACE("Timer[%d] THr triggered", i); + } + if (timerState->state != TMR_OFF) newTimerVal++; } } switch (timerState->state) { case TMR_RUNNING: - if (timerStart && newTimerVal>=(tmrval_t)timerStart) { + if (timerStart && newTimerVal >= (tmrval_t)timerStart) { AUDIO_TIMER_ELAPSED(i); timerState->state = TMR_NEGATIVE; // TRACE("Timer[%d] negative", i); @@ -143,7 +155,8 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) break; } - if (timerStart) newTimerVal = timerStart - newTimerVal; // if counting backwards - display backwards + // if counting backwards - display backwards + if (timerStart) newTimerVal = timerStart - newTimerVal; if (newTimerVal != timerState->val) { timerState->val = newTimerVal;