>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r
#include
+#elif defined(CONFIG_BOARD_TYPE_TDECK)
+#include
#else
#include
#include
diff --git a/main/input/tdeck.inc b/main/input/tdeck.inc
new file mode 100644
index 000000000..b865b6c63
--- /dev/null
+++ b/main/input/tdeck.inc
@@ -0,0 +1,194 @@
+#include "gui.h"
+#include "jade_assert.h"
+#include "jade_tasks.h"
+
+#include "button_gpio.h"
+#include "iot_button.h"
+
+#include
+#include
+#include
+
+#define BOARD_POWERON_GPIO 10
+#define KB_I2C_ADDR 0x55
+#define KB_I2C_TIMEOUT_MS 50
+
+// Keyboard controller command: set backlight brightness (0-255, 0 = off)
+#define KB_BRIGHTNESS_CMD 0x01
+
+#define TB_LEFT_GPIO 1
+#define TB_RIGHT_GPIO 2
+#define TB_UP_GPIO 3
+#define TB_DOWN_GPIO 15
+#define TB_PRESS_GPIO 0
+
+// Ignore edges arriving faster than this - filters contact noise and
+// caps the overall scroll rate
+#define TB_DEBOUNCE_US 2000
+
+// Edges required per scroll step - raise to reduce sensitivity
+#define TB_EDGES_PER_EVENT 2
+
+// Edges further apart than this do not accumulate towards a step
+#define TB_GESTURE_TIMEOUT_US 300000
+
+// Pending keyboard backlight level (0-255), or -1 when no change pending.
+// Set from other tasks (eg. the screen backlight code) - applied by the
+// keyboard task below, which owns the i2c device.
+static volatile int16_t kb_backlight_req = -1;
+
+void tdeck_set_keyboard_backlight(const uint8_t value) { kb_backlight_req = value; }
+
+static void tdeck_keyboard_task(void* unused)
+{
+ gpio_set_direction(BOARD_POWERON_GPIO, GPIO_MODE_OUTPUT);
+ gpio_set_level(BOARD_POWERON_GPIO, 1);
+
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+
+ i2c_master_bus_config_t bus_config = {
+ .i2c_port = I2C_NUM_0,
+ .sda_io_num = CONFIG_I2C_SDA,
+ .scl_io_num = CONFIG_I2C_SCL,
+ .clk_source = I2C_CLK_SRC_DEFAULT,
+ .glitch_ignore_cnt = 7,
+ .flags.enable_internal_pullup = true,
+ };
+ i2c_master_bus_handle_t kb_i2c_bus;
+ ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &kb_i2c_bus));
+
+ i2c_device_config_t dev_cfg = {
+ .dev_addr_length = I2C_ADDR_BIT_LEN_7,
+ .device_address = KB_I2C_ADDR,
+ .scl_speed_hz = CONFIG_I2C_MASTER_CLK,
+ };
+ i2c_master_dev_handle_t kb_i2c_dev;
+ ESP_ERROR_CHECK(i2c_master_bus_add_device(kb_i2c_bus, &dev_cfg, &kb_i2c_dev));
+
+ for (;;) {
+ // Apply any pending keyboard backlight change (retried on failure)
+ const int16_t kb_bl = kb_backlight_req;
+ if (kb_bl >= 0) {
+ const uint8_t cmd[] = { KB_BRIGHTNESS_CMD, (uint8_t)kb_bl };
+ if (i2c_master_transmit(kb_i2c_dev, cmd, sizeof(cmd), KB_I2C_TIMEOUT_MS) == ESP_OK) {
+ kb_backlight_req = -1;
+ }
+ }
+
+ uint8_t key_byte = 0;
+ const esp_err_t ret = i2c_master_receive(kb_i2c_dev, &key_byte, 1, KB_I2C_TIMEOUT_MS);
+ if (ret == ESP_OK && key_byte != 0) {
+ JADE_LOGD("keyboard key 0x%02x", key_byte);
+ if (key_byte == 0x0D) {
+ esp_event_post(GUI_BUTTON_EVENT, BTN_KEYBOARD_ENTER, NULL, 0, portMAX_DELAY);
+ } else if (key_byte == 0x08) {
+ esp_event_post(GUI_BUTTON_EVENT, BTN_KEYBOARD_BACKSPACE, NULL, 0, portMAX_DELAY);
+ } else if (key_byte >= 32 && key_byte < 128) {
+ esp_event_post(GUI_BUTTON_EVENT, BTN_KEYBOARD_ASCII_OFFSET + key_byte, NULL, 0, portMAX_DELAY);
+ }
+ }
+ vTaskDelay(30 / portTICK_PERIOD_MS);
+ }
+
+ vTaskDelete(NULL);
+}
+
+static void tb_press_trigger(void* arg, void* ctx) { gui_front_click(); }
+
+// The trackball direction outputs are not buttons - each line toggles
+// level once per step of ball travel, so treat every edge as one scroll
+// increment (the resting level of a line is arbitrary).
+typedef enum { TB_NAV_PREV, TB_NAV_NEXT } tb_nav_t;
+
+static QueueHandle_t tb_queue = NULL;
+
+static void tb_isr(void* arg)
+{
+ static int64_t last_event_us = 0;
+ static tb_nav_t last_nav = TB_NAV_PREV;
+ static uint8_t edge_count = 0;
+
+ const int64_t now = esp_timer_get_time();
+ const int64_t elapsed = now - last_event_us;
+ if (elapsed < TB_DEBOUNCE_US) {
+ return;
+ }
+ last_event_us = now;
+
+ const tb_nav_t nav = (tb_nav_t)(uintptr_t)arg;
+ if (nav != last_nav || elapsed > TB_GESTURE_TIMEOUT_US) {
+ last_nav = nav;
+ edge_count = 0;
+ }
+ if (++edge_count < TB_EDGES_PER_EVENT) {
+ return;
+ }
+ edge_count = 0;
+
+ BaseType_t higher_priority_woken = pdFALSE;
+ xQueueSendFromISR(tb_queue, &nav, &higher_priority_woken);
+ if (higher_priority_woken == pdTRUE) {
+ portYIELD_FROM_ISR();
+ }
+}
+
+static void tdeck_trackball_task(void* unused)
+{
+ for (;;) {
+ tb_nav_t nav;
+ if (xQueueReceive(tb_queue, &nav, portMAX_DELAY) == pdTRUE) {
+ if (nav == TB_NAV_PREV) {
+ gui_prev();
+ } else {
+ gui_next();
+ }
+ }
+ }
+}
+
+void navigation_init(void)
+{
+ BaseType_t retval = xTaskCreatePinnedToCore(
+ &tdeck_keyboard_task, "tdeck_kbd", 3 * 1024, NULL, JADE_TASK_PRIO_WHEEL, NULL, JADE_CORE_SECONDARY);
+ JADE_ASSERT_MSG(retval == pdPASS, "Failed to create tdeck_keyboard task, xTaskCreatePinnedToCore() returned %d",
+ retval);
+
+ tb_queue = xQueueCreate(8, sizeof(tb_nav_t));
+ JADE_ASSERT(tb_queue);
+
+ retval = xTaskCreatePinnedToCore(
+ &tdeck_trackball_task, "tdeck_tball", 2 * 1024, NULL, JADE_TASK_PRIO_WHEEL, NULL, JADE_CORE_SECONDARY);
+ JADE_ASSERT_MSG(retval == pdPASS, "Failed to create tdeck_trackball task, xTaskCreatePinnedToCore() returned %d",
+ retval);
+
+ const gpio_config_t tb_cfg = {
+ .pin_bit_mask = (1ULL << TB_LEFT_GPIO) | (1ULL << TB_RIGHT_GPIO) | (1ULL << TB_UP_GPIO)
+ | (1ULL << TB_DOWN_GPIO),
+ .mode = GPIO_MODE_INPUT,
+ .pull_up_en = GPIO_PULLUP_ENABLE,
+ .intr_type = GPIO_INTR_ANYEDGE,
+ };
+ ESP_ERROR_CHECK(gpio_config(&tb_cfg));
+
+ const esp_err_t rc = gpio_install_isr_service(0);
+ JADE_ASSERT(rc == ESP_OK || rc == ESP_ERR_INVALID_STATE);
+ ESP_ERROR_CHECK(gpio_isr_handler_add(TB_LEFT_GPIO, tb_isr, (void*)(uintptr_t)TB_NAV_PREV));
+ ESP_ERROR_CHECK(gpio_isr_handler_add(TB_UP_GPIO, tb_isr, (void*)(uintptr_t)TB_NAV_PREV));
+ ESP_ERROR_CHECK(gpio_isr_handler_add(TB_RIGHT_GPIO, tb_isr, (void*)(uintptr_t)TB_NAV_NEXT));
+ ESP_ERROR_CHECK(gpio_isr_handler_add(TB_DOWN_GPIO, tb_isr, (void*)(uintptr_t)TB_NAV_NEXT));
+
+ const button_config_t btn_cfg = {
+ .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+ .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+ };
+
+ button_handle_t btn_press = NULL;
+ const button_gpio_config_t press_gpio_cfg = { .gpio_num = TB_PRESS_GPIO, .active_level = 0 };
+ ESP_ERROR_CHECK(iot_button_new_gpio_device(&btn_cfg, &press_gpio_cfg, &btn_press));
+ JADE_ASSERT(btn_press);
+ ESP_ERROR_CHECK(iot_button_register_cb(btn_press, BUTTON_PRESS_UP, NULL, tb_press_trigger, NULL));
+}
+
+void select_init(void)
+{
+}
diff --git a/main/power.c b/main/power.c
index 1dd7624ba..7a691eed9 100644
--- a/main/power.c
+++ b/main/power.c
@@ -33,6 +33,8 @@
#elif defined(CONFIG_BOARD_TYPE_TTGO_TDISPLAY)
// T-Display can read battery level but lacks hardware to read charging status
#include "power/tdisplay.inc"
+#elif defined(CONFIG_BOARD_TYPE_TDECK)
+#include "power/tdeck.inc"
#else
// Stubs for other hw boards (ie. no power management)
#include "power/minimal.inc"
diff --git a/main/power/tdeck.inc b/main/power/tdeck.inc
new file mode 100644
index 000000000..f21ccfd9e
--- /dev/null
+++ b/main/power/tdeck.inc
@@ -0,0 +1,198 @@
+#include "jade_assert.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef CONFIG_JADE_USE_USB_JTAG_SERIAL
+#include
+#else
+#include "tusb.h"
+#endif
+
+#define BOARD_POWERON_GPIO 10
+#define BACKLIGHT_GPIO 42
+#define BACKLIGHT_STEPS 16
+
+// Implemented in input/tdeck.inc - the keyboard task owns the i2c bus
+void tdeck_set_keyboard_backlight(uint8_t value);
+
+#define BATTERY_ADC_CHANNEL ADC_CHANNEL_3
+#define BATTERY_ADC_ATTEN ADC_ATTEN_DB_12
+#define BATTERY_ADC_SAMPLES 8
+#define BATTERY_EMA_ALPHA 0.3f
+
+// Voltage divider ratio is 2.0 (100k/100k) plus ~10% correction for
+// undervoltage while the display loads the rail (as per other t-deck
+// firmwares, eg. meshtastic)
+#define BATTERY_VDIV_MULTIPLIER 2.11f
+
+// Above this the sense node is being held up by the charger with the
+// battery full (charge terminated) rather than actively charging
+#define BATTERY_CHARGED_MILLIVOLTS 4300
+
+static bool usb_connected(void)
+{
+#ifdef CONFIG_JADE_USE_USB_JTAG_SERIAL
+ // TinyUSB is not initialised in this mode - infer usb connection
+ // from the usb-serial-jtag peripheral instead
+ return usb_serial_jtag_is_connected();
+#else
+ return tud_mounted() && !tud_suspended();
+#endif
+}
+
+static adc_oneshot_unit_handle_t adc1_handle = NULL;
+static adc_cali_handle_t adc1_cali_chan0_handle = NULL;
+static float ema_voltage = 0.0f;
+static uint8_t bl_level = 0;
+
+// The backlight dimmer starts at full brightness on a rising edge after
+// being off, and each short low pulse steps it down one level (wrapping
+// back to max). Holding the line low for >2.5ms shuts it down.
+static void set_backlight(uint8_t value)
+{
+ if (value > BACKLIGHT_STEPS) {
+ value = BACKLIGHT_STEPS;
+ }
+ if (value == 0) {
+ gpio_set_level(BACKLIGHT_GPIO, 0);
+ esp_rom_delay_us(3000);
+ bl_level = 0;
+ return;
+ }
+ if (bl_level == 0) {
+ gpio_set_level(BACKLIGHT_GPIO, 1);
+ esp_rom_delay_us(30);
+ bl_level = BACKLIGHT_STEPS;
+ }
+ const int num = (BACKLIGHT_STEPS + bl_level - value) % BACKLIGHT_STEPS;
+ for (int i = 0; i < num; i++) {
+ gpio_set_level(BACKLIGHT_GPIO, 0);
+ esp_rom_delay_us(1);
+ gpio_set_level(BACKLIGHT_GPIO, 1);
+ esp_rom_delay_us(1);
+ }
+ bl_level = value;
+}
+
+esp_err_t power_init(void)
+{
+ gpio_set_direction(BOARD_POWERON_GPIO, GPIO_MODE_OUTPUT);
+ gpio_set_level(BOARD_POWERON_GPIO, 1);
+
+ gpio_set_direction(BACKLIGHT_GPIO, GPIO_MODE_OUTPUT);
+ gpio_set_level(BACKLIGHT_GPIO, 0);
+
+ adc_oneshot_unit_init_cfg_t init_config1 = { .unit_id = ADC_UNIT_1 };
+ ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
+ JADE_ASSERT(adc1_handle);
+
+ adc_oneshot_chan_cfg_t config = { .atten = BATTERY_ADC_ATTEN, .bitwidth = ADC_BITWIDTH_DEFAULT };
+ ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, BATTERY_ADC_CHANNEL, &config));
+
+ adc_cali_curve_fitting_config_t cali_config = {
+ .unit_id = ADC_UNIT_1,
+ .atten = BATTERY_ADC_ATTEN,
+ .bitwidth = ADC_BITWIDTH_DEFAULT,
+ };
+ if (adc_cali_create_scheme_curve_fitting(&cali_config, &adc1_cali_chan0_handle) != ESP_OK) {
+ JADE_LOGW("ADC calibration not available, continuing without it");
+ adc1_cali_chan0_handle = NULL;
+ }
+ return ESP_OK;
+}
+
+esp_err_t power_shutdown(void)
+{
+ esp_deep_sleep_start();
+ return ESP_OK;
+}
+
+esp_err_t power_screen_on(void) { return ESP_OK; }
+esp_err_t power_screen_off(void) { return ESP_OK; }
+
+esp_err_t power_backlight_on(uint8_t brightness)
+{
+ // Brightness is a level BACKLIGHT_MIN (1) to BACKLIGHT_MAX (5),
+ // mapped linearly onto dimmer steps 1 to BACKLIGHT_STEPS.
+ if (brightness < BACKLIGHT_MIN) {
+ brightness = BACKLIGHT_MIN;
+ } else if (brightness > BACKLIGHT_MAX) {
+ brightness = BACKLIGHT_MAX;
+ }
+ const uint8_t scaled
+ = 1 + ((BACKLIGHT_STEPS - 1) * (brightness - BACKLIGHT_MIN)) / (BACKLIGHT_MAX - BACKLIGHT_MIN);
+ set_backlight(scaled);
+
+ // Have the keyboard backlight track the screen backlight (levels 1-5 -> 51-255)
+ tdeck_set_keyboard_backlight(brightness * 51);
+ return ESP_OK;
+}
+
+esp_err_t power_backlight_off(void)
+{
+ set_backlight(0);
+ tdeck_set_keyboard_backlight(0);
+ return ESP_OK;
+}
+
+esp_err_t power_camera_on(void) { return ESP_OK; }
+esp_err_t power_camera_off(void) { return ESP_OK; }
+
+uint16_t power_get_vbat(void)
+{
+ JADE_ASSERT(adc1_handle);
+
+ // Average several samples to smooth adc noise
+ int raw_sum = 0;
+ for (int i = 0; i < BATTERY_ADC_SAMPLES; ++i) {
+ int raw_adc;
+ ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, BATTERY_ADC_CHANNEL, &raw_adc));
+ raw_sum += raw_adc;
+ }
+
+ int voltage;
+ const int raw_avg = raw_sum / BATTERY_ADC_SAMPLES;
+ if (adc1_cali_chan0_handle) {
+ ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan0_handle, raw_avg, &voltage));
+ } else {
+ voltage = (raw_avg * 3300) / 4095;
+ }
+
+ const float ema_v = (ema_voltage > 0) ? ema_voltage : (float)voltage;
+ ema_voltage = (BATTERY_EMA_ALPHA * voltage) + ((1.0f - BATTERY_EMA_ALPHA) * ema_v);
+ return (uint16_t)(ema_voltage * BATTERY_VDIV_MULTIPLIER);
+}
+
+uint8_t power_get_battery_status(void)
+{
+ const uint16_t vbat = power_get_vbat();
+ if (vbat > 4000) return 5;
+ if (vbat > 3800) return 4;
+ if (vbat > 3600) return 3;
+ if (vbat > 3400) return 2;
+ if (vbat > 3200) return 1;
+ return 0;
+}
+
+bool power_get_battery_charging(void)
+{
+ // No charger status line on the t-deck - infer charging from usb
+ // power being present while the battery is not yet full
+ return usb_connected() && power_get_vbat() < BATTERY_CHARGED_MILLIVOLTS;
+}
+
+uint16_t power_get_ibat_charge(void) { return 0; }
+uint16_t power_get_ibat_discharge(void) { return 0; }
+uint16_t power_get_vusb(void) { return 0; }
+uint16_t power_get_iusb(void) { return 0; }
+uint16_t power_get_temp(void) { return 0; }
+
+void disable_usb_host(void) {}
+void enable_usb_host(void) {}
+
+bool usb_is_powered(void) { return usb_connected(); }
diff --git a/main/process/dashboard.c b/main/process/dashboard.c
index 67d191d50..191e950c7 100644
--- a/main/process/dashboard.c
+++ b/main/process/dashboard.c
@@ -1703,7 +1703,7 @@ static void handle_view_otps(void)
// NOTE: Only Jade v1.1's and v2's have brightness controls
#if defined(CONFIG_BOARD_TYPE_JADE_V1_1) || defined(CONFIG_BOARD_TYPE_JADE_V2_ANY) \
- || defined(CONFIG_BOARD_TYPE_WS_TOUCH_LCD2)
+ || defined(CONFIG_BOARD_TYPE_WS_TOUCH_LCD2) || defined(CONFIG_BOARD_TYPE_TDECK)
static void handle_screen_brightness(void)
{
static const char* LABELS[] = { "Min(1)", "Low(2)", "Medium(3)", "High(4)", "Max(5)" };
@@ -2039,7 +2039,7 @@ static void handle_display_battery_volts(void)
#ifdef CONFIG_HAS_AXP
const int ret = snprintf(power_status, sizeof(power_status), "%umv", power_get_vbat());
JADE_ASSERT(ret > 0 && ret < sizeof(power_status));
-#elif defined(CONFIG_BOARD_TYPE_M5_STICKC_PLUS_2)
+#elif defined(CONFIG_BOARD_TYPE_M5_STICKC_PLUS_2) || defined(CONFIG_BOARD_TYPE_TDECK)
const uint16_t vbat = power_get_vbat();
if (vbat > 0) {
const int ret = snprintf(power_status, sizeof(power_status), "%umv", vbat);
@@ -2281,7 +2281,7 @@ static void handle_settings(const bool startup_menu)
// NOTE: Only Jade v1.1's and v2's have brightness controls
#if defined(CONFIG_BOARD_TYPE_JADE_V1_1) || defined(CONFIG_BOARD_TYPE_JADE_V2_ANY) \
- || defined(CONFIG_BOARD_TYPE_WS_TOUCH_LCD2)
+ || defined(CONFIG_BOARD_TYPE_WS_TOUCH_LCD2) || defined(CONFIG_BOARD_TYPE_TDECK)
case BTN_SETTINGS_DISPLAY_BRIGHTNESS:
handle_screen_brightness();
break;
diff --git a/main/process/mnemonic.c b/main/process/mnemonic.c
index db726f90b..9ae56d930 100644
--- a/main/process/mnemonic.c
+++ b/main/process/mnemonic.c
@@ -47,7 +47,6 @@ gui_activity_t* make_enter_wordlist_word_activity(gui_view_node_t** titletext, b
size_t keys_len);
gui_activity_t* make_calculate_final_word_activity(void);
-gui_activity_t* make_confirm_passphrase_activity(const char* passphrase, gui_view_node_t** textbox);
gui_activity_t* make_export_qr_overview_activity(const Icon* icon, bool initial);
gui_activity_t* make_export_qr_fragment_activity(
@@ -818,13 +817,24 @@ static size_t get_wordlist_words(
// Wait for kb button click
gui_activity_wait_event(enter_word_activity, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0);
selected_backspace = (ev_id == BTN_KEYBOARD_BACKSPACE);
- done_entering_words = (ev_id == BTN_KEYBOARD_ENTER);
- if (!selected_backspace && !done_entering_words) {
- // Character/letter was clicked
- const char letter_selected = ev_id - BTN_KEYBOARD_ASCII_OFFSET;
+ // A physical keyboard can send 'enter' at any time - only honour it
+ // where the on-screen enter button would be active (see enable_relevant_chars)
+ done_entering_words = (ev_id == BTN_KEYBOARD_ENTER && !is_mnemonic && !char_index);
+ if (!selected_backspace && ev_id >= BTN_KEYBOARD_ASCII_OFFSET) {
+ // Character/letter was clicked/typed (physical keyboards send lowercase)
+ const char letter_selected = toupper((int)(ev_id - BTN_KEYBOARD_ASCII_OFFSET));
if (letter_selected >= 'A' && letter_selected <= 'Z') {
+ // Only accept a letter if some word still matches the new stem -
+ // the on-screen keyboard disables impossible letters, but a
+ // physical keyboard cannot
word[char_index] = tolower(letter_selected);
- word[++char_index] = '\0';
+ word[char_index + 1] = '\0';
+ if (valid_words(word, char_index + 1, p_filter_words, num_filter_words, possible_word_list,
+ NUM_WORDS_SELECT, &exact_match)) {
+ ++char_index;
+ } else {
+ word[char_index] = '\0';
+ }
}
}
}
@@ -1179,13 +1189,9 @@ static void get_freetext_passphrase(char* passphrase, const size_t passphrase_le
JADE_ASSERT(passphrase_len);
passphrase[0] = '\0';
- // We will need this activity later when confirming
- gui_view_node_t* text_to_confirm = NULL;
- gui_activity_t* const confirm_passphrase_activity = make_confirm_passphrase_activity(passphrase, &text_to_confirm);
- int32_t ev_id;
-
// For passphrase we want all 4 keyboards
keyboard_entry_t kb_entry = { .max_allowed_len = passphrase_len - 1 };
+ SENSITIVE_PUSH(kb_entry.strdata, sizeof(kb_entry.strdata));
kb_entry.keyboards[0] = KB_LOWER_CASE_CHARS;
kb_entry.keyboards[1] = KB_UPPER_CASE_CHARS;
kb_entry.keyboards[2] = KB_NUMBERS_SYMBOLS;
@@ -1195,20 +1201,39 @@ static void get_freetext_passphrase(char* passphrase, const size_t passphrase_le
make_keyboard_entry_activity(&kb_entry, "Enter Passphrase");
JADE_ASSERT(kb_entry.activity);
+ // A second, deliberately blank, entry screen to confirm by blind re-entry
+ // (do not display or pre-populate with the initial entry)
+ keyboard_entry_t kb_confirm = { .max_allowed_len = passphrase_len - 1 };
+ SENSITIVE_PUSH(kb_confirm.strdata, sizeof(kb_confirm.strdata));
+ memcpy(kb_confirm.keyboards, kb_entry.keyboards, sizeof(kb_entry.keyboards));
+ kb_confirm.num_kbs = kb_entry.num_kbs;
+
+ make_keyboard_entry_activity(&kb_confirm, "Confirm Passphrase");
+ JADE_ASSERT(kb_confirm.activity);
+
bool done = false;
while (!done) {
// Run the keyboard entry loop to get a typed passphrase
run_keyboard_entry_loop(&kb_entry);
- // Ask user to confirm passphrase
- gui_update_text(text_to_confirm, kb_entry.len > 0 ? kb_entry.strdata : "");
- gui_set_current_activity(confirm_passphrase_activity);
- gui_activity_wait_event(confirm_passphrase_activity, GUI_BUTTON_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0);
- done = (ev_id == BTN_YES);
+ // Ask user to re-enter the passphrase to confirm
+ kb_confirm.strdata[0] = '\0';
+ kb_confirm.len = 0;
+ run_keyboard_entry_loop(&kb_confirm);
+
+ done = (kb_entry.len == kb_confirm.len) && !strcmp(kb_entry.strdata, kb_confirm.strdata);
+ if (!done) {
+ // Mismatch - start over from scratch
+ await_error_2("Passphrases do not", "match - please retry");
+ kb_entry.strdata[0] = '\0';
+ kb_entry.len = 0;
+ }
}
JADE_ASSERT(kb_entry.len < passphrase_len);
strcpy(passphrase, kb_entry.strdata);
+ SENSITIVE_POP(kb_confirm.strdata);
+ SENSITIVE_POP(kb_entry.strdata);
}
void get_passphrase(char* passphrase, const size_t passphrase_len)
diff --git a/main/process/ota_defines.h b/main/process/ota_defines.h
index bb1541232..b8a386269 100644
--- a/main/process/ota_defines.h
+++ b/main/process/ota_defines.h
@@ -47,6 +47,8 @@
#define JADE_OTA_BOARD_TYPE "TTGO_TDISPLAYS3PROCAMERA"
#elif defined(CONFIG_BOARD_TYPE_WS_TOUCH_LCD2)
#define JADE_OTA_BOARD_TYPE "WAVESHARE_TOUCH_LCD2"
+#elif defined(CONFIG_BOARD_TYPE_TDECK)
+#define JADE_OTA_BOARD_TYPE "TDECK"
#elif defined(CONFIG_BOARD_TYPE_QEMU) || defined(CONFIG_BOARD_TYPE_QEMU_LARGER)
#define JADE_OTA_BOARD_TYPE "QEMU"
#else
diff --git a/main/ui/dashboard.c b/main/ui/dashboard.c
index b637cfba5..82fca4ed9 100644
--- a/main/ui/dashboard.c
+++ b/main/ui/dashboard.c
@@ -394,7 +394,7 @@ gui_activity_t* make_display_settings_activity(void)
// NOTE: Only Jade v1.1's and v2's have brightness controls
// NOTE: Jade v1.1's do not support Flip Orientation because of issues with screen offsets
-#if defined(CONFIG_BOARD_TYPE_JADE_V2_ANY) || defined(CONFIG_BOARD_TYPE_WS_TOUCH_LCD2)
+#if defined(CONFIG_BOARD_TYPE_JADE_V2_ANY) || defined(CONFIG_BOARD_TYPE_WS_TOUCH_LCD2) || defined(CONFIG_BOARD_TYPE_TDECK)
btn_data_t menubtns[]
= { { .txt = "Display Brightness", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_DISPLAY_BRIGHTNESS },
{ .txt = "Flip Orientation", .font = GUI_DEFAULT_FONT, .ev_id = BTN_SETTINGS_DISPLAY_ORIENTATION },
diff --git a/main/ui/digit_entry.c b/main/ui/digit_entry.c
index c6e2ee84b..bc80e6d01 100644
--- a/main/ui/digit_entry.c
+++ b/main/ui/digit_entry.c
@@ -4,6 +4,7 @@
#include "../random.h"
#include "../ui.h"
+
#define CHAR_BACKSPACE '|'
#define CHAR_ENTER '~'
static const char ENTRY_CHARS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', CHAR_BACKSPACE, CHAR_ENTER };
@@ -202,6 +203,56 @@ static bool prev_selected_digit(digit_entry_t* digit_entry)
return true;
}
+#ifdef CONFIG_HAS_KEYBOARD
+// Map physical key events to digits - only actual ascii digits qualify
+// (on the T-Deck keyboard these are typed with the SYM modifier held)
+static int keyboard_event_digit(const int32_t ev_id)
+{
+ if (ev_id <= BTN_KEYBOARD_ASCII_OFFSET) {
+ return -1;
+ }
+ const int ch = ev_id - BTN_KEYBOARD_ASCII_OFFSET;
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ }
+ return -1;
+}
+
+// Handle a physical keyboard event - returns true (with 'done' set) when
+// entry is complete or abandoned, false to continue the entry loop.
+static bool handle_keyboard_event(digit_entry_t* digit_entry, const int32_t ev_id, bool* done)
+{
+ JADE_ASSERT(digit_entry);
+ JADE_ASSERT(done);
+
+ if (ev_id == BTN_KEYBOARD_BACKSPACE) {
+ if (!prev_selected_digit(digit_entry)) {
+ // Backspace on first digit - entry abandoned
+ *done = false;
+ return true;
+ }
+ } else if (ev_id == BTN_KEYBOARD_ENTER) {
+ // Only valid for 'index' entry (short numbers) - PIN requires all digits
+ if (digit_entry->entry_type == DIGIT_ENTRY_INDEX) {
+ // Enter on first digit abandons entry, otherwise completes it
+ *done = (digit_entry->selected_digit > 0);
+ return true;
+ }
+ } else {
+ const int digit = keyboard_event_digit(ev_id);
+ if (digit >= 0) {
+ digit_entry->current_selected_value = digit;
+ if (!next_selected_digit(digit_entry)) {
+ // Last digit typed - entry complete
+ *done = true;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#endif // CONFIG_HAS_KEYBOARD
+
// Returns true if number entry completes and digit_entry->digit is valid,
// and false if number entry abandoned and digit_entry->digit is not to be used.
bool run_digit_entry_loop(digit_entry_t* digit_entry)
@@ -211,8 +262,22 @@ bool run_digit_entry_loop(digit_entry_t* digit_entry)
int32_t ev_id;
while (true) {
+#ifdef CONFIG_HAS_KEYBOARD
+ // Wait for a GUI event or a physical keyboard event
+ esp_event_base_t ev_base = NULL;
+ gui_activity_wait_event_of_either_base(
+ digit_entry->activity, GUI_EVENT, GUI_BUTTON_EVENT, &ev_base, &ev_id, NULL, 0);
+ if (ev_base == GUI_BUTTON_EVENT) {
+ bool done = false;
+ if (handle_keyboard_event(digit_entry, ev_id, &done)) {
+ return done;
+ }
+ continue;
+ }
+#else
// wait for a GUI event
gui_activity_wait_event(digit_entry->activity, GUI_EVENT, ESP_EVENT_ANY_ID, NULL, &ev_id, NULL, 0);
+#endif
if (entry_invert_navigation()) {
// Swap left/right wheel events
if (ev_id == GUI_WHEEL_LEFT_EVENT) {
diff --git a/main/ui/keyboard.c b/main/ui/keyboard.c
index 6d14dfd00..64ac7fb4b 100644
--- a/main/ui/keyboard.c
+++ b/main/ui/keyboard.c
@@ -40,14 +40,27 @@ static void make_keyboard_screen(link_activity_t* kb_screen_activity, const char
// NOTE: final three characters ('|', '>', 'S') are rendered in different symbols fonts
// and are buttons for 'backspace', 'shift/next kb', and 'enter/done' (see below)
if (kb_type == KB_LOWER_CASE_CHARS) {
+#ifdef CONFIG_HAS_KEYBOARD
+ // Match the physical keyboard layout (space key ends the middle row)
+ lines[0] = "qwertyuiop";
+ lines[1] = "asdfghjkl ";
+ lines[2] = "zxcvbnm|>S";
+#else
lines[0] = "abcdefghij";
lines[1] = "klmnopqrst";
lines[2] = "uvwxyz |>S";
+#endif
// 'sizes' ok
} else if (kb_type == KB_UPPER_CASE_CHARS) {
+#ifdef CONFIG_HAS_KEYBOARD
+ lines[0] = "QWERTYUIOP";
+ lines[1] = "ASDFGHJKL ";
+ lines[2] = "ZXCVBNM|>S";
+#else
lines[0] = "ABCDEFGHIJ";
lines[1] = "KLMNOPQRST";
lines[2] = "UVWXYZ |>S";
+#endif
// 'sizes' ok
} else if (kb_type == KB_NUMBERS_SYMBOLS) {
lines[0] = "1234567890";
diff --git a/main/ui/mnemonic.c b/main/ui/mnemonic.c
index a50aded48..5b2f236b7 100644
--- a/main/ui/mnemonic.c
+++ b/main/ui/mnemonic.c
@@ -290,9 +290,16 @@ gui_activity_t* make_enter_wordlist_word_activity(gui_view_node_t** titletext, c
// second row, keyboard
char* lines[NUM_KEYBOARD_ROWS];
+#ifdef CONFIG_HAS_KEYBOARD
+ // Match the physical keyboard layout
+ lines[0] = (char[]){ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P' };
+ lines[1] = (char[]){ 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L' };
+ lines[2] = (char[]){ 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ' ', '|' };
+#else
lines[0] = (char[]){ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' };
lines[1] = (char[]){ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S' };
lines[2] = (char[]){ 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '|' };
+#endif
const size_t sizes[NUM_KEYBOARD_ROWS] = { 10, 9, 9 };
JADE_ASSERT(CONFIG_DISPLAY_WIDTH >= 240);
@@ -376,34 +383,6 @@ gui_activity_t* make_calculate_final_word_activity(void)
return act;
}
-gui_activity_t* make_confirm_passphrase_activity(const char* passphrase, gui_view_node_t** textbox)
-{
- JADE_ASSERT(passphrase);
- JADE_INIT_OUT_PPTR(textbox);
-
- gui_activity_t* const act = gui_make_activity();
- gui_view_node_t* const parent = add_title_bar(act, "Confirm Passphrase", NULL, 0, NULL);
-
- gui_view_node_t* vsplit;
- gui_make_vsplit(&vsplit, GUI_SPLIT_RELATIVE, 2, 70, 30);
- gui_set_parent(vsplit, parent);
-
- // passphrase
- gui_make_text(textbox, passphrase, TFT_WHITE);
- gui_set_text_noise(*textbox, TFT_BLACK);
- gui_set_parent(*textbox, vsplit);
-
- gui_set_padding(*textbox, GUI_MARGIN_ALL_DIFFERENT, 12, 2, 0, 4);
- gui_set_align(*textbox, GUI_ALIGN_LEFT, GUI_ALIGN_TOP);
-
- // third row, Yes and No buttons
- btn_data_t ftrbtns[] = { { .txt = "No", .font = GUI_DEFAULT_FONT, .ev_id = BTN_NO, .borders = GUI_BORDER_TOPRIGHT },
- { .txt = "Yes", .font = GUI_DEFAULT_FONT, .ev_id = BTN_YES, .borders = GUI_BORDER_TOPLEFT } };
- add_buttons(vsplit, UI_ROW, ftrbtns, 2);
-
- return act;
-}
-
gui_activity_t* make_export_qr_overview_activity(const Icon* icon, const bool initial)
{
JADE_ASSERT(icon);
diff --git a/tools/switch_to.sh b/tools/switch_to.sh
index 4fe03df42..b16356add 100755
--- a/tools/switch_to.sh
+++ b/tools/switch_to.sh
@@ -75,6 +75,7 @@ while true; do
jade_v1_1) CONFIG=$1; ARCH="esp32"; shift ;;
jade_v2) CONFIG=$1; ARCH="esp32s3"; shift ;;
jade_v2c) CONFIG=$1; ARCH="esp32s3"; shift ;;
+ tdeck) CONFIG=$1; ARCH="esp32s3"; shift ;;
qemu) CONFIG=$1; ARCH="esp32"; shift ;;
--dev) DEVELOPMENT="y"; shift ;;
--noradio) NORADIO=1; shift ;;