legacy/firmware: implement dual-button ime for gen 1 device

poc/dualbuttonime
exeabyss 5 years ago
parent d029920540
commit 8931ac1ef1

@ -119,11 +119,14 @@ be added to the storage u2f_counter to get the real counter value.
* storage.u2f_counter + config_u2f_offset.
* This corresponds to the number of cleared bits in the U2FAREA.
*/
static secbool sessionUseOnDeviceTextInputCached = secfalse;
static secbool sessionUseOnDeviceTextInput;
static secbool sessionSeedCached, sessionSeedUsesPassphrase;
static uint8_t CONFIDENTIAL sessionSeed[64];
static secbool sessionPassphraseCached = secfalse;
static char CONFIDENTIAL sessionPassphrase[51];
static char CONFIDENTIAL sessionPassphrase[MAX_PASSPHRASE_LEN];
#define autoLockDelayMsDefault (10 * 60 * 1000U) // 10 minutes
static secbool autoLockDelayMsCached = secfalse;
@ -407,8 +410,10 @@ void config_init(void) {
void session_clear(bool lock) {
sessionSeedCached = secfalse;
memzero(&sessionSeed, sizeof(sessionSeed));
sessionPassphraseCached = secfalse;
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
if (!session_isUseOnDeviceTextInput()) {
sessionPassphraseCached = secfalse;
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
}
if (lock) {
storage_lock();
}
@ -823,6 +828,22 @@ bool session_getState(const uint8_t *salt, uint8_t *state,
bool session_isUnlocked(void) { return sectrue == storage_is_unlocked(); }
bool session_isUseOnDeviceTextInputCached(void)
{
return sectrue == sessionUseOnDeviceTextInputCached;
}
bool session_isUseOnDeviceTextInput(void)
{
return sectrue == sessionUseOnDeviceTextInput;
}
void session_setUseOnDeviceTextInput(bool use)
{
sessionUseOnDeviceTextInputCached = sectrue;
sessionUseOnDeviceTextInput = use ? sectrue : secfalse;
}
bool config_isInitialized(void) {
bool initialized = false;
config_get_bool(KEY_INITIALIZED, &initialized);

@ -83,6 +83,7 @@ extern Storage configUpdate;
#define MAX_LABEL_LEN 32
#define MAX_LANGUAGE_LEN 16
#define MAX_MNEMONIC_LEN 240
#define MAX_PASSPHRASE_LEN 51
#define HOMESCREEN_SIZE 1024
#define UUID_SIZE 12
@ -108,6 +109,10 @@ bool config_getPassphraseProtection(bool *passphrase_protection);
bool config_getHomescreen(uint8_t *dest, uint16_t dest_size);
void config_setHomescreen(const uint8_t *data, uint32_t size);
bool session_isUseOnDeviceTextInputCached(void);
bool session_isUseOnDeviceTextInput(void);
void session_setUseOnDeviceTextInput(bool use);
void session_cachePassphrase(const char *passphrase);
bool session_isPassphraseCached(void);
bool session_getState(const uint8_t *salt, uint8_t *state,

@ -243,6 +243,111 @@ void layoutProgressSwipe(const char *desc, int permil) {
layoutProgress(desc, permil);
}
void layoutScrollInput(const char *text, int text_width, int num_total, int num_screen, int current_index, const char entries[], int horizontal_padding, int num_group, const int groups[], int num_skip_in_groups, bool draw_caret)
{
layoutLast = layoutScrollInput;
oledClear();
if (num_screen % 2 > 0)
++num_screen;
if (num_total <= 0 || num_screen <= 0 || current_index < 0 || current_index >= num_total || groups[num_group - 1] > num_total)
return;
const int CenterX = OLED_WIDTH / 2;
const int Y = 2;
int width = OLED_WIDTH - horizontal_padding * 2;
int gap = width / (num_screen - 1);
int t = (num_screen - 1) / 2;
char letter[2];
letter[1] = 0;
for (int i = 0; i < num_screen && i < num_total; ++i)
{
int x = CenterX - gap * (t - i);
int n = (current_index + i - t + num_total) % num_total;
letter[0] = entries[n];
oledDrawStringCenter(x, Y, letter, 1);
}
int halfgap = gap / 2;
oledInvert(CenterX - halfgap, Y - 2, CenterX + halfgap - 1, Y + FONT_HEIGHT + 1);
const int LineY = Y + FONT_HEIGHT * 2 - 2;
width--;
oledPartialHLine(horizontal_padding, OLED_WIDTH - horizontal_padding, LineY);
for (int i = 0; i < num_group; ++i)
oledPartialVLine(horizontal_padding + width * groups[i] / num_total, LineY - 1, LineY);
int x = horizontal_padding + width * current_index / num_total;
oledPartialHLine(x, x + 1, LineY - 1);
oledPartialHLine(x - 1, x + 2, LineY - 2);
oledPartialHLine(x - 2, x + 3, LineY - 3);
char groupinfo[] = "?-?";
int group;
for (group = 0; group < num_group - 1; ++group)
{
if (current_index >= groups[group] && current_index < groups[group + 1])
break;
}
groupinfo[0] = entries[groups[group]];
groupinfo[2] = entries[groups[group + 1] - num_skip_in_groups - 1];
oledDrawStringCenter(x, LineY + 2, groupinfo, 1);
oledHLine(OLED_HEIGHT / 2 - 5);
if (text)
oledDrawStringCenterMultiline(OLED_HEIGHT - FONT_HEIGHT * 2 - 1, text, 1, text_width);
if (draw_caret)
oledDrawCaret();
oledRefresh();
}
void layoutCheckPassphrase(const char *passphrase, int text_width, bool enable_edit, bool enable_done_yes)
{
layoutLast = layoutCheckPassphrase;
layoutSwipe();
oledClear();
if (enable_edit && enable_done_yes)
oledDrawString(0, 0 * 9, "Confirm passphrase:", 0);
else if (enable_edit)
oledDrawString(0, 0 * 9, "Passphrases mismatched:", 0);
else
oledDrawString(0, 0 * 9, "Passphrase confirmed:", 0);
oledDrawStringCenterMultiline(OLED_HEIGHT / 2 - 2, passphrase, 1, text_width);
if (enable_edit)
{
const char *btnNo = "Edit";
oledDrawString(1, OLED_HEIGHT - 8, "\x15", 0);
oledDrawString(fontCharWidth(0, '\x15') + 3, OLED_HEIGHT - 8, btnNo, 0);
oledInvert(0, OLED_HEIGHT - 9, fontCharWidth(0, '\x15') + oledStringWidth(btnNo, 0) + 2, OLED_HEIGHT - 1);
}
if (enable_done_yes)
{
const char *DONE = "Done";
const char *NEXT = "Next";
const char *btnYes = enable_edit ? DONE : NEXT;
oledDrawString(OLED_WIDTH - fontCharWidth(0, '\x06') - 1, OLED_HEIGHT - 8, "\x06", 0);
oledDrawString(OLED_WIDTH - oledStringWidth(btnYes, 0) - fontCharWidth(0, '\x06') - 3, OLED_HEIGHT - 8, btnYes, 0);
oledInvert(OLED_WIDTH - oledStringWidth(btnYes, 0) - fontCharWidth(0, '\x06') - 4, OLED_HEIGHT - 9, OLED_WIDTH - 1, OLED_HEIGHT - 1);
}
oledHLine(OLED_HEIGHT - 13);
oledRefresh();
}
void layoutScreensaver(void) {
layoutLast = layoutScreensaver;
oledClear();

@ -44,6 +44,9 @@ void layoutDialogSwipe(const BITMAP *icon, const char *btnNo,
const char *line5, const char *line6);
void layoutProgressSwipe(const char *desc, int permil);
void layoutScrollInput(const char *text, int text_width, int num_total, int num_screen, int current_index, const char entries[], int horizontal_padding, int num_group, const int groups[], int num_skip_in_groups, bool draw_caret);
void layoutCheckPassphrase(const char *passphrase, int text_width, bool enable_edit, bool enable_done);
void layoutScreensaver(void);
void layoutHome(void);
void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out);

@ -32,12 +32,149 @@
#include "pinmatrix.h"
#include "usb.h"
#include "util.h"
#include "rng.h"
#define MAX_WRONG_PINS 15
#define BACKSPACE '\x08'
#define SPACE '\x09'
#define DONE '\x06'
#define INPUT_DONE -1
#define NUM_PASSPHRASE_LINES 3
#define CHAR_AND_SPACE_WIDTH (5 + 1)
#define PASSPHRASE_WIDTH (MAX_PASSPHRASE_LEN / NUM_PASSPHRASE_LINES * CHAR_AND_SPACE_WIDTH)
#define LAST_PASSPHRASE_INDEX (MAX_PASSPHRASE_LEN - 1)
#define CARET_SHOW 80
#define CARET_CYCLE (CARET_SHOW * 2)
bool protectAbortedByCancel = false;
bool protectAbortedByInitialize = false;
void buttonCheckRepeat(bool *yes, bool *no, bool *confirm)
{
*yes = false;
*no = false;
*confirm = false;
const int Threshold0 = 20;
const int Thresholds[] = { Threshold0, 80, 20, 18, 16, 14, 12, 10, 8, 6, 4 };
const int MaxThresholdLevel = sizeof(Thresholds)/sizeof(Thresholds[0]) - 1;
static int yesthreshold = Threshold0;
static int nothreshold = Threshold0;
static int yeslevel = 0;
static int nolevel = 0;
static bool both = false;
usbSleep(5);
buttonUpdate();
if (both)
{
if (!button.YesDown && !button.NoDown)
{
both = false;
yeslevel = 0;
nolevel = 0;
yesthreshold = Thresholds[0];
nothreshold = Thresholds[0];
}
}
else if ((button.YesDown && button.NoDown)
|| (button.YesUp && button.NoDown)
|| (button.YesDown && button.NoUp)
|| (button.YesUp && button.NoUp))
{
if (!yeslevel && !nolevel)
{
both = true;
*confirm = true;
}
}
else
{
if (button.YesUp)
{
if (!yeslevel)
*yes = true;
yeslevel = 0;
yesthreshold = Thresholds[0];
}
else if (button.YesDown >= yesthreshold)
{
if (yeslevel < MaxThresholdLevel)
yeslevel++;
yesthreshold += Thresholds[yeslevel];
*yes = true;
}
if (button.NoUp)
{
if (!nolevel)
*no = true;
nolevel = 0;
nothreshold = Thresholds[0];
}
else if (button.NoDown >= nothreshold)
{
if (nolevel < MaxThresholdLevel)
nolevel++;
nothreshold += Thresholds[nolevel];
*no = true;
}
}
}
void buttonWaitForYesUp(void)
{
buttonUpdate();
for(;;)
{
usbSleep(5);
buttonUpdate();
if (button.YesUp)
break;
}
}
void buttonWaitForIdle(void)
{
buttonUpdate();
for(;;)
{
usbSleep(5);
buttonUpdate();
if (!button.YesDown && !button.YesUp && !button.NoDown && !button.NoUp)
break;
}
}
void requestOnDeviceTextInput(void)
{
layoutDialog(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you like to use"), _("on-device text input?"), NULL, NULL, NULL, NULL);
buttonUpdate();
for(;;)
{
usbSleep(5);
buttonUpdate();
if (button.YesUp || button.NoUp)
break;
}
layoutSwipe();
session_setUseOnDeviceTextInput(button.YesUp);
}
bool protectButton(ButtonRequestType type, bool confirm_only) {
ButtonRequest resp = {0};
bool result = false;
@ -276,13 +413,7 @@ bool protectChangePin(bool removal) {
return ret;
}
bool protectPassphrase(void) {
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (!passphrase_protection || session_isPassphraseCached()) {
return true;
}
bool protectPassphraseComputer(void) {
PassphraseRequest resp = {0};
memzero(&resp, sizeof(PassphraseRequest));
usbTiny(1);
@ -318,3 +449,210 @@ bool protectPassphrase(void) {
layoutHome();
return result;
}
int inputPassphraseScroll(char *passphrase, int *passphrasecharindex, const char entries[], int entryindex, int numtotal, int numscreen, int padding, const int groups[], int numgroup, int skip, int *caret)
{
for (; ; *caret = (*caret + 1) % CARET_CYCLE)
{
bool yes, no, confirm;
buttonCheckRepeat(&yes, &no, &confirm);
if (confirm)
{
buttonWaitForIdle();
if (entries[entryindex] == BACKSPACE)
{
if (*passphrasecharindex > 0)
{
--(*passphrasecharindex);
passphrase[*passphrasecharindex] = 0;
}
}
else if (entries[entryindex] == DONE)
{
return INPUT_DONE;
}
else
{
if (*passphrasecharindex < LAST_PASSPHRASE_INDEX)
{
passphrase[*passphrasecharindex] = entries[entryindex];
++(*passphrasecharindex);
}
return entryindex;
}
entryindex = random32() % numtotal;
}
else
{
if (yes)
entryindex = (entryindex + 1) % numtotal;
if (no)
entryindex = (entryindex - 1 + numtotal) % numtotal;
}
layoutScrollInput(passphrase, PASSPHRASE_WIDTH, numtotal, numscreen, entryindex, entries, padding, numgroup, groups, skip, *caret < CARET_SHOW);
}
}
int findCharIndex(const char entries[], char needle, int numtotal, int startindex, bool forward)
{
int index = startindex;
int step = forward ? 1 : -1;
while (index >= 0 && index < numtotal)
{
if (entries[index] == needle)
return index;
index += step;
}
return startindex;
}
void inputPassphrase(char *passphrase)
{
const char Entries[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', BACKSPACE, DONE,
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', BACKSPACE, DONE,
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', SPACE, BACKSPACE, DONE,
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', BACKSPACE, DONE,
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', BACKSPACE, DONE,
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', SPACE, BACKSPACE, DONE,
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', BACKSPACE, DONE,
'!', '@', '#', '$', '\x25', '^', '&', '*', '(', ')', BACKSPACE, DONE,
'`', '-', '=', '[', ']', '\\', ';', '\'', ',', '.', '/', BACKSPACE, DONE,
'~', '_', '+', '{', '}', '|', ':', '\'', '<', '>', '?', BACKSPACE, DONE
};
const int EntriesGroups[] = { 0, 11, 22, 33, 44, 55, 66, 78, 90, 103, 116 };
int numentries = sizeof(Entries) / sizeof(Entries[0]);
int numentriesgroups = sizeof(EntriesGroups) / sizeof(EntriesGroups[0]);
usbSleep(5);
buttonUpdate();
int passphrasecharindex = strlen(passphrase);
int caret = 0;
for (;;)
{
int entryindex = random32() % numentries;
if (passphrasecharindex >= LAST_PASSPHRASE_INDEX)
entryindex = findCharIndex(Entries, DONE, numentries, entryindex, entryindex < numentries / 2);
entryindex = inputPassphraseScroll(passphrase, &passphrasecharindex, Entries, entryindex, numentries, 9, 9, EntriesGroups, numentriesgroups, 2, &caret);
if (entryindex == INPUT_DONE)
return;
}
}
bool checkPassphrase(const char *passphrase, bool enable_edit, bool enable_done)
{
layoutCheckPassphrase(passphrase, PASSPHRASE_WIDTH, enable_edit, enable_done);
buttonUpdate();
for(;;)
{
usbSleep(5);
buttonUpdate();
if (enable_done && button.YesUp)
return true;
if (enable_edit && button.NoUp)
return false;
}
}
bool protectPassphraseDevice(void)
{
static char CONFIDENTIAL passphrase[MAX_PASSPHRASE_LEN];
memzero(passphrase, sizeof(passphrase));
buttonUpdate();
layoutDialog(NULL, NULL, _("Next"), NULL, _("You are about to enter"), _("the passphrase."), _("Select how many times"), _("you'd like to do it."), NULL, NULL);
buttonWaitForYesUp();
layoutSwipe();
layoutDialog(NULL, _("Twice"), _("Once"), NULL, _("If you are creating a new"), _("wallet, it is advised"), _("that you select Twice."), NULL, NULL, NULL);
for(;;)
{
usbSleep(5);
buttonUpdate();
if (button.YesUp || button.NoUp)
break;
}
layoutSwipe();
bool twice = button.NoUp;
layoutDialog(NULL, NULL, _("Next"), NULL, _("Enter the passphrase"), _("on the next screen."), _("- Single button: scroll."), _("- Hold: auto-scroll."), _("- Both buttons: confirm."), NULL);
buttonWaitForYesUp();
layoutSwipe();
for (;;)
{
inputPassphrase(passphrase);
if (checkPassphrase(passphrase, true, true))
break;
oledSwipeRight();
}
if (twice)
{
static char CONFIDENTIAL passphrase2[MAX_PASSPHRASE_LEN];
memzero(passphrase2, sizeof(passphrase2));
layoutSwipe();
layoutDialog(NULL, NULL, _("Next"), NULL, _("Re-enter the passphrase."), NULL, NULL, NULL, NULL, NULL);
buttonWaitForYesUp();
layoutSwipe();
for (;;)
{
inputPassphrase(passphrase2);
if (strcmp(passphrase, passphrase2) == 0)
break;
checkPassphrase(passphrase2, true, false);
oledSwipeRight();
}
memzero(passphrase2, sizeof(passphrase2));
}
checkPassphrase(passphrase, false, true);
for (int i = 0; i < MAX_PASSPHRASE_LEN && passphrase[i]; ++i)
if (passphrase[i] == SPACE)
passphrase[i] = ' ';
session_cachePassphrase(passphrase);
memzero(passphrase, sizeof(passphrase));
layoutHome();
return true;
}
bool protectPassphrase(void)
{
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (!passphrase_protection || session_isPassphraseCached()) {
return true;
}
bool result;
if (!session_isUseOnDeviceTextInputCached())
requestOnDeviceTextInput();
if (session_isUseOnDeviceTextInput())
result = protectPassphraseDevice();
else
result = protectPassphraseComputer();
return result;
}

@ -1,3 +1,35 @@
/* 0x00 _ */ (uint8_t *)"\x01\x00",
/* 0x01 _ */ (uint8_t *)"\x01\x00",
/* 0x02 _ */ (uint8_t *)"\x01\x00",
/* 0x03 _ */ (uint8_t *)"\x01\x00",
/* 0x04 _ */ (uint8_t *)"\x01\x00",
/* 0x05 _ */ (uint8_t *)"\x01\x00",
/* 0x06 _ */ (uint8_t *)"\x07\x18\x1c\x0e\x18\x30\x40\x80",
/* 0x07 _ */ (uint8_t *)"\x01\x00",
/* 0x08 _ */ (uint8_t *)"\x01\x00",
/* 0x09 _ */ (uint8_t *)"\x01\x00",
/* 0x0a _ */ (uint8_t *)"\x01\x00",
/* 0x0b _ */ (uint8_t *)"\x01\x00",
/* 0x0c _ */ (uint8_t *)"\x01\x00",
/* 0x0d _ */ (uint8_t *)"\x01\x00",
/* 0x0e _ */ (uint8_t *)"\x01\x00",
/* 0x0f _ */ (uint8_t *)"\x01\x00",
/* 0x10 _ */ (uint8_t *)"\x01\x00",
/* 0x11 _ */ (uint8_t *)"\x01\x00",
/* 0x12 _ */ (uint8_t *)"\x01\x00",
/* 0x13 _ */ (uint8_t *)"\x01\x00",
/* 0x14 _ */ (uint8_t *)"\x01\x00",
/* 0x15 _ */ (uint8_t *)"\x07\x44\xee\x7c\x38\x7c\xee\x44",
/* 0x16 _ */ (uint8_t *)"\x01\x00",
/* 0x17 _ */ (uint8_t *)"\x01\x00",
/* 0x18 _ */ (uint8_t *)"\x01\x00",
/* 0x19 _ */ (uint8_t *)"\x01\x00",
/* 0x1a _ */ (uint8_t *)"\x01\x00",
/* 0x1b _ */ (uint8_t *)"\x01\x00",
/* 0x1c _ */ (uint8_t *)"\x01\x00",
/* 0x1d _ */ (uint8_t *)"\x01\x00",
/* 0x1e _ */ (uint8_t *)"\x01\x00",
/* 0x1f _ */ (uint8_t *)"\x01\x00",
/* 0x20 */ (uint8_t *)"\x01\x00",
/* 0x21 ! */ (uint8_t *)"\x02\xfa\xfa",
/* 0x22 " */ (uint8_t *)"\x03\xc0\x00\xc0",

@ -1,19 +1,51 @@
/* 0x00 _ */ (uint8_t *)"\x01\x00",
/* 0x01 _ */ (uint8_t *)"\x01\x00",
/* 0x02 _ */ (uint8_t *)"\x01\x00",
/* 0x03 _ */ (uint8_t *)"\x01\xff",
/* 0x04 _ */ (uint8_t *)"\x01\x00",
/* 0x05 _ */ (uint8_t *)"\x01\x00",
/* 0x06 _ */ (uint8_t *)"\x07\x18\x1c\x0e\x18\x30\x40\x80",
/* 0x07 _ */ (uint8_t *)"\x01\x00",
/* 0x08 _ */ (uint8_t *)"\x09\x10\x28\x44\x82\xaa\x92\xaa\x82\xfe",
/* 0x09 _ */ (uint8_t *)"\x05\x0e\x02\x02\x02\x0e",
/* 0x0a _ */ (uint8_t *)"\x01\x00",
/* 0x0b _ */ (uint8_t *)"\x01\x00",
/* 0x0c _ */ (uint8_t *)"\x01\x00",
/* 0x0d _ */ (uint8_t *)"\x01\x00",
/* 0x0e _ */ (uint8_t *)"\x01\x00",
/* 0x0f _ */ (uint8_t *)"\x01\x00",
/* 0x10 _ */ (uint8_t *)"\x01\x00",
/* 0x11 _ */ (uint8_t *)"\x01\x00",
/* 0x12 _ */ (uint8_t *)"\x01\x00",
/* 0x13 _ */ (uint8_t *)"\x01\x00",
/* 0x14 _ */ (uint8_t *)"\x01\x00",
/* 0x15 _ */ (uint8_t *)"\x07\x44\xee\x7c\x38\x7c\xee\x44",
/* 0x16 _ */ (uint8_t *)"\x01\x00",
/* 0x17 _ */ (uint8_t *)"\x01\x00",
/* 0x18 _ */ (uint8_t *)"\x01\x00",
/* 0x19 _ */ (uint8_t *)"\x01\x00",
/* 0x1a _ */ (uint8_t *)"\x01\x00",
/* 0x1b _ */ (uint8_t *)"\x01\x00",
/* 0x1c _ */ (uint8_t *)"\x01\x00",
/* 0x1d _ */ (uint8_t *)"\x01\x00",
/* 0x1e _ */ (uint8_t *)"\x01\x00",
/* 0x1f _ */ (uint8_t *)"\x01\x00",
/* 0x20 */ (uint8_t *)"\x01\x00",
/* 0x21 ! */ (uint8_t *)"\x03\x60\xfa\x60",
/* 0x21 ! */ (uint8_t *)"\x05\x00\x60\xfa\x60\x00",
/* 0x22 " */ (uint8_t *)"\x05\x00\xe0\x00\xe0\x00",
/* 0x23 # */ (uint8_t *)"\x05\x6c\xfe\x6c\xfe\x6c",
/* 0x24 $ */ (uint8_t *)"\x05\x32\xff\x5a\xff\x4c",
/* 0x25 % */ (uint8_t *)"\x05\xc2\xcc\x10\x66\x86",
/* 0x26 & */ (uint8_t *)"\x05\x5c\xa2\xb2\x4c\x1a",
/* 0x27 ' */ (uint8_t *)"\x05\x00\x00\xe0\x00\x00",
/* 0x28 ( */ (uint8_t *)"\x03\x38\x44\x82",
/* 0x29 ) */ (uint8_t *)"\x03\x82\x44\x38",
/* 0x28 ( */ (uint8_t *)"\x05\x00\x38\x44\x82\x00",
/* 0x29 ) */ (uint8_t *)"\x05\x00\x82\x44\x38\x00",
/* 0x2a * */ (uint8_t *)"\x05\x44\x28\xfe\x28\x44",
/* 0x2b + */ (uint8_t *)"\x05\x10\x10\x7c\x10\x10",
/* 0x2c , */ (uint8_t *)"\x03\x01\x06\x00",
/* 0x2d - */ (uint8_t *)"\x04\x10\x10\x10\x10",
/* 0x2e . */ (uint8_t *)"\x03\x00\x02\x00",
/* 0x2f / */ (uint8_t *)"\x03\x06\x38\xc0",
/* 0x2c , */ (uint8_t *)"\x05\x00\x01\x06\x00\x00",
/* 0x2d - */ (uint8_t *)"\x05\x10\x10\x10\x10\x00",
/* 0x2e . */ (uint8_t *)"\x05\x00\x00\x02\x00\x00",
/* 0x2f / */ (uint8_t *)"\x05\x00\x06\x38\xc0\x00",
/* 0x30 0 */ (uint8_t *)"\x05\x7c\x82\x92\x82\x7c",
/* 0x31 1 */ (uint8_t *)"\x05\x22\x42\xfe\x02\x02",
/* 0x32 2 */ (uint8_t *)"\x05\x42\x86\x8a\x92\x62",
@ -24,11 +56,11 @@
/* 0x37 7 */ (uint8_t *)"\x05\x80\x80\x8e\xb0\xc0",
/* 0x38 8 */ (uint8_t *)"\x05\x6c\x92\x92\x92\x6c",
/* 0x39 9 */ (uint8_t *)"\x05\x64\x92\x92\x92\x7c",
/* 0x3a : */ (uint8_t *)"\x03\x00\x24\x00",
/* 0x3b ; */ (uint8_t *)"\x03\x01\x26\x00",
/* 0x3c < */ (uint8_t *)"\x04\x10\x28\x44\x82",
/* 0x3d = */ (uint8_t *)"\x04\x28\x28\x28\x28",
/* 0x3e > */ (uint8_t *)"\x04\x82\x44\x28\x10",
/* 0x3a : */ (uint8_t *)"\x05\x00\x00\x24\x00\x00",
/* 0x3b ; */ (uint8_t *)"\x05\x00\x01\x26\x00\x00",
/* 0x3c < */ (uint8_t *)"\x05\x10\x28\x44\x82\x00",
/* 0x3d = */ (uint8_t *)"\x05\x28\x28\x28\x28\x00",
/* 0x3e > */ (uint8_t *)"\x05\x00\x82\x44\x28\x10",
/* 0x3f ? */ (uint8_t *)"\x05\x40\x80\x9a\xa0\x40",
/* 0x40 @ */ (uint8_t *)"\x05\x7c\x82\x9a\xaa\x72",
/* 0x41 A */ (uint8_t *)"\x05\x7e\x90\x90\x90\x7e",
@ -57,10 +89,10 @@
/* 0x58 X */ (uint8_t *)"\x05\xc6\x28\x10\x28\xc6",
/* 0x59 Y */ (uint8_t *)"\x05\xc0\x20\x1e\x20\xc0",
/* 0x5a Z */ (uint8_t *)"\x05\x86\x8a\x92\xa2\xc2",
/* 0x5b [ */ (uint8_t *)"\x03\xfe\x82\x82",
/* 0x5c \ */ (uint8_t *)"\x03\xe0\x38\x0e",
/* 0x5d ] */ (uint8_t *)"\x03\x82\x82\xfe",
/* 0x5e ^ */ (uint8_t *)"\x03\x60\xc0\x60",
/* 0x5b [ */ (uint8_t *)"\x05\x00\xfe\x82\x82\x00",
/* 0x5c \ */ (uint8_t *)"\x05\x00\xe0\x38\x0e\x00",
/* 0x5d ] */ (uint8_t *)"\x05\x00\x82\x82\xfe\x00",
/* 0x5e ^ */ (uint8_t *)"\x05\x00\x60\xc0\x60\x00",
/* 0x5f _ */ (uint8_t *)"\x05\x02\x02\x02\x02\x02",
/* 0x60 ` */ (uint8_t *)"\x05\x00\x80\x40\x20\x00",
/* 0x61 a */ (uint8_t *)"\x05\x04\x2a\x2a\x2a\x1e",
@ -89,8 +121,8 @@
/* 0x78 x */ (uint8_t *)"\x05\x22\x36\x08\x36\x22",
/* 0x79 y */ (uint8_t *)"\x05\x38\x05\x05\x05\x3e",
/* 0x7a z */ (uint8_t *)"\x05\x22\x26\x2a\x32\x22",
/* 0x7b { */ (uint8_t *)"\x04\x10\x7c\xee\x82",
/* 0x7c | */ (uint8_t *)"\x03\x00\xfe\x00",
/* 0x7d } */ (uint8_t *)"\x04\x82\xee\x7c\x10",
/* 0x7b { */ (uint8_t *)"\x05\x10\x7c\xee\x82\x00",
/* 0x7c | */ (uint8_t *)"\x05\x00\x00\xfe\x00\x00",
/* 0x7d } */ (uint8_t *)"\x05\x00\x82\xee\x7c\x10",
/* 0x7e ~ */ (uint8_t *)"\x05\x18\x20\x10\x08\x30",
/* 0x7f _ */ (uint8_t *)"\x01\x00",

@ -1,6 +1,6 @@
#include "fonts.h"
const uint8_t *const font_data[FONTS][128 - 32] = {
const uint8_t *const font_data[FONTS][128] = {
{
#include "font.inc"
},
@ -12,10 +12,10 @@ const uint8_t *const font_data[FONTS][128 - 32] = {
};
int fontCharWidth(uint8_t font, uint8_t c) {
return (c < 0x20 || c >= 0x80) ? 0 : font_data[font % FONTS][c - 32][0];
return (c >= 0x80) ? 0 : font_data[font % FONTS][c][0];
}
const uint8_t *fontCharData(uint8_t font, uint8_t c) {
return (c < 0x20 || c >= 0x80) ? (const uint8_t *)""
: font_data[font % FONTS][c - 32] + 1;
return (c >= 0x80) ? (const uint8_t *)""
: font_data[font % FONTS][c] + 1;
}

@ -15,7 +15,7 @@
#define FONT_DOUBLE 0x80
extern const uint8_t *const font_data[FONTS][128 - 32];
extern const uint8_t *const font_data[FONTS][128];
int fontCharWidth(uint8_t font, uint8_t c);
const uint8_t *fontCharData(uint8_t font, uint8_t c);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -23,7 +23,7 @@ def convert(imgfile, outfile):
img = Img(imgfile)
cur = ""
with open(outfile, "w") as f:
for i in range(128 - 32):
for i in range(128):
x = (i % 16) * 10
y = (i // 16) * 10
cur = ""
@ -32,10 +32,14 @@ def convert(imgfile, outfile):
x += 1
cur += "\\x%02x" % int(val, 2)
cur = "\\x%02x" % (len(cur) // 4) + cur
i += 32
ch = "_" if (i == 127) else chr(i)
ch = chr(i) if i >= 32 and i <= 126 else "_"
f.write('\t/* 0x%02x %c */ (uint8_t *)"%s",\n' % (i, ch, cur))
convert("fonts/fontfixed.png", "fontfixed.inc")
convert("fonts/font.png", "font.inc")
print("Converting fontfixed...")
convert("fontfixed.png", "../fontfixed.inc")
print("Done")
print("Converting font...")
convert("font.png", "../font.inc")
print("Done")

@ -65,6 +65,9 @@
*/
static uint8_t _oledbuffer[OLED_BUFSIZE];
static int caret_x = 0;
static int caret_y = 0;
static int caret_zoom = 1;
static bool is_debug_link = 0;
/*
@ -241,12 +244,12 @@ void oledSetBuffer(uint8_t *buf) {
memcpy(_oledbuffer, buf, sizeof(_oledbuffer));
}
void oledDrawChar(int x, int y, char c, uint8_t font) {
void oledDrawCharUpdateCaret(int x, int y, char c, uint8_t font, int zoom, bool update_caret) {
if (x >= OLED_WIDTH || y >= OLED_HEIGHT || y <= -FONT_HEIGHT) {
return;
}
int zoom = (font & FONT_DOUBLE) ? 2 : 1;
zoom = (zoom <= 0) ? ((font & FONT_DOUBLE) ? 2 : 1) : zoom;
int char_width = fontCharWidth(font & 0x7f, (uint8_t)c);
const uint8_t *char_data = fontCharData(font & 0x7f, (uint8_t)c);
@ -266,6 +269,21 @@ void oledDrawChar(int x, int y, char c, uint8_t font) {
}
}
}
if (update_caret)
{
caret_x = x + (char_width + 1) * zoom;
caret_y = y;
caret_zoom = zoom;
}
}
void oledDrawChar(int x, int y, char c, uint8_t font) {
oledDrawCharUpdateCaret(x, y, c, font, -1, true);
}
void oledDrawCaret(void) {
oledDrawCharUpdateCaret(caret_x, caret_y, '\x03', 1, caret_zoom, false);
}
static uint8_t convert_char(const char a) {
@ -273,13 +291,6 @@ static uint8_t convert_char(const char a) {
uint8_t c = a;
// non-printable ASCII character
if (c < ' ') {
last_was_utf8 = 0;
return '_';
}
// regular ASCII character
if (c < 0x80) {
last_was_utf8 = 0;
return c;
@ -304,11 +315,11 @@ static uint8_t convert_char(const char a) {
return 0;
}
int oledStringWidth(const char *text, uint8_t font) {
int oledPartialStringWidth(const char *text, uint8_t font, int num) {
if (!text) return 0;
int space = (font & FONT_DOUBLE) ? 2 : 1;
int l = 0;
for (; *text; text++) {
for (; *text && num; text++, num--) {
uint8_t c = convert_char(*text);
if (c) {
l += fontCharWidth(font & 0x7f, c) + space;
@ -317,6 +328,10 @@ int oledStringWidth(const char *text, uint8_t font) {
return l;
}
int oledStringWidth(const char *text, uint8_t font) {
return oledPartialStringWidth(text, font, -1);
}
void oledDrawString(int x, int y, const char *text, uint8_t font) {
if (!text) return;
int l = 0;
@ -340,6 +355,93 @@ void oledDrawStringRight(int x, int y, const char *text, uint8_t font) {
oledDrawString(x, y, text, font);
}
void oledDrawPartialStringSize(int x, int y, const char* text, uint8_t font, int size, int num)
{
if (!text) return;
int l = 0;
for (; *text && num; text++, num--) {
uint8_t c = convert_char(*text);
if (c) {
oledDrawCharUpdateCaret(x + l, y, c, font, size, true);
l += (fontCharWidth(font, c) + 1) * size;
}
}
}
void oledDrawPartialString(int x, int y, const char* text, uint8_t font, int num)
{
oledDrawPartialStringSize(x, y, text, font, 1, num);
}
void oledDrawStringCenterMultiline(int y, const char* text, uint8_t font, int maxwidth)
{
const int CenterX = OLED_WIDTH / 2;
const int Height = FONT_HEIGHT + 1;
if (!text || !*text)
{
caret_x = CenterX;
caret_y = y - Height / 2;
caret_zoom = 1;
return;
}
if (maxwidth <= 0 || maxwidth > OLED_WIDTH)
maxwidth = OLED_WIDTH;
const char* end = text + strlen(text);
int lines = 0;
int chars[8];
const char* str;
for (str = text; str < end;)
{
int lo = 1;
int hi = strlen(str);
int len = lo;
for (;;)
{
if (lo > hi)
break;
int mid = (lo + hi) / 2;
int width = oledPartialStringWidth(str, font, mid);
if (width < maxwidth)
{
len = mid;
lo = mid + 1;
}
else if (width > maxwidth)
{
if (mid == len + 1)
break;
else
hi = mid - 1;
}
else
{
len = mid;
break;
}
}
str += len;
chars[lines] = len;
lines++;
if (lines >= 8)
break;
}
y -= Height * lines / 2;
str = text;
for (int i = 0; i < lines; i++)
{
int len = chars[i];
oledDrawPartialString(CenterX - oledPartialStringWidth(str, font, len) / 2, y, str, font, len);
str += len;
y += Height;
}
}
void oledDrawBitmap(int x, int y, const BITMAP *bmp) {
for (int i = 0; i < bmp->width; i++) {
for (int j = 0; j < bmp->height; j++) {
@ -382,13 +484,28 @@ void oledBox(int x1, int y1, int x2, int y2, bool set) {
}
}
void oledPartialHLine(int x1, int x2, int y)
{
if (y < 0 || y >= OLED_HEIGHT || x1 >= x2 || x1 >= OLED_WIDTH || x2 <= 0) {
return;
}
for (int x = x1; x < x2; x++) {
oledDrawPixel(x, y);
}
}
void oledPartialVLine(int x, int y1, int y2)
{
if (x < 0 || x >= OLED_WIDTH || y1 >= y2 || y1 >= OLED_HEIGHT || y2 <= 0) {
return;
}
for (int y = y1; y < y2; y++) {
oledDrawPixel(x, y);
}
}
void oledHLine(int y) {
if (y < 0 || y >= OLED_HEIGHT) {
return;
}
for (int x = 0; x < OLED_WIDTH; x++) {
oledDrawPixel(x, y);
}
oledPartialHLine(0, OLED_WIDTH, y);
}
/*

@ -44,13 +44,17 @@ void oledDrawPixel(int x, int y);
void oledClearPixel(int x, int y);
void oledInvertPixel(int x, int y);
void oledDrawChar(int x, int y, char c, uint8_t font);
void oledDrawCaret(void);
int oledStringWidth(const char *text, uint8_t font);
void oledDrawString(int x, int y, const char *text, uint8_t font);
void oledDrawStringCenter(int x, int y, const char *text, uint8_t font);
void oledDrawStringCenterMultiline(int y, const char* text, uint8_t font, int width);
void oledDrawStringRight(int x, int y, const char *text, uint8_t font);
void oledDrawBitmap(int x, int y, const BITMAP *bmp);
void oledInvert(int x1, int y1, int x2, int y2);
void oledBox(int x1, int y1, int x2, int y2, bool set);
void oledPartialHLine(int x1, int x2, int y);
void oledPartialVLine(int x, int y1, int y2);
void oledHLine(int y);
void oledFrame(int x1, int y1, int x2, int y2);
void oledSwipeLeft(void);

Loading…
Cancel
Save