Working menu!

This commit is contained in:
Renze Nicolai 2022-04-16 01:16:36 +02:00
parent 748ca757b9
commit 4e3ca2e19b
17 changed files with 1268 additions and 290 deletions

View file

@ -1,4 +1,4 @@
idf_component_register(
SRCS "main.c" "menu.c" "fpga_test.c"
SRCS "main.c" "menu.c" "fpga_test.c" "pax_keyboard.c" "button_wrapper.c" "system_wrapper.c" "appfs_wrapper.c" "graphics_wrapper.c"
INCLUDE_DIRS "." "include"
)

76
main/appfs_wrapper.c Normal file
View file

@ -0,0 +1,76 @@
#include "appfs_wrapper.h"
#include "esp_sleep.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
esp_err_t appfs_init(void) {
return appfsInit(APPFS_PART_TYPE, APPFS_PART_SUBTYPE);
}
uint8_t* load_file_to_ram(FILE* fd, size_t* fsize) {
fseek(fd, 0, SEEK_END);
*fsize = ftell(fd);
fseek(fd, 0, SEEK_SET);
uint8_t* file = malloc(*fsize);
if (file == NULL) return NULL;
fread(file, *fsize, 1, fd);
return file;
}
/*void appfs_store_app(void) {
draw_message("Installing app...");
esp_err_t res;
appfs_handle_t handle;
FILE* app_fd = fopen("/sd/gnuboy.bin", "rb");
if (app_fd == NULL) {
draw_message("Failed to open gnuboy.bin");
ESP_LOGE(TAG, "Failed to open gnuboy.bin");
vTaskDelay(100 / portTICK_PERIOD_MS);
return;
}
size_t app_size;
uint8_t* app = load_file_to_ram(app_fd, &app_size);
if (app == NULL) {
draw_message("Failed to load app to RAM");
ESP_LOGE(TAG, "Failed to load application into RAM");
vTaskDelay(100 / portTICK_PERIOD_MS);
return;
}
ESP_LOGI(TAG, "Application size %d", app_size);
res = appfsCreateFile("gnuboy", app_size, &handle);
if (res != ESP_OK) {
draw_message("Failed to create on AppFS");
ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res);
vTaskDelay(100 / portTICK_PERIOD_MS);
free(app);
return;
}
res = appfsWrite(handle, 0, app, app_size);
if (res != ESP_OK) {
draw_message("Failed to write to AppFS");
ESP_LOGE(TAG, "Failed to write to file on AppFS (%d)", res);
vTaskDelay(100 / portTICK_PERIOD_MS);
free(app);
return;
}
free(app);
ESP_LOGI(TAG, "Application is now stored in AppFS");
draw_message("App installed!");
vTaskDelay(100 / portTICK_PERIOD_MS);
return;
}*/
void appfs_boot_app(int fd) {
if (fd<0 || fd>255) {
REG_WRITE(RTC_CNTL_STORE0_REG, 0);
} else {
REG_WRITE(RTC_CNTL_STORE0_REG, 0xA5000000|fd);
}
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
esp_sleep_enable_timer_wakeup(10);
esp_deep_sleep_start();
}

25
main/button_wrapper.c Normal file
View file

@ -0,0 +1,25 @@
#include "button_wrapper.h"
xQueueHandle queue;
void button_handler(uint8_t pin, bool value) {
button_message_t message;
message.button = pin;
message.state = value;
xQueueSend(queue, &message, portMAX_DELAY);
}
void button_init(PCA9555* aPca9555, xQueueHandle aQueue) {
queue = aQueue;
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_START, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_SELECT, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_MENU, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_HOME, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_LEFT, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_PRESS, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_DOWN, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_UP, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_RIGHT, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_BACK, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_ACCEPT, button_handler);
}

View file

@ -8,7 +8,7 @@
#include "ili9341.h"
#include "ice40.h"
#include "hardware.h"
#include "button_message.h"
#include "button_wrapper.h"
static const char *TAG = "fpga_test";

25
main/graphics_wrapper.c Normal file
View file

@ -0,0 +1,25 @@
#include "graphics_wrapper.h"
void message_render(pax_buf_t *aBuffer, char* message, float aPosX, float aPosY, float aWidth, float aHeight) {
pax_col_t fgColor = 0xFFFF0000;
pax_col_t bgColor = 0xFFFFD4D4;
pax_clip(aBuffer, aPosX, aPosY, aWidth, aHeight);
pax_simple_rect(aBuffer, bgColor, aPosX, aPosY, aWidth, aHeight);
pax_outline_rect(aBuffer, fgColor, aPosX, aPosY, aWidth, aHeight);
pax_clip(aBuffer, aPosX + 1, aPosY + 1, aWidth - 2, aHeight - 2);
pax_draw_text(aBuffer, fgColor, NULL, 18, aPosX + 1, aPosY + 1, message);
pax_noclip(aBuffer);
}
esp_err_t graphics_task(pax_buf_t* pax_buffer, ILI9341* ili9341, uint8_t* framebuffer, menu_t* menu, char* message) {
pax_background(pax_buffer, 0xCCCCCC);
if (menu != NULL) {
menu_render(pax_buffer, menu, 10, 10, 320-20, 240-20);
}
if (message != NULL) {
message_render(pax_buffer, message, 20, 110, 320-40, 20);
}
return ili9341_write(ili9341, framebuffer);
}

Binary file not shown.

View file

@ -0,0 +1,17 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include <sdkconfig.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <esp_system.h>
#include <esp_err.h>
#include <esp_log.h>
#include "appfs.h"
esp_err_t appfs_init(void);
uint8_t* load_file_to_ram(FILE* fd, size_t* fsize);
void appfs_store_app(void);
void appfs_boot_app(int fd);

View file

@ -1,6 +0,0 @@
#pragma once
typedef struct _button_message {
uint8_t button;
bool state;
} button_message_t;

View file

@ -0,0 +1,15 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include <sdkconfig.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include "hardware.h"
typedef struct _button_message {
uint8_t button;
bool state;
} button_message_t;
void button_init(PCA9555* aPca9555, xQueueHandle aQueue);

View file

@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
#include <sdkconfig.h>
#include <esp_system.h>
#include "pax_gfx.h"
#include "ili9341.h"
#include "menu.h"
esp_err_t graphics_task(pax_buf_t* pax_buffer, ILI9341* ili9341, uint8_t* framebuffer, menu_t* menu, char* message);

View file

@ -37,6 +37,7 @@ bool menu_navigate_to(menu_t* aMenu, size_t aPosition);
void menu_navigate_previous(menu_t* aMenu);
void menu_navigate_next(menu_t* aMenu);
size_t menu_get_position(menu_t* aMenu);
size_t menu_get_length(menu_t* aMenu);
void* menu_get_callback_args(menu_t* aMenu, size_t aPosition);
void menu_debug(menu_t* aMenu);
void menu_render(pax_buf_t *aBuffer, menu_t *aMenu, float aPosX, float aPosY, float aWidth, float aHeight);

147
main/include/pax_keyboard.h Normal file
View file

@ -0,0 +1,147 @@
/*
MIT License
Copyright (c) 2022 Julian Scheffers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef PAX_KEYBOARD_H
#define PAX_KEYBOARD_H
#include <pax_gfx.h>
#include <pax_shapes.h>
#include <pax_fonts.h>
// A number of inputs supported by the PAX keyboard.
typedef enum {
// Represents no input being pressed.
PKB_NO_INPUT,
// Movement of the cursor.
PKB_UP, PKB_DOWN, PKB_LEFT, PKB_RIGHT,
// Delete to the left or the selection. Backspace key.
PKB_DELETE_BEFORE,
// Delete to the right or the selection. Delete key.
PKB_DELETE_AFTER,
// Switch between lower case, upper case and symbols.
PKB_MODESELECT,
// Enter a character.
PKB_CHARSELECT,
// The same thing as the shift key.
// Goes between PKB_LOWERCASE and PKB_UPPERCASE or PKB_NUMBERS and PKB_SYMBOLS.
PKB_SHIFT,
} pkb_input_t;
// The type of keyboard currently selected.
typedef enum {
// Lowercase and .,
PKB_LOWERCASE,
// Uppercase and <>
PBK_UPPERCASE,
// Numbers and symbols 1/2
PKB_NUMBERS,
// Symbols 2/2
PKB_SYMBOLS,
} pkb_keyboard_t;
// The PAX keyboard context used for drawing and alike.
typedef struct {
// Position on screen of the keyboard.
int x, y;
// Maximum size of the keyboard.
int width, height;
// Content of the keyboard.
char *content;
// Size in bytes of capacity of the content buffer.
size_t content_cap;
// Starting position of the selection in the text box.
int selection;
// Cursor position of the text box.
int cursor;
// Cursor position of the keyboard.
int key_x, key_y;
// The currently held input.
pkb_input_t held;
// The time that holding the input started.
int64_t hold_start;
// The last time pkb_press was called.
int64_t last_press;
// Whether the keyboard is multi-line.
bool multiline;
// Whether the keyboard is in insert mode.
bool insert;
// The board that is currently selected.
pkb_keyboard_t board_sel;
// The font to use for the keyboard.
pax_font_t *kb_font;
// The font size to use for the keyboard.
float kb_font_size;
// The font to use for the text.
pax_font_t *text_font;
// The font size to use for the text.
float text_font_size;
// The text color to use.
pax_col_t text_col;
// The text color to use when a character is being held down.
pax_col_t sel_text_col;
// The selection color to use.
pax_col_t sel_col;
// The background color to use.
pax_col_t bg_col;
// Whether something has changed since last draw.
bool dirty;
// Whether the text has changed since last draw.
bool text_dirty;
// Whether the keyboard has changed since last draw.
bool kb_dirty;
// Whether just the selected character has changed since last draw.
bool sel_dirty;
// Previous cursor position of the keyboard.
// Used for sel_dirty.
int last_key_x, last_key_y;
// Indicates that the input has been accepted.
bool input_accepted;
} pkb_ctx_t;
// Initialise the context with default settings.
void pkb_init (pax_buf_t *buf, pkb_ctx_t *ctx);
// Free any memory associated with the context.
void pkb_destroy(pkb_ctx_t *ctx);
// Redraw the complete on-screen keyboard.
void pkb_render (pax_buf_t *buf, pkb_ctx_t *ctx);
// Redraw only the changed parts of the on-screen keyboard.
void pkb_redraw (pax_buf_t *buf, pkb_ctx_t *ctx);
// The loop that allows input repeating.
void pkb_loop (pkb_ctx_t *ctx);
// A pressing of the input.
void pkb_press (pkb_ctx_t *ctx, pkb_input_t input);
// A relealing of the input.
void pkb_release(pkb_ctx_t *ctx, pkb_input_t input);
#endif //PAX_KEYBOARD_H

View file

@ -0,0 +1,3 @@
#pragma once
void restart();

View file

@ -5,7 +5,6 @@
#include <freertos/task.h>
#include <freertos/queue.h>
#include <esp_system.h>
//#include <esp_spi_flash.h>
#include <esp_err.h>
#include <esp_log.h>
#include "hardware.h"
@ -15,33 +14,24 @@
#include "appfs.h"
#include "driver_framebuffer.h"
#include "esp_sleep.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "rp2040.h"
#include "fpga_test.h"
#include "menu.h"
#include "button_message.h"
#include "button_wrapper.h"
#include "system_wrapper.h"
#include "graphics_wrapper.h"
#include "appfs_wrapper.h"
static const char *TAG = "main";
bool calibrate = true;
bool display_bno_value = false;
ILI9341* ili9341 = NULL;
ICE40* ice40 = NULL;
BNO055* bno055 = NULL;
RP2040* rp2040 = NULL;
uint8_t* framebuffer = NULL;
pax_buf_t* pax_buffer = NULL;
xQueueHandle buttonQueue;
typedef enum action {
ACTION_NONE,
ACTION_APPFS,
ACTION_INSTALLER,
ACTION_SETTINGS,
ACTION_OTA,
ACTION_FPGA
} menu_action_t;
@ -50,160 +40,102 @@ typedef struct _menu_args {
menu_action_t action;
} menu_args_t;
void button_handler(uint8_t pin, bool value) {
button_message_t message;
message.button = pin;
message.state = value;
xQueueSend(buttonQueue, &message, portMAX_DELAY);
}
void button_init() {
PCA9555* pca9555 = get_pca9555();
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_START, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_SELECT, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_MENU, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_HOME, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_JOY_LEFT, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_JOY_PRESS, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_JOY_DOWN, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_JOY_UP, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_JOY_RIGHT, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_BACK, button_handler);
pca9555_set_interrupt_handler(pca9555, PCA9555_PIN_BTN_ACCEPT, button_handler);
}
void restart() {
for (int i = 3; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, uint8_t* framebuffer, menu_action_t* menu_action, appfs_handle_t* appfs_fd) {
menu_t* menu = menu_alloc("Main menu");
*appfs_fd = APPFS_INVALID_FD;
*menu_action = ACTION_NONE;
while (1) {
*appfs_fd = appfsNextEntry(*appfs_fd);
if (*appfs_fd == APPFS_INVALID_FD) break;
const char* name = NULL;
appfsEntryInfo(*appfs_fd, &name, NULL);
menu_args_t* args = malloc(sizeof(menu_args_t));
args->fd = *appfs_fd;
args->action = ACTION_APPFS;
menu_insert_item(menu, name, NULL, (void*) args, -1);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}
*appfs_fd = APPFS_INVALID_FD;
void message_render(pax_buf_t *aBuffer, char* message, float aPosX, float aPosY, float aWidth, float aHeight) {
pax_col_t fgColor = 0xFFFF0000;
pax_col_t bgColor = 0xFFFFD4D4;
pax_clip(aBuffer, aPosX, aPosY, aWidth, aHeight);
pax_simple_rect(aBuffer, bgColor, aPosX, aPosY, aWidth, aHeight);
pax_outline_rect(aBuffer, fgColor, aPosX, aPosY, aWidth, aHeight);
pax_clip(aBuffer, aPosX + 1, aPosY + 1, aWidth - 2, aHeight - 2);
pax_draw_text(aBuffer, fgColor, NULL, 18, aPosX + 1, aPosY + 1, message);
pax_noclip(aBuffer);
}
menu_args_t* install_args = malloc(sizeof(menu_args_t));
install_args->action = ACTION_INSTALLER;
menu_insert_item(menu, "Hatchery", NULL, install_args, -1);
menu_args_t* settings_args = malloc(sizeof(menu_args_t));
settings_args->action = ACTION_SETTINGS;
menu_insert_item(menu, "System settings", NULL, settings_args, -1);
menu_args_t* ota_args = malloc(sizeof(menu_args_t));
ota_args->action = ACTION_OTA;
menu_insert_item(menu, "Firmware update", NULL, ota_args, -1);
menu_args_t* fpga_args = malloc(sizeof(menu_args_t));
fpga_args->action = ACTION_FPGA;
menu_insert_item(menu, "FPGA", NULL, fpga_args, -1);
esp_err_t graphics_task(pax_buf_t* buffer, ILI9341* ili9341, uint8_t* framebuffer, menu_t* menu, char* message) {
pax_background(pax_buffer, 0xCCCCCC);
if (menu != NULL) {
menu_render(pax_buffer, menu, 10, 10, 320-20, 240-20);
for (uint8_t i = 0; i < 30; i++) {
menu_args_t* dummy_args = malloc(sizeof(menu_args_t));
dummy_args->action = ACTION_NONE;
menu_insert_item(menu, "Dummy menu item", NULL, dummy_args, -1);
}
bool render = true;
menu_args_t* menuArgs = NULL;
while (1) {
button_message_t buttonMessage = {0};
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
uint8_t pin = buttonMessage.button;
bool value = buttonMessage.state;
switch(pin) {
case PCA9555_PIN_BTN_JOY_DOWN:
if (value) {
menu_navigate_next(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_JOY_UP:
if (value) {
menu_navigate_previous(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_ACCEPT:
if (value) {
menuArgs = menu_get_callback_args(menu, menu_get_position(menu));
}
break;
default:
break;
}
}
if (render) {
graphics_task(pax_buffer, ili9341, framebuffer, menu, NULL);
render = false;
}
if (menuArgs != NULL) {
*appfs_fd = menuArgs->fd;
*menu_action = menuArgs->action;
graphics_task(pax_buffer, ili9341, framebuffer, menu, "Please wait...");
break;
}
}
if (message != NULL) {
message_render(pax_buffer, message, 20, 110, 320-40, 20);
}
return ili9341_write(ili9341, framebuffer);
}
esp_err_t draw_message(char* message) {
pax_background(pax_buffer, 0xFFFFFF);
pax_draw_text(pax_buffer, pax_col_rgb(0,0,0), PAX_FONT_DEFAULT, 18, 0, 0, message);
return ili9341_write(ili9341, framebuffer);
}
esp_err_t appfs_init(void) {
return appfsInit(APPFS_PART_TYPE, APPFS_PART_SUBTYPE);
}
uint8_t* load_file_to_ram(FILE* fd, size_t* fsize) {
fseek(fd, 0, SEEK_END);
*fsize = ftell(fd);
fseek(fd, 0, SEEK_SET);
uint8_t* file = malloc(*fsize);
if (file == NULL) return NULL;
fread(file, *fsize, 1, fd);
return file;
}
void appfs_store_app(void) {
draw_message("Installing app...");
esp_err_t res;
appfs_handle_t handle;
FILE* app_fd = fopen("/sd/gnuboy.bin", "rb");
if (app_fd == NULL) {
draw_message("Failed to open gnuboy.bin");
ESP_LOGE(TAG, "Failed to open gnuboy.bin");
vTaskDelay(100 / portTICK_PERIOD_MS);
return;
}
size_t app_size;
uint8_t* app = load_file_to_ram(app_fd, &app_size);
if (app == NULL) {
draw_message("Failed to load app to RAM");
ESP_LOGE(TAG, "Failed to load application into RAM");
vTaskDelay(100 / portTICK_PERIOD_MS);
return;
for (size_t index = 0; index < menu_get_length(menu); index++) {
free(menu_get_callback_args(menu, index));
}
ESP_LOGI(TAG, "Application size %d", app_size);
res = appfsCreateFile("gnuboy", app_size, &handle);
if (res != ESP_OK) {
draw_message("Failed to create on AppFS");
ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res);
vTaskDelay(100 / portTICK_PERIOD_MS);
free(app);
return;
}
res = appfsWrite(handle, 0, app, app_size);
if (res != ESP_OK) {
draw_message("Failed to write to AppFS");
ESP_LOGE(TAG, "Failed to write to file on AppFS (%d)", res);
vTaskDelay(100 / portTICK_PERIOD_MS);
free(app);
return;
}
free(app);
ESP_LOGI(TAG, "Application is now stored in AppFS");
draw_message("App installed!");
vTaskDelay(100 / portTICK_PERIOD_MS);
return;
}
void appfs_boot_app(int fd) {
if (fd<0 || fd>255) {
REG_WRITE(RTC_CNTL_STORE0_REG, 0);
} else {
REG_WRITE(RTC_CNTL_STORE0_REG, 0xA5000000|fd);
}
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
esp_sleep_enable_timer_wakeup(10);
esp_deep_sleep_start();
}
void appfs_test(bool sdcard_ready) {
appfs_handle_t fd = appfsOpen("gnuboy");
if (fd < 0) {
ESP_LOGW(TAG, "gnuboy not found in appfs");
draw_message("gnuboy not found in fs!");
/*if (sdcard_ready) {
appfs_store_app();
appfs_test(false); // Recursive, but who cares :D
}*/
} else {
draw_message("Booting gnuboy...");
ESP_LOGE(TAG, "booting gnuboy from appfs (%d)", fd);
appfs_boot_app(fd);
}
menu_free(menu);
}
void app_main(void) {
esp_err_t res;
buttonQueue = xQueueCreate(10, sizeof(button_message_t));
/* Initialize memory */
xQueueHandle buttonQueue = xQueueCreate(10, sizeof(button_message_t));
if (buttonQueue == NULL) {
ESP_LOGE(TAG, "Failed to allocate queue");
@ -211,14 +143,14 @@ void app_main(void) {
}
framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT);
uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT);
if (framebuffer == NULL) {
ESP_LOGE(TAG, "Failed to allocate framebuffer");
restart();
}
memset(framebuffer, 0, ILI9341_BUFFER_SIZE);
pax_buffer = malloc(sizeof(pax_buf_t));
pax_buf_t* pax_buffer = malloc(sizeof(pax_buf_t));
if (framebuffer == NULL) {
ESP_LOGE(TAG, "Failed to allocate pax buffer");
restart();
@ -228,6 +160,8 @@ void app_main(void) {
pax_buf_init(pax_buffer, framebuffer, ILI9341_WIDTH, ILI9341_HEIGHT, PAX_BUF_16_565RGB);
driver_framebuffer_init(framebuffer);
/* Initialize hardware */
res = board_init();
if (res != ESP_OK) {
@ -235,13 +169,14 @@ void app_main(void) {
restart();
}
ili9341 = get_ili9341();
ice40 = get_ice40();
bno055 = get_bno055();
rp2040 = get_rp2040();
ILI9341* ili9341 = get_ili9341();
ICE40* ice40 = get_ice40();
BNO055* bno055 = get_bno055();
RP2040* rp2040 = get_rp2040();
PCA9555* pca9555 = get_pca9555();
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "Button init...");
button_init();
button_init(pca9555, buttonQueue);
rp2040_set_led_mode(rp2040, true, true);
@ -259,113 +194,22 @@ void app_main(void) {
if (sdcard_ready) {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "SD card mounted");
} else {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "No SD card");
}
menu_t* menu = menu_alloc("Launcher");
appfs_handle_t current_fd = APPFS_INVALID_FD;
while (1) {
current_fd = appfsNextEntry(current_fd);
if (current_fd == APPFS_INVALID_FD) break;
const char* name = NULL;
appfsEntryInfo(current_fd, &name, NULL);
menu_args_t* args = malloc(sizeof(menu_args_t));
args->fd = current_fd;
args->action = ACTION_APPFS;
menu_insert_item(menu, name, NULL, (void*) args, -1);
}
menu_args_t* fpga_args = malloc(sizeof(menu_args_t));
fpga_args->action = ACTION_FPGA;
menu_insert_item(menu, "FPGA test", NULL, fpga_args, -1);
menu_args_t* install_args = malloc(sizeof(menu_args_t));
install_args->action = ACTION_INSTALLER;
menu_insert_item(menu, "Install app...", NULL, install_args, -1);
bool render = true;
menu_args_t* menuAction = NULL;
while (1) {
button_message_t buttonMessage = {0};
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
uint8_t pin = buttonMessage.button;
bool value = buttonMessage.state;
switch(pin) {
case PCA9555_PIN_BTN_JOY_LEFT:
printf("Joystick horizontal %s\n", value ? "left" : "center");
break;
case PCA9555_PIN_BTN_JOY_PRESS:
printf("Joystick %s\n", value ? "pressed" : "released");
break;
case PCA9555_PIN_BTN_JOY_DOWN:
printf("Joystick vertical %s\n", value ? "down" : "center");
if (value) {
menu_navigate_next(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_JOY_UP:
printf("Joystick vertical %s\n", value ? "up" : "center");
if (value) {
menu_navigate_previous(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_JOY_RIGHT:
printf("Joystick horizontal %s\n", value ? "right" : "center");
break;
case PCA9555_PIN_BTN_HOME:
printf("Home button %s\n", value ? "pressed" : "released");
break;
case PCA9555_PIN_BTN_MENU:
printf("Menu button %s\n", value ? "pressed" : "released");
//if (value) reset_to_menu = true;
break;
case PCA9555_PIN_BTN_START: {
printf("Start button %s\n", value ? "pressed" : "released");
break;
}
case PCA9555_PIN_BTN_SELECT: {
printf("Select button %s\n", value ? "pressed" : "released");
break;
}
case PCA9555_PIN_BTN_BACK:
printf("Back button %s\n", value ? "pressed" : "released");
break;
case PCA9555_PIN_BTN_ACCEPT:
printf("Accept button %s\n", value ? "pressed" : "released");
if (value) {
menuAction = menu_get_callback_args(menu, menu_get_position(menu));
printf("Position: %u\n", menu_get_position(menu));
}
break;
default:
printf("Unknown button %d %s\n", pin, value ? "pressed" : "released");
}
}
if (render) {
graphics_task(pax_buffer, ili9341, framebuffer, menu, NULL);
render = false;
}
if (menuAction != NULL) {
graphics_task(pax_buffer, ili9341, framebuffer, menu, "Please wait...");
if (menuAction->action == ACTION_APPFS) {
appfs_boot_app(menuAction->fd);
} else if (menuAction->action == ACTION_FPGA) {
graphics_task(pax_buffer, ili9341, framebuffer, menu, "FPGA TEST");
fpga_test(ili9341, ice40, buttonQueue);
}else if (menuAction->action == ACTION_INSTALLER) {
graphics_task(pax_buffer, ili9341, framebuffer, menu, "INSTALLER");
appfs_store_app();
}
menuAction = NULL;
render = true;
while (true) {
menu_action_t menu_action;
appfs_handle_t appfs_fd;
menu_launcher(buttonQueue, pax_buffer, ili9341, framebuffer, &menu_action, &appfs_fd);
if (menu_action == ACTION_APPFS) {
appfs_boot_app(appfs_fd);
} else if (menu_action == ACTION_FPGA) {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "FPGA TEST");
fpga_test(ili9341, ice40, buttonQueue);
} else if (menu_action == ACTION_INSTALLER) {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "INSTALLER");
//appfs_store_app();
}
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "Loop!");
}

View file

@ -22,9 +22,9 @@ menu_t* menu_alloc(const char* aTitle) {
void _menu_free_item(menu_item_t* aMenuItem) {
free(aMenuItem->label);
if (aMenuItem->callbackArgs != NULL) {
free(aMenuItem->callbackArgs);
}
//if (aMenuItem->callbackArgs != NULL) {
// free(aMenuItem->callbackArgs);
//}
free(aMenuItem);
}
@ -147,6 +147,10 @@ size_t menu_get_position(menu_t* aMenu) {
return aMenu->position;
}
size_t menu_get_length(menu_t* aMenu) {
return aMenu->length;
}
void* menu_get_callback_args(menu_t* aMenu, size_t aPosition) {
menu_item_t* item = _menu_find_item(aMenu, aPosition);
if (item == NULL) return NULL;
@ -174,17 +178,15 @@ void menu_debug(menu_t* aMenu) {
}
void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, float aWidth, float aHeight) {
size_t itemOffset = 0;
pax_col_t fgColor = 0xFF000000;
pax_col_t bgColor = 0xFFFFFFFF;
pax_col_t borderColor = 0xFF000000;
pax_col_t titleColor = 0xFFFFFFFF;
float scroll = 0;
pax_col_t scrollbarBgColor = 0xFF555555;
pax_col_t scrollbarFgColor = 0xFFCCCCCC;
pax_col_t scrollbarSlColor = 0xFFFFFFFF;
float entry_height = 18 + 2;
size_t maxItems = aBuffer->height / entry_height;
size_t entry_offset = scroll / entry_height;
scroll -= entry_offset * entry_height;
size_t maxItems = aHeight / entry_height;
float posY = aPosY;
@ -202,8 +204,11 @@ void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, fl
pax_clip(aBuffer, aPosX, posY, aWidth, aHeight);
pax_outline_rect(aBuffer, borderColor, aPosX, aPosY, aWidth, aHeight);
posY -= scroll;
size_t itemOffset = 0;
if (aMenu->position >= maxItems) {
itemOffset = aMenu->position - maxItems + 1;
}
for (size_t index = itemOffset; (index < itemOffset + maxItems) && (index < aMenu->length); index++) {
menu_item_t* item = _menu_find_item(aMenu, index);
if (item == NULL) {
@ -213,16 +218,32 @@ void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, fl
if (index == aMenu->position) {
pax_clip(aBuffer, aPosX, posY, aWidth, entry_height);
pax_simple_rect(aBuffer, fgColor, aPosX + 1, posY, aWidth - 2, entry_height);
pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 2, entry_height - 2);
pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 4, entry_height - 2);
pax_draw_text(aBuffer, bgColor, NULL, entry_height - 2, aPosX + 1, posY + 1, item->label);
} else {
pax_clip(aBuffer, aPosX, posY, aWidth, entry_height);
pax_clip(aBuffer, aPosX, posY, aWidth, entry_height - 1);
pax_simple_rect(aBuffer, bgColor, aPosX + 1, posY, aWidth - 2, entry_height);
pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 2, entry_height - 2);
pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 4, entry_height - 2);
pax_draw_text(aBuffer, fgColor, NULL, entry_height - 2, aPosX + 1, posY + 1, item->label);
}
posY += entry_height;
}
pax_clip(aBuffer, aPosX + aWidth - 5, aPosY + entry_height, 4, aHeight - 1 - entry_height);
float fractionStart = itemOffset / (aMenu->length * 1.0);
float fractionSelected = aMenu->position / (aMenu->length * 1.0);
float fractionEnd = (itemOffset + maxItems) / (aMenu->length * 1.0);
if (fractionEnd > 1.0) fractionEnd = 1.0;
float scrollbarHeight = aHeight - entry_height;
float scrollbarStart = scrollbarHeight * fractionStart;
float scrollbarSelected = scrollbarHeight * fractionSelected;
float scrollbarEnd = scrollbarHeight * fractionEnd;
pax_simple_rect(aBuffer, scrollbarBgColor, aPosX + aWidth - 5, aPosY + entry_height - 1, 4, scrollbarHeight);
pax_simple_rect(aBuffer, scrollbarFgColor, aPosX + aWidth - 5, aPosY + entry_height - 1 + scrollbarStart, 4, scrollbarEnd - scrollbarStart);
pax_simple_rect(aBuffer, scrollbarSlColor, aPosX + aWidth - 5, aPosY + entry_height - 1 + scrollbarSelected, 4, scrollbarHeight / aMenu->length);
pax_noclip(aBuffer);
}

781
main/pax_keyboard.c Normal file
View file

@ -0,0 +1,781 @@
/*
MIT License
Copyright (c) 2022 Julian Scheffers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pax_keyboard.h>
#include <esp_timer.h>
#include <string.h>
#include <malloc.h>
/* ==== Miscellaneous ==== */
// Initialise the context with default settings.
void pkb_init(pax_buf_t *buf, pkb_ctx_t *ctx) {
// Allocate a bufffer.
char *buffer = malloc(4);
memset(buffer, 0, 4);
// Some defaults.
*ctx = (pkb_ctx_t) {
// Position on screen of the keyboard.
.x = 0,
.y = 0,
// Maximum size of the keyboard.
.width = buf->width,
.height = buf->height,
// Content of the keyboard.
.content = buffer,
// Size in bytes of capacity of the content buffer.
.content_cap = 4,
// Starting position of the selection in the text box.
.selection = 0,
// Cursor position of the text box.
.cursor = 0,
// Cursor position of the keyboard.
.key_x = 3,
.key_y = 1,
// The currently held input.
.held = PKB_NO_INPUT,
// The time that holding the input started.
.hold_start = 0,
// The last time pkb_press was called.
.last_press = 0,
// Whether the keyboard is multi-line.
.multiline = false,
// Whether the keyboard is in insert mode.
.insert = false,
// The board that is currently selected.
.board_sel = PKB_LOWERCASE,
// The font to use for the keyboard.
.kb_font = PAX_FONT_DEFAULT,
// The font size to use for the keyboard.
.kb_font_size = 27,
// The font to use for the text.
.text_font = PAX_FONT_DEFAULT,
// The font size to use for the text.
.text_font_size = 18,
// The text color to use.
.text_col = 0xffffffff,
// The text color to use when a character is being held down.
.sel_text_col = 0xff000000,
// The selection color to use.
.sel_col = 0xff007fff,
// The background color to use.
.bg_col = 0xff000000,
// Whether something has changed since last draw.
.dirty = true,
// Whether the text has changed since last draw.
.text_dirty = true,
// Whether the keyboard has changed since last draw.
.kb_dirty = true,
// Whether just the selected character has changed since last draw.
.sel_dirty = true,
// Previous cursor position of the keyboard.
// Used for sel_dirty.
.last_key_x = 3,
.last_key_y = 1,
// Indicates that the input has been accepted.
.input_accepted = false,
};
// TODO: Pick fancier text sizes.
}
// Free any memory associated with the context.
void pkb_destroy(pkb_ctx_t *ctx) {
free(ctx->content);
ctx->content = NULL;
ctx->content_cap = 0;
}
const char *uppercase_board[] = {
"QWERTYUIOP",
"ASDFGHJKL",
" ZXCVBNM ",
" < > ",
"<", ">",
};
const char *lowercase_board[] = {
"qwertyuiop",
"asdfghjkl",
" zxcvbnm ",
" , . ",
",", ".",
};
const char *number_board[] = {
"1234567890",
"@#$_&-+()/",
" *\"';:!? ",
" = \\ ",
"=", "\\",
};
const char *symbols_board[] = {
"()[]{}`~",
"/|\\+-_=",
" ^%<>'\" ",
" , . ",
",", ".",
};
const char **boards[] = {
lowercase_board, uppercase_board,
number_board, symbols_board,
};
/* ==== Special key art ==== */
// Art for the accept key.
// Expects x to be the horizontal center of the key.
static void pkb_art_accept(pax_buf_t *buf, pkb_ctx_t *ctx, float x, float y, float dx, bool selected) {
pax_col_t col = selected && ctx->held == PKB_CHARSELECT ? ctx->sel_text_col : ctx->text_col;
float scale = fminf(ctx->kb_font_size - 4, dx - 4);
pax_push_2d (buf);
pax_apply_2d (buf, matrix_2d_translate(x-dx/2+2, y+2));
pax_apply_2d (buf, matrix_2d_scale(scale, scale));
pax_draw_line(buf, col, 0.25, 0.5, 0.5, 1);
pax_draw_line(buf, col, 0.5, 1, 1, 0);
pax_pop_2d (buf);
}
// Art for the backspace key.
// Expects x to be the horizontal center of the key.
static void pkb_art_bksp(pax_buf_t *buf, pkb_ctx_t *ctx, float x, float y, float dx, bool selected) {
pax_col_t col = selected && ctx->held == PKB_CHARSELECT ? ctx->sel_text_col : ctx->text_col;
float scale = fminf(ctx->kb_font_size - 4, dx - 4);
pax_push_2d (buf);
pax_apply_2d (buf, matrix_2d_translate(x-dx/2+2, y+2));
pax_apply_2d (buf, matrix_2d_scale(scale, scale));
// The stopper.
pax_draw_line(buf, col, 0, 0.25, 0, 0.75);
// The arrow.
pax_draw_line(buf, col, 0.1, 0.5, 0.35, 0.25);
pax_draw_line(buf, col, 0.1, 0.5, 0.35, 0.75);
pax_draw_line(buf, col, 0.1, 0.5, 1, 0.5);
pax_pop_2d (buf);
}
// Art for the shift key.
// Expects x to be the horizontal center of the key.
static void pkb_art_shift(pax_buf_t *buf, pkb_ctx_t *ctx, float x, float y, float dx, bool selected) {
bool active = ctx->board_sel & 1;
pax_col_t col = selected && ctx->held == PKB_CHARSELECT ? ctx->sel_text_col : ctx->text_col;
float scale = fminf(ctx->kb_font_size - 4, dx - 4);
pax_push_2d (buf);
pax_apply_2d(buf, matrix_2d_translate(x, y+2));
pax_apply_2d(buf, matrix_2d_scale(scale, scale));
if (active) {
// Filled in shift key.
pax_draw_tri (buf, col, -0.5, 0.5, 0, 0, 0.5, 0.5);
pax_draw_rect(buf, col, -0.25, 0.5, 0.5, 0.5);
} else {
// Outlined shift key.
pax_draw_line(buf, col, 0, 0, 0.5, 0.5);
pax_draw_line(buf, col, 0.5, 0.5, 0.25, 0.5);
pax_draw_line(buf, col, 0.25, 0.5, 0.25, 1);
pax_draw_line(buf, col, 0.25, 1, -0.25, 1);
pax_draw_line(buf, col, -0.25, 0.5, -0.25, 1);
pax_draw_line(buf, col, -0.5, 0.5, -0.25, 0.5);
pax_draw_line(buf, col, 0, 0, -0.5, 0.5);
}
pax_pop_2d (buf);
}
// Art for the letters/numbers key.
// Expects x to be the horizontal center of the key.
static void pkb_art_select(pax_buf_t *buf, pkb_ctx_t *ctx, float x, float y, float dx, bool selected) {
pax_col_t col = selected && ctx->held == PKB_CHARSELECT ? ctx->sel_text_col : ctx->text_col;
// Pick the stuff to show.
char *short_str;
char *long_str;
if (ctx->board_sel == PKB_NUMBERS) {
// Show some symbols.
short_str = "%";
long_str = "%&~";
} else if (ctx->board_sel == PKB_SYMBOLS) {
// Show some letters.
short_str = "A";
long_str = "Abc";
} else {
// Show some numbers.
short_str = "#";
long_str = "123";
}
// Calculate font size.
char *str = long_str;
pax_vec1_t dims = pax_text_size(ctx->kb_font, 0, str);
int font_size = 9;//(dx - 4) / dims.x * dims.y;
if (font_size < dims.y) {
str = short_str;
dims = pax_text_size(ctx->kb_font, 0, str);
font_size = 9;//(dx - 4) / dims.x * dims.y;
}
dims = pax_text_size(ctx->kb_font, font_size, str);
// Now draw it.
pax_push_2d (buf);
pax_apply_2d (buf, matrix_2d_translate(x-dims.x/2, y+(ctx->kb_font_size-font_size)/2));
pax_draw_text(buf, col, ctx->kb_font, font_size, 0, 0, str);
pax_pop_2d (buf);
}
/* ==== Rendering ==== */
// Draw one key of the keyboard.
// Expects x to be the horizontal center of the key.
static void pkb_char(pax_buf_t *buf, pkb_ctx_t *ctx, float x, float y, float dx, char *text, bool selected) {
pax_vec1_t dims = pax_text_size(ctx->kb_font, ctx->kb_font_size, text);
if (selected && ctx->held == PKB_CHARSELECT) {
// Infilll!
pax_draw_rect(buf, ctx->sel_col, x-dx/2, y, dx, ctx->kb_font_size);
pax_draw_text(buf, ctx->sel_text_col, ctx->kb_font, ctx->kb_font_size, x-dims.x*0.5, y, text);
} else {
pax_draw_text(buf, ctx->text_col, ctx->kb_font, ctx->kb_font_size, x-dims.x*0.5, y, text);
// Outline?
if (selected) {
pax_push_2d(buf);
pax_apply_2d(buf, matrix_2d_translate(-dx/2, 0));
pax_draw_line(buf, ctx->sel_col, x, y, x + dx - 1, y);
pax_draw_line(buf, ctx->sel_col, x, y+ctx->kb_font_size-1, x + dx - 1, y+ctx->kb_font_size-1);
pax_draw_line(buf, ctx->sel_col, x + dx - 1, y, x + dx - 1, y+ctx->kb_font_size-1);
pax_draw_line(buf, ctx->sel_col, x, y, x, y+ctx->kb_font_size-1);
pax_pop_2d(buf);
}
}
}
// Draw one full row of the keyboard.
static void pkb_row(pax_buf_t *buf, pkb_ctx_t *ctx, int rownum, int selected, float dx, float y) {
// Calculate some stuff.
char *row = boards[ctx->board_sel][rownum];
size_t len = strlen(row);
int x = ctx->x + (ctx->width - len * dx + dx) / 2;
char tmp[2] = {0,0};
// Show all of them.
for (int i = 0; i < len; i++) {
// Draw a KEY.
*tmp = row[i];
pkb_char(buf, ctx, x, y, dx, tmp, selected == i);
if (i == 0 && rownum == 2) {
// Draw shift key art.
pkb_art_shift(buf, ctx, x, y, dx, selected == i);
} else if (rownum == 2 && i == len - 1) {
// Draw the backspace key art.
pkb_art_bksp(buf, ctx, x, y, dx, selected == i);
}
x += dx;
}
}
// Draw a specific key in a row of the keyboard.
static void pkb_row_key(pax_buf_t *buf, pkb_ctx_t *ctx, int rownum, bool selected, float dx, float y, int keyno) {
// Calculate some stuff.
char *row = boards[ctx->board_sel][rownum];
size_t len = strlen(row);
int x = ctx->x + (ctx->width - len * dx + dx) / 2 + dx * keyno;
char tmp[2] = {0,0};
// Show one of them.
*tmp = row[keyno];
pax_draw_rect(buf, ctx->bg_col, x-dx/2, y, dx, ctx->kb_font_size);
pkb_char(buf, ctx, x, y, dx, tmp, selected);
if (rownum == 2 && keyno == 0) {
// Draw the shift key art.
pkb_art_shift(buf, ctx, x, y, dx, selected);
} else if (rownum == 2 && keyno == len - 1) {
// Draw the backspace key art.
pkb_art_bksp(buf, ctx, x, y, dx, selected);
}
}
// Draw just the board part.
static void pkb_render_keyb(pax_buf_t *buf, pkb_ctx_t *ctx, bool do_bg) {
// Draw background.
if (do_bg) {
pax_draw_rect(buf, ctx->bg_col, ctx->x, ctx->y + ctx->height - ctx->kb_font_size*4, ctx->width, ctx->kb_font_size*4);
}
// Select the board to display.
char **board = boards[ctx->board_sel];
float dx = ctx->width / 10;
float y = ctx->y + ctx->height - ctx->kb_font_size * 4;
// Draw the first three rows.
for (int i = 0; i < 3; i ++) {
int sel = -1;
if (i == ctx->key_y) {
sel = ctx->key_x;
}
pkb_row(buf, ctx, i, sel, dx, y);
y += ctx->kb_font_size;
}
// Spacebar row time.
bool space_sel = ctx->key_y == 3 && ctx->key_x > 1 && ctx->key_x < 7;
float x = ctx->x + (ctx->width - 8 * dx) / 2;
// The thingy selector.
pkb_char(buf, ctx, x, y, dx, " ", ctx->key_y == 3 && ctx->key_x == 0);
pkb_art_select(buf, ctx, x, y, dx, ctx->key_y == 3 && ctx->key_x == 0);
x += 1.0 * dx;
// Left char.
pkb_char(buf, ctx, x, y, dx, board[4], ctx->key_y == 3 && ctx->key_x == 1);
x += 1.0 * dx;
// SPACE.
if (space_sel && ctx->held == PKB_CHARSELECT) {
pax_draw_rect(buf, ctx->sel_col, x-dx/2, y, dx*5, ctx->kb_font_size);
pax_draw_rect(buf, ctx->sel_text_col, x, y + ctx->kb_font_size/3, dx*4, ctx->kb_font_size/3);
} else {
pax_draw_rect(buf, ctx->text_col, x, y + ctx->kb_font_size/3, dx*4, ctx->kb_font_size/3);
if (space_sel) {
// Outline rect?
pax_push_2d(buf);
pax_apply_2d(buf, matrix_2d_translate(-dx/2, 0));
pax_draw_line(buf, ctx->sel_col, x, y, x + dx*5 - 1, y);
pax_draw_line(buf, ctx->sel_col, x, y+ctx->kb_font_size-1, x + dx*5 - 1, y+ctx->kb_font_size-1);
pax_draw_line(buf, ctx->sel_col, x + dx*5 - 1, y, x + dx*5 - 1, y+ctx->kb_font_size-1);
pax_draw_line(buf, ctx->sel_col, x, y, x, y+ctx->kb_font_size-1);
pax_pop_2d(buf);
}
}
// Right char.
x += 5.0 * dx;
pkb_char(buf, ctx, x, y, dx, board[5], ctx->key_y == 3 && ctx->key_x == 7);
x += 1.0 * dx;
// The thingy acceptor.
pkb_char(buf, ctx, x, y, dx, " ", ctx->key_y == 3 && ctx->key_x == 8);
pkb_art_accept(buf, ctx, x, y, dx, ctx->key_y == 3 && ctx->key_x == 8);
}
// Draw just the text part.
static void pkb_render_text(pax_buf_t *buf, pkb_ctx_t *ctx, bool do_bg) {
// Draw background.
if (do_bg) {
pax_draw_rect(buf, ctx->bg_col, ctx->x, ctx->y, ctx->width, ctx->height - ctx->kb_font_size*4);
}
if (ctx->key_y == -1) {
// Outline us.
pax_outline_rect(buf, ctx->sel_col, ctx->x, ctx->y, ctx->width, ctx->height - ctx->kb_font_size*4);
}
// Some setup.
float x = ctx->x + 2;
float y = ctx->y + 2;
char tmp[2] = {0, 0};
// Draw everything.
for (int i = 0; i < strlen(ctx->content); i++) {
if (ctx->cursor == i) {
// The cursor in between the input.
pax_draw_line(buf, ctx->sel_col, x, y, x, y + ctx->text_font_size - 1);
}
// The character of the input.
tmp[0] = ctx->content[i];
pax_vec1_t dims = pax_text_size(ctx->text_font, ctx->text_font_size, tmp);
if (x + dims.x > ctx->width - 2) {
// Word wrap.
x = 2;
y += ctx->text_font_size;
}
pax_draw_text(buf, ctx->text_col, ctx->text_font, ctx->text_font_size, x, y, tmp);
x += dims.x;
}
if (ctx->cursor == strlen(ctx->content)) {
// The cursor after the input.
pax_draw_line(buf, ctx->sel_col, x, y, x, y + ctx->text_font_size - 1);
}
}
// Draw one specific key.
static void pkb_render_key(pax_buf_t *buf, pkb_ctx_t *ctx, int key_x, int key_y) {
if (key_y == -1) {
// If key_y is -1, the text box is selected to render.
pkb_render_text(buf, ctx, true);
return;
}
// Select the board to display.
char **board = boards[ctx->board_sel];
float dx = ctx->width / 10;
float y = ctx->y + ctx->height - ctx->kb_font_size * 4;
if (key_y < 3) {
// Draw one of the first three rows.
y += ctx->kb_font_size * key_y;
pkb_row_key(buf, ctx, key_y, key_y == ctx->key_y && key_x == ctx->key_x, dx, y, key_x);
y += ctx->kb_font_size;
} else {
// Spacebar row time.
y += ctx->kb_font_size * 3;
bool space_sel = ctx->key_y == 3 && ctx->key_x > 1 && ctx->key_x < 7;
float x = ctx->x + (ctx->width - 8 * dx) / 2;
// The thingy selector.
if (key_x == 0) {
pax_draw_rect(buf, ctx->bg_col, x-dx/2, y, dx, ctx->kb_font_size);
pkb_char(buf, ctx, x, y, dx, " ", ctx->key_y == 3 && ctx->key_x == 0);
pkb_art_select(buf, ctx, x, y, dx, ctx->key_y == 3 && ctx->key_x == 0);
}
x += 1.0 * dx;
if (key_x == 1) {
pax_draw_rect(buf, ctx->bg_col, x-dx/2, y, dx, ctx->kb_font_size);
pkb_char(buf, ctx, x, y, dx, board[4], ctx->key_y == 3 && ctx->key_x == 1);
}
x += 1.0 * dx;
// SPACE.
if (space_sel && ctx->held == PKB_CHARSELECT) {
pax_draw_rect(buf, ctx->sel_col, x-dx/2, y, dx*5, ctx->kb_font_size);
pax_draw_rect(buf, ctx->sel_text_col, x, y + ctx->kb_font_size/3, dx*4, ctx->kb_font_size/3);
} else {
pax_draw_rect(buf, ctx->bg_col, x-dx/2, y, dx*5, ctx->kb_font_size);
pax_draw_rect(buf, ctx->text_col, x, y + ctx->kb_font_size/3, dx*4, ctx->kb_font_size/3);
if (space_sel) {
// Outline rect?
pax_push_2d(buf);
pax_apply_2d(buf, matrix_2d_translate(-dx/2, 0));
pax_draw_line(buf, ctx->sel_col, x, y, x + dx*5 - 1, y);
pax_draw_line(buf, ctx->sel_col, x, y+ctx->kb_font_size-1, x + dx*5 - 1, y+ctx->kb_font_size-1);
pax_draw_line(buf, ctx->sel_col, x + dx*5 - 1, y, x + dx*5 - 1, y+ctx->kb_font_size-1);
pax_draw_line(buf, ctx->sel_col, x, y, x, y+ctx->kb_font_size-1);
pax_pop_2d(buf);
}
}
// Right char.
x += 5.0 * dx;
if (key_x == 7) {
pax_draw_rect(buf, ctx->bg_col, x-dx/2, y, dx, ctx->kb_font_size);
pkb_char(buf, ctx, x, y, dx, board[5], ctx->key_y == 3 && ctx->key_x == 7);
}
x += 1.0 * dx;
// The thingy acceptor.
if (key_x == 8) {
pax_draw_rect(buf, ctx->bg_col, x-dx/2, y, dx, ctx->kb_font_size);
pkb_char(buf, ctx, x, y, dx, " ", ctx->key_y == 3 && ctx->key_x == 8);
pkb_art_accept(buf, ctx, x, y, dx, ctx->key_y == 3 && ctx->key_x == 8);
}
}
}
// Redraw the complete on-screen keyboard.
void pkb_render(pax_buf_t *buf, pkb_ctx_t *ctx) {
if (matrix_2d_is_identity(buf->stack_2d.value)
&& ctx->x == 0 && ctx->y == 0
&& ctx->width == buf->width
&& ctx->height == buf->height) {
// We can just fill the entire screen.
pax_background(buf, ctx->bg_col);
} else {
// We'll need to fill a rectangle.
pax_draw_rect(
buf, ctx->bg_col,
ctx->x, ctx->y,
ctx->width, ctx->height
);
}
// Draw the board.
pkb_render_keyb(buf, ctx, false);
// Time to draw some text.
pkb_render_text(buf, ctx, false);
// Mark as not dirty.
ctx->dirty = false;
ctx->kb_dirty = false;
ctx->sel_dirty = false;
ctx->text_dirty = false;
ctx->last_key_x = ctx->key_x;
ctx->last_key_y = ctx->key_y;
}
// Redraw only the changed parts of the on-screen keyboard.
void pkb_redraw(pax_buf_t *buf, pkb_ctx_t *ctx) {
if (ctx->text_dirty) {
pkb_render_text(buf, ctx, true);
}
if (ctx->kb_dirty) {
pkb_render_keyb(buf, ctx, true);
} else if (ctx->sel_dirty) {
pkb_render_key(buf, ctx, ctx->last_key_x, ctx->last_key_y);
pkb_render_key(buf, ctx, ctx->key_x, ctx->key_y);
}
// Mark as not dirty.
ctx->dirty = false;
ctx->kb_dirty = false;
ctx->sel_dirty = false;
ctx->text_dirty = false;
ctx->last_key_x = ctx->key_x;
ctx->last_key_y = ctx->key_y;
}
/* ==== Text editing ==== */
// Handling of delete or backspace.
static void pkb_delete(pkb_ctx_t *ctx, bool is_backspace) {
size_t oldlen = strlen(ctx->content);
if (!is_backspace && ctx->cursor == oldlen) {
// No forward deleting at the end of the line.
return;
} else if (is_backspace && ctx->cursor == 0) {
// No backward deleting at the start of the line.
return;
} else if (!is_backspace) {
// Advanced backspace.
ctx->cursor ++;
}
// Copy back everything including null terminator.
ctx->cursor --;
for (int i = ctx->cursor; i < oldlen; i++) {
ctx->content[i] = ctx->content[i+1];
}
// DECREMENT length.
if (oldlen * 2 < ctx->content_cap) {
// Not decrementing here is important as oldlen excludes the null terminator.
ctx->content_cap = oldlen;
ctx->content = realloc(ctx->content, ctx->content_cap);
}
ctx->text_dirty = true;
}
// Handling of normal input.
static void pkb_append(pkb_ctx_t *ctx, char value) {
size_t oldlen = strlen(ctx->content);
if (oldlen + 2 >= ctx->content_cap) {
// Expand the buffer just a bit.
ctx->content_cap *= 2;
ctx->content = realloc(ctx->content, ctx->content_cap);
}
// Copy over the remainder of the buffer.
// If there's no text this still copies the null terminator.
for (int i = oldlen; i >= ctx->cursor; i --) {
ctx->content[i + 1] = ctx->content[i];
}
// And finally insert at the character.
ctx->content[ctx->cursor] = value;
ctx->cursor ++;
ctx->text_dirty = true;
}
/* ==== Input handling ==== */
// The loop that allows input repeating.
void pkb_loop(pkb_ctx_t *ctx) {
int64_t now = esp_timer_get_time();
if (!ctx->held) return;
bool is_dir = (ctx->held >= PKB_UP) && (ctx->held <= PKB_RIGHT);
if ((ctx->hold_start + 1000000 < now) || (is_dir && ctx->hold_start + 250000 < now)) {
// 8 repeats per second.
if (ctx->last_press + 125000 < now) {
pkb_press(ctx, ctx->held);
}
}
}
// A pressing of the input.
void pkb_press(pkb_ctx_t *ctx, pkb_input_t input) {
char **board = boards[ctx->board_sel];
ctx->last_press = esp_timer_get_time();
switch (input) {
// Cursor movements.
size_t rowlen;
case PKB_UP:
ctx->key_y --;
if (ctx->key_y < -1) ctx->key_y = 3;
else if (ctx->key_y != -1) {
rowlen = strlen(board[ctx->key_y]);
if (ctx->key_x >= rowlen) ctx->key_x = rowlen - 1;
}
ctx->sel_dirty = true;
break;
case PKB_DOWN:
ctx->key_y ++;
ctx->key_y %= 4;
rowlen = strlen(board[ctx->key_y]);
if (ctx->key_x >= rowlen) ctx->key_x = rowlen - 1;
ctx->sel_dirty = true;
break;
case PKB_LEFT:
if (ctx->key_y == -1) {
if (ctx->cursor > 0) ctx->cursor --;
} else if (ctx->key_y == 3 && ctx->key_x > 1 && ctx->key_x < 7) {
ctx->key_x = 1;
} else {
ctx->key_x --;
if (ctx->key_x < 0) ctx->key_x = strlen(board[ctx->key_y]) - 1;
}
ctx->sel_dirty = true;
break;
case PKB_RIGHT:
if (ctx->key_y == -1) {
if (ctx->cursor < strlen(ctx->content)) ctx->cursor ++;
} else if (ctx->key_y == 3 && ctx->key_x > 1 && ctx->key_x < 7) {
ctx->key_x = 7;
} else {
ctx->key_x ++;
ctx->key_x %= strlen(board[ctx->key_y]);
}
ctx->sel_dirty = true;
break;
// Enter a character.
case PKB_CHARSELECT:
ctx->sel_dirty |= ctx->held != PKB_CHARSELECT;
if (ctx->key_y == 3) {
switch (ctx->key_x) {
case 0:
// Board selector.
if (ctx->board_sel == PKB_NUMBERS) ctx->board_sel = PKB_SYMBOLS;
else if (ctx->board_sel == PKB_SYMBOLS) ctx->board_sel = PKB_LOWERCASE;
else ctx->board_sel = PKB_NUMBERS;
ctx->kb_dirty = true;
break;
case 1:
// Magic.
pkb_append(ctx, *board[4]);
break;
default:
// Spacebar.
pkb_append(ctx, ' ');
break;
case 7:
// mAGIC.
pkb_append(ctx, *board[5]);
break;
case 8:
// Enter idk.
ctx->input_accepted = true;
break;
}
} else if (ctx->key_y == 2) {
if (ctx->key_x == 0) {
// cAPS LOCK KEY.
if (ctx->held == PKB_CHARSELECT) ctx->held = PKB_NO_INPUT;
ctx->board_sel ^= 1;
ctx->kb_dirty = true;
} else if (ctx->key_x == strlen(board[2])-1) {
// Backspace.
pkb_delete(ctx, true);
} else {
// nORMAL CHAR.
pkb_append(ctx, board[2][ctx->key_x]);
}
} else {
// Normal char.
pkb_append(ctx, board[ctx->key_y][ctx->key_x]);
}
break;
// Shift key, the pressening.
case PKB_SHIFT:
ctx->board_sel |= 1;
ctx->kb_dirty = true;
break;
// Next keyboard.
case PKB_MODESELECT:
ctx->board_sel ++;
ctx->board_sel %= 4;
rowlen = strlen(board[ctx->key_y]);
if (ctx->key_x >= rowlen) ctx->key_x = rowlen - 1;
ctx->kb_dirty = true;
break;
// Backspace.
case PKB_DELETE_BEFORE:
pkb_delete(ctx, true);
break;
// Delete.
case PKB_DELETE_AFTER:
pkb_delete(ctx, false);
break;
default:
break;
}
if (input != PKB_SHIFT && input != ctx->held) {
ctx->held = input;
ctx->hold_start = esp_timer_get_time();
}
ctx->dirty = true;
}
// A relealing of the input.
void pkb_release(pkb_ctx_t *ctx, pkb_input_t input) {
switch (input) {
// Shift key, the releasening.
case PKB_SHIFT:
ctx->dirty = true;
ctx->board_sel &= ~1;
ctx->kb_dirty = true;
break;
// Unpress them char.
case PKB_CHARSELECT:
ctx->sel_dirty = true;
break;
default:
break;
}
if (ctx->held == input) {
ctx->held = PKB_NO_INPUT;
ctx->dirty = true;
}
}

17
main/system_wrapper.c Normal file
View file

@ -0,0 +1,17 @@
#include <stdio.h>
#include <string.h>
#include <sdkconfig.h>
#include <esp_system.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "system_wrapper.h"
void restart() {
for (int i = 3; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}