From f7589fc1ee8f980340ae3213b9bc089e2f87c81d Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 16:39:31 +0000 Subject: [PATCH 01/23] Migrate CPShell to Hollyhock-3 (v3) SDK Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .clangd | 16 ++++ .devcontainer/Dockerfile | 16 ++++ .devcontainer/devcontainer.json | 18 ++++ .github/dependabot.yml | 35 ++++++++ .github/workflows/c-cpp.yml | 24 ++++++ .github/workflows/codeql.yml | 98 +++++++++++++++++++++ .gitignore | 15 ++-- .vscode/settings.json | 6 ++ .vscode/tasks.json | 33 +++++++ Makefile | 142 ++++++++++++++----------------- calc.cpp | 11 +-- calc.hpp | 11 +-- div.s => div.S | 0 lib/core/touch_event_handler.hpp | 7 +- lib/draw_functions.hpp | 26 +++--- linker.bin.ld | 28 ------ linker.ld | 5 -- main.cpp | 2 +- src/commands/cat.cpp | 20 ++--- src/commands/cd.cpp | 14 +-- src/commands/history.cpp | 20 ++--- src/commands/ls.cpp | 15 ++-- src/cpshell.cpp | 4 +- src/loader.hpp | 22 ++--- src/utilities.hpp | 34 ++++---- start.s | 9 -- 26 files changed, 409 insertions(+), 222 deletions(-) create mode 100644 .clangd create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/c-cpp.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json rename div.s => div.S (100%) delete mode 100644 linker.bin.ld delete mode 100644 linker.ld delete mode 100644 start.s diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..984ee96 --- /dev/null +++ b/.clangd @@ -0,0 +1,16 @@ +CompileFlags: + Add: + - -Wall + - -Wextra + - -Werror + Remove: + - -m* + - -fstrict-volatile-bitfields + +Style: + FullyQualifiedNamespaces: + - "*" + +Diagnostics: + ClangTidy: + FastCheckFilter: Loose \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..bda9f24 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +FROM ghcr.io/classpaddev/hollyhock-3:v2.2.1 AS toolchain + +FROM mcr.microsoft.com/devcontainers/base:debian13 AS base + +# Install required packages KEEP IN SYNC (+clangd bear nano) +RUN apt-get update -y && apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + make libncurses6 zstd zlib1g \ + gawk wget bzip2 xz-utils unzip \ + patch libstdc++6 rsync git mold \ + nano clangd-19 bear +RUN apt-get install -y --reinstall ca-certificates +COPY --from=toolchain /toolchain /toolchain +COPY --from=toolchain /sdk /sdk +ENV PATH=$PATH:/toolchain/bin +ENV SDK_DIR=/sdk diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..81eb9a4 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,18 @@ +{ + "build": { + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.makefile-tools", + "ms-vscode.cpptools-themes", + "ms-vscode.cpptools", + "xaver.clang-format", + "llvm-vs-code-extensions.vscode-clangd", + "ZixuanWang.linkerscript", + "ms-azuretools.vscode-docker" + ] + } + } +} \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d73b1f5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,35 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "docker" + directory: "/.devcontainer" + schedule: + interval: "weekly" + + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: "weekly" + target-branch: "ugfx" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + target-branch: "ugfx" + - package-ecosystem: "docker" + directory: "/.devcontainer" + schedule: + interval: "weekly" + target-branch: "ugfx" diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 0000000..04a402b --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,24 @@ +name: C/C++ CI + +on: [push] + +permissions: + contents: read + +jobs: + build-artifact: + runs-on: ubuntu-latest + container: + image: ghcr.io/classpaddev/hollyhock-3:v2.2.1 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.github_token }} + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: make -j + - uses: actions/upload-artifact@v4 + with: + path: dist/** diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..9323ddc --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,98 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main", "ugfx" ] + pull_request: + branches: [ "main", "ugfx" ] + schedule: + - cron: '32 6 * * 2' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: c-cpp + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + submodules: true + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + #- name: Download docker image + # run: docker pull ghcr.io/classpaddev/hollyhock-3:v2.1.1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + #- if: matrix.build-mode == 'manual' + # name: Do manual build + # run: docker run --rm -t -v $PWD:/work ghcr.io/classpaddev/hollyhock-3:v2.1.1 make -j + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.gitignore b/.gitignore index 4539ce3..e461a48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,5 @@ -# Ignore make files -start.o -main.o -calc.o -div.o -_hhkengine.elf -# hhkengine.hhk - -# Ignore workplace files -settings.json \ No newline at end of file +dist/ +obj/ +.cache/ +.deps/ +compile_commands.json \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1686474 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "clangd.arguments": [ + "--query-driver=/toolchain/bin/sh4a_nofpueb-elf-*" + ], + "clangd.path": "clangd-19" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3f04180 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,33 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "make", + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "clean", + "type": "shell", + "command": "make clean", + "problemMatcher": [], + "group": "build" + }, + { + "label": "compile commands", + "type": "shell", + "command": "make compile_commands.json", + "problemMatcher": [], + "group": "build" + } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile index dc8d27c..d3df217 100644 --- a/Makefile +++ b/Makefile @@ -1,102 +1,86 @@ -APP_NAME:=CPShell +SOURCEDIR = src +BUILDDIR = obj +OUTDIR = dist +DEPDIR = .deps -#This Makefile compiles a program for the calculator running hollyhock-2 and for the pc. -#You need the hollyhock sdk for the calculator version and sdl2 for the pc version. -# github.com/SnailMath/hollyhock-2 -#export SDK_DIR:=/path/to/the/folder/hollyhock-2/sdk/ +AS:=sh4a_nofpueb-elf-gcc +AS_FLAGS:=-gdwarf-5 +SDK_DIR?=/sdk -#you can use 'make all' to compile all and 'make clean' to remove the output files. -#you can compile only the pc version with 'make pc' (you won't need hollyhock) -#or only the calculator version with 'make hhk' (you won't need sdl for this) +DEPFLAGS=-MT $@ -MMD -MP -MF $(DEPDIR)/$*.d +WARNINGS=-Wall -Wextra -pedantic -Werror -pedantic-errors +INCLUDES=-I$(SDK_DIR)/include -I. +DEFINES= +FUNCTION_FLAGS=-flto=auto -ffat-lto-objects -fno-builtin -ffunction-sections -fdata-sections -gdwarf-5 -O2 +COMMON_FLAGS=$(FUNCTION_FLAGS) $(INCLUDES) $(WARNINGS) $(DEFINES) +CC:=sh4a_nofpueb-elf-gcc +CC_FLAGS=-std=c23 $(COMMON_FLAGS) -#this makefile is based on the makefile in app_tamplate from the original hollyhock project by The6P4C +CXX:=sh4a_nofpueb-elf-g++ +CXX_FLAGS=-std=c++20 $(COMMON_FLAGS) +LD:=sh4a_nofpueb-elf-g++ +LD_FLAGS:=$(FUNCTION_FLAGS) -Wl,--gc-sections +LIBS:=-L$(SDK_DIR) -lsdk -#ifndef SDK_DIR -#$(error You need to define the SDK_DIR environment variable, and point it to the sdk/ folder) -#endif +READELF:=sh4a_nofpueb-elf-readelf +OBJCOPY:=sh4a_nofpueb-elf-objcopy +STRIP:=sh4a_nofpueb-elf-strip -#If you use AS sources make sure to write the same functions in C++ because sh4 as does not run on the pc +APP_ELF := $(OUTDIR)/CPShell.elf +APP_HH3 := $(APP_ELF:.elf=.hh3) -#The pc compiler -C_PC:=gcc -C_PC_FLAGS:=-W -Wall -DPC -lSDL2 +AS_SOURCES:=$(wildcard *.S) +CC_SOURCES:=$(wildcard *.c) +CXX_SOURCES:=$(wildcard *.cpp) +OBJECTS := $(addprefix $(BUILDDIR)/,$(AS_SOURCES:.S=.o)) \ + $(addprefix $(BUILDDIR)/,$(CC_SOURCES:.c=.o)) \ + $(addprefix $(BUILDDIR)/,$(CXX_SOURCES:.cpp=.o)) -#The sh4 assembler, compiler and linker: -AS:=sh4-elf-as -AS_FLAGS:= +NOLTOOBJS := $(foreach obj, $(OBJECTS), $(if $(findstring /nolto/, $(obj)), $(obj))) -CC:=sh4-elf-gcc -CC_FLAGS:=-ffreestanding -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ -I $(SDK_DIR)/newlib/sh-elf/include -j8 +DEPFILES := $(OBJECTS:$(BUILDDIR)/%.o=$(DEPDIR)/%.d) -CXX:=sh4-elf-g++ -CXX_FLAGS:=-ffreestanding -fno-exceptions -fno-rtti -fshort-wchar -Wall -Wextra -O2 -I $(SDK_DIR)/include/ -I $(SDK_DIR)/newlib/sh-elf/include -m4a-nofpu +hh3: $(APP_HH3) Makefile +elf: $(APP_ELF) Makefile -LD:=sh4-elf-gcc -LD_FLAGS:=-nostartfiles -m4-nofpu -Wno-undef -L$(SDK_DIR)/newlib/sh-elf/lib +all: elf hh3 +.DEFAULT_GOAL := all +.SECONDARY: # Prevents intermediate files from being deleted -READELF:=sh4-elf-readelf -OBJCOPY:=sh4-elf-objcopy +.NOTPARALLEL: clean +clean: + rm -rf $(BUILDDIR) $(OUTDIR) $(DEPDIR) -AS_SOURCES:=$(wildcard *.s) -CC_SOURCES:=$(wildcard *.c) -CXX_SOURCES:=$(wildcard *.cpp) -H_INC:=$(wildcard *.h) -HPP_INC:=$(wildcard *.hpp) -OBJECTS:=$(AS_SOURCES:.s=.o) $(CC_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) +%.hh3: %.elf + $(STRIP) -o $@ $^ -APP_PC:=_$(APP_NAME).elf +$(APP_ELF): $(OBJECTS) + @mkdir -p $(dir $@) + $(LD) -Wl,-Map $@.map -o $@ $(LD_FLAGS) $^ $(LIBS) -APP_ELF:=$(APP_NAME).hhk +$(NOLTOOBJS): FUNCTION_FLAGS+=-fno-lto -APP_BIN:=$(APP_NAME).bin +$(BUILDDIR)/%.o: %.S + @mkdir -p $(dir $@) + $(AS) -c $< -o $@ $(AS_FLAGS) -all: pc hhk +$(BUILDDIR)/%.o: %.c + @mkdir -p $(dir $@) + @mkdir -p $(dir $(DEPDIR)/$<) + +$(CC) -c $< -o $@ $(CC_FLAGS) $(DEPFLAGS) -bin: $(APP_BIN) Makefile +$(BUILDDIR)/%.o: %.cpp + @mkdir -p $(dir $@) + @mkdir -p $(dir $(DEPDIR)/$<) + +$(CXX) -c $< -o $@ $(CXX_FLAGS) $(DEPFLAGS) -pc: $(APP_PC) Makefile +compile_commands.json: + $(MAKE) $(MAKEFLAGS) clean + bear -- sh -c "$(MAKE) $(MAKEFLAGS) --keep-going all || exit 0" -hhk: $(APP_ELF) Makefile +.PHONY: elf hh3 all clean compile_commands.json -clean: - rm -f $(OBJECTS) $(APP_ELF) $(APP_PC) $(APP_BIN) - -$(APP_PC): $(CC_SOURCES) $(CXX_SOURCES) $(H_INC) $(HPP_INC) - $(C_PC) $(CC_SOURCES) $(CXX_SOURCES) -o $(APP_PC) $(C_PC_FLAGS) - -$(APP_ELF): $(OBJECTS) $(SDK_DIR)/sdk.o linker.ld - $(LD) -T linker.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o - $(OBJCOPY) --set-section-flags .hollyhock_name=contents,strings,readonly $(APP_ELF) $(APP_ELF) - $(OBJCOPY) --set-section-flags .hollyhock_description=contents,strings,readonly $(APP_ELF) $(APP_ELF) - $(OBJCOPY) --set-section-flags .hollyhock_author=contents,strings,readonly $(APP_ELF) $(APP_ELF) - $(OBJCOPY) --set-section-flags .hollyhock_version=contents,strings,readonly $(APP_ELF) $(APP_ELF) - -$(APP_BIN): $(OBJECTS) $(SDK_DIR)/sdk.o linker.bin.ld - $(LD) -Wl, --oformat binary -T linker.bin.ld -o $@ $(LD_FLAGS) $(OBJECTS) $(SDK_DIR)/sdk.o - -# We're not actually building sdk.o, just telling the user they need to do it -# themselves. Just using the target to trigger an error when the file is -# required but does not exist. -$(SDK_DIR)/sdk.o: - $(error You need to build the SDK before using it. Run make in the SDK directory, and check the README.md in the SDK directory for more information) - -%.o: %.s - $(AS) $< -o $@ $(AS_FLAGS) - -%.o: %.c $(H_INC) - $(CC) -c $< -o $@ $(CC_FLAGS) - -# Break the build if global constructors are present: -# Read the sections from the object file (with readelf -S) and look for any -# called .ctors - if they exist, give the user an error message, delete the -# object file (so that on subsequent runs of make the build will still fail) -# and exit with an error code to halt the build. -%.o: %.cpp $(HPP_INC) - $(CXX) -c $< -o $@ $(CXX_FLAGS) - @$(READELF) $@ -S | grep ".ctors" > /dev/null && echo "ERROR: Global constructors aren't supported." && rm $@ && exit 1 || exit 0 - -#tell make that 'all' 'clean' 'hhk' and 'pc' are not actually files. -.PHONY: all clean hhk pc +-include $(DEPFILES) \ No newline at end of file diff --git a/calc.cpp b/calc.cpp index 37edb5a..58a7e2c 100644 --- a/calc.cpp +++ b/calc.cpp @@ -15,11 +15,11 @@ extern void main2(); //in file main.cpp #endif -extern "C" #ifdef PC +extern "C" int main(){ #else -void main(){ +int main(){ #endif //Initialisation #ifdef PC @@ -32,9 +32,7 @@ void main(){ SDL_RenderClear(renderer); texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,320,528); #else - vram = LCD_GetVRAMAddress(); LCD_GetSize(&width, &height); - LCD_VRAMBackup(); //Stores the VRAM content #endif //The actual program @@ -45,9 +43,12 @@ void main(){ SDL_DestroyWindow(win); SDL_Quit(); #else - LCD_VRAMRestore(); //Restores the VRAM content LCD_Refresh(); #endif + + #ifndef PC + return 0; + #endif } //println is printf for up to 4 arguments diff --git a/calc.hpp b/calc.hpp index 449b763..881ca8e 100644 --- a/calc.hpp +++ b/calc.hpp @@ -11,10 +11,10 @@ extern int width; extern int height; #else - #include //This contains stdint (uint32_t and so on) - #include - #include //a few functions from CPappTemplate got moved here... - #include + #include //This contains stdint (uint32_t and so on) + #include + #include //a few functions from CPappTemplate got moved here... + #include //extern "C" void getKey(uint32_t *key1, uint32_t *key2); //extern uint16_t *vram; extern uint8_t debugprintline; @@ -61,7 +61,8 @@ inline void setPixel(int x,int y, uint32_t color){ rect.x = x; rect.y = y; rect.w =1; rect.h = 1; SDL_UpdateTexture(texture, &rect , (void*)pixels, 4); //The last number defines the number of bytes per row. ( width * bytePerPixel ) #else - *((uint16_t*)( (uint32_t)vram + ((width*y + x)*2) )) = color; + uint16_t *vram = LCD_GetVRAMAddress(); + vram[width*y + x] = color; #endif } } diff --git a/div.s b/div.S similarity index 100% rename from div.s rename to div.S diff --git a/lib/core/touch_event_handler.hpp b/lib/core/touch_event_handler.hpp index c99ab61..4e1bdd8 100644 --- a/lib/core/touch_event_handler.hpp +++ b/lib/core/touch_event_handler.hpp @@ -8,7 +8,8 @@ #pragma once #include "../../calc.hpp" -#include +#include +#include struct TouchHandler { uint32_t minX; @@ -32,7 +33,7 @@ uint8_t touchHandlersLength = 0; struct ActBarHandler actBarHandlers[6]; uint8_t actBarHandlersLength = 0; -struct InputEvent event; +struct Input_Event event; void checkTouchEvents() { @@ -48,7 +49,7 @@ void checkTouchEvents() { // if at 00 00 04 B0 then just got input (10 mins internally) if (powerOffTime == 0x004B0000) return; - memset(&event, 0, sizeof(event)); + Mem_Memset(&event, 0, sizeof(event)); GetInput(&event, 0xFFFFFFFF, 0x10); // polls switch (event.type) { diff --git a/lib/draw_functions.hpp b/lib/draw_functions.hpp index 9cbd173..11ef5fb 100644 --- a/lib/draw_functions.hpp +++ b/lib/draw_functions.hpp @@ -25,9 +25,9 @@ free(menuFont); #define PATH_PREFIX "\\fls0\\usr\\textures\\CPShell\\" // #define FONT_PREFIX "\\fls0\\usr\\fonts\\CPShell" // I don't use this, but you can if you want to -#include -#include -#include +#include +#include +#include #include "shaders.hpp" #include "../calc.hpp" @@ -54,18 +54,18 @@ uint16_t *load_texture(const char *texturepath) { strcpy(concatpath, "\\fls0\\usr\\textures\\"); // try not to do this, it's a bad idea #endif strcat(concatpath, texturepath); - int fd = open(concatpath, OPEN_READ); + int fd = File_Open(concatpath, FILE_OPEN_READ); if (fd > -1) { uint16_t info[2]; - read(fd, info, 4); + File_Read(fd, info, 4); uint16_t w = info[0]; uint16_t h = info[1]; uint16_t *result = (uint16_t*)malloc(w*h*2+4); memUsed += (w*h*2)+4; txLoaded += 1; - lseek(fd, 0, SEEK_SET); - read(fd, result, w*h*2+4); - close(fd); + File_Lseek(fd, 0, FILE_SEEK_SET); + File_Read(fd, result, w*h*2+4); + File_Close(fd); return result; } return 0; @@ -91,18 +91,18 @@ uint8_t *load_font(const char *fontpath) { strcpy(concatpath, "\\fls0\\usr\\fonts\\"); #endif strcat(concatpath, fontpath); - int fd = open(concatpath, OPEN_READ); + int fd = File_Open(concatpath, FILE_OPEN_READ); if (fd > -1) { uint16_t info[4]; - read(fd, info, 4); + File_Read(fd, info, 4); uint16_t w = info[0]; uint16_t h = info[1]; uint8_t *result = (uint8_t*)malloc(95*w*h/8+5); memUsed += (95*w*h/8)+5; fLoaded += 1; - lseek(fd, 0, SEEK_SET); - read(fd, result, (95*w*h/8)+5); - close(fd); + File_Lseek(fd, 0, FILE_SEEK_SET); + File_Read(fd, result, (95*w*h/8)+5); + File_Close(fd); return result; } return 0; diff --git a/linker.bin.ld b/linker.bin.ld deleted file mode 100644 index ed8efa6..0000000 --- a/linker.bin.ld +++ /dev/null @@ -1,28 +0,0 @@ -ENTRY(_main); - -SECTIONS { - start_address = 0x8CFF0000; - .init start_address : AT(start_address) { - *(.init) - } - info_address = 0x8CFF0010; - . = info_address; - .hollyhock_name : { - *(.hollyhock_name) - } - .hollyhock_description : { - *(.hollyhock_description) - } - .hollyhock_author : { - *(.hollyhock_author) - } - .hollyhock_version : { - *(.hollyhock_version) - } - .text : { - *(.text) - } -} - - - diff --git a/linker.ld b/linker.ld deleted file mode 100644 index 7217949..0000000 --- a/linker.ld +++ /dev/null @@ -1,5 +0,0 @@ -ENTRY(_main); - -SECTIONS { - . = 0x8CFF0000; -} diff --git a/main.cpp b/main.cpp index e6a52bb..bb5af32 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,4 @@ -#include +#include // main #include "calc.hpp" diff --git a/src/commands/cat.cpp b/src/commands/cat.cpp index c07a3cc..26b98a8 100644 --- a/src/commands/cat.cpp +++ b/src/commands/cat.cpp @@ -7,7 +7,7 @@ */ #include "../internal.hpp" -#include +#include extern int cat_main(int argc, char **argv) { @@ -36,34 +36,34 @@ extern int cat_main(int argc, char **argv) // check if the path exists int findHandle; wchar_t fileName[100]; - struct findInfo findInfoBuf; - int ret = findFirst(wpath, &findHandle, fileName, &findInfoBuf); + struct File_FindInfo findInfoBuf; + int ret = File_FindFirst(wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); terminal->WriteChars(outBuf); // close the file - findClose(findHandle); + File_FindClose(findHandle); return 0; } // path exists, check if it is a directory - if (findInfoBuf.type == findInfoBuf.EntryTypeDirectory) { + if (findInfoBuf.type == File_EntryTypeDirectory) { // path is a directory strcpy(outBuf, "Path is a directory.\n"); terminal->WriteChars(outBuf); // close the file - findClose(findHandle); + File_FindClose(findHandle); return 0; } // close the file - findClose(findHandle); + File_FindClose(findHandle); // now that we know the path is a file, open it - int fd = open(path, OPEN_READ); + int fd = File_Open(path, FILE_OPEN_READ); if (fd < 0) { // An error occurred calling open strcpy(outBuf, "An error occurred calling open.\n"); @@ -73,10 +73,10 @@ extern int cat_main(int argc, char **argv) // copy memory address uint8_t* addr; - getAddr(fd,0,(const void**)&addr); + File_GetAddr(fd,0,(const void**)&addr); // close the file - ret = close(fd); + ret = File_Close(fd); if (ret < 0) { // An error occurred calling close strcpy(outBuf, "An error occurred calling close.\n"); diff --git a/src/commands/cd.cpp b/src/commands/cd.cpp index 6d7ddc7..a796fdb 100644 --- a/src/commands/cd.cpp +++ b/src/commands/cd.cpp @@ -7,7 +7,7 @@ */ #include "../internal.hpp" -#include +#include extern int cd_main(int argc, char **argv) { @@ -26,31 +26,31 @@ extern int cd_main(int argc, char **argv) // check if the path exists int findHandle; wchar_t fileName[100]; - struct findInfo findInfoBuf; - int ret = findFirst(g_wpath, &findHandle, fileName, &findInfoBuf); + struct File_FindInfo findInfoBuf; + int ret = File_FindFirst(g_wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); terminal->WriteChars(outBuf); // close the file - findClose(findHandle); + File_FindClose(findHandle); return 0; } // path exists, check if it is a directory - if (findInfoBuf.type != findInfoBuf.EntryTypeDirectory) { + if (findInfoBuf.type != File_EntryTypeDirectory) { // path is not a directory strcpy(outBuf, "Path is not a directory.\n"); terminal->WriteChars(outBuf); // close the file - findClose(findHandle); + File_FindClose(findHandle); return 0; } // close the file - findClose(findHandle); + File_FindClose(findHandle); // check for .. - will need to change this in the future to support ../../.. etc if (strcmp(argv[1], "..") == 0) { diff --git a/src/commands/history.cpp b/src/commands/history.cpp index deeb380..a804d34 100644 --- a/src/commands/history.cpp +++ b/src/commands/history.cpp @@ -7,7 +7,7 @@ */ #include "../internal.hpp" -#include +#include // write to history file with int argc, char **argv extern int history_main(int argc, char **argv) { @@ -17,36 +17,36 @@ extern int history_main(int argc, char **argv) { // check if history file exists int findHandle; wchar_t fileName[100]; - struct findInfo findInfoBuf; - int ret = findFirst(g_whistory, &findHandle, fileName, &findInfoBuf); + struct File_FindInfo findInfoBuf; + int ret = File_FindFirst(g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // history file does not exist // create the file - int fd = open(g_history, OPEN_WRITE | OPEN_CREATE); + int fd = File_Open(g_history, FILE_OPEN_WRITE | FILE_OPEN_CREATE); if (fd < 0) { // failed to create file strcpy(outBuf, "Failed to create history file.\n"); terminal->WriteChars(outBuf); - findClose(findHandle); - close(fd); + File_FindClose(findHandle); + File_Close(fd); return -1; } // close the file - close(fd); + File_Close(fd); // as history file will be empty, return return 0; // history file exists, check if it is a directory - } else if (findInfoBuf.type == findInfoBuf.EntryTypeDirectory) { + } else if (findInfoBuf.type == File_EntryTypeDirectory) { // history file is a directory strcpy(outBuf, "History file is a directory.\n"); terminal->WriteChars(outBuf); - findClose(findHandle); + File_FindClose(findHandle); return -1; } - findClose(findHandle); + File_FindClose(findHandle); // just cat the file for now char *argv2[3]; diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index 89509e0..a97ee52 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -7,7 +7,8 @@ */ #include "../internal.hpp" -#include +#include +#include extern int ls_main(int argc, char **argv) { @@ -20,19 +21,19 @@ extern int ls_main(int argc, char **argv) int findHandle; wchar_t fileName[100]; char outBuf[110]; - struct findInfo findInfoBuf; - int ret = findFirst(g_wpath, &findHandle, fileName, &findInfoBuf); + struct File_FindInfo findInfoBuf; + int ret = File_FindFirst(g_wpath, &findHandle, fileName, &findInfoBuf); while (ret>=0){ //create dirEntry structure struct dirEntry thisfile; - memset(&thisfile, 0, sizeof(thisfile)); + Mem_Memset(&thisfile, 0, sizeof(thisfile)); //copy file name for (int i=0; fileName[i]!=0; i++){ wchar_t ch = fileName[i]; thisfile.fileName[i] = ch; } //copy file type - thisfile.type=findInfoBuf.type==findInfoBuf.EntryTypeDirectory?'D':'F'; + thisfile.type=findInfoBuf.type==File_EntryTypeDirectory?'D':'F'; //display this strcpy(outBuf, thisfile.fileName); // check if it will fit on the screen or we are in second half @@ -51,9 +52,9 @@ extern int ls_main(int argc, char **argv) directory[dirFiles++] = thisfile; //serch the next - ret = findNext(findHandle, fileName, &findInfoBuf); + ret = File_FindNext(findHandle, fileName, &findInfoBuf); } - findClose(findHandle); + File_FindClose(findHandle); terminal->WriteBuffer('\n', false); return 0; // return 0 on success diff --git a/src/cpshell.cpp b/src/cpshell.cpp index 87010ab..ead6d85 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -149,13 +149,13 @@ void cpshell_init() { strcpy(applets[12].name, "username"); applets[12].main = username_main; - memset(&applets[13], 0, sizeof(Applet)); + Mem_Memset(&applets[13], 0, sizeof(Applet)); // init file system // Reference: SnailMath/filemgr //initialize g_path to home ("\\fls0\\") - memset(g_path,0,sizeof(g_path)); + Mem_Memset(g_path,0,sizeof(g_path)); strcpy (g_path, g_home); //convert from char to wchar diff --git a/src/loader.hpp b/src/loader.hpp index 55c392f..7d7dece 100644 --- a/src/loader.hpp +++ b/src/loader.hpp @@ -11,7 +11,7 @@ #include "internal.hpp" #include "utilities.hpp" #include "../lib/functions/convert.hpp" -#include +#include // Terminal - Defaults here uint32_t TERM_COLOR = 0xFFFF; @@ -29,36 +29,36 @@ int load_userprofile() { int findHandle; wchar_t fileName[100]; - struct findInfo findInfoBuf; - int ret = findFirst(g_wuserprofile, &findHandle, fileName, &findInfoBuf); + struct File_FindInfo findInfoBuf; + int ret = File_FindFirst(g_wuserprofile, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // Does not exist so skip strcpy(outBuf, "LOAD: Skipping user profile load.\n"); terminal->WriteChars(outBuf); - findClose(findHandle); + File_FindClose(findHandle); load_settings(); return 0; - } else if (findInfoBuf.type == findInfoBuf.EntryTypeDirectory) { + } else if (findInfoBuf.type == File_EntryTypeDirectory) { strcpy(outBuf, "LOAD: User profile is a directory.\n"); terminal->WriteChars(outBuf); - findClose(findHandle); + File_FindClose(findHandle); return -2; } - findClose(findHandle); + File_FindClose(findHandle); - int fd = open(g_userprofile, OPEN_READ); + int fd = File_Open(g_userprofile, FILE_OPEN_READ); if (fd < 0) { - close(fd); + File_Close(fd); strcpy(outBuf, "LOAD: File error.\n"); terminal->WriteChars(outBuf); return -1; } uint8_t* addr; - getAddr(fd,0,(const void**)&addr); + File_GetAddr(fd,0,(const void**)&addr); - ret = close(fd); + ret = File_Close(fd); if (ret < 0) { strcpy(outBuf, "LOAD: File error.\n"); terminal->WriteChars(outBuf); diff --git a/src/utilities.hpp b/src/utilities.hpp index 3916400..9d3e915 100644 --- a/src/utilities.hpp +++ b/src/utilities.hpp @@ -9,7 +9,7 @@ #pragma once #include "internal.hpp" -#include +#include // Get the username from memory address 0x8C1BE984 char *getUsername() @@ -45,34 +45,34 @@ int comparePartial(const char *str1, const char *str2, int start) { int safe_internal(int ret, char *msg) { if (ret < 0) { terminal->WriteChars(msg); - close(ret); + File_Close(ret); return -1; } return ret; } int safe_read(int fd, char *buf, int len) { - int ret = read(fd, buf, len); + int ret = File_Read(fd, buf, len); return safe_internal(ret, "An error occurred calling read.\n"); } int safe_write(int fd, char *buf, int len) { - int ret = write(fd, buf, len); + int ret = File_Write(fd, buf, len); return safe_internal(ret, "An error occurred calling write.\n"); } int safe_open(char *path, int flags) { - int ret = open(path, flags); + int ret = File_Open(path, flags); return safe_internal(ret, "An error occurred calling open.\n"); } int safe_close(int fd) { - int ret = close(fd); + int ret = File_Close(fd); return safe_internal(ret, "An error occurred calling close.\n"); } int safe_lseek(int fd, int offset, int whence) { - int ret = lseek(fd, offset, whence); + int ret = File_Lseek(fd, offset, whence); return safe_internal(ret, "An error occurred calling lseek.\n"); } @@ -84,39 +84,39 @@ int add_history(int argc, char **argv) { // check if history file exists int findHandle; wchar_t fileName[100]; - struct findInfo findInfoBuf; - int ret = findFirst(g_whistory, &findHandle, fileName, &findInfoBuf); + struct File_FindInfo findInfoBuf; + int ret = File_FindFirst(g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // history file does not exist // create the file - int fd = open(g_history, OPEN_WRITE | OPEN_CREATE); + int fd = File_Open(g_history, FILE_OPEN_WRITE | FILE_OPEN_CREATE); if (fd < 0) { // failed to create file strcpy(outBuf, "Failed to create history file.\n"); terminal->WriteChars(outBuf); - close(fd); - findClose(findHandle); + File_Close(fd); + File_FindClose(findHandle); return -1; } // close the file safe_close(fd); // history file exists, check if it is a directory - } else if (findInfoBuf.type == findInfoBuf.EntryTypeDirectory) { + } else if (findInfoBuf.type == File_EntryTypeDirectory) { // history file is a directory strcpy(outBuf, "History file is a directory.\n"); terminal->WriteChars(outBuf); - findClose(findHandle); + File_FindClose(findHandle); return -1; } - findClose(findHandle); + File_FindClose(findHandle); // history file exists, add to it - int fd = safe_open(g_history, OPEN_WRITE); + int fd = safe_open(g_history, FILE_OPEN_WRITE); // write to the end of the file - safe_lseek(fd, 0, SEEK_END); + safe_lseek(fd, 0, FILE_SEEK_END); // write to file for (int i = 0; i < argc; i++) { diff --git a/start.s b/start.s deleted file mode 100644 index 8ad8dbe..0000000 --- a/start.s +++ /dev/null @@ -1,9 +0,0 @@ -.section .init -mov.l main_addr, r0 -jmp @r0 -nop -.align 2 -main_addr: -.long _main -load_addr: -.long 0x8cff0000 From 83984c121acee52e815e29b9ddc36f7cf730fc2f Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 16:49:47 +0000 Subject: [PATCH 02/23] Fix CI build errors: width/height conflict and missing header Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- calc.cpp | 177 +++++++++++++++++++++++++++++++++++++++++++++--- calc.hpp | 10 +-- lib/shaders.hpp | 2 +- 3 files changed, 173 insertions(+), 16 deletions(-) diff --git a/calc.cpp b/calc.cpp index 58a7e2c..77db0a7 100644 --- a/calc.cpp +++ b/calc.cpp @@ -7,8 +7,8 @@ extern void main2(); //in file main.cpp SDL_Window *win; SDL_Renderer *renderer; SDL_Texture *texture; - int width; - int height; + int app_width; + int app_height; #else //uint16_t *vram; //this got moved to sdk/calc/calc.hpp uint8_t debugprintline = 0; @@ -23,16 +23,19 @@ int main(){ #endif //Initialisation #ifdef PC - width = 320; - height = 528; + app_width = 320; + app_height = 528; SDL_Init(SDL_INIT_EVERYTHING); - win = SDL_CreateWindow("CP3D", 100,100,width,height,SDL_WINDOW_SHOWN); + win = SDL_CreateWindow("CP3D", 100,100,app_width,app_height,SDL_WINDOW_SHOWN); renderer = SDL_CreateRenderer(win, -1, 0); SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); SDL_RenderClear(renderer); texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,320,528); #else - LCD_GetSize(&width, &height); + unsigned int w, h; + LCD_GetSize(&w, &h); + app_width = (int)w; + app_height = (int)h; #endif //The actual program @@ -202,6 +205,158 @@ The triangle has three points P0, P1 and P2 and three lines a, b and c. We go fr line(x2,y2,x0,y0,colorLine); } +void vline(int x, int y1, int y2, uint16_t color){ + if (y1>y2) { int z=y2; y2=y1; y1=z;} + for (int y=y1; y<=y2; y++) + setPixel(x,y,color); +} +#endif + +void fillScreen(uint16_t color){ + //#ifdef PC + unsigned char pixels[4]; // { A, B, G, R } + //Convert 565 colors to RGBA + /*R*/ pixels[3] = (color >> 8) & 0b11111000; + /*G*/ pixels[2] = (color >> 3) & 0b11111100; + /*B*/ pixels[1] = (color << 3) & 0b11111000; + /*A*/ pixels[0] = 0; + + //create an array with the whole screen filled with this color + unsigned char screen[4*app_width*app_height] ; + for(long i=0;i<(app_width*app_height);i++){ + screen[4*i + 0] = pixels[0]; + screen[4*i + 1] = pixels[1]; + screen[4*i + 2] = pixels[2]; + screen[4*i + 3] = pixels[3]; + } + SDL_Rect rect; + rect.x = 0; rect.y = 0; rect.w =app_width; rect.h = app_height; + SDL_UpdateTexture(texture, &rect , (void*)screen, 4*app_width); //The last number defines the number of bytes per row. ( width * bytePerPixel ) + //#else + // const uint32_t size = app_width * app_height; + // for(uint32_t i = 0; ix1 ? (ix=1, x2-x1) : (ix=-1, x1-x2) ); + int dy = (y2>y1 ? (iy=1, y2-y1) : (iy=-1, y1-y2) ); + + setPixel(x1,y1,color); + if(dx>=dy){ //the derivative is less than 1 (not so steep) + //y1 is the whole number of the y value + //error is the fractional part (times dx to make it a whole number) + // y = y1 + (error/dx) + //if error/dx is greater than 0.5 (error is greater than dx/2) we add 1 to y1 and subtract dx from error (so error/dx is now around -0.5) + int error = 0; + while (x1!=x2) { + x1 += ix; //go one step in x direction + error += dy;//add dy/dx to the y value. + if (error>=(dx>>1)){ //If error is greater than dx/2 (error/dx is >=0.5) + y1+=iy; + error-=dx; + } + setPixel(x1,y1,color); + } + }else{ //the derivative is greater than 1 (very steep) + int error = 0; + while (y1!=y2) { //The same thing, just go up y and look at x + y1 += iy; //go one step in y direction + error += dx;//add dx/dy to the x value. + if (error>=(dy>>1)){ //If error is greater than dx/2 (error/dx is >=0.5) + x1+=ix; + error-=dy; + } + setPixel(x1,y1,color); + } + } +} + +void vline(int x, int y1, int y2, uint16_t color); //Defined further down. + +//Draw a filled triangle. +void triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint16_t colorFill, uint16_t colorLine){ +//Filled triangles are a lot of vertical lines. +/* - + a ___________----------P3 - + P0 _________--------- ____--- - + ---____ _____----- - + b ----___ _----- c - + P2 - +The triangle has three points P0, P1 and P2 and three lines a, b and c. We go from left to right, calculating the point on a and the point on b or c and then we draw a vertical line connecting these two. +*/ +//Sort the points by x coordinate + {int z; + if(x0>x2){ z=x2; x2=x0; x0=z; z=y2; y2=y0; y0=z; } + if(x1>x2){ z=x2; x2=x1; x1=z; z=y2; y2=y1; y1=z; } + if(x0>x1){ z=x1; x1=x0; x0=z; z=y1; y1=y0; y0=z; }} + + int x = x0; //x is the variable that counts from left to right + + //Values for line a + int ay = y0; //The point y for the current x on the line a + int aiy; //The direction of line a + int adx = (x2>x0 ? ( x2-x0) : ( x0-x2) ); + int ady = (y2>y0 ? (aiy=1, y2-y0) : (aiy=-1, y0-y2) ); + int aerr = 0; //The y value of a (fractional part). y is actually ay+(aerr/adx) + + //Values for line b + int by = y0; //The point y for the current x on the line b + int biy; //The direction of line b + int bdx = (x1>x0 ? ( x1-x0) : ( x0-x1) ); + int bdy = (y1>y0 ? (biy=1, y1-y0) : (biy=-1, y0-y1) ); + int berr = 0; + + //Values for line c + int cy = y1; //The point y for the current x on the line y (starting at P1) + int ciy; //The direction of line c + int cdx = (x2>x1 ? ( x2-x1) : ( x1-x2) ); + int cdy = (y2>y1 ? (ciy=1, y2-y1) : (ciy=-1, y1-y2) ); + int cerr = 0; + + //First draw area between a and b + while (x=adx >> 2){ //if aerr/adx >= 0.5 + aerr-=adx; + ay+=aiy; + } + berr+=bdy; + while(berr>=bdx >> 2){ //if berr/bdx >= 0.5 + berr-=bdx; + by+=biy; + } + vline(x,ay,by,colorFill); + } + + //Then draw area between a and c + while (x=adx >> 2){ //if aerr/adx >= 0.5 + aerr-=adx; + ay+=aiy; + } + cerr+=cdy; + while(cerr>=cdx >> 2){ //if berr/bdx >= 0.5 + cerr-=cdx; + cy+=ciy; + } + vline(x,ay,cy,colorFill); + } + + line(x0,y0,x1,y1,colorLine); + line(x1,y1,x2,y2,colorLine); + line(x2,y2,x0,y0,colorLine); +} + void vline(int x, int y1, int y2, uint16_t color){ if (y1>y2) { int z=y2; y2=y1; y1=z;} for (int y=y1; y<=y2; y++) @@ -218,18 +373,18 @@ void fillScreen(uint16_t color){ /*A*/ pixels[0] = 0; //create an array with the whole screen filled with this color - unsigned char screen[4*width*height] ; - for(long i=0;i<(width*height);i++){ + unsigned char screen[4*app_width*app_height] ; + for(long i=0;i<(app_width*app_height);i++){ screen[4*i + 0] = pixels[0]; screen[4*i + 1] = pixels[1]; screen[4*i + 2] = pixels[2]; screen[4*i + 3] = pixels[3]; } SDL_Rect rect; - rect.x = 0; rect.y = 0; rect.w =width; rect.h = height; - SDL_UpdateTexture(texture, &rect , (void*)screen, 4*width); //The last number defines the number of bytes per row. ( width * bytePerPixel ) + rect.x = 0; rect.y = 0; rect.w =app_width; rect.h = app_height; + SDL_UpdateTexture(texture, &rect , (void*)screen, 4*app_width); //The last number defines the number of bytes per row. ( width * bytePerPixel ) //#else - // const uint32_t size = width * height; + // const uint32_t size = app_width * app_height; // for(uint32_t i = 0; i>3) & 0b0000000000011111)); } inline void setPixel(int x,int y, uint32_t color){ - if(x>=0 && x < width && y>=0 && y < height){ + if(x>=0 && x < app_width && y>=0 && y < app_height){ #ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA @@ -62,7 +64,7 @@ inline void setPixel(int x,int y, uint32_t color){ SDL_UpdateTexture(texture, &rect , (void*)pixels, 4); //The last number defines the number of bytes per row. ( width * bytePerPixel ) #else uint16_t *vram = LCD_GetVRAMAddress(); - vram[width*y + x] = color; + vram[app_width*y + x] = color; #endif } } diff --git a/lib/shaders.hpp b/lib/shaders.hpp index 8353dcd..51b9bfe 100644 --- a/lib/shaders.hpp +++ b/lib/shaders.hpp @@ -9,7 +9,7 @@ #pragma once -#include +#include #include "trig_functions.hpp" #define TRANSPARENCY_COLOR 0xF81F // (255, 0, 255) or #FF00FF From 9fbdb0d2b6be645a4e7595d81612544838f15ed7 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 16:57:47 +0000 Subject: [PATCH 03/23] Fix CI build by using SDK constants for width, height, and vram Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- calc.cpp | 35 +++++++++++++++-------------------- calc.hpp | 13 ++++++------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/calc.cpp b/calc.cpp index 77db0a7..204edcb 100644 --- a/calc.cpp +++ b/calc.cpp @@ -7,8 +7,8 @@ extern void main2(); //in file main.cpp SDL_Window *win; SDL_Renderer *renderer; SDL_Texture *texture; - int app_width; - int app_height; + int width; + int height; #else //uint16_t *vram; //this got moved to sdk/calc/calc.hpp uint8_t debugprintline = 0; @@ -23,19 +23,14 @@ int main(){ #endif //Initialisation #ifdef PC - app_width = 320; - app_height = 528; + width = 320; + height = 528; SDL_Init(SDL_INIT_EVERYTHING); - win = SDL_CreateWindow("CP3D", 100,100,app_width,app_height,SDL_WINDOW_SHOWN); + win = SDL_CreateWindow("CP3D", 100,100,width,height,SDL_WINDOW_SHOWN); renderer = SDL_CreateRenderer(win, -1, 0); SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); SDL_RenderClear(renderer); texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,320,528); - #else - unsigned int w, h; - LCD_GetSize(&w, &h); - app_width = (int)w; - app_height = (int)h; #endif //The actual program @@ -213,7 +208,7 @@ void vline(int x, int y1, int y2, uint16_t color){ #endif void fillScreen(uint16_t color){ - //#ifdef PC + #ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA /*R*/ pixels[3] = (color >> 8) & 0b11111000; @@ -222,21 +217,21 @@ void fillScreen(uint16_t color){ /*A*/ pixels[0] = 0; //create an array with the whole screen filled with this color - unsigned char screen[4*app_width*app_height] ; - for(long i=0;i<(app_width*app_height);i++){ + unsigned char screen[4*width*height] ; + for(long i=0;i<(width*height);i++){ screen[4*i + 0] = pixels[0]; screen[4*i + 1] = pixels[1]; screen[4*i + 2] = pixels[2]; screen[4*i + 3] = pixels[3]; } SDL_Rect rect; - rect.x = 0; rect.y = 0; rect.w =app_width; rect.h = app_height; - SDL_UpdateTexture(texture, &rect , (void*)screen, 4*app_width); //The last number defines the number of bytes per row. ( width * bytePerPixel ) - //#else - // const uint32_t size = app_width * app_height; - // for(uint32_t i = 0; i>3) & 0b0000000000011111)); } inline void setPixel(int x,int y, uint32_t color){ - if(x>=0 && x < app_width && y>=0 && y < app_height){ + if(x>=0 && x < (int)width && y>=0 && y < (int)height){ #ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA @@ -63,8 +63,7 @@ inline void setPixel(int x,int y, uint32_t color){ rect.x = x; rect.y = y; rect.w =1; rect.h = 1; SDL_UpdateTexture(texture, &rect , (void*)pixels, 4); //The last number defines the number of bytes per row. ( width * bytePerPixel ) #else - uint16_t *vram = LCD_GetVRAMAddress(); - vram[app_width*y + x] = color; + vram[width*y + x] = color; #endif } } From 5db1e5496fc60fd4632dc6fe937cfc4af8582886 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:06:23 +0000 Subject: [PATCH 04/23] Fix CI build errors: missing headers, type mismatches, unused vars Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/draw_functions.hpp | 12 ++++++------ main.cpp | 1 + src/commands/cat.cpp | 10 ++++++---- src/commands/cd.cpp | 8 +++++--- src/commands/clear.cpp | 4 +++- src/commands/credits.cpp | 4 +++- src/commands/date.cpp | 4 +++- src/commands/echo.cpp | 4 +++- src/commands/exit.cpp | 4 +++- src/commands/help.cpp | 4 +++- src/commands/history.cpp | 10 ++++++---- src/commands/ls.cpp | 10 ++++++---- src/commands/osname.cpp | 4 +++- src/commands/rand.cpp | 4 +++- src/commands/test.cpp | 4 +++- src/commands/username.cpp | 4 +++- src/cpshell.cpp | 3 +++ src/loader.hpp | 6 +++--- src/utilities.hpp | 4 ++-- 19 files changed, 68 insertions(+), 36 deletions(-) diff --git a/lib/draw_functions.hpp b/lib/draw_functions.hpp index 11ef5fb..5531fd8 100644 --- a/lib/draw_functions.hpp +++ b/lib/draw_functions.hpp @@ -57,14 +57,14 @@ uint16_t *load_texture(const char *texturepath) { int fd = File_Open(concatpath, FILE_OPEN_READ); if (fd > -1) { uint16_t info[2]; - File_Read(fd, info, 4); + (void)File_Read(fd, info, 4); uint16_t w = info[0]; uint16_t h = info[1]; uint16_t *result = (uint16_t*)malloc(w*h*2+4); memUsed += (w*h*2)+4; txLoaded += 1; - File_Lseek(fd, 0, FILE_SEEK_SET); - File_Read(fd, result, w*h*2+4); + (void)File_Lseek(fd, 0, FILE_SEEK_SET); + (void)File_Read(fd, result, w*h*2+4); File_Close(fd); return result; } @@ -94,14 +94,14 @@ uint8_t *load_font(const char *fontpath) { int fd = File_Open(concatpath, FILE_OPEN_READ); if (fd > -1) { uint16_t info[4]; - File_Read(fd, info, 4); + (void)File_Read(fd, info, 4); uint16_t w = info[0]; uint16_t h = info[1]; uint8_t *result = (uint8_t*)malloc(95*w*h/8+5); memUsed += (95*w*h/8)+5; fLoaded += 1; - File_Lseek(fd, 0, FILE_SEEK_SET); - File_Read(fd, result, (95*w*h/8)+5); + (void)File_Lseek(fd, 0, FILE_SEEK_SET); + (void)File_Read(fd, result, (95*w*h/8)+5); File_Close(fd); return result; } diff --git a/main.cpp b/main.cpp index bb5af32..a92c89c 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,4 @@ +#include #include // main diff --git a/src/commands/cat.cpp b/src/commands/cat.cpp index 26b98a8..6b68c1d 100644 --- a/src/commands/cat.cpp +++ b/src/commands/cat.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file cat.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -9,7 +11,7 @@ #include "../internal.hpp" #include -extern int cat_main(int argc, char **argv) +extern int cat_main(int argc, char **argv) { (void)argc; (void)argv; { terminal->ClearBuffer(); @@ -37,7 +39,7 @@ extern int cat_main(int argc, char **argv) int findHandle; wchar_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); @@ -49,7 +51,7 @@ extern int cat_main(int argc, char **argv) } // path exists, check if it is a directory - if (findInfoBuf.type == File_EntryTypeDirectory) { + if (findInfoBuf.type == EntryTypeDirectory) { // path is a directory strcpy(outBuf, "Path is a directory.\n"); terminal->WriteChars(outBuf); @@ -73,7 +75,7 @@ extern int cat_main(int argc, char **argv) // copy memory address uint8_t* addr; - File_GetAddr(fd,0,(const void**)&addr); + (void)File_GetAddr(fd,0,(const void**)&addr); // close the file ret = File_Close(fd); diff --git a/src/commands/cd.cpp b/src/commands/cd.cpp index a796fdb..5ce08e6 100644 --- a/src/commands/cd.cpp +++ b/src/commands/cd.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file cd.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -9,7 +11,7 @@ #include "../internal.hpp" #include -extern int cd_main(int argc, char **argv) +extern int cd_main(int argc, char **argv) { (void)argc; (void)argv; { terminal->ClearBuffer(); @@ -27,7 +29,7 @@ extern int cd_main(int argc, char **argv) int findHandle; wchar_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); @@ -39,7 +41,7 @@ extern int cd_main(int argc, char **argv) } // path exists, check if it is a directory - if (findInfoBuf.type != File_EntryTypeDirectory) { + if (findInfoBuf.type != EntryTypeDirectory) { // path is not a directory strcpy(outBuf, "Path is not a directory.\n"); terminal->WriteChars(outBuf); diff --git a/src/commands/clear.cpp b/src/commands/clear.cpp index 666704b..2417b32 100644 --- a/src/commands/clear.cpp +++ b/src/commands/clear.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file clear.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-06-06 */ -extern int clear_main(int argc, char **argv) +extern int clear_main(int argc, char **argv) { (void)argc; (void)argv; { // clear the screen to black fillScreen(0); diff --git a/src/commands/credits.cpp b/src/commands/credits.cpp index 6ff884c..7350f43 100644 --- a/src/commands/credits.cpp +++ b/src/commands/credits.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file credits.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-06-10 */ -extern int credits_main(int argc, char **argv) +extern int credits_main(int argc, char **argv) { (void)argc; (void)argv; { terminal->ClearBuffer(); char msg[] = "Written by Sean McGinty (s3ansh33p)\nCredits to:\n- diddyholz for command initialization\n- Interchan for drawing functions\n- SnailMath for file handling\n- Pho3 for general assistance\n"; diff --git a/src/commands/date.cpp b/src/commands/date.cpp index 6294ef6..0581a18 100644 --- a/src/commands/date.cpp +++ b/src/commands/date.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file date.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +10,7 @@ #include "../../lib/core/rtc.hpp" -extern int date_main(int argc, char **argv) +extern int date_main(int argc, char **argv) { (void)argc; (void)argv; { // check if second argument starts with '-' char outBuf[BUF_SIZE]; diff --git a/src/commands/echo.cpp b/src/commands/echo.cpp index cd8fec6..e66e8cb 100644 --- a/src/commands/echo.cpp +++ b/src/commands/echo.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file echo.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-06-13 */ -extern int echo_main(int argc, char **argv) +extern int echo_main(int argc, char **argv) { (void)argc; (void)argv; { terminal->ClearBuffer(); // check if there is an argument diff --git a/src/commands/exit.cpp b/src/commands/exit.cpp index cac3e84..4c0eb04 100644 --- a/src/commands/exit.cpp +++ b/src/commands/exit.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file exit.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-06-06 */ -extern int exit_main(int argc, char **argv) +extern int exit_main(int argc, char **argv) { (void)argc; (void)argv; { shell_running = false; return 0; diff --git a/src/commands/help.cpp b/src/commands/help.cpp index 6c230a0..01264cc 100644 --- a/src/commands/help.cpp +++ b/src/commands/help.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file help.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +10,7 @@ #include "../internal.hpp" -extern int help_main(int argc, char **argv) +extern int help_main(int argc, char **argv) { (void)argc; (void)argv; { struct Applet *a = applets; terminal->ClearBuffer(); diff --git a/src/commands/history.cpp b/src/commands/history.cpp index a804d34..6ec37ae 100644 --- a/src/commands/history.cpp +++ b/src/commands/history.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file history.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -10,7 +12,7 @@ #include // write to history file with int argc, char **argv -extern int history_main(int argc, char **argv) { +extern int history_main(int argc, char **argv) { { (void)argc; (void)argv; terminal->ClearBuffer(); char outBuf[BUF_SIZE]; @@ -18,7 +20,7 @@ extern int history_main(int argc, char **argv) { int findHandle; wchar_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_whistory, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // history file does not exist // create the file @@ -38,7 +40,7 @@ extern int history_main(int argc, char **argv) { return 0; // history file exists, check if it is a directory - } else if (findInfoBuf.type == File_EntryTypeDirectory) { + } else if (findInfoBuf.type == EntryTypeDirectory) { // history file is a directory strcpy(outBuf, "History file is a directory.\n"); terminal->WriteChars(outBuf); @@ -50,7 +52,7 @@ extern int history_main(int argc, char **argv) { // just cat the file for now char *argv2[3]; - argv2[0] = "cat"; + argv2[0] = (char*)"cat"; argv2[1] = g_history; argv2[2] = 0; cat_main(2, argv2); diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index a97ee52..1c9a581 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file ls.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -10,7 +12,7 @@ #include #include -extern int ls_main(int argc, char **argv) +extern int ls_main(int argc, char **argv) { (void)argc; (void)argv; { // clear buffer terminal->ClearBuffer(); @@ -22,7 +24,7 @@ extern int ls_main(int argc, char **argv) wchar_t fileName[100]; char outBuf[110]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); while (ret>=0){ //create dirEntry structure struct dirEntry thisfile; @@ -33,7 +35,7 @@ extern int ls_main(int argc, char **argv) thisfile.fileName[i] = ch; } //copy file type - thisfile.type=findInfoBuf.type==File_EntryTypeDirectory?'D':'F'; + thisfile.type=findInfoBuf.type==EntryTypeDirectory?'D':'F'; //display this strcpy(outBuf, thisfile.fileName); // check if it will fit on the screen or we are in second half @@ -52,7 +54,7 @@ extern int ls_main(int argc, char **argv) directory[dirFiles++] = thisfile; //serch the next - ret = File_FindNext(findHandle, fileName, &findInfoBuf); + ret = File_FindNext(findHandle, (char_const16_t*)fileName, &findInfoBuf); } File_FindClose(findHandle); diff --git a/src/commands/osname.cpp b/src/commands/osname.cpp index 80e6922..adfa1af 100644 --- a/src/commands/osname.cpp +++ b/src/commands/osname.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file osname.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-07-05 */ -extern int osname_main(int argc, char **argv) +extern int osname_main(int argc, char **argv) { (void)argc; (void)argv; { char outBuf[BUF_SIZE]; diff --git a/src/commands/rand.cpp b/src/commands/rand.cpp index d81e662..fe67707 100644 --- a/src/commands/rand.cpp +++ b/src/commands/rand.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file rand.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-07-02 */ -extern int rand_main(int argc, char **argv) +extern int rand_main(int argc, char **argv) { (void)argc; (void)argv; { // check if second argument starts with '-' char outBuf[BUF_SIZE]; diff --git a/src/commands/test.cpp b/src/commands/test.cpp index c1c2104..da8b0de 100644 --- a/src/commands/test.cpp +++ b/src/commands/test.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file test.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-06-06 */ -extern int test_main(int argc, char **argv) +extern int test_main(int argc, char **argv) { (void)argc; (void)argv; { // print all arguments for (int i = 0; i < argc; i++) { diff --git a/src/commands/username.cpp b/src/commands/username.cpp index 34e0110..65b2252 100644 --- a/src/commands/username.cpp +++ b/src/commands/username.cpp @@ -1,3 +1,5 @@ +#include +#include /** * @file username.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -6,7 +8,7 @@ * @date 2022-07-05 */ -extern int username_main(int argc, char **argv) +extern int username_main(int argc, char **argv) { (void)argc; (void)argv; { // check if second argument starts with '-' char outBuf[BUF_SIZE]; diff --git a/src/cpshell.cpp b/src/cpshell.cpp index ead6d85..b8454d8 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -1,3 +1,6 @@ +#include +#include +#include #pragma once #include "../lib/functions/convert.hpp" diff --git a/src/loader.hpp b/src/loader.hpp index 7d7dece..10a6b50 100644 --- a/src/loader.hpp +++ b/src/loader.hpp @@ -30,7 +30,7 @@ int load_userprofile() { int findHandle; wchar_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_wuserprofile, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_wuserprofile, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // Does not exist so skip strcpy(outBuf, "LOAD: Skipping user profile load.\n"); @@ -38,7 +38,7 @@ int load_userprofile() { File_FindClose(findHandle); load_settings(); return 0; - } else if (findInfoBuf.type == File_EntryTypeDirectory) { + } else if (findInfoBuf.type == EntryTypeDirectory) { strcpy(outBuf, "LOAD: User profile is a directory.\n"); terminal->WriteChars(outBuf); File_FindClose(findHandle); @@ -56,7 +56,7 @@ int load_userprofile() { } uint8_t* addr; - File_GetAddr(fd,0,(const void**)&addr); + (void)File_GetAddr(fd,0,(const void**)&addr); ret = File_Close(fd); if (ret < 0) { diff --git a/src/utilities.hpp b/src/utilities.hpp index 9d3e915..417f6e6 100644 --- a/src/utilities.hpp +++ b/src/utilities.hpp @@ -85,7 +85,7 @@ int add_history(int argc, char **argv) { int findHandle; wchar_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_whistory, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // history file does not exist // create the file @@ -102,7 +102,7 @@ int add_history(int argc, char **argv) { safe_close(fd); // history file exists, check if it is a directory - } else if (findInfoBuf.type == File_EntryTypeDirectory) { + } else if (findInfoBuf.type == EntryTypeDirectory) { // history file is a directory strcpy(outBuf, "History file is a directory.\n"); terminal->WriteChars(outBuf); From dc7c8d910d5b9c342d27c111538a409a2f489e6d Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:08:36 +0000 Subject: [PATCH 05/23] Fix CI build errors: fix syntax, unused vars, and unused results Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/draw_functions.hpp | 13 ++++++------- main.cpp | 2 +- src/commands/cat.cpp | 8 ++++---- src/commands/cd.cpp | 6 +++--- src/commands/clear.cpp | 6 +++--- src/commands/credits.cpp | 6 +++--- src/commands/date.cpp | 6 +++--- src/commands/echo.cpp | 6 +++--- src/commands/exit.cpp | 6 +++--- src/commands/help.cpp | 6 +++--- src/commands/history.cpp | 8 ++++---- src/commands/ls.cpp | 6 +++--- src/commands/osname.cpp | 6 +++--- src/commands/rand.cpp | 6 +++--- src/commands/test.cpp | 6 +++--- src/commands/username.cpp | 6 +++--- src/cpshell.cpp | 6 +++--- src/loader.hpp | 2 +- 18 files changed, 55 insertions(+), 56 deletions(-) diff --git a/lib/draw_functions.hpp b/lib/draw_functions.hpp index 5531fd8..553ba1b 100644 --- a/lib/draw_functions.hpp +++ b/lib/draw_functions.hpp @@ -57,14 +57,14 @@ uint16_t *load_texture(const char *texturepath) { int fd = File_Open(concatpath, FILE_OPEN_READ); if (fd > -1) { uint16_t info[2]; - (void)File_Read(fd, info, 4); + if (File_Read(fd, info, 4) < 0) {} uint16_t w = info[0]; uint16_t h = info[1]; uint16_t *result = (uint16_t*)malloc(w*h*2+4); memUsed += (w*h*2)+4; txLoaded += 1; - (void)File_Lseek(fd, 0, FILE_SEEK_SET); - (void)File_Read(fd, result, w*h*2+4); + if (File_Lseek(fd, 0, FILE_SEEK_SET) < 0) {} + if (File_Read(fd, result, w*h*2+4) < 0) {} File_Close(fd); return result; } @@ -94,14 +94,14 @@ uint8_t *load_font(const char *fontpath) { int fd = File_Open(concatpath, FILE_OPEN_READ); if (fd > -1) { uint16_t info[4]; - (void)File_Read(fd, info, 4); + if (File_Read(fd, info, 4) < 0) {} uint16_t w = info[0]; uint16_t h = info[1]; uint8_t *result = (uint8_t*)malloc(95*w*h/8+5); memUsed += (95*w*h/8)+5; fLoaded += 1; - (void)File_Lseek(fd, 0, FILE_SEEK_SET); - (void)File_Read(fd, result, (95*w*h/8)+5); + if (File_Lseek(fd, 0, FILE_SEEK_SET) < 0) {} + if (File_Read(fd, result, (95*w*h/8)+5) < 0) {} File_Close(fd); return result; } @@ -140,4 +140,3 @@ void draw_font_shader(uint8_t *fontpointer, const char *text, int16_t x, int16_t } } - diff --git a/main.cpp b/main.cpp index a92c89c..dcdd08e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,4 @@ -#include +#include #include // main diff --git a/src/commands/cat.cpp b/src/commands/cat.cpp index 6b68c1d..2f1653a 100644 --- a/src/commands/cat.cpp +++ b/src/commands/cat.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file cat.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -11,7 +11,7 @@ #include "../internal.hpp" #include -extern int cat_main(int argc, char **argv) { (void)argc; (void)argv; +extern int cat_main(int argc, char **argv) { terminal->ClearBuffer(); @@ -75,7 +75,7 @@ extern int cat_main(int argc, char **argv) { (void)argc; (void)argv; // copy memory address uint8_t* addr; - (void)File_GetAddr(fd,0,(const void**)&addr); + if (File_GetAddr(fd,0,(const void**)&addr) < 0) {} // close the file ret = File_Close(fd); diff --git a/src/commands/cd.cpp b/src/commands/cd.cpp index 5ce08e6..e615722 100644 --- a/src/commands/cd.cpp +++ b/src/commands/cd.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file cd.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -11,7 +11,7 @@ #include "../internal.hpp" #include -extern int cd_main(int argc, char **argv) { (void)argc; (void)argv; +extern int cd_main(int argc, char **argv) { terminal->ClearBuffer(); diff --git a/src/commands/clear.cpp b/src/commands/clear.cpp index 2417b32..cf0d268 100644 --- a/src/commands/clear.cpp +++ b/src/commands/clear.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file clear.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-06-06 */ -extern int clear_main(int argc, char **argv) { (void)argc; (void)argv; +extern int clear_main(int, char **) { // clear the screen to black fillScreen(0); diff --git a/src/commands/credits.cpp b/src/commands/credits.cpp index 7350f43..7a1c5a8 100644 --- a/src/commands/credits.cpp +++ b/src/commands/credits.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file credits.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-06-10 */ -extern int credits_main(int argc, char **argv) { (void)argc; (void)argv; +extern int credits_main(int, char **) { terminal->ClearBuffer(); char msg[] = "Written by Sean McGinty (s3ansh33p)\nCredits to:\n- diddyholz for command initialization\n- Interchan for drawing functions\n- SnailMath for file handling\n- Pho3 for general assistance\n"; diff --git a/src/commands/date.cpp b/src/commands/date.cpp index 0581a18..792b65f 100644 --- a/src/commands/date.cpp +++ b/src/commands/date.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file date.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -10,7 +10,7 @@ #include "../../lib/core/rtc.hpp" -extern int date_main(int argc, char **argv) { (void)argc; (void)argv; +extern int date_main(int argc, char **argv) { // check if second argument starts with '-' char outBuf[BUF_SIZE]; diff --git a/src/commands/echo.cpp b/src/commands/echo.cpp index e66e8cb..c700cc0 100644 --- a/src/commands/echo.cpp +++ b/src/commands/echo.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file echo.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-06-13 */ -extern int echo_main(int argc, char **argv) { (void)argc; (void)argv; +extern int echo_main(int argc, char **argv) { terminal->ClearBuffer(); // check if there is an argument diff --git a/src/commands/exit.cpp b/src/commands/exit.cpp index 4c0eb04..51b3964 100644 --- a/src/commands/exit.cpp +++ b/src/commands/exit.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file exit.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-06-06 */ -extern int exit_main(int argc, char **argv) { (void)argc; (void)argv; +extern int exit_main(int, char **) { shell_running = false; return 0; diff --git a/src/commands/help.cpp b/src/commands/help.cpp index 01264cc..b9e1633 100644 --- a/src/commands/help.cpp +++ b/src/commands/help.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file help.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -10,7 +10,7 @@ #include "../internal.hpp" -extern int help_main(int argc, char **argv) { (void)argc; (void)argv; +extern int help_main(int, char **) { struct Applet *a = applets; terminal->ClearBuffer(); diff --git a/src/commands/history.cpp b/src/commands/history.cpp index 6ec37ae..7d3337d 100644 --- a/src/commands/history.cpp +++ b/src/commands/history.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file history.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -11,8 +11,8 @@ #include "../internal.hpp" #include -// write to history file with int argc, char **argv -extern int history_main(int argc, char **argv) { { (void)argc; (void)argv; +// write to history file with int, char ** +extern int history_main(int, char **) { terminal->ClearBuffer(); char outBuf[BUF_SIZE]; diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index 1c9a581..3efc4cb 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file ls.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -12,7 +12,7 @@ #include #include -extern int ls_main(int argc, char **argv) { (void)argc; (void)argv; +extern int ls_main(int, char **) { // clear buffer terminal->ClearBuffer(); diff --git a/src/commands/osname.cpp b/src/commands/osname.cpp index adfa1af..d9f8cd7 100644 --- a/src/commands/osname.cpp +++ b/src/commands/osname.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file osname.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-07-05 */ -extern int osname_main(int argc, char **argv) { (void)argc; (void)argv; +extern int osname_main(int, char **) { char outBuf[BUF_SIZE]; diff --git a/src/commands/rand.cpp b/src/commands/rand.cpp index fe67707..b5603cd 100644 --- a/src/commands/rand.cpp +++ b/src/commands/rand.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file rand.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-07-02 */ -extern int rand_main(int argc, char **argv) { (void)argc; (void)argv; +extern int rand_main(int argc, char **argv) { // check if second argument starts with '-' char outBuf[BUF_SIZE]; diff --git a/src/commands/test.cpp b/src/commands/test.cpp index da8b0de..924d429 100644 --- a/src/commands/test.cpp +++ b/src/commands/test.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file test.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-06-06 */ -extern int test_main(int argc, char **argv) { (void)argc; (void)argv; +extern int test_main(int argc, char **argv) { // print all arguments for (int i = 0; i < argc; i++) { diff --git a/src/commands/username.cpp b/src/commands/username.cpp index 65b2252..b9e5333 100644 --- a/src/commands/username.cpp +++ b/src/commands/username.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include /** * @file username.cpp * @author Sean McGinty (newfolderlocation@gmail.com) @@ -8,7 +8,7 @@ * @date 2022-07-05 */ -extern int username_main(int argc, char **argv) { (void)argc; (void)argv; +extern int username_main(int argc, char **argv) { // check if second argument starts with '-' char outBuf[BUF_SIZE]; diff --git a/src/cpshell.cpp b/src/cpshell.cpp index b8454d8..984885b 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -1,6 +1,6 @@ -#include -#include -#include +#include +#include +#include #pragma once #include "../lib/functions/convert.hpp" diff --git a/src/loader.hpp b/src/loader.hpp index 10a6b50..71cac49 100644 --- a/src/loader.hpp +++ b/src/loader.hpp @@ -56,7 +56,7 @@ int load_userprofile() { } uint8_t* addr; - (void)File_GetAddr(fd,0,(const void**)&addr); + if (File_GetAddr(fd,0,(const void**)&addr) < 0) {} ret = File_Close(fd); if (ret < 0) { From e2c27706a3ed3aff8c454137d9ad93463ef69b15 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:30:58 +0000 Subject: [PATCH 06/23] Fix CI build errors: fix sign comparisons and const correctness Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/core/touch_event_handler.hpp | 4 ++-- lib/draw_functions.hpp | 1 + lib/functions/convert.hpp | 2 +- src/utilities.hpp | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/core/touch_event_handler.hpp b/lib/core/touch_event_handler.hpp index 4e1bdd8..a754bb9 100644 --- a/lib/core/touch_event_handler.hpp +++ b/lib/core/touch_event_handler.hpp @@ -61,8 +61,8 @@ void checkTouchEvents() { // check if there are any touch handlers if (touchHandlersLength > 0) { for (uint8_t i = 0; i < touchHandlersLength; i++) { - if (event.data.touch_single.p1_x >= touchHandlers[i].minX && event.data.touch_single.p1_x <= touchHandlers[i].maxX && - event.data.touch_single.p1_y >= touchHandlers[i].minY && event.data.touch_single.p1_y <= touchHandlers[i].maxY) { + if ((uint32_t)event.data.touch_single.p1_x >= touchHandlers[i].minX && (uint32_t)event.data.touch_single.p1_x <= touchHandlers[i].maxX && + (uint32_t)event.data.touch_single.p1_y >= touchHandlers[i].minY && (uint32_t)event.data.touch_single.p1_y <= touchHandlers[i].maxY) { // check direction if (event.data.touch_single.direction == touchHandlers[i].direction) { (*touchHandlers[i].callback)(); diff --git a/lib/draw_functions.hpp b/lib/draw_functions.hpp index 553ba1b..c8a6a88 100644 --- a/lib/draw_functions.hpp +++ b/lib/draw_functions.hpp @@ -1,3 +1,4 @@ +#include // draw_functions.hpp by InterChan // minor changes by s3ansh33p for new file structure diff --git a/lib/functions/convert.hpp b/lib/functions/convert.hpp index 3053319..b4afd6d 100644 --- a/lib/functions/convert.hpp +++ b/lib/functions/convert.hpp @@ -56,7 +56,7 @@ uint32_t hexStringToInt(char *str) { uint32_t num = 0; int i = 0; if(str[0] == '0' && str[1] == 'x') i = 2; - while (str[i] && (str[i] >= '0' && str[i] <= '9' || str[i] >= 'a' && str[i] <= 'f' || str[i] >= 'A' && str[i] <= 'F')){ + while (str[i] && ((str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'f') || (str[i] >= 'A' && str[i] <= 'F'))){ if(str[i] >= '0' && str[i] <= '9'){ num = num * 16 + (str[i] - '0'); } else if(str[i] >= 'a' && str[i] <= 'f'){ diff --git a/src/utilities.hpp b/src/utilities.hpp index 417f6e6..d4d73a8 100644 --- a/src/utilities.hpp +++ b/src/utilities.hpp @@ -33,7 +33,7 @@ char *getUsername() // String comparison int comparePartial(const char *str1, const char *str2, int start) { - for (int i = start; i < start + strlen(str2); i++) { + for (int i = start; i < start + (int)strlen(str2); i++) { if (str1[i] != str2[i - start]) { return 0; } @@ -42,7 +42,7 @@ int comparePartial(const char *str1, const char *str2, int start) { } // "Safe" file operations -int safe_internal(int ret, char *msg) { +int safe_internal(int ret, const char *msg) { if (ret < 0) { terminal->WriteChars(msg); File_Close(ret); From 4fd4579d7f864908dfee93a91260a6698f2da0d2 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:47:21 +0000 Subject: [PATCH 07/23] Fix CI build errors: fix type mismatches, scopes, and const correctness Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/commands/cat.cpp | 8 ++++---- src/commands/cd.cpp | 8 ++++---- src/commands/history.cpp | 6 +++--- src/commands/ls.cpp | 6 +++--- src/cpshell.cpp | 2 +- src/internal.hpp | 8 +++++--- src/loader.hpp | 6 +++--- src/terminal.hpp | 4 ++-- src/utilities.hpp | 10 +++++----- temp_hhk3_sdk | 1 + 10 files changed, 31 insertions(+), 28 deletions(-) create mode 160000 temp_hhk3_sdk diff --git a/src/commands/cat.cpp b/src/commands/cat.cpp index 2f1653a..1098369 100644 --- a/src/commands/cat.cpp +++ b/src/commands/cat.cpp @@ -30,16 +30,16 @@ extern int cat_main(int argc, char **argv) if (argv[1][0] != '\\') strcpy(path, g_path); strcat(path, argv[1]); // convert to wchar_t - wchar_t wpath[PATH_LEN]; + char_const16_t wpath[PATH_LEN]; for (int i = 0; i < PATH_LEN; i++) { wpath[i] = path[i]; } // check if the path exists int findHandle; - wchar_t fileName[100]; + char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst((const char_const16_t*)wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst(wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); @@ -51,7 +51,7 @@ extern int cat_main(int argc, char **argv) } // path exists, check if it is a directory - if (findInfoBuf.type == EntryTypeDirectory) { + if (findInfoBuf.type == File_FindInfo::EntryTypeDirectory) { // path is a directory strcpy(outBuf, "Path is a directory.\n"); terminal->WriteChars(outBuf); diff --git a/src/commands/cd.cpp b/src/commands/cd.cpp index e615722..cbbd0da 100644 --- a/src/commands/cd.cpp +++ b/src/commands/cd.cpp @@ -27,9 +27,9 @@ extern int cd_main(int argc, char **argv) // check if the path exists int findHandle; - wchar_t fileName[100]; + char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst((const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst(g_wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); @@ -41,7 +41,7 @@ extern int cd_main(int argc, char **argv) } // path exists, check if it is a directory - if (findInfoBuf.type != EntryTypeDirectory) { + if (findInfoBuf.type != File_FindInfo::EntryTypeDirectory) { // path is not a directory strcpy(outBuf, "Path is not a directory.\n"); terminal->WriteChars(outBuf); @@ -91,7 +91,7 @@ extern int cd_main(int argc, char **argv) //convert from char to wchar for(int i=0; g_path[i]!=0; i++){ - wchar_t ch = g_path[i]; + char_const16_t ch = g_path[i]; g_wpath[i] = ch; } diff --git a/src/commands/history.cpp b/src/commands/history.cpp index 7d3337d..e810959 100644 --- a/src/commands/history.cpp +++ b/src/commands/history.cpp @@ -18,9 +18,9 @@ extern int history_main(int, char **) { // check if history file exists int findHandle; - wchar_t fileName[100]; + char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst((const char_const16_t*)g_whistory, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst(g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // history file does not exist // create the file @@ -40,7 +40,7 @@ extern int history_main(int, char **) { return 0; // history file exists, check if it is a directory - } else if (findInfoBuf.type == EntryTypeDirectory) { + } else if (findInfoBuf.type == File_FindInfo::EntryTypeDirectory) { // history file is a directory strcpy(outBuf, "History file is a directory.\n"); terminal->WriteChars(outBuf); diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index 3efc4cb..e677c9f 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -21,7 +21,7 @@ extern int ls_main(int, char **) int dirFiles = 0; int findHandle; - wchar_t fileName[100]; + char_const16_t fileName[100]; char outBuf[110]; struct File_FindInfo findInfoBuf; int ret = File_FindFirst((const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); @@ -31,11 +31,11 @@ extern int ls_main(int, char **) Mem_Memset(&thisfile, 0, sizeof(thisfile)); //copy file name for (int i=0; fileName[i]!=0; i++){ - wchar_t ch = fileName[i]; + char_const16_t ch = fileName[i]; thisfile.fileName[i] = ch; } //copy file type - thisfile.type=findInfoBuf.type==EntryTypeDirectory?'D':'F'; + thisfile.type=findInfoBuf.type==File_FindInfo::EntryTypeDirectory?'D':'F'; //display this strcpy(outBuf, thisfile.fileName); // check if it will fit on the screen or we are in second half diff --git a/src/cpshell.cpp b/src/cpshell.cpp index 984885b..67d1a6c 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -163,7 +163,7 @@ void cpshell_init() { //convert from char to wchar for(int i=0; g_path[i]!=0; i++){ - wchar_t ch = g_path[i]; + char_const16_t ch = g_path[i]; g_wpath[i] = ch; } diff --git a/src/internal.hpp b/src/internal.hpp index b78482b..0ba3faa 100644 --- a/src/internal.hpp +++ b/src/internal.hpp @@ -26,6 +26,8 @@ // Files +#include + struct dirEntry{ char fileName[100]; char type; @@ -33,13 +35,13 @@ struct dirEntry{ struct dirEntry directory[64]; char g_path[PATH_LEN]; -wchar_t g_wpath[PATH_LEN]; +char_const16_t g_wpath[PATH_LEN]; char g_home[PATH_LEN] = "\\fls0\\"; // default home for now -wchar_t g_whistory[32] = L"\\fls0\\usr\\.history"; +char_const16_t g_whistory[32] = u"\\fls0\\usr\\.history"; char g_history[32] = "\\fls0\\usr\\.history"; -wchar_t g_wuserprofile[32] = L"\\fls0\\usr\\.profile"; +char_const16_t g_wuserprofile[32] = u"\\fls0\\usr\\.profile"; char g_userprofile[32] = "\\fls0\\usr\\.profile"; struct Applet { diff --git a/src/loader.hpp b/src/loader.hpp index 71cac49..3592442 100644 --- a/src/loader.hpp +++ b/src/loader.hpp @@ -28,9 +28,9 @@ int load_userprofile() { terminal->ClearBuffer(); int findHandle; - wchar_t fileName[100]; + char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst((const char_const16_t*)g_wuserprofile, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst(g_wuserprofile, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // Does not exist so skip strcpy(outBuf, "LOAD: Skipping user profile load.\n"); @@ -38,7 +38,7 @@ int load_userprofile() { File_FindClose(findHandle); load_settings(); return 0; - } else if (findInfoBuf.type == EntryTypeDirectory) { + } else if (findInfoBuf.type == File_FindInfo::EntryTypeDirectory) { strcpy(outBuf, "LOAD: User profile is a directory.\n"); terminal->WriteChars(outBuf); File_FindClose(findHandle); diff --git a/src/terminal.hpp b/src/terminal.hpp index aef21aa..45fbcf5 100644 --- a/src/terminal.hpp +++ b/src/terminal.hpp @@ -16,7 +16,7 @@ class Terminal { public: void ClearBuffer(); void WriteBuffer(char c, bool hideCursor = true); - void WriteChars(char* charArray, bool skipClear = false); + void WriteChars(const char* charArray, bool skipClear = false); void RemoveLast(); void ShowCursor(); void HideCursor(); @@ -89,7 +89,7 @@ void Terminal::WriteBuffer(char c, bool hideCursor) { * Terminal* terminal; * terminal->WriteChars(myMessage); */ -void Terminal::WriteChars(char *charArray, bool skipClear) { +void Terminal::WriteChars(const char *charArray, bool skipClear) { int len = strlen(charArray); for (int i = 0; i < len; i++) { this->WriteBuffer(charArray[i], false); diff --git a/src/utilities.hpp b/src/utilities.hpp index d4d73a8..d4433f4 100644 --- a/src/utilities.hpp +++ b/src/utilities.hpp @@ -56,12 +56,12 @@ int safe_read(int fd, char *buf, int len) { return safe_internal(ret, "An error occurred calling read.\n"); } -int safe_write(int fd, char *buf, int len) { +int safe_write(int fd, const char *buf, int len) { int ret = File_Write(fd, buf, len); return safe_internal(ret, "An error occurred calling write.\n"); } -int safe_open(char *path, int flags) { +int safe_open(const char *path, int flags) { int ret = File_Open(path, flags); return safe_internal(ret, "An error occurred calling open.\n"); } @@ -72,7 +72,7 @@ int safe_close(int fd) { } int safe_lseek(int fd, int offset, int whence) { - int ret = File_Lseek(fd, offset, whence); + int ret = File_Lseek(fd, offset, (File_Whence)whence); return safe_internal(ret, "An error occurred calling lseek.\n"); } @@ -83,7 +83,7 @@ int add_history(int argc, char **argv) { // check if history file exists int findHandle; - wchar_t fileName[100]; + char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; int ret = File_FindFirst((const char_const16_t*)g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { @@ -102,7 +102,7 @@ int add_history(int argc, char **argv) { safe_close(fd); // history file exists, check if it is a directory - } else if (findInfoBuf.type == EntryTypeDirectory) { + } else if (findInfoBuf.type == File_FindInfo::EntryTypeDirectory) { // history file is a directory strcpy(outBuf, "History file is a directory.\n"); terminal->WriteChars(outBuf); diff --git a/temp_hhk3_sdk b/temp_hhk3_sdk new file mode 160000 index 0000000..3f0eaed --- /dev/null +++ b/temp_hhk3_sdk @@ -0,0 +1 @@ +Subproject commit 3f0eaed37e8fac34c2f4ec8c57389985d2eb372d From 2d87f6892843430113e4988d658f8244197eab11 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:13:19 +0000 Subject: [PATCH 08/23] Fix CI submodule error by removing temporary SDK folder Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .gitignore | 2 +- temp_hhk3_sdk | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 160000 temp_hhk3_sdk diff --git a/.gitignore b/.gitignore index e461a48..6fe5b09 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ dist/ obj/ .cache/ .deps/ -compile_commands.json \ No newline at end of file +compile_commands.jsontemp_hhk3_sdk/ diff --git a/temp_hhk3_sdk b/temp_hhk3_sdk deleted file mode 160000 index 3f0eaed..0000000 --- a/temp_hhk3_sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3f0eaed37e8fac34c2f4ec8c57389985d2eb372d From 38d9405e0f662a4da4a076193d77f7cfee8d4ea1 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:50:46 +0000 Subject: [PATCH 09/23] Fix CI build errors: fix char16_t types and signed comparisons Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- main.cpp | 4 ++-- src/commands/cat.cpp | 2 +- src/commands/cd.cpp | 4 ++-- src/commands/help.cpp | 2 +- src/commands/history.cpp | 2 +- src/commands/ls.cpp | 6 +++--- src/cpshell.cpp | 2 +- src/internal.hpp | 6 +++--- src/loader.hpp | 2 +- src/utilities.hpp | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/main.cpp b/main.cpp index dcdd08e..a79ca14 100644 --- a/main.cpp +++ b/main.cpp @@ -127,7 +127,7 @@ void kbEnter() { callingArgs[terminal->bufferInPos] = '\0'; int argc = 0; // count number spaces in callingArgs - for (int i = 0; i < strlen(callingArgs); i++) { + for (int i = 0; i < (int)strlen(callingArgs); i++) { if (callingArgs[i] == ' ') { argc++; } @@ -143,7 +143,7 @@ void kbEnter() { char currentArg[ARGV_SIZE]; int currentArgIndex = 0; - for (int i = 0; i < strlen(callingArgs); i++) { + for (int i = 0; i < (int)strlen(callingArgs); i++) { if (callingArgs[i] == ' ') { argv[argvIndex] = new char[currentArgIndex + 1]; for (int j = 0; j < currentArgIndex; j++) { diff --git a/src/commands/cat.cpp b/src/commands/cat.cpp index 1098369..f8ed3b7 100644 --- a/src/commands/cat.cpp +++ b/src/commands/cat.cpp @@ -39,7 +39,7 @@ extern int cat_main(int argc, char **argv) int findHandle; char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); diff --git a/src/commands/cd.cpp b/src/commands/cd.cpp index cbbd0da..c734714 100644 --- a/src/commands/cd.cpp +++ b/src/commands/cd.cpp @@ -29,7 +29,7 @@ extern int cd_main(int argc, char **argv) int findHandle; char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // path does not exist strcpy(outBuf, "Path does not exist.\n"); @@ -60,7 +60,7 @@ extern int cd_main(int argc, char **argv) char parentDir[PATH_LEN]; // search for the last '\\' int lastSlash = -1; - for (int i = 0; i < strlen(g_path) - 1; i++) { // len -1 as will always be a slash + for (int i = 0; i < (int)strlen(g_path) - 1; i++) { // len -1 as will always be a slash if (g_path[i] == '\\') { lastSlash = i; } diff --git a/src/commands/help.cpp b/src/commands/help.cpp index b9e1633..6ff99f7 100644 --- a/src/commands/help.cpp +++ b/src/commands/help.cpp @@ -24,7 +24,7 @@ extern int help_main(int, char **) while (a->name[0] != 0) { strcpy(cmds, (a++)->name); // check if terminal->bufferCX is at the end of the line + 2 for ', ' - if ((terminal->bufferCX + strlen(a->name) + 2) >= terminal->xmax) { + if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2) >= terminal->xmax) { terminal->WriteBuffer('\n', false); terminal->ClearBuffer(); } diff --git a/src/commands/history.cpp b/src/commands/history.cpp index e810959..2323f73 100644 --- a/src/commands/history.cpp +++ b/src/commands/history.cpp @@ -20,7 +20,7 @@ extern int history_main(int, char **) { int findHandle; char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_whistory, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // history file does not exist // create the file diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index e677c9f..953950b 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -24,7 +24,7 @@ extern int ls_main(int, char **) char_const16_t fileName[100]; char outBuf[110]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst((const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)(const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); while (ret>=0){ //create dirEntry structure struct dirEntry thisfile; @@ -39,7 +39,7 @@ extern int ls_main(int, char **) //display this strcpy(outBuf, thisfile.fileName); // check if it will fit on the screen or we are in second half - if ((terminal->bufferCX + strlen(outBuf)) >= terminal->xmax || (terminal->bufferCX + 1) >= (terminal->xmax/2)) { + if ((terminal->bufferCX + (int16_t)strlen(outBuf)) >= terminal->xmax || (terminal->bufferCX + 1) >= (terminal->xmax/2)) { terminal->WriteBuffer('\n', false); terminal->ClearBuffer(); } else if (terminal->bufferCX > 0) { @@ -54,7 +54,7 @@ extern int ls_main(int, char **) directory[dirFiles++] = thisfile; //serch the next - ret = File_FindNext(findHandle, (char_const16_t*)fileName, &findInfoBuf); + ret = File_FindNext(findHandle, (char_const16_t*)(char_const16_t*)fileName, &findInfoBuf); } File_FindClose(findHandle); diff --git a/src/cpshell.cpp b/src/cpshell.cpp index 67d1a6c..89f2fc4 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -104,7 +104,7 @@ int cpshell_main(int argc, char **argv) while (a->name[0] != 0) { strcpy(cmds, (a++)->name); // check if terminal->bufferCX is at the end of the line + 2 for ', ' - if ((terminal->bufferCX + strlen(a->name) + 2) >= terminal->xmax) { + if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2) >= terminal->xmax) { terminal->WriteBuffer('\n', false); terminal->ClearBuffer(); } diff --git a/src/internal.hpp b/src/internal.hpp index 0ba3faa..1a1ad70 100644 --- a/src/internal.hpp +++ b/src/internal.hpp @@ -35,13 +35,13 @@ struct dirEntry{ struct dirEntry directory[64]; char g_path[PATH_LEN]; -char_const16_t g_wpath[PATH_LEN]; +char16_t g_wpath[PATH_LEN]; char g_home[PATH_LEN] = "\\fls0\\"; // default home for now -char_const16_t g_whistory[32] = u"\\fls0\\usr\\.history"; +char16_t g_whistory[32] = u"\\fls0\\usr\\.history"; char g_history[32] = "\\fls0\\usr\\.history"; -char_const16_t g_wuserprofile[32] = u"\\fls0\\usr\\.profile"; +char16_t g_wuserprofile[32] = u"\\fls0\\usr\\.profile"; char g_userprofile[32] = "\\fls0\\usr\\.profile"; struct Applet { diff --git a/src/loader.hpp b/src/loader.hpp index 3592442..ab1f3a8 100644 --- a/src/loader.hpp +++ b/src/loader.hpp @@ -30,7 +30,7 @@ int load_userprofile() { int findHandle; char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst(g_wuserprofile, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)g_wuserprofile, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // Does not exist so skip strcpy(outBuf, "LOAD: Skipping user profile load.\n"); diff --git a/src/utilities.hpp b/src/utilities.hpp index d4433f4..0244fbc 100644 --- a/src/utilities.hpp +++ b/src/utilities.hpp @@ -85,7 +85,7 @@ int add_history(int argc, char **argv) { int findHandle; char_const16_t fileName[100]; struct File_FindInfo findInfoBuf; - int ret = File_FindFirst((const char_const16_t*)g_whistory, &findHandle, fileName, &findInfoBuf); + int ret = File_FindFirst((const char_const16_t*)(const char_const16_t*)g_whistory, &findHandle, fileName, &findInfoBuf); if (ret < 0) { // history file does not exist // create the file From 157cce8fa1c1cec06a7af2d0fb909d612dbae86b Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:18:38 +0000 Subject: [PATCH 10/23] Fix CI build errors: fix syntax errors from regex replacement Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/commands/help.cpp | 2 +- src/cpshell.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/help.cpp b/src/commands/help.cpp index 6ff99f7..56035fc 100644 --- a/src/commands/help.cpp +++ b/src/commands/help.cpp @@ -24,7 +24,7 @@ extern int help_main(int, char **) while (a->name[0] != 0) { strcpy(cmds, (a++)->name); // check if terminal->bufferCX is at the end of the line + 2 for ', ' - if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2) >= terminal->xmax) { + if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2)) >= terminal->xmax) { terminal->WriteBuffer('\n', false); terminal->ClearBuffer(); } diff --git a/src/cpshell.cpp b/src/cpshell.cpp index 89f2fc4..61ad0e8 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -104,7 +104,7 @@ int cpshell_main(int argc, char **argv) while (a->name[0] != 0) { strcpy(cmds, (a++)->name); // check if terminal->bufferCX is at the end of the line + 2 for ', ' - if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2) >= terminal->xmax) { + if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2)) >= terminal->xmax) { terminal->WriteBuffer('\n', false); terminal->ClearBuffer(); } From 5a207cff4824b768aa6287bf3df0c7e76c904ca2 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:35:09 +0000 Subject: [PATCH 11/23] Fix CI linker error: rename fillScreen to app_fillScreen Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- calc.cpp | 4 ++-- calc.hpp | 4 ++-- main.cpp | 2 +- src/commands/clear.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/calc.cpp b/calc.cpp index 204edcb..aedbb8b 100644 --- a/calc.cpp +++ b/calc.cpp @@ -207,7 +207,7 @@ void vline(int x, int y1, int y2, uint16_t color){ } #endif -void fillScreen(uint16_t color){ +void app_fillScreen(uint16_t color){ #ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA @@ -358,7 +358,7 @@ void vline(int x, int y1, int y2, uint16_t color){ setPixel(x,y,color); } -void fillScreen(uint16_t color){ +void app_fillScreen(uint16_t color){ //#ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA diff --git a/calc.hpp b/calc.hpp index 08cc652..bef844d 100644 --- a/calc.hpp +++ b/calc.hpp @@ -37,11 +37,11 @@ void delay(uint32_t time); #ifdef PC void line(int x1, int y1, int x2, int y2, uint16_t color); void triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint16_t colorFill, uint16_t colorLine); -void fillScreen(uint16_t color); +void app_fillScreen(uint16_t color); #endif #ifdef PC - inline void LCD_ClearScreen(){ fillScreen((uint16_t)0xFFFF);} + inline void LCD_ClearScreen(){ app_fillScreen((uint16_t)0xFFFF);} #endif #ifdef PC diff --git a/main.cpp b/main.cpp index a79ca14..83f2f98 100644 --- a/main.cpp +++ b/main.cpp @@ -223,7 +223,7 @@ void main2() { // load the textures and fonts LOAD_FONT_PTR("7x8", f_7x8); - fillScreen(0); // clear the screen to black (0,0,0) + app_fillScreen(0); // clear the screen to black (0,0,0) RandomGenerator rngp; rng = &rngp; diff --git a/src/commands/clear.cpp b/src/commands/clear.cpp index cf0d268..8502d78 100644 --- a/src/commands/clear.cpp +++ b/src/commands/clear.cpp @@ -11,7 +11,7 @@ extern int clear_main(int, char **) { // clear the screen to black - fillScreen(0); + app_fillScreen(0); // rerender the keyboard keyboard->Render(); // keyboard highlight From bfb9fc3817c634c0c3bcb4a0b64614331ccd4bb8 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:48:37 +0000 Subject: [PATCH 12/23] Fix CI linker error: resolve multiple definition of fillScreen Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- calc.cpp | 4 ++-- calc.hpp | 4 ++-- main.cpp | 2 +- src/commands/clear.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/calc.cpp b/calc.cpp index aedbb8b..204edcb 100644 --- a/calc.cpp +++ b/calc.cpp @@ -207,7 +207,7 @@ void vline(int x, int y1, int y2, uint16_t color){ } #endif -void app_fillScreen(uint16_t color){ +void fillScreen(uint16_t color){ #ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA @@ -358,7 +358,7 @@ void vline(int x, int y1, int y2, uint16_t color){ setPixel(x,y,color); } -void app_fillScreen(uint16_t color){ +void fillScreen(uint16_t color){ //#ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA diff --git a/calc.hpp b/calc.hpp index bef844d..08cc652 100644 --- a/calc.hpp +++ b/calc.hpp @@ -37,11 +37,11 @@ void delay(uint32_t time); #ifdef PC void line(int x1, int y1, int x2, int y2, uint16_t color); void triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint16_t colorFill, uint16_t colorLine); -void app_fillScreen(uint16_t color); +void fillScreen(uint16_t color); #endif #ifdef PC - inline void LCD_ClearScreen(){ app_fillScreen((uint16_t)0xFFFF);} + inline void LCD_ClearScreen(){ fillScreen((uint16_t)0xFFFF);} #endif #ifdef PC diff --git a/main.cpp b/main.cpp index 83f2f98..a79ca14 100644 --- a/main.cpp +++ b/main.cpp @@ -223,7 +223,7 @@ void main2() { // load the textures and fonts LOAD_FONT_PTR("7x8", f_7x8); - app_fillScreen(0); // clear the screen to black (0,0,0) + fillScreen(0); // clear the screen to black (0,0,0) RandomGenerator rngp; rng = &rngp; diff --git a/src/commands/clear.cpp b/src/commands/clear.cpp index 8502d78..cf0d268 100644 --- a/src/commands/clear.cpp +++ b/src/commands/clear.cpp @@ -11,7 +11,7 @@ extern int clear_main(int, char **) { // clear the screen to black - app_fillScreen(0); + fillScreen(0); // rerender the keyboard keyboard->Render(); // keyboard highlight From 53ac988f7bf66de857d8fed5d53f51b7b1e1635b Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:05:09 +0000 Subject: [PATCH 13/23] Fix CI linker error: remove fillScreen redefinition on Calc Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- calc.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/calc.cpp b/calc.cpp index 204edcb..48b15fd 100644 --- a/calc.cpp +++ b/calc.cpp @@ -207,8 +207,8 @@ void vline(int x, int y1, int y2, uint16_t color){ } #endif +#ifdef PC void fillScreen(uint16_t color){ - #ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA /*R*/ pixels[3] = (color >> 8) & 0b11111000; @@ -227,12 +227,8 @@ void fillScreen(uint16_t color){ SDL_Rect rect; rect.x = 0; rect.y = 0; rect.w =width; rect.h = height; SDL_UpdateTexture(texture, &rect , (void*)screen, 4*width); //The last number defines the number of bytes per row. ( width * bytePerPixel ) - #else - const uint32_t size = width * height; - for(uint32_t i = 0; i Date: Wed, 25 Feb 2026 21:51:25 +0000 Subject: [PATCH 14/23] Fix crash due to missing font file: add null check Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.cpp b/main.cpp index a79ca14..f5b054b 100644 --- a/main.cpp +++ b/main.cpp @@ -223,6 +223,12 @@ void main2() { // load the textures and fonts LOAD_FONT_PTR("7x8", f_7x8); + if (!f_7x8) { + Debug_Printf(0, 0, false, 0, "Error loading font!"); + LCD_Refresh(); + while(1); + } + fillScreen(0); // clear the screen to black (0,0,0) RandomGenerator rngp; From 130156b61b65627a848b9e587c336991479a2b33 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:28:25 +0000 Subject: [PATCH 15/23] Fix crash: align Input_Event struct to 4 bytes Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/core/touch_event_handler.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/touch_event_handler.hpp b/lib/core/touch_event_handler.hpp index a754bb9..34f581b 100644 --- a/lib/core/touch_event_handler.hpp +++ b/lib/core/touch_event_handler.hpp @@ -28,12 +28,12 @@ struct ActBarHandler { // can be changed to accomodate more events if needed struct TouchHandler touchHandlers[64]; -uint8_t touchHandlersLength = 0; +uint32_t touchHandlersLength = 0; struct ActBarHandler actBarHandlers[6]; -uint8_t actBarHandlersLength = 0; +uint32_t actBarHandlersLength = 0; -struct Input_Event event; +struct Input_Event event __attribute__((aligned(4))); void checkTouchEvents() { From b31a6c242b014709f2b7139b76fcbb9b54d6bd3f Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Wed, 25 Feb 2026 23:38:42 +0000 Subject: [PATCH 16/23] Fix crash in ls: align file buffers to 4 bytes Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/core/touch_event_handler.hpp | 6 +++--- src/commands/ls.cpp | 6 +++--- src/internal.hpp | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/core/touch_event_handler.hpp b/lib/core/touch_event_handler.hpp index 34f581b..a754bb9 100644 --- a/lib/core/touch_event_handler.hpp +++ b/lib/core/touch_event_handler.hpp @@ -28,12 +28,12 @@ struct ActBarHandler { // can be changed to accomodate more events if needed struct TouchHandler touchHandlers[64]; -uint32_t touchHandlersLength = 0; +uint8_t touchHandlersLength = 0; struct ActBarHandler actBarHandlers[6]; -uint32_t actBarHandlersLength = 0; +uint8_t actBarHandlersLength = 0; -struct Input_Event event __attribute__((aligned(4))); +struct Input_Event event; void checkTouchEvents() { diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index 953950b..eb6ef55 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -21,10 +21,10 @@ extern int ls_main(int, char **) int dirFiles = 0; int findHandle; - char_const16_t fileName[100]; + char_const16_t fileName[100] __attribute__((aligned(4))); char outBuf[110]; - struct File_FindInfo findInfoBuf; - int ret = File_FindFirst((const char_const16_t*)(const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); + struct File_FindInfo findInfoBuf __attribute__((aligned(4))); + int ret = File_FindFirst((const char_const16_t*)g_wpath, &findHandle, fileName, &findInfoBuf); while (ret>=0){ //create dirEntry structure struct dirEntry thisfile; diff --git a/src/internal.hpp b/src/internal.hpp index 1a1ad70..55a55af 100644 --- a/src/internal.hpp +++ b/src/internal.hpp @@ -34,14 +34,14 @@ struct dirEntry{ }; struct dirEntry directory[64]; -char g_path[PATH_LEN]; -char16_t g_wpath[PATH_LEN]; +char g_path[PATH_LEN] __attribute__((aligned(4))); +char16_t g_wpath[PATH_LEN] __attribute__((aligned(4))); char g_home[PATH_LEN] = "\\fls0\\"; // default home for now -char16_t g_whistory[32] = u"\\fls0\\usr\\.history"; +char16_t g_whistory[32] __attribute__((aligned(4))) = u"\\fls0\\usr\\.history"; char g_history[32] = "\\fls0\\usr\\.history"; -char16_t g_wuserprofile[32] = u"\\fls0\\usr\\.profile"; +char16_t g_wuserprofile[32] __attribute__((aligned(4))) = u"\\fls0\\usr\\.profile"; char g_userprofile[32] = "\\fls0\\usr\\.profile"; struct Applet { From 13e5ca9376d361091654d77c0fe24b8bcf1bc1ea Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Thu, 26 Feb 2026 00:07:15 +0000 Subject: [PATCH 17/23] Fix crash: re-apply alignment fix for Input_Event Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/core/touch_event_handler.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/touch_event_handler.hpp b/lib/core/touch_event_handler.hpp index a754bb9..34f581b 100644 --- a/lib/core/touch_event_handler.hpp +++ b/lib/core/touch_event_handler.hpp @@ -28,12 +28,12 @@ struct ActBarHandler { // can be changed to accomodate more events if needed struct TouchHandler touchHandlers[64]; -uint8_t touchHandlersLength = 0; +uint32_t touchHandlersLength = 0; struct ActBarHandler actBarHandlers[6]; -uint8_t actBarHandlersLength = 0; +uint32_t actBarHandlersLength = 0; -struct Input_Event event; +struct Input_Event event __attribute__((aligned(4))); void checkTouchEvents() { From 78729107210822391a103b7e7373a3106946101e Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Thu, 26 Feb 2026 00:36:56 +0000 Subject: [PATCH 18/23] Finalize v3 migration and workspace cleanup Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> From c01fffadfe4dfa12239d80f41758edaee965ca79 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Thu, 26 Feb 2026 00:49:12 +0000 Subject: [PATCH 19/23] Implement new virtual keyboard with toggle and terminal scrolling Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- cinput.py | 1109 ++++++++++++++++++++++++++++++++++++++ main.cpp | 302 +++++------ src/terminal.hpp | 217 +++++--- src/virtual_keyboard.hpp | 496 ++++++++++++----- 4 files changed, 1771 insertions(+), 353 deletions(-) create mode 100644 cinput.py diff --git a/cinput.py b/cinput.py new file mode 100644 index 0000000..09a550d --- /dev/null +++ b/cinput.py @@ -0,0 +1,1109 @@ +from gint import * +import time +import math + +# ============================================================================= +# CONSTANTS & CONFIG +# ============================================================================= + +SCREEN_W = 320 +SCREEN_H = 528 + +# Layout Dimensions +KBD_H = 260 +TAB_H = 30 +PICK_HEADER_H = 40 +PICK_FOOTER_H = 45 +PICK_ITEM_H = 50 + +# ============================================================================= +# THEMES +# ============================================================================= + +def safe_rgb(r, g, b): + return C_RGB(r, g, b) + +THEMES = { + 'light': { + 'modal_bg': C_WHITE, + 'kbd_bg': C_WHITE, + 'key_bg': C_WHITE, + 'key_spec': safe_rgb(28, 29, 28), # Secondary (Light Grey) + 'key_out': C_DARK, # Dark (Unused for key borders now) + 'txt': safe_rgb(4, 4, 4), + 'txt_dim': safe_rgb(8, 8, 8), + 'accent': safe_rgb(1, 11, 26), + 'txt_acc': C_WHITE, + 'hl': safe_rgb(28, 29, 28), # Highlight matches secondary + 'check': C_WHITE + }, + 'dark': { + 'modal_bg': safe_rgb(7, 7, 8), + 'kbd_bg': safe_rgb(7, 7, 8), + 'key_bg': safe_rgb(7, 7, 8), + 'key_spec': safe_rgb(11, 11, 12), # Secondary (Dark Grey) + 'key_out': safe_rgb(12, 19, 31), + 'txt': C_WHITE, + 'txt_dim': safe_rgb(8, 8, 8), + 'accent': safe_rgb(12, 19, 31), + 'txt_acc': C_WHITE, + 'hl': safe_rgb(11, 11, 12), + 'check': C_WHITE + }, + 'grey': { + 'modal_bg': C_LIGHT, + 'kbd_bg': C_LIGHT, + 'key_bg': C_WHITE, + 'key_spec': 0xCE59, + 'key_out': C_BLACK, + 'txt': C_BLACK, + 'txt_dim': safe_rgb(8, 8, 8), + 'accent': C_BLACK, + 'txt_acc': C_WHITE, + 'hl': 0xCE59, + 'check': C_WHITE + } +} + +def get_theme(name_or_dict) -> dict: + if isinstance(name_or_dict, dict): return name_or_dict + return THEMES.get(name_or_dict, THEMES['light']) + +# ============================================================================= +# REUSABLE LIST VIEW +# ============================================================================= + +class ListView: + def __init__(self, rect, items, row_h=40, theme='light', headers_h=None): + """ + rect: (x, y, w, h) tuple + items: List of dicts {'text': str, 'type': 'item'|'section', 'height': int, ...} OR list of strings + """ + self.x, self.y, self.w, self.h = rect + # Normalize items + self.items = [] + for it in items: + if isinstance(it, dict): self.items.append(it) + else: self.items.append({'text': str(it), 'type': 'item'}) + + self.base_row_h = row_h + self.headers_h = headers_h if headers_h else row_h + self.theme = get_theme(theme) + + # Layout State + self.total_h = 0 + self.recalc_layout() + + # Selection & Scroll + self.selected_index = -1 + self.scroll_y = 0 + self.max_scroll = max(0, self.total_h - self.h) + + # Select first selectable item + self.select_next(0, 1) + + # Touch State + self.is_dragging = False + self.touch_start_y = 0 + self.touch_start_idx = 0 + self.touch_start_time = 0 + self.touch_acc_y = 0.0 # Accumulator for drag distance + self.touch_initial_item_idx = -1 + self.long_press_triggered = False + + # Configuration + self.drag_threshold = 10 + self.long_press_delay = 0.5 # seconds + self.snap_sensitivity = 1.0 # 1.0 = 1 item height drag moves 1 item + + def recalc_layout(self): + """Calculate positions and heights of all items.""" + total_h = 0 + for it in self.items: + h = it.get('height', self.headers_h if it.get('type') == 'section' else self.base_row_h) + it['_h'] = h + it['_y'] = total_h + total_h += h + self.total_h = total_h + self.max_scroll = max(0, self.total_h - self.h) + + def select_next(self, start_idx, step): + """Find next selectable item""" + idx = start_idx + count = len(self.items) + if count == 0: + self.selected_index = -1 + return + + # Safety loop limit + for _ in range(count): + if 0 <= idx < count: + if self.items[idx].get('type', 'item') != 'section': + self.selected_index = idx + self.ensure_visible() + return + idx += step + # Clamp logic + if idx < 0 or idx >= count: break + + def ensure_visible(self): + """Scroll to keep selected_index in view""" + if self.selected_index < 0 or self.selected_index >= len(self.items): return + + it = self.items[self.selected_index] + item_top = it['_y'] + item_bot = item_top + it['_h'] + + view_top = self.scroll_y + view_bot = self.scroll_y + self.h + + if item_top < view_top: + self.scroll_y = item_top + elif item_bot > view_bot: + self.scroll_y = item_bot - self.h + + self.clamp_scroll() + + def clamp_scroll(self): + self.max_scroll = max(0, self.total_h - self.h) + self.scroll_y = max(0, min(self.max_scroll, self.scroll_y)) + + def update(self, events): + """ + Process events. + """ + now = time.monotonic() + + # Touch Handling + touch_up = None + touch_down = None + current_touch = None + + # Pre-process touch events + for e in events: + if e.type == KEYEV_TOUCH_DOWN: + touch_down = e + current_touch = e + elif e.type == KEYEV_TOUCH_UP: + touch_up = e + elif e.type == KEYEV_TOUCH_DRAG: # Some envs might emit this + current_touch = e + + # Also look for simulated drag via repeatedly polled DOWN events if native drag not available + if not current_touch and touch_down: + current_touch = touch_down + + # 1. Start Touch + if touch_down and not self.is_dragging and self.touch_start_time == 0: + if self.x <= touch_down.x < self.x + self.w and self.y <= touch_down.y < self.y + self.h: + self.touch_start_y = touch_down.y + self.touch_start_time = now + + # Determine which item was touched initially + local_y = touch_down.y - self.y + self.scroll_y + self.touch_initial_item_idx = self.get_index_at(local_y) + + # Anchor drag to the item under finger if valid, else selection + if self.touch_initial_item_idx != -1: + self.touch_start_idx = self.touch_initial_item_idx + # Immediate visual feedback + if 0 <= self.touch_initial_item_idx < len(self.items): + if self.items[self.touch_initial_item_idx].get('type') != 'section': + self.selected_index = self.touch_initial_item_idx + self.ensure_visible() + else: + self.touch_start_idx = self.selected_index + + self.is_dragging = False + self.long_press_triggered = False + self.touch_acc_y = 0.0 + + # Long Press Detection (Time-based) + if self.touch_start_time != 0 and not self.is_dragging and not self.long_press_triggered: + if now - self.touch_start_time > 0.8: # 800ms threshold + self.long_press_triggered = True + # Return 'long' action immediately + idx = self.selected_index + if 0 <= idx < len(self.items): + return ('long', idx, self.items[idx]) + + # ... Or use the last known touch if we are already in a touch sequence + # We need a way to know "current finger position" even if no new event came this frame, + # but typically we get continuous events. If we don't, we can't drag. + + # 2. Touch Move / Drag + if self.touch_start_time != 0: + # Find the most recent position event + last_pos = current_touch if current_touch else touch_down + + if last_pos: + dy = last_pos.y - self.touch_start_y + + # Check for drag threshold + # User req: "If the drag is more than one item height, then apply scrolling" + if not self.is_dragging: + if abs(dy) > self.base_row_h: # Threshold is one item height + self.is_dragging = True + self.long_press_triggered = True + + if self.is_dragging: + # Scroll Logic (Snap) + # Dragging UP (negative dy) -> Move down in list + + # More advanced "Pixel-based" mapping to index + # We map the total pixel offset (start_y + scroll_y_offset - dy) to an index + # This allows variable row heights natural feeling + + # 1. Calculate theoretical pixel position + # We want to move the "originally selected item" by -dy pixels relative to the view center? + # Or simpler: The "selection cursor" moves by -dy pixels. + + # Current selection top in pixels + if 0 <= self.touch_start_idx < len(self.items): + start_item_y = self.items[self.touch_start_idx]['_y'] + + # Target Y for the selection top + target_y = start_item_y - dy + + # Find index at target_y + # Wescan items to find which one contains target_y + # But to fix the "clamping" issue with sections: + # If the index found is a section, or we are crossing a section... + + found_idx = -1 + for i, it in enumerate(self.items): + if it['_y'] <= target_y < it['_y'] + it['_h']: + found_idx = i + break + + if found_idx == -1: + if target_y < 0: found_idx = 0 + else: found_idx = len(self.items) - 1 + + # Apply Clamping Logic: + # If found_idx is a SECTION, we check where in the section we are. + # User wants: "consider the selection to enter the next section first item + # only when the scroll amount is above the title height." + + # Implementation interpretation: + # If we land on a section header (found_idx is Section), + # we should STAY on the *previous* item unless we are past the section header? + # Or STAY on the section header (But sections aren't selectable...)? + # "Selectable" logic usually skips sections. + + # Refined logic: Drag maps to a pixel Y. That pixel Y lands on an item. + # If it lands on a Section: + # If we came from above (moving down), we need to drag PAST the section entirely to select the item below it. + # If we came from below (moving up), we need to drag ABOVE the section entirely to select item above. + + # Effectively, the "active area" for the section header does not trigger selection change until passed? + # Let's try: if match is section, map to previous valid item if (target_y < section_center) else next valid item? + # User said: "only when the scroll amount is above the title height" + + if self.items[found_idx].get('type') == 'section': + # We are hovering over a section + # Check overlap + sec_y = self.items[found_idx]['_y'] + sec_h = self.items[found_idx]['_h'] + overlap = target_y - sec_y + + # If we are dragging DOWN (dy < 0, target_y increasing), we are moving to next section + if dy < 0: + # We are moving down the list. + # Only jump to next item if we are really deep into the section or past it? + # Actually, if we are conceptually "dragging the paper", moving finger UP (negative dy) + # means we want to see lower items. + pass + + # Logic: + # If we land on section, look at adjacent items. + # If we are closer to the top of section, pick item above. + # If we are closer to bottom, pick item below. + # "Glitch: separator ... flicker between the two voices" + + # Let's enforce a "dead zone" corresponding to the section height. + # If target_y falls within a section's vertical space, keep the selection + # on the *previously selected item* (from start of drag or previous frame) + # UNLESS we have crossed it? + + # Simpler: Don't select the section. Select the adjacent valid item based on direction, + # BUT require the "target_y" to be fully into the valid item's space. + + # If target_y is in section -> Don't change selection from what it would be if we were at the edge? + + if self.selected_index < found_idx: + # We were above, moving down. + # Stay above until we are purely past the section? + # effectively, for the section to be skipped, target_y must be >= sec_y + sec_h + # But found_idx says we are < sec_y + sec_h. + # So stay at found_idx - 1 (or whatever was valid above) + final_idx = found_idx - 1 + else: + # We were below, moving up. + # Stay below until target_y < sec_y + final_idx = found_idx + 1 + + # Boundary checks + final_idx = max(0, min(len(self.items)-1, final_idx)) + + else: + final_idx = found_idx + + if 0 <= final_idx < len(self.items) and self.items[final_idx].get('type') != 'section': + self.selected_index = final_idx + self.ensure_visible() + + # 3. Touch Release + if touch_up: + if self.touch_start_time != 0: + # Valid release sequence + ret = None + + if not self.is_dragging and not self.long_press_triggered: + # Click Candidate + local_y = touch_up.y - self.y + self.scroll_y + release_idx = self.get_index_at(local_y) + + # Logic: If release is on same item as start, it's a click. + if release_idx == self.touch_initial_item_idx and release_idx >= 0: + if self.items[release_idx].get('type') != 'section': + # Ensure selection updates to the clicked item + self.selected_index = release_idx + self.ensure_visible() + ret = ('click', release_idx, self.items[release_idx]) + + # Reset + self.touch_start_time = 0 + self.is_dragging = False + return ret + + # If touch_up happened but we weren't tracking, ignore it (stray event) + + # 4. Long Press + if self.touch_start_time != 0 and not self.is_dragging and not self.long_press_triggered: + if now - self.touch_start_time > self.long_press_delay: + self.long_press_triggered = True + # Trigger on current selection or initial? + # Usually initial item + if 0 <= self.touch_initial_item_idx < len(self.items): + it = self.items[self.touch_initial_item_idx] + if it.get('type') != 'section': + self.selected_index = self.touch_initial_item_idx + return ('long', self.touch_initial_item_idx, it) + + # 5. Keys + for e in events: + if e.type == KEYEV_DOWN or (e.type == KEYEV_HOLD and e.key in [KEY_UP, KEY_DOWN]): + if e.key == KEY_UP: + self.select_next(self.selected_index - 1, -1) + elif e.key == KEY_DOWN: + self.select_next(self.selected_index + 1, 1) + elif e.key == KEY_EXE: + if self.selected_index >= 0: + return ('click', self.selected_index, self.items[self.selected_index]) + + return None + + def get_index_at(self, y): + # Linear scan is sufficient for likely list sizes (<500) + # Binary search could be used since _y is sorted + for i, it in enumerate(self.items): + if it['_y'] <= y < it['_y'] + it['_h']: + return i + return -1 + + def draw_item(self, x, y, item, is_selected): + # Can be overridden by subclass or assigned + t = self.theme + h = item['_h'] + + if item.get('type') == 'section': + drect(x, y, x + self.w, y + h, t['key_spec']) + drect_border(x, y, x + self.w, y + h, C_NONE, 1, t['key_spec']) + dtext_opt(x + 10, y + h//2, t['txt_dim'], C_NONE, DTEXT_LEFT, DTEXT_MIDDLE, str(item['text']), -1) + else: + bg = t['hl'] if is_selected else t['modal_bg'] + drect(x, y, x + self.w, y + h, bg) + drect_border(x, y, x + self.w, y + h, C_NONE, 1, t['key_spec']) + + x_off = 20 + if item.get('checked'): + self.draw_check(x + 10, y + (h-20)//2, t) + x_off = 40 + + dtext_opt(x + x_off, y + h//2, t['txt'], C_NONE, DTEXT_LEFT, DTEXT_MIDDLE, str(item['text']), -1) + + # Draw Arrow if requested + if item.get('arrow'): + ar_x = x + self.w - 15 + ar_y = y + h//2 + c = t['txt_dim'] + dline(ar_x - 4, ar_y - 4, ar_x, ar_y, c) + dline(ar_x - 4, ar_y + 4, ar_x, ar_y, c) + + def draw(self): + t = self.theme + drect(self.x, self.y, self.x + self.w, self.y + self.h, t['modal_bg']) + + # Lazy Rendering: Find start index + start_y = self.scroll_y + end_y = self.scroll_y + self.h + + # Simple scan to find start (optimize later if needed) + start_idx = 0 + for i, it in enumerate(self.items): + if it['_y'] + it['_h'] > start_y: + start_idx = i + break + + # Draw visible items + for i in range(start_idx, len(self.items)): + it = self.items[i] + if it['_y'] >= end_y: break # Stop if below view + + item_y = self.y + it['_y'] - self.scroll_y + self.draw_item(self.x, item_y, it, (i == self.selected_index)) + + # Scrollbar + if self.max_scroll > 0: + sb_w = 4 + ratio = self.h / (self.total_h if self.total_h > 0 else 1) + thumb_h = max(20, int(self.h * ratio)) + + scroll_ratio = self.scroll_y / self.max_scroll + thumb_y = self.y + int(scroll_ratio * (self.h - thumb_h)) + + sb_x = self.x + self.w - sb_w - 2 + drect(sb_x, thumb_y, sb_x + sb_w, thumb_y + thumb_h, t['accent']) + + def draw_check(self, x, y, t): + drect(x, y, x+20, y+20, t['accent']) + c = t['check'] + dline(x+4, y+10, x+8, y+14, c) + dline(x+8, y+14, x+15, y+5, c) + +# ============================================================================= +# KEYBOARD WIDGET +# ============================================================================= + +LAYOUTS = { + 'qwerty': [list("1234567890"), list("qwertyuiop"), list("asdfghjkl:"), list("zxcvbnm,._")], + 'azerty': [list("1234567890"), list("azertyuiop"), list("qsdfghjklm"), list("wxcvbn,._:")], + 'qwertz': [list("1234567890"), list("qwertzuiop"), list("asdfghjkl:"), list("yxcvbnm,._")], + 'abc': [list("1234567890"), list("abcdefghij"), list("klmnopqrst"), list("uvwxyz,._:")] +} + +LAYOUT_SYM = [ + list("1234567890"), + list("@#$_&-+()/"), + list("=\\<*\"':;!?"), + list("{}[]^~`|<>") +] + +class Keyboard: + def __init__(self, default_tab=0, enable_tabs=True, numpad_opts=None, theme='light', layout='qwerty'): + self.y = SCREEN_H - KBD_H + self.visible = True + self.current_tab = default_tab + self.enable_tabs = enable_tabs + self.shift = False + self.tabs = ["ABC", "Sym", "Math"] + self.last_key = None + self.numpad_opts = numpad_opts if numpad_opts else {'float': True, 'neg': True} + + self.theme: dict = get_theme(theme) + self.layout_alpha = LAYOUTS.get(layout, LAYOUTS['qwerty']) + if layout != 'qwerty': + self.tabs[0] = layout.upper() if len(layout) <= 3 else "Txt" + + def draw_key(self, x, y, w, h, label, is_special=False, is_pressed=False, is_accent=False): + t = self.theme + # Background + if is_pressed: bg = t['hl'] + elif is_accent: bg = t['accent'] + elif is_special: bg = t['key_spec'] + else: bg = t['key_bg'] + + txt_col = t['txt_acc'] if is_accent else t['txt'] + # Soft Border using secondary color + border_col = t['key_spec'] + + drect(x + 1, y + 1, x + w - 1, y + h - 1, bg) + drect_border(x, y, x + w, y + h, C_NONE, 1, border_col) + dtext_opt(x + w//2, y + h//2, txt_col, C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, label, -1) + + def draw_tabs(self): + t = self.theme + tab_w = SCREEN_W // 3 + border_col = t['key_spec'] + for i, tab_name in enumerate(self.tabs): + tx = i * tab_w + is_active = (i == self.current_tab) + bg = t['kbd_bg'] if is_active else t['key_spec'] + drect(tx, self.y, tx + tab_w, self.y + TAB_H, bg) + drect_border(tx, self.y, tx + tab_w, self.y + TAB_H, C_NONE, 1, border_col) + if is_active: + drect(tx + 1, self.y + TAB_H - 1, tx + tab_w - 1, self.y + TAB_H + 1, t['kbd_bg']) + dtext_opt(tx + tab_w//2, self.y + TAB_H//2, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, tab_name, -1) + + def draw_grid(self): + layout = LAYOUT_SYM if self.current_tab == 1 else self.layout_alpha + grid_y = self.y + TAB_H + row_h = 45 + for r, row in enumerate(layout): + count = len(row) + kw = SCREEN_W // count + for c, char in enumerate(row): + kx = c * kw + ky = grid_y + r * row_h + label = char.upper() if (self.current_tab == 0 and self.shift) else char + is_pressed = (self.last_key == label) + self.draw_key(kx, ky, kw, row_h, label, False, is_pressed) + + # Bottom Control Row + bot_y = grid_y + 4 * row_h + bot_h = row_h + self.draw_key(0, bot_y, 50, bot_h, "CAPS", True, self.shift, False) + self.draw_key(50, bot_y, 50, bot_h, "<-", True, self.last_key == "BACKSPACE", False) + self.draw_key(100, bot_y, 160, bot_h, "Space", False, self.last_key == " ", False) + self.draw_key(260, bot_y, 60, bot_h, "EXE", False, self.last_key == "ENTER", True) + + def update_grid(self, x, y, type): + grid_y = self.y + TAB_H + row_h = 45 + row_idx = (y - grid_y) // row_h + if 0 <= row_idx < 4: + layout = LAYOUT_SYM if self.current_tab == 1 else self.layout_alpha + if row_idx >= len(layout): return None + row_chars = layout[row_idx] + kw = SCREEN_W // len(row_chars) + col_idx = min(len(row_chars)-1, max(0, x // kw)) + char = row_chars[col_idx] + if self.current_tab == 0 and self.shift: char = char.upper() + if type == KEYEV_TOUCH_DOWN: self.last_key = char + return char + elif row_idx == 4: + cmd = None + if x < 50: + if type == KEYEV_TOUCH_DOWN: self.shift = not self.shift + elif x < 100: cmd = "BACKSPACE" + elif x < 260: cmd = " " + else: cmd = "ENTER" + if type == KEYEV_TOUCH_DOWN: self.last_key = cmd + return cmd + return None + + def get_math_rects(self): + keys = [] + start_y = self.y + TAB_H + total_h = KBD_H - TAB_H + row_h = total_h // 4 + side_w = 50 + center_w = SCREEN_W - (side_w * 2) + numpad_w = center_w // 3 + for i, char in enumerate(["+", "-", "*", "/"]): + keys.append((0, start_y + i*row_h, side_w, row_h, char, False, True, False)) + r_chars = [("%", False, True, False), (" ", False, True, False), ("<-", "BACKSPACE", True, False), ("EXE", "ENTER", False, True)] + for i, (disp, val, spec, acc) in enumerate(r_chars): + keys.append((SCREEN_W - side_w, start_y + i*row_h, side_w, row_h, disp, val, spec, acc)) + nums = [["1","2","3"], ["4","5","6"], ["7","8","9"]] + for r in range(3): + for c in range(3): + keys.append((side_w + c*numpad_w, start_y + r*row_h, numpad_w, row_h, nums[r][c], False, False, False)) + y_bot = start_y + 3*row_h + unit_w = center_w // 6 + bot_row = [",", "#", "0", "=", "."] + widths = [1, 1, 2, 1, 1] + cur_x = side_w + for i, char in enumerate(bot_row): + w = widths[i] * unit_w + if i == len(bot_row) - 1: w = (side_w + center_w) - cur_x + keys.append((cur_x, y_bot, w, row_h, char, False, False, False)) + cur_x += w + return keys + + def get_numpad_rects(self): + keys = [] + start_y = self.y + total_h = KBD_H + row_h = total_h // 4 + action_w = 80 + digit_w = (SCREEN_W - action_w) // 3 + keys.append((SCREEN_W - action_w, start_y, action_w, row_h, "<-", "BACKSPACE", True, False)) + keys.append((SCREEN_W - action_w, start_y + row_h, action_w, row_h*3, "EXE", "ENTER", False, True)) + nums = [["1","2","3"], ["4","5","6"], ["7","8","9"]] + for r in range(3): + for c in range(3): + keys.append((c*digit_w, start_y + r*row_h, digit_w, row_h, nums[r][c], False, False, False)) + y_bot = start_y + 3*row_h + bot_keys = [] + if self.numpad_opts['neg']: bot_keys.append("-") + bot_keys.append("0") + if self.numpad_opts['float']: bot_keys.append(".") + if len(bot_keys) > 0: + bw = (SCREEN_W - action_w) // len(bot_keys) + cur_x = 0 + for i, k in enumerate(bot_keys): + w = bw + if i == len(bot_keys) - 1: w = (SCREEN_W - action_w) - cur_x + keys.append((cur_x, y_bot, w, row_h, k, False, False, False)) + cur_x += w + return keys + + def draw_keys_from_rects(self, rects): + for x, y, w, h, label, val, is_spec, is_acc in rects: + check_val = val if val is not False else label + is_pressed = (self.last_key == check_val) + self.draw_key(x, y, w, h, label, is_spec, is_pressed, is_acc) + + def update_keys_from_rects(self, rects, x, y, type): + for rx, ry, rw, rh, label, val, is_spec, is_acc in rects: + if rx <= x < rx + rw and ry <= y < ry + rh: + ret = val if val is not False else label + if type == KEYEV_TOUCH_DOWN: self.last_key = ret + return ret + return None + + def draw(self): + if not self.visible: return + t = self.theme + drect(0, self.y, SCREEN_W, SCREEN_H, t['kbd_bg']) + dhline(self.y, t['key_spec']) + if self.enable_tabs: + self.draw_tabs() + if self.current_tab == 2: + self.draw_keys_from_rects(self.get_math_rects()) + else: + self.draw_grid() + else: + self.draw_keys_from_rects(self.get_numpad_rects()) + + def update(self, ev): + # We only set visual feedback on TOUCH DOWN + if ev.type == KEYEV_TOUCH_DOWN: + self.last_key = None # Clear previous visual press + + if not self.visible: return None + + x, y = ev.x, ev.y + if y < self.y: return None + + # Only process taps on tabs, ignore drags + if self.enable_tabs and y < self.y + TAB_H: + if ev.type == KEYEV_TOUCH_DOWN: + tab_w = SCREEN_W // 3 + self.current_tab = min(2, max(0, x // tab_w)) + return None + + # Determine active update method + method = None + if not self.enable_tabs: method = lambda t: self.update_keys_from_rects(self.get_numpad_rects(), x, y, t) + elif self.current_tab == 2: method = lambda t: self.update_keys_from_rects(self.get_math_rects(), x, y, t) + else: method = lambda t: self.update_grid(x, y, t) + + return method(ev.type) + +# ============================================================================= +# LIST PICKER WIDGET +# ============================================================================= + +class ListPicker: + def __init__(self, options, prompt="Select:", theme="light", multi=False, touch_mode=KEYEV_TOUCH_DOWN): + self.options = options + self.prompt = prompt + self.theme_name = theme if isinstance(theme, str) else 'light' + self.theme: dict = get_theme(theme) + self.multi = multi + self.touch_mode = touch_mode + self.selected_indices = set() if multi else {0} + + self.header_h = PICK_HEADER_H + self.footer_h = PICK_FOOTER_H + self.view_h = SCREEN_H - self.header_h - self.footer_h + self.btn_w = 60 + self.last_action = None + + # Initialize ListView + # Convert options to dicts if needed, or ListView handles strings + # We need to inject 'checked' state for multi-select + self.lv_items = [] + for i, opt in enumerate(options): + it = {'text': str(opt), 'type': 'item', 'idx': i} + if multi and i in self.selected_indices: it['checked'] = True + self.lv_items.append(it) + + rect = (0, self.header_h, SCREEN_W, self.view_h) + self.list_view = ListView(rect, self.lv_items, row_h=PICK_ITEM_H, theme=self.theme_name) + if not multi and len(self.lv_items) > 0: + self.list_view.selected_index = 0 + + def draw_nav_btn(self, x, w, h, type, is_pressed): + t = self.theme + bg = t['hl'] if is_pressed else t['key_spec'] + y = SCREEN_H - h + + drect(x, y, x + w, SCREEN_H, bg) + drect_border(x, y, x + w, SCREEN_H, C_NONE, 1, t['key_spec']) + + cx, cy = x + w//2, y + h//2 + col = t['txt'] + + if type == "UP": + dpoly([cx, cy-5, cx-5, cy+5, cx+5, cy+5], col, C_NONE) + elif type == "DOWN": + dpoly([cx, cy+5, cx-5, cy-5, cx+5, cy-5], col, C_NONE) + + def draw_close_icon(self, x, y, sz, col): + dline(x, y, x+sz, y+sz, col) + dline(x, y+sz, x+sz, y, col) + dline(x+1, y, x+sz+1, y+sz, col) + dline(x+1, y+sz, x+sz+1, y, col) + + def draw(self): + t = self.theme + dclear(t['modal_bg']) + + # ListView (Draw first so Header/Footer cover any spill) + # Sync multi-select checkmarks + if self.multi: + for it in self.list_view.items: + it['checked'] = (it['idx'] in self.selected_indices) + + self.list_view.draw() + + # Header + drect(0, 0, SCREEN_W, self.header_h, t['accent']) + dtext_opt(SCREEN_W//2, self.header_h//2, t['txt_acc'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, self.prompt, -1) + self.draw_close_icon(15, 15, 10, t['txt_acc']) + + # Footer + fy = SCREEN_H - self.footer_h + drect(0, fy, SCREEN_W, SCREEN_H, t['key_spec']) + dhline(fy, t['key_spec']) + + self.draw_nav_btn(0, self.btn_w, self.footer_h, "UP", self.last_action == "PAGE_UP") + self.draw_nav_btn(SCREEN_W - self.btn_w, self.btn_w, self.footer_h, "DOWN", self.last_action == "PAGE_DOWN") + + ok_pressed = (self.last_action == "OK") + ok_bg = t['hl'] if ok_pressed else t['key_spec'] + ok_rect_x = self.btn_w + ok_rect_w = SCREEN_W - 2 * self.btn_w + drect(ok_rect_x, fy, ok_rect_x + ok_rect_w, SCREEN_H, ok_bg) + drect_border(ok_rect_x, fy, ok_rect_x + ok_rect_w, SCREEN_H, C_NONE, 1, t['key_spec']) + + label = "OK" if self.multi else "Select" + dtext_opt(SCREEN_W//2, fy + self.footer_h//2, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, label, -1) + + def run(self): + # Clear any lingering events + clearevents() + cleareventflips() + + touch_latched = False + + while True: + self.draw() + dupdate() + cleareventflips() + + ev = pollevent() + events = [] + while ev.type != KEYEV_NONE: + events.append(ev) + ev = pollevent() + + if keypressed(KEY_EXIT) or keypressed(KEY_DEL): + return None + + # --- Footer / Nav Keys --- + key_pg_up = keypressed(KEY_LEFT) + key_pg_dn = keypressed(KEY_RIGHT) + + # Pass events to ListView first + # We filter out touches that are in header/footer to prevent ListView from acting on them + lv_events = [] + footer_touch = None + header_touch = None + + for e in events: + if e.type in [KEYEV_TOUCH_DOWN, KEYEV_TOUCH_UP, KEYEV_TOUCH_DRAG]: + if e.y < self.header_h: + if e.type == KEYEV_TOUCH_DOWN: header_touch = e # Capture down for header + elif e.type == KEYEV_TOUCH_UP: header_touch = e + elif e.y >= SCREEN_H - self.footer_h: + if e.type == KEYEV_TOUCH_DOWN: footer_touch = e + elif e.type == KEYEV_TOUCH_UP: footer_touch = e + else: + lv_events.append(e) # Pass to list view + else: + lv_events.append(e) # Pass keys + + action = self.list_view.update(lv_events) + + if action: + type, idx, item = action + if type == 'click': + if self.multi: + real_idx = item['idx'] + if real_idx in self.selected_indices: self.selected_indices.remove(real_idx) + else: self.selected_indices.add(real_idx) + else: + return self.options[item['idx']] + + if key_pg_up: + self.list_view.scroll_y = max(0, self.list_view.scroll_y - self.list_view.h) + self.list_view.clamp_scroll() + if key_pg_dn: + self.list_view.scroll_y = min(self.list_view.max_scroll, self.list_view.scroll_y + self.list_view.h) + self.list_view.clamp_scroll() + + # Handle Footer/Header Touches + # Check Latch for DOWN mode + ignore_action = (self.touch_mode == KEYEV_TOUCH_DOWN and touch_latched) + + # Header + if header_touch and header_touch.type == self.touch_mode: + if not ignore_action and header_touch.x < 40: + clearevents() + cleareventflips() + return None + + # Footer + if footer_touch: + if footer_touch.type == self.touch_mode and not ignore_action: + if footer_touch.x < self.btn_w: self.last_action = "PAGE_UP" + elif footer_touch.x > SCREEN_W - self.btn_w: self.last_action = "PAGE_DOWN" + else: self.last_action = "OK" + + if self.touch_mode == KEYEV_TOUCH_DOWN: touch_latched = True + + # Reset latch on any UP + for e in events: + if e.type == KEYEV_TOUCH_UP: touch_latched = False + + if self.last_action: + if self.last_action == "PAGE_UP": + self.list_view.scroll_y = max(0, self.list_view.scroll_y - self.list_view.h) + self.list_view.clamp_scroll() + elif self.last_action == "PAGE_DOWN": + self.list_view.scroll_y = min(self.list_view.max_scroll, self.list_view.scroll_y + self.list_view.h) + self.list_view.clamp_scroll() + elif self.last_action == "OK": + clearevents() + cleareventflips() + if self.multi: return [self.options[i] for i in sorted(self.selected_indices)] + else: + if self.list_view.selected_index >= 0: + return self.options[self.list_view.items[self.list_view.selected_index]['idx']] + self.last_action = None + + time.sleep(0.01) + + # Enforce scroll bounds after any input + # if self.cursor_idx < self.page_start: self.page_start = self.cursor_idx + # elif self.cursor_idx >= self.page_start + self.items_per_page: self.page_start = self.cursor_idx - self.items_per_page + 1 + + # Small sleep to prevent busy loop eating battery/CPU + time.sleep(0.01) + + +# ============================================================================= +# PUBLIC FUNCTIONS +# ============================================================================= + + +def input(prompt="Input:", type="alpha_numeric", theme="light", layout="qwerty", touch_mode=KEYEV_TOUCH_DOWN): + # Re-implementing input function since it seems missing or was overwritten + t = get_theme(theme) + clearevents() + cleareventflips() + + # Kbd config + start_tab = 0 + enable_tabs = True + numpad_opts = None + + if type.startswith("numeric_"): + enable_tabs = False + allow_float = "int" not in type + allow_neg = "negative" in type + numpad_opts = {'float': allow_float, 'neg': allow_neg} + elif type == "math": start_tab = 2 + + kbd = Keyboard(default_tab=start_tab, enable_tabs=enable_tabs, numpad_opts=numpad_opts, theme=theme, layout=layout) + text = "" + running = True + touch_latched = False + + while running: + dclear(t['modal_bg']) + + # Header + drect(0, 0, SCREEN_W, PICK_HEADER_H, t['accent']) + dtext_opt(SCREEN_W//2, PICK_HEADER_H//2, t['txt_acc'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, prompt, -1) + + # Close Button + dline(15, 15, 25, 25, t['txt_acc']) + dline(25, 15, 15, 25, t['txt_acc']) + dline(16, 15, 26, 25, t['txt_acc']) + dline(26, 15, 16, 25, t['txt_acc']) + + # Input Box + box_y = 60; box_h = 40 + drect_border(10, box_y, SCREEN_W - 10, box_y + box_h, t['hl'], 2, t['txt']) + dtext(15, box_y + 10, t['txt'], text + "_") + + kbd.draw() + dupdate() + cleareventflips() + + ev = pollevent() + events = [] + while ev.type != KEYEV_NONE: + events.append(ev) + ev = pollevent() + + # Physical Keys + if keypressed(KEY_EXIT): return None + if keypressed(KEY_EXE): return text + if keypressed(KEY_DEL) and len(text) > 0: text = text[:-1] + + # Touch & Virtual Kbd + for e in events: + # Latch Reset + if e.type == KEYEV_TOUCH_UP: + touch_latched = False + kbd.last_key = None + + # Close Button (Respect touch_mode) + should_close = (e.type == touch_mode) + if touch_mode == KEYEV_TOUCH_DOWN and touch_latched: should_close = False + + if should_close and e.y < PICK_HEADER_H and e.x < 40: + clearevents() + cleareventflips() + return None + + # Keyboard (Touch Down Only) + if e.type == KEYEV_TOUCH_DOWN and not touch_latched: + # Don't type if touching close button area + if e.y < PICK_HEADER_H and e.x < 40: + pass + else: + touch_latched = True + res = kbd.update(e) + if res: + if res == "ENTER": + clearevents() + cleareventflips() + return text + elif res == "BACKSPACE": text = text[:-1] + elif len(res) == 1: text += res + + time.sleep(0.01) + return text + + +def pick(options, prompt="Select:", theme="light", multi=False, touch_mode=KEYEV_TOUCH_DOWN): + picker = ListPicker(options, prompt, theme, multi, touch_mode=touch_mode) + return picker.run() + +class ConfirmationDialog: + def __init__(self, title, body, ok_text="OK", cancel_text="Cancel", theme="light", touch_mode=KEYEV_TOUCH_DOWN): + self.title = title + self.body = body + self.ok_text = ok_text + self.cancel_text = cancel_text + self.theme = get_theme(theme) + self.touch_mode = touch_mode + self.header_h = 40 + self.footer_h = 45 + self.btn_w = SCREEN_W // 2 + + def draw_btn(self, x, y, w, h, text, pressed): + t = self.theme + bg = t['hl'] if pressed else t['key_spec'] + drect(x, y, x + w, y + h, bg) + drect_border(x, y, x + w, y + h, C_NONE, 1, t['key_spec']) + dtext_opt(x + w//2, y + h//2, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, text, -1) + + def draw(self, btn_ok_pressed, btn_cn_pressed): + t = self.theme + dclear(t['modal_bg']) + + # Header + drect(0, 0, SCREEN_W, self.header_h, t['accent']) + dtext_opt(SCREEN_W//2, self.header_h//2, t['txt_acc'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, self.title, -1) + + # Body + cy = (SCREEN_H - self.header_h - self.footer_h) // 2 + self.header_h + dtext_opt(SCREEN_W//2, cy, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, self.body, -1) + + # Footer + fy = SCREEN_H - self.footer_h + self.draw_btn(0, fy, self.btn_w, self.footer_h, self.cancel_text, btn_cn_pressed) + self.draw_btn(self.btn_w, fy, self.btn_w, self.footer_h, self.ok_text, btn_ok_pressed) + + def run(self): + clearevents() + cleareventflips() + + touch_latched = False + btn_ok_pressed = False + btn_cn_pressed = False + + while True: + self.draw(btn_ok_pressed, btn_cn_pressed) + dupdate() + cleareventflips() + + # Key Handling + if keypressed(KEY_EXIT) or keypressed(KEY_DEL): return False + if keypressed(KEY_EXE): return True + + # Event Handling + ev = pollevent() + events = [] + while ev.type != KEYEV_NONE: + events.append(ev) + ev = pollevent() + + touch = None + for e in events: + if e.type == KEYEV_TOUCH_DOWN and not touch_latched: + touch_latched = True + touch = e + elif e.type == KEYEV_TOUCH_UP: + touch_latched = False + touch = e + + if touch: + tx, ty = touch.x, touch.y + fy = SCREEN_H - self.footer_h + + is_cancel = (ty >= fy and tx < self.btn_w) + is_ok = (ty >= fy and tx >= self.btn_w) + + if touch.type == KEYEV_TOUCH_DOWN: + if is_cancel: btn_cn_pressed = True + elif is_ok: btn_ok_pressed = True + + if touch.type == self.touch_mode: + if is_cancel: + clearevents() + cleareventflips() + return False + elif is_ok: + clearevents() + cleareventflips() + return True + + if touch.type == KEYEV_TOUCH_UP: + btn_cn_pressed = False + btn_ok_pressed = False + + time.sleep(0.01) + +def ask(title, body, ok_text="OK", cancel_text="Cancel", theme="light", touch_mode=KEYEV_TOUCH_DOWN): + dlg = ConfirmationDialog(title, body, ok_text, cancel_text, theme, touch_mode=touch_mode) + return dlg.run() \ No newline at end of file diff --git a/main.cpp b/main.cpp index f5b054b..31c08d0 100644 --- a/main.cpp +++ b/main.cpp @@ -39,182 +39,145 @@ void updateRNG() { rng->Generate(50); } -// Kayboard Pseudo functions -void kbShift() { - keyboard->ToggleShift(); -} -void kbLeft() { - if (keyboard->xcursor > 0) { - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor, keyboard->keyColor, true); - keyboard->xcursor--; - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor); - } -} -void kbRight() { - if (keyboard->xcursor < 2 || (keyboard->xcursor < 21 && keyboard->ycursor != 3)) { - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor, keyboard->keyColor, true); - keyboard->xcursor++; - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor); - } -} -void kbUp() { - if (keyboard->ycursor > 0) { - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor, keyboard->keyColor, true); - if (keyboard->yfix > 4) { - // check if going from specials to normal - if (keyboard->ycursor == 3) { - if (keyboard->xcursor == 2) { - keyboard->xcursor = 15; - } else if (keyboard->xcursor == 1) { - keyboard->xcursor = 8; - } else { - keyboard->xcursor = 0; - } - } - keyboard->ycursor--; - keyboard->yfix = 0; - } else { - keyboard->yfix++; - } - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor); - } -} -void kbDown() { - if (keyboard->ycursor < 3) { - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor, keyboard->keyColor, true); - if (keyboard->yfix > 4) { - keyboard->ycursor++; - keyboard->yfix = 0; - // check if going to specials - if (keyboard->ycursor == 3) { - if (keyboard->xcursor < 8) { - keyboard->xcursor = 0; - } else if (keyboard->xcursor < 15) { - keyboard->xcursor = 1; - } else { - keyboard->xcursor = 2; - } - } - } else { - keyboard->yfix++; +void ProcessCommand() { + // store buffer + if (terminal->bufferInPos != 0) { + terminal->HideCursor(); + + // Move to next line history is handled in WriteBuffer('\n') ? + // No, WriteBuffer handles \n. + // If we press Enter, we should just WriteBuffer('\n')? + // But we need to execute the command. + + // Copy command + char callingArgs[BUF_SIZE]; + // bufferIn is not null terminated by default? + // WriteBuffer adds to bufferIn. + // Let's assume bufferIn has content up to bufferInPos. + for (int i = 0; i < terminal->bufferInPos; i++) { + callingArgs[i] = terminal->bufferIn[i]; } - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor); - } -} + callingArgs[terminal->bufferInPos] = '\0'; -void kbBackspace() { - terminal->RemoveLast(); -} + // Write newline to visual terminal + terminal->WriteBuffer('\n', false); -void kbEnter() { - // write to the char buffer - char activeKey = keyboard->activeKey[0]; - // checks for specials - if (keyboard->ycursor == 3) { - if (keyboard->xcursor == 2) { - // enter - // store buffer - if (terminal->bufferInPos != 0) { - terminal->HideCursor(); - terminal->bufferCY++; + // Parse args + int argc = 0; + // count number spaces in callingArgs + for (int i = 0; i < (int)strlen(callingArgs); i++) { + if (callingArgs[i] == ' ') { + argc++; + } + } + // check if final space is in callingArgs + if (strlen(callingArgs) > 0 && callingArgs[strlen(callingArgs) - 1] != ' ') { + argc++; + } - // copy bufferIn to callingArgs - char callingArgs[BUF_SIZE]; - for (int i = 0; i < terminal->bufferInPos; i++) { - callingArgs[i] = terminal->bufferIn[i]; - } - // add space to end - callingArgs[terminal->bufferInPos] = '\0'; - int argc = 0; - // count number spaces in callingArgs - for (int i = 0; i < (int)strlen(callingArgs); i++) { - if (callingArgs[i] == ' ') { - argc++; - } - } - // check if final space is in callingArgs - if (callingArgs[strlen(callingArgs) - 1] != ' ') { - argc++; - } - // create instance of argv - char** argv = new char*[argc]; - // split callingArgs into argv - int argvIndex = 0; - - char currentArg[ARGV_SIZE]; - int currentArgIndex = 0; - for (int i = 0; i < (int)strlen(callingArgs); i++) { - if (callingArgs[i] == ' ') { - argv[argvIndex] = new char[currentArgIndex + 1]; - for (int j = 0; j < currentArgIndex; j++) { - argv[argvIndex][j] = currentArg[j]; - } - argv[argvIndex][currentArgIndex] = '\0'; - argvIndex++; - currentArgIndex = 0; - } else { - currentArg[currentArgIndex] = callingArgs[i]; - currentArgIndex++; + if (argc > 0) { + // create instance of argv + char** argv = new char*[argc]; + // split callingArgs into argv + int argvIndex = 0; + + char currentArg[ARGV_SIZE]; + int currentArgIndex = 0; + for (int i = 0; i < (int)strlen(callingArgs); i++) { + if (callingArgs[i] == ' ') { + argv[argvIndex] = new char[currentArgIndex + 1]; + for (int j = 0; j < currentArgIndex; j++) { + argv[argvIndex][j] = currentArg[j]; } + argv[argvIndex][currentArgIndex] = '\0'; + argvIndex++; + currentArgIndex = 0; + } else { + currentArg[currentArgIndex] = callingArgs[i]; + currentArgIndex++; } - // set last argv to last arg + } + // set last argv to last arg + if (argvIndex < argc) { argv[argvIndex] = new char[currentArgIndex + 1]; for (int j = 0; j < currentArgIndex; j++) { argv[argvIndex][j] = currentArg[j]; } argv[argvIndex][currentArgIndex] = '\0'; - - // call cpshell_main - // testfunc_main(argc, argv); - psuedo_main(argc, argv); - // display host after psuedo_main - display_host(); } - // else ignore as it is a blank line - } else if (keyboard->xcursor == 1) { - // back - kbBackspace(); - } else { - // space - terminal->WriteBuffer(0x20); + // call cpshell_main + psuedo_main(argc, argv); + + // Clean up argv + for(int i=0; iWriteBuffer(activeKey); + // Empty line + terminal->WriteBuffer('\n', false); + display_host(); } } +// Kayboard Pseudo functions +void kbToggle() { + keyboard->Toggle(); + terminal->keyboardVisible = keyboard->visible; + terminal->UpdateLayout(); + + // Clear screen and redraw + fillScreen(0); + keyboard->Render(); + terminal->Render(); +} + +void kbUp() { + if (!keyboard->visible) { + terminal->Scroll(-1); // Scroll up + } +} + +void kbDown() { + if (!keyboard->visible) { + terminal->Scroll(1); // Scroll down + } +} + +void kbBackspace() { + terminal->RemoveLast(); +} + +void kbEnter() { + ProcessCommand(); +} + void HandleTouchForKeyboard() { - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor, keyboard->keyColor, true); + if (!keyboard->visible) return; + uint16_t xIn = event.data.touch_single.p1_x; uint16_t yIn = event.data.touch_single.p1_y; - Debug_Printf(1,34,true,0,"%d | %d",xIn,yIn); - // get x and y to be top left of keyboard - xIn -= keyboard->x; - yIn -= keyboard->y; - // read column / row - uint16_t xOffset = xIn / keyboard->xmargin; - uint16_t yOffset = yIn / keyboard->ymargin; - // to investigate later - if (yOffset > 3) yOffset = 3; - // check if on bottom line - if (yOffset > 2) { - if (xIn < keyboard->xmargin * 7) { - xOffset = 0; - } else if (xIn < keyboard->xmargin * 14) { - xOffset = 1; + uint32_t type = event.data.touch_single.direction; // TOUCH_DOWN etc. + + const char* key = keyboard->Update(xIn, yIn, type); + if (key) { + if (strcmp(key, "BACKSPACE") == 0) { + kbBackspace(); + } else if (strcmp(key, "ENTER") == 0) { + kbEnter(); + } else if (strcmp(key, "SPACE") == 0) { // Check space key string from VirtualKeyboard + terminal->WriteBuffer(' ', false); } else { - xOffset = 2; + terminal->WriteChars(key, true); } } - keyboard->xcursor = xOffset; - keyboard->ycursor = yOffset; - keyboard->Highlight(xOffset, yOffset); - kbEnter(); } void testTouch() { - Debug_Printf(10,32,true,0,"Working"); + // Debug_Printf(10,32,true,0,"Working"); } //The acutal main @@ -238,30 +201,45 @@ void main2() { VirtualKeyboard keyboardp; keyboard = &keyboardp; keyboard->SetFont(f_7x8); - keyboard->Render(); - keyboard->Highlight(); // 0,0 Terminal terminalp; terminal = &terminalp; terminal->SetFont(f_7x8); - // Debug_Printf(10,29,true,0,"Max X: %i", terminal->xmax); - // Debug_Printf(10,30,true,0,"W x H: %i x %i", terminal->termWidth, terminal->termHeight); + // Sync layout + terminal->keyboardVisible = keyboard->visible; + terminal->UpdateLayout(); + + // Initial Render + terminal->Render(); + keyboard->Render(); // Add event listeners addListener(KEY_CLEAR, endShell); // end the shell - cmd now addListener(KEY_BACKSPACE, kbBackspace); // remove last character - addListener(KEY_SHIFT, kbShift); // toggle Shift + // addListener(KEY_SHIFT, kbShift); // toggle Shift - handled by virtual keyboard button now? Or keep physical key too? + // keyboard->shift is public, can toggle it. + + addListener(KEYCODE_KEYBOARD, kbToggle); // Keyboard Listeners - addListener(KEY_LEFT, kbLeft); // left cursor - addListener(KEY_RIGHT, kbRight); // right cursor - addListener2(KEY_UP, kbUp); // up cursor - addListener2(KEY_DOWN, kbDown); // down cursor - addListener(KEY_EXE, kbEnter); // send the key - - addTouchListener(0, 0, 300, 100, testTouch); // touch listener - addTouchListener(keyboard->x, keyboard->y, keyboard->x + keyboard->xmargin * 22 - 1, keyboard->y + keyboard->ymargin * 4 - 1, HandleTouchForKeyboard); + // addListener(KEY_LEFT, kbLeft); + // addListener(KEY_RIGHT, kbRight); + addListener2(KEY_UP, kbUp); + addListener2(KEY_DOWN, kbDown); + addListener(KEY_EXE, kbEnter); + + // addTouchListener(0, 0, 300, 100, testTouch); // touch listener + // Touch listener covers whole screen for keyboard check? + // VirtualKeyboard::Update checks bounds. + // But `checkTouchEvents` checks bounds before calling callback. + // We should add a listener for the keyboard area. + // But keyboard area size changes or is fixed KBD_H? + // It's KBD_H at bottom. + addTouchListener(0, height - KBD_H, width, height, HandleTouchForKeyboard, TOUCH_DOWN); + addTouchListener(0, height - KBD_H, width, height, HandleTouchForKeyboard, TOUCH_UP); + // Also need TOUCH_HOLD_DRAG? cinput.py uses it. + // addTouchListener(0, height - KBD_H, width, height, HandleTouchForKeyboard, TOUCH_HOLD_DRAG); // Initialize the shell diff --git a/src/terminal.hpp b/src/terminal.hpp index 45fbcf5..2fab3e6 100644 --- a/src/terminal.hpp +++ b/src/terminal.hpp @@ -1,15 +1,10 @@ -/** - * @file terminal.hpp - * @author Sean McGinty (newfolderlocation@gmail.com) - * @brief A terminal class for CPShell, which handles all terminal input and output. - * @version 1.1 - * @date 2022-06-09 - */ - #pragma once #include "../calc.hpp" #include "../lib/functions/shapes.hpp" +#include +#include +#include // Terminal Class class Terminal { @@ -23,22 +18,54 @@ class Terminal { void SetFont(uint8_t* font); void SetColor(uint32_t newColor); void SetCursorColor(uint32_t newColor); + void Render(); // Redraw visible lines + void Scroll(int lines); + void ScrollToBottom(); + // buffer for command input char bufferIn[BUF_SIZE]; int8_t bufferInPos = 0; - // buffer cursor position - int16_t bufferCX = 0; - int16_t bufferCY = 0; - int16_t bufferOffsetX = 2; - int16_t bufferOffsetY = 2; + + // History Buffer + std::vector history; + + // View State + int scrollOffset = 0; // 0 means showing the top. Or maybe 0 means bottom? + // Let's say scrollOffset is the index of the first visible line. + // But we want auto-scroll to end. + // Maybe scrollOffset is "lines from bottom"? + // Let's use: scrollY = index of top visible line. + int scrollY = 0; + + // Dimensions int16_t xmargin = 8; // character width - int16_t ymargin = 9; // character height - uint8_t* font; + int16_t ymargin = 10; // character height (7x8 font + spacing) int16_t termWidth = width - xmargin * 2; - int16_t termHeight = 40; + // termHeight depends on keyboard visibility? + // User said: "prompt would always be visible". + // When keyboard is visible, terminal area is smaller. + // When hidden, full screen. + int16_t termHeight = height - 40; // Default full screen minus some margin? int16_t xmax = termWidth / xmargin; + int16_t ymax; // Calculated based on height + + uint8_t* font; uint32_t color = 0xFFFF; // white uint32_t cursorColor = 0xFFFF; // white + + bool keyboardVisible = true; + + Terminal() { + history.push_back(""); // Start with one empty line + UpdateLayout(); + } + + void UpdateLayout() { + int kbdH = keyboardVisible ? KBD_H : 0; + termHeight = height - kbdH; + ymax = termHeight / ymargin; + ScrollToBottom(); + } }; void Terminal::SetFont(uint8_t* font) { @@ -47,84 +74,148 @@ void Terminal::SetFont(uint8_t* font) { void Terminal::ClearBuffer() { this->bufferInPos = 0; - this->bufferCX = 0; + // No, we don't clear history here. Just the current input line buffer. + // But Wait, WriteBuffer writes to `bufferIn` AND history? + // `bufferIn` tracks the *current command being typed*. + // The history vector tracks *displayed lines*. + // When user types, we update the last line of history? + // Or we treat `bufferIn` separate? + // Standard terminal: history contains committed lines + current line being edited. + // Let's sync them. +} + +void Terminal::Scroll(int lines) { + scrollY += lines; + int maxScroll = std::max(0, (int)history.size() - ymax); + if (scrollY < 0) scrollY = 0; + if (scrollY > maxScroll) scrollY = maxScroll; + Render(); +} + +void Terminal::ScrollToBottom() { + int maxScroll = std::max(0, (int)history.size() - ymax); + scrollY = maxScroll; } void Terminal::WriteBuffer(char c, bool hideCursor) { - // hideCursor should only be false from WriteChars as writing to vram is slow if (hideCursor) this->HideCursor(); - // check if '\n' + + std::string& currentLine = history.back(); + if (c == '\n') { - this->ClearBuffer(); - this->bufferCY++; + history.push_back(""); + bufferInPos = 0; + ScrollToBottom(); + Render(); // Need to redraw to show scroll return; } - if (this->bufferInPos < (BUF_SIZE - 1)) { - // add the character to the buffer - this->bufferIn[this->bufferInPos] = c; - this->bufferInPos++; - - // buf of chars array that will have c in it - char buf[2] = {c, '\0'}; - - // draw the character - DRAW_FONT(this->font, buf, this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->color, this->termWidth); - - // check for overflow on screen - if (this->bufferCX < this->xmax) { - this->bufferCX++; - } else { - this->bufferCX = 0; - this->bufferCY++; + // Handle Backspace (passed as special char or handled in RemoveLast?) + // RemoveLast calls this? No. + + if (c >= 32 && c <= 126) { + currentLine += c; + bufferIn[bufferInPos++] = c; // Keep bufferIn for command processing + + // Wrap text + if (currentLine.length() >= (size_t)xmax) { + // Move excess to next line? + // Simple wrapping: just let it grow and Renderer handles it? + // Or split? + // Splitting is hard. Let's just break line. + std::string remainder = currentLine.substr(xmax); + currentLine = currentLine.substr(0, xmax); + history.push_back(remainder); } - } else { - Debug_Printf(1,1,true,0,"Buffer overflow!"); } + + ScrollToBottom(); + Render(); } -/* - * Usage - * char myMessage[] = "Hello World!\nSupports newlines!"; - * Terminal* terminal; - * terminal->WriteChars(myMessage); - */ void Terminal::WriteChars(const char *charArray, bool skipClear) { int len = strlen(charArray); for (int i = 0; i < len; i++) { this->WriteBuffer(charArray[i], false); } - // clear the buffer - if (!skipClear) this->ClearBuffer(); + if (!skipClear) { + bufferInPos = 0; // Reset input buffer, but history persists + // memset(bufferIn, 0, BUF_SIZE); + } } void Terminal::RemoveLast() { - if (this->bufferInPos == 0) return; - // remove the active cursor - this->HideCursor(); - // check for wrapping - if (this->bufferCX == 0) { - this->bufferCY--; - this->bufferCX = this->xmax; + std::string& currentLine = history.back(); + if (currentLine.length() > 0) { + currentLine.pop_back(); + if (bufferInPos > 0) bufferInPos--; + Render(); } else { - this->bufferCX--; + // Merge with previous line if empty? + if (history.size() > 1) { + history.pop_back(); + // bufferInPos needs to be restored? + // This is complex if we allow backspacing over newlines. + // For now, simple backspace on current line. + Render(); + } + } +} + +void Terminal::Render() { + // Clear visible area + int visibleH = keyboardVisible ? (height - KBD_H) : height; + drawFilledRectangle(0, 0, width, visibleH, 0x0000); // Black bg + + if (!font) return; + + int startLine = scrollY; + int endLine = std::min((int)history.size(), startLine + ymax + 1); + + for (int i = startLine; i < endLine; i++) { + int screenY = (i - startLine) * ymargin; + if (screenY >= visibleH) break; + + const std::string& line = history[i]; + // const char* text = line.c_str(); // DRAW_FONT takes char*? const char* fix applied. + // Need mutable for DRAW_FONT? No, fixed to const char*. + + DRAW_FONT(font, line.c_str(), 2, screenY, color, width); } - this->bufferInPos--; - // remove the last character from vram - drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, 0); // 0 is always color black / 0,0,0 + // Draw cursor if at bottom? + if (scrollY >= (int)history.size() - ymax) { + ShowCursor(); + } } // Cursor Show void Terminal::ShowCursor() { - // draw the cursor - drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, this->cursorColor); + // Find cursor position + // It's at the end of the last line + if (history.empty()) return; + + int lineIdx = history.size() - 1; + // Is it visible? + if (lineIdx < scrollY || lineIdx >= scrollY + ymax) return; + + int screenY = (lineIdx - scrollY) * ymargin; + int screenX = 2 + history.back().length() * xmargin; + + drawFilledRectangle(screenX, screenY, xmargin, ymargin, cursorColor); } // Cursor Hide void Terminal::HideCursor() { - // draw the cursor - drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, 0); // 0 is always color black / 0,0,0 + // Find cursor position and clear it + if (history.empty()) return; + int lineIdx = history.size() - 1; + if (lineIdx < scrollY || lineIdx >= scrollY + ymax) return; + + int screenY = (lineIdx - scrollY) * ymargin; + int screenX = 2 + history.back().length() * xmargin; + + drawFilledRectangle(screenX, screenY, xmargin, ymargin, 0x0000); } // Set Color diff --git a/src/virtual_keyboard.hpp b/src/virtual_keyboard.hpp index aa82035..3f0ec65 100644 --- a/src/virtual_keyboard.hpp +++ b/src/virtual_keyboard.hpp @@ -1,132 +1,372 @@ #pragma once -// KeyBoard Class -class VirtualKeyboard { - public: - void Render(); - void SetFont(uint8_t* font); - void Highlight(uint8_t x, uint8_t y, uint32_t highlightColor = color(255,255,0), bool hide = false); - void ToggleShift(); - bool caps = false; - int16_t x = 8; - int16_t y = 482; - int16_t xmargin = 10; - int16_t ymargin = 10; - uint8_t* font; - int8_t xcursor = 0; - int8_t ycursor = 0; - int8_t yfix = 0; - const char lower[27] = "abcdefghijklmnopqrstuvwxyz"; - const char characters[67] = "1234567890=!@#$%^&*_+-ABCDEFGHIJKLM\\|;:',./?NOPQRSTUVWXYZ[]{}()\"~`"; - const char specials[23] = "[SPACE] [BACK] [ENTER]"; - char activeKey[2] = ""; - int32_t keyColor = color(255, 255, 255); +#include "../calc.hpp" +#include "../lib/functions/shapes.hpp" +#include +#include +#include + +// Constants +#define KBD_H 260 +#define TAB_H 30 +#define SCREEN_W width +#define SCREEN_H height + +// Theme colors (Light theme from cinput.py) +#define C_WHITE 0xFFFF +#define C_BLACK 0x0000 +#define C_LIGHT 0xCE59 // Grey +#define C_DARK 0x0000 + +// Theme dictionary equivalent +struct Theme { + uint16_t modal_bg = C_WHITE; + uint16_t kbd_bg = C_WHITE; + uint16_t key_bg = C_WHITE; + uint16_t key_spec = 0xCE59; // Secondary (Light Grey) ~ RGB(200, 200, 200) + uint16_t key_out = C_BLACK; + uint16_t txt = 0x0000; + uint16_t txt_dim = 0x4208; // Dark Grey + uint16_t accent = 0x0010; // Dark Blue-ish + uint16_t txt_acc = C_WHITE; + uint16_t hl = 0xCE59; }; -void VirtualKeyboard::ToggleShift() { - // call render to update the keyboard - // will need something to only update the qwerty keys - - // set the color to black so that we cannot see the text - this->keyColor = color(0, 0, 0); - this->Render(); - this->caps = !this->caps; - // set back to white - this->keyColor = color(255, 255, 255); - this->Render(); - // render highlighted key - this->Highlight(this->xcursor, this->ycursor); - -} - -// x is between 0 and 21 (inclusive) -// y is between 0 and 2 (inclusive) -void VirtualKeyboard::Highlight(uint8_t xOffset = 0, uint8_t yOffset = 0, uint32_t highlightColor, bool hide) { - // KEYBOARD - // 1 2 3 4 5 6 7 8 9 0 = ! @ # $ % ^ & * _ + - - // A B C D E F G H I J K L M \ | ; : ' , . / ? - // N O P Q R S T U V W X Y Z [ ] { } ( ) " ~ ` - // [ S P A C E ] [ B A C K ] [ E N T E R ] - - // get active key - // check if within first 13 chars on y=1,2 - if (xOffset < 13 && !this->caps && yOffset != 0 && yOffset != 3) { - if (yOffset == 1) { - this->activeKey[0] = this->lower[xOffset]; - } else { - this->activeKey[0] = this->lower[xOffset + 13]; - } - } else { - this->activeKey[0] = this->characters[xOffset + (yOffset * 22)]; - } - - if (yOffset != 3) { - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * xOffset, this->y + this->ymargin * yOffset, highlightColor, 0); - - // draw rectangle around the character - - if (hide) { - highlightColor = color(0, 0, 0); - } - line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * yOffset - 2, highlightColor); - line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); - line(this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); - line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * (yOffset + 1) - 1, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); - } else { - // else specials - char specialChar[2] = ""; - - if (xOffset == 0) { - for (int i = 0; i < 7; i++) { - specialChar[0] = this->specials[i]; - DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i), this->y + this->ymargin * yOffset, highlightColor, 0); - } - } else if (xOffset == 1) { - for (int i = 8; i < 14; i++) { - specialChar[0] = this->specials[i]; - DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i - 1), this->y + this->ymargin * yOffset, highlightColor, 0); - } - } else if (xOffset == 2) { - for (int i = 15; i < 22; i++) { - specialChar[0] = this->specials[i]; - DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i - 2), this->y + this->ymargin * yOffset, highlightColor, 0); - } - } - } -} - -void VirtualKeyboard::SetFont(uint8_t* font) { - this->font = font; -} - -void VirtualKeyboard::Render() { - - // render normals - for (int i = 0; i < 66; i++) { - this->activeKey[0] = this->characters[i]; - // if in top row - if (i < 22) { - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * i, this->y, this->keyColor, 0); - // if in 2nd row - } else if (i < 44) { - // check for left 13 keys - if (i < 35 && !this->caps) { - this->activeKey[0] = this->lower[i - 22]; - } - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * (i - 22), this->y + this->ymargin, this->keyColor, 0); - // 3rd - } else { - // check for left 13 keys - if (i < 57 && !this->caps) { - this->activeKey[0] = this->lower[i - 44 + 13]; - } - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * (i - 44), this->y + this->ymargin * 2, this->keyColor, 0); - } - } - // render specials - for (int i = 0; i < 23; i++) { - this->activeKey[0] = this->specials[i]; - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * i, this->y + this->ymargin * 3, this->keyColor, 0); - } - -} +struct KeyRect { + int16_t x, y, w, h; + const char* label; + const char* val; // if NULL, use label + bool is_spec; + bool is_acc; +}; + +class VirtualKeyboard { +public: + bool visible = true; + int current_tab = 0; // 0: ABC, 1: Sym, 2: Math + bool shift = false; + const char* last_key = nullptr; + uint8_t* font; + Theme theme; + + // Layouts + const char* tabs[3] = {"ABC", "Sym", "Math"}; + + // QWERTY Layout + const char* layout_alpha[4] = { + "1234567890", + "qwertyuiop", + "asdfghjkl:", + "zxcvbnm,._" + }; + + const char* layout_sym[4] = { + "1234567890", + "@#$_&-+()/", + "=\\<*\"':;!?", + "{}[]^~`|<>" + }; + + VirtualKeyboard() { + // Initialize theme + } + + void SetFont(uint8_t* f) { + font = f; + } + + void draw_rect_border(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t border_col) { + drawRectangle(x, y, w, h, border_col); + } + + void draw_text_centered(int16_t cx, int16_t cy, const char* text, uint16_t color) { + if (!font) return; + // Estimate width (8px per char approx) + int len = strlen(text); + int w = len * 8; + int h = 10; + DRAW_FONT(font, text, cx - w/2, cy - h/2, color, 0); + } + + void draw_key(int16_t x, int16_t y, int16_t w, int16_t h, const char* label, bool is_special, bool is_pressed, bool is_accent) { + uint16_t bg; + if (is_pressed) bg = theme.hl; + else if (is_accent) bg = theme.accent; + else if (is_special) bg = theme.key_spec; + else bg = theme.key_bg; + + uint16_t txt_col = is_accent ? theme.txt_acc : theme.txt; + uint16_t border_col = theme.key_spec; + + drawFilledRectangle(x + 1, y + 1, w - 2, h - 2, bg); + draw_rect_border(x, y, w, h, border_col); + draw_text_centered(x + w/2, y + h/2, label, txt_col); + } + + void draw_tabs() { + int16_t y_pos = SCREEN_H - KBD_H; + int16_t tab_w = SCREEN_W / 3; + uint16_t border_col = theme.key_spec; + + for (int i = 0; i < 3; i++) { + int16_t tx = i * tab_w; + bool is_active = (i == current_tab); + uint16_t bg = is_active ? theme.kbd_bg : theme.key_spec; + + drawFilledRectangle(tx, y_pos, tab_w, TAB_H, bg); + draw_rect_border(tx, y_pos, tab_w, TAB_H, border_col); + + // "Active" look (cover bottom border) + if (is_active) { + // drawFilledRectangle(tx + 1, y_pos + TAB_H - 1, tab_w - 2, 2, theme.kbd_bg); + } + + const char* name = tabs[i]; + // If tab 0 and not qwerty (not implemented yet), change name? + draw_text_centered(tx + tab_w/2, y_pos + TAB_H/2, name, theme.txt); + } + } + + void draw_grid() { + int16_t grid_y = (SCREEN_H - KBD_H) + TAB_H; + int16_t row_h = 45; + + const char** layout = (current_tab == 1) ? layout_sym : layout_alpha; + + char labelBuf[2] = {0, 0}; + + for (int r = 0; r < 4; r++) { + const char* row = layout[r]; + int count = strlen(row); + int16_t kw = SCREEN_W / count; + + for (int c = 0; c < count; c++) { + int16_t kx = c * kw; + int16_t ky = grid_y + r * row_h; + + char char_code = row[c]; + if (current_tab == 0 && shift) { + if (char_code >= 'a' && char_code <= 'z') char_code -= 32; + } + + labelBuf[0] = char_code; + + // Check if pressed + bool is_pressed = false; + // We need to know if this specific key is pressed. + // For simplicity, we can rely on immediate redraw or state. + // Assuming last_key stores the char string. + if (last_key && last_key[0] == char_code && last_key[1] == 0) is_pressed = true; + + draw_key(kx, ky, kw, row_h, labelBuf, false, is_pressed, false); + } + } + + // Bottom Control Row + int16_t bot_y = grid_y + 4 * row_h; + int16_t bot_h = row_h; + + bool caps_pressed = shift; + bool back_pressed = (last_key && strcmp(last_key, "BACKSPACE") == 0); + bool space_pressed = (last_key && strcmp(last_key, " ") == 0); + bool enter_pressed = (last_key && strcmp(last_key, "ENTER") == 0); + + draw_key(0, bot_y, 50, bot_h, "CAPS", true, caps_pressed, false); + draw_key(50, bot_y, 50, bot_h, "<-", true, back_pressed, false); + draw_key(100, bot_y, 160, bot_h, "Space", false, space_pressed, false); + draw_key(260, bot_y, 60, bot_h, "EXE", false, enter_pressed, true); + } + + // Helper for Math layout (simplified from cinput.py) + void draw_math() { + int16_t start_y = (SCREEN_H - KBD_H) + TAB_H; + int16_t total_h = KBD_H - TAB_H; + int16_t row_h = total_h / 4; + int16_t side_w = 50; + int16_t center_w = SCREEN_W - (side_w * 2); + int16_t numpad_w = center_w / 3; + + // Left Col: + - * / + const char* ops[] = {"+", "-", "*", "/"}; + for (int i=0; i<4; i++) { + bool pressed = (last_key && strcmp(last_key, ops[i]) == 0); + draw_key(0, start_y + i*row_h, side_w, row_h, ops[i], false, pressed, false); + } + + // Right Col + // %, " ", <-, EXE + // const char* r_labels[] = {"%", " ", "<-", "EXE"}; + // const char* r_vals[] = {"%", " ", "BACKSPACE", "ENTER"}; + // is_spec, is_acc + + // % + draw_key(SCREEN_W - side_w, start_y, side_w, row_h, "%", false, (last_key && strcmp(last_key, "%") == 0), false); + // Space + draw_key(SCREEN_W - side_w, start_y + row_h, side_w, row_h, " ", false, (last_key && strcmp(last_key, " ") == 0), false); + // Backspace + draw_key(SCREEN_W - side_w, start_y + 2*row_h, side_w, row_h, "<-", true, (last_key && strcmp(last_key, "BACKSPACE") == 0), false); + // Enter + draw_key(SCREEN_W - side_w, start_y + 3*row_h, side_w, row_h, "EXE", false, (last_key && strcmp(last_key, "ENTER") == 0), true); + + // Numpad 1-9 + const char* nums[3][3] = {{"1","2","3"}, {"4","5","6"}, {"7","8","9"}}; + for (int r=0; r<3; r++) { + for (int c=0; c<3; c++) { + bool pressed = (last_key && strcmp(last_key, nums[r][c]) == 0); + draw_key(side_w + c*numpad_w, start_y + r*row_h, numpad_w, row_h, nums[r][c], false, pressed, false); + } + } + + // Bottom Row: , # 0 = . + int16_t y_bot = start_y + 3*row_h; + int16_t unit_w = center_w / 6; + const char* bot_row[] = {",", "#", "0", "=", "."}; + int widths[] = {1, 1, 2, 1, 1}; + int16_t cur_x = side_w; + + for (int i=0; i<5; i++) { + int16_t w = widths[i] * unit_w; + if (i == 4) w = (side_w + center_w) - cur_x; + bool pressed = (last_key && strcmp(last_key, bot_row[i]) == 0); + draw_key(cur_x, y_bot, w, row_h, bot_row[i], false, pressed, false); + cur_x += w; + } + } + + void Render() { + if (!visible) return; + + int16_t y_pos = SCREEN_H - KBD_H; + // Background + drawFilledRectangle(0, y_pos, SCREEN_W, KBD_H, theme.kbd_bg); + line(0, y_pos, SCREEN_W, y_pos, theme.key_spec); // Top border line + + draw_tabs(); + + if (current_tab == 2) { + draw_math(); + } else { + draw_grid(); + } + } + + // Returns the key string if pressed, or NULL + const char* Update(uint16_t touch_x, uint16_t touch_y, uint32_t type) { + if (!visible) return nullptr; + int16_t kbd_y = SCREEN_H - KBD_H; + if (touch_y < kbd_y) return nullptr; + + // Reset last key on touch down + if (type == TOUCH_DOWN) last_key = nullptr; + + // Tabs + if (touch_y < kbd_y + TAB_H) { + if (type == TOUCH_DOWN) { + int16_t tab_w = SCREEN_W / 3; + current_tab = touch_x / tab_w; + if (current_tab > 2) current_tab = 2; + Render(); // Redraw immediately + } + return nullptr; + } + + const char* ret = nullptr; + + if (current_tab == 2) { + // Math Layout Hit Test + int16_t start_y = kbd_y + TAB_H; + int16_t total_h = KBD_H - TAB_H; + int16_t row_h = total_h / 4; + int16_t side_w = 50; + int16_t center_w = SCREEN_W - (side_w * 2); + int16_t numpad_w = center_w / 3; + + int row = (touch_y - start_y) / row_h; + if (row < 0 || row > 3) return nullptr; + + // Left Col + if (touch_x < side_w) { + const char* ops[] = {"+", "-", "*", "/"}; + ret = ops[row]; + } + // Right Col + else if (touch_x >= SCREEN_W - side_w) { + const char* vals[] = {"%", " ", "BACKSPACE", "ENTER"}; + ret = vals[row]; + } + // Center + else { + if (row < 3) { + int col = (touch_x - side_w) / numpad_w; + const char* nums[3][3] = {{"1","2","3"}, {"4","5","6"}, {"7","8","9"}}; + if (col >= 0 && col < 3) ret = nums[row][col]; + } else { + // Bottom row + int16_t rel_x = touch_x - side_w; + int16_t unit_w = center_w / 6; + // , # 0 = . widths: 1 1 2 1 1 + if (rel_x < unit_w) ret = ","; + else if (rel_x < unit_w*2) ret = "#"; + else if (rel_x < unit_w*4) ret = "0"; + else if (rel_x < unit_w*5) ret = "="; + else ret = "."; + } + } + + } else { + // Grid Layout Hit Test + int16_t grid_y = kbd_y + TAB_H; + int16_t row_h = 45; + int row_idx = (touch_y - grid_y) / row_h; + + if (row_idx >= 0 && row_idx < 4) { + const char** layout = (current_tab == 1) ? layout_sym : layout_alpha; + const char* row_str = layout[row_idx]; + int count = strlen(row_str); + int16_t kw = SCREEN_W / count; + int col_idx = touch_x / kw; + if (col_idx >= count) col_idx = count - 1; + + static char charStr[2] = {0, 0}; + char char_code = row_str[col_idx]; + if (current_tab == 0 && shift && char_code >= 'a' && char_code <= 'z') { + char_code -= 32; + } + charStr[0] = char_code; + ret = charStr; + + } else if (row_idx == 4) { + if (touch_x < 50) { + if (type == TOUCH_DOWN) { + shift = !shift; + Render(); + } + return nullptr; // Shift toggle doesn't return key char + } else if (touch_x < 100) { + ret = "BACKSPACE"; + } else if (touch_x < 260) { + ret = " "; + } else { + ret = "ENTER"; + } + } + } + + if (type == TOUCH_DOWN) { + last_key = ret; + Render(); // Visual feedback + } else if (type == TOUCH_UP) { + last_key = nullptr; + Render(); // Clear feedback + } + + return ret; + } + + // Toggle Visibility + void Toggle() { + visible = !visible; + // Need to clear screen area if hidden? + // The main loop usually redraws everything, or we might need to trigger a full refresh. + } +}; From 34e657e41a77d18d556bb16ff8b862f70c6b3cbc Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Thu, 26 Feb 2026 01:32:37 +0000 Subject: [PATCH 20/23] Fix new keyboard and terminal implementation with V3 SDK Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- cinput.py | 1109 ------------------------------ lib/core/exceptions.hpp | 6 +- lib/core/touch_event_handler.hpp | 57 +- main.cpp | 47 +- src/commands/clear.cpp | 5 +- src/commands/help.cpp | 5 - src/commands/ls.cpp | 9 +- src/cpshell.cpp | 5 - src/internal.hpp | 6 + src/terminal.hpp | 217 ++---- src/virtual_keyboard.hpp | 496 ++++--------- 11 files changed, 265 insertions(+), 1697 deletions(-) delete mode 100644 cinput.py diff --git a/cinput.py b/cinput.py deleted file mode 100644 index 09a550d..0000000 --- a/cinput.py +++ /dev/null @@ -1,1109 +0,0 @@ -from gint import * -import time -import math - -# ============================================================================= -# CONSTANTS & CONFIG -# ============================================================================= - -SCREEN_W = 320 -SCREEN_H = 528 - -# Layout Dimensions -KBD_H = 260 -TAB_H = 30 -PICK_HEADER_H = 40 -PICK_FOOTER_H = 45 -PICK_ITEM_H = 50 - -# ============================================================================= -# THEMES -# ============================================================================= - -def safe_rgb(r, g, b): - return C_RGB(r, g, b) - -THEMES = { - 'light': { - 'modal_bg': C_WHITE, - 'kbd_bg': C_WHITE, - 'key_bg': C_WHITE, - 'key_spec': safe_rgb(28, 29, 28), # Secondary (Light Grey) - 'key_out': C_DARK, # Dark (Unused for key borders now) - 'txt': safe_rgb(4, 4, 4), - 'txt_dim': safe_rgb(8, 8, 8), - 'accent': safe_rgb(1, 11, 26), - 'txt_acc': C_WHITE, - 'hl': safe_rgb(28, 29, 28), # Highlight matches secondary - 'check': C_WHITE - }, - 'dark': { - 'modal_bg': safe_rgb(7, 7, 8), - 'kbd_bg': safe_rgb(7, 7, 8), - 'key_bg': safe_rgb(7, 7, 8), - 'key_spec': safe_rgb(11, 11, 12), # Secondary (Dark Grey) - 'key_out': safe_rgb(12, 19, 31), - 'txt': C_WHITE, - 'txt_dim': safe_rgb(8, 8, 8), - 'accent': safe_rgb(12, 19, 31), - 'txt_acc': C_WHITE, - 'hl': safe_rgb(11, 11, 12), - 'check': C_WHITE - }, - 'grey': { - 'modal_bg': C_LIGHT, - 'kbd_bg': C_LIGHT, - 'key_bg': C_WHITE, - 'key_spec': 0xCE59, - 'key_out': C_BLACK, - 'txt': C_BLACK, - 'txt_dim': safe_rgb(8, 8, 8), - 'accent': C_BLACK, - 'txt_acc': C_WHITE, - 'hl': 0xCE59, - 'check': C_WHITE - } -} - -def get_theme(name_or_dict) -> dict: - if isinstance(name_or_dict, dict): return name_or_dict - return THEMES.get(name_or_dict, THEMES['light']) - -# ============================================================================= -# REUSABLE LIST VIEW -# ============================================================================= - -class ListView: - def __init__(self, rect, items, row_h=40, theme='light', headers_h=None): - """ - rect: (x, y, w, h) tuple - items: List of dicts {'text': str, 'type': 'item'|'section', 'height': int, ...} OR list of strings - """ - self.x, self.y, self.w, self.h = rect - # Normalize items - self.items = [] - for it in items: - if isinstance(it, dict): self.items.append(it) - else: self.items.append({'text': str(it), 'type': 'item'}) - - self.base_row_h = row_h - self.headers_h = headers_h if headers_h else row_h - self.theme = get_theme(theme) - - # Layout State - self.total_h = 0 - self.recalc_layout() - - # Selection & Scroll - self.selected_index = -1 - self.scroll_y = 0 - self.max_scroll = max(0, self.total_h - self.h) - - # Select first selectable item - self.select_next(0, 1) - - # Touch State - self.is_dragging = False - self.touch_start_y = 0 - self.touch_start_idx = 0 - self.touch_start_time = 0 - self.touch_acc_y = 0.0 # Accumulator for drag distance - self.touch_initial_item_idx = -1 - self.long_press_triggered = False - - # Configuration - self.drag_threshold = 10 - self.long_press_delay = 0.5 # seconds - self.snap_sensitivity = 1.0 # 1.0 = 1 item height drag moves 1 item - - def recalc_layout(self): - """Calculate positions and heights of all items.""" - total_h = 0 - for it in self.items: - h = it.get('height', self.headers_h if it.get('type') == 'section' else self.base_row_h) - it['_h'] = h - it['_y'] = total_h - total_h += h - self.total_h = total_h - self.max_scroll = max(0, self.total_h - self.h) - - def select_next(self, start_idx, step): - """Find next selectable item""" - idx = start_idx - count = len(self.items) - if count == 0: - self.selected_index = -1 - return - - # Safety loop limit - for _ in range(count): - if 0 <= idx < count: - if self.items[idx].get('type', 'item') != 'section': - self.selected_index = idx - self.ensure_visible() - return - idx += step - # Clamp logic - if idx < 0 or idx >= count: break - - def ensure_visible(self): - """Scroll to keep selected_index in view""" - if self.selected_index < 0 or self.selected_index >= len(self.items): return - - it = self.items[self.selected_index] - item_top = it['_y'] - item_bot = item_top + it['_h'] - - view_top = self.scroll_y - view_bot = self.scroll_y + self.h - - if item_top < view_top: - self.scroll_y = item_top - elif item_bot > view_bot: - self.scroll_y = item_bot - self.h - - self.clamp_scroll() - - def clamp_scroll(self): - self.max_scroll = max(0, self.total_h - self.h) - self.scroll_y = max(0, min(self.max_scroll, self.scroll_y)) - - def update(self, events): - """ - Process events. - """ - now = time.monotonic() - - # Touch Handling - touch_up = None - touch_down = None - current_touch = None - - # Pre-process touch events - for e in events: - if e.type == KEYEV_TOUCH_DOWN: - touch_down = e - current_touch = e - elif e.type == KEYEV_TOUCH_UP: - touch_up = e - elif e.type == KEYEV_TOUCH_DRAG: # Some envs might emit this - current_touch = e - - # Also look for simulated drag via repeatedly polled DOWN events if native drag not available - if not current_touch and touch_down: - current_touch = touch_down - - # 1. Start Touch - if touch_down and not self.is_dragging and self.touch_start_time == 0: - if self.x <= touch_down.x < self.x + self.w and self.y <= touch_down.y < self.y + self.h: - self.touch_start_y = touch_down.y - self.touch_start_time = now - - # Determine which item was touched initially - local_y = touch_down.y - self.y + self.scroll_y - self.touch_initial_item_idx = self.get_index_at(local_y) - - # Anchor drag to the item under finger if valid, else selection - if self.touch_initial_item_idx != -1: - self.touch_start_idx = self.touch_initial_item_idx - # Immediate visual feedback - if 0 <= self.touch_initial_item_idx < len(self.items): - if self.items[self.touch_initial_item_idx].get('type') != 'section': - self.selected_index = self.touch_initial_item_idx - self.ensure_visible() - else: - self.touch_start_idx = self.selected_index - - self.is_dragging = False - self.long_press_triggered = False - self.touch_acc_y = 0.0 - - # Long Press Detection (Time-based) - if self.touch_start_time != 0 and not self.is_dragging and not self.long_press_triggered: - if now - self.touch_start_time > 0.8: # 800ms threshold - self.long_press_triggered = True - # Return 'long' action immediately - idx = self.selected_index - if 0 <= idx < len(self.items): - return ('long', idx, self.items[idx]) - - # ... Or use the last known touch if we are already in a touch sequence - # We need a way to know "current finger position" even if no new event came this frame, - # but typically we get continuous events. If we don't, we can't drag. - - # 2. Touch Move / Drag - if self.touch_start_time != 0: - # Find the most recent position event - last_pos = current_touch if current_touch else touch_down - - if last_pos: - dy = last_pos.y - self.touch_start_y - - # Check for drag threshold - # User req: "If the drag is more than one item height, then apply scrolling" - if not self.is_dragging: - if abs(dy) > self.base_row_h: # Threshold is one item height - self.is_dragging = True - self.long_press_triggered = True - - if self.is_dragging: - # Scroll Logic (Snap) - # Dragging UP (negative dy) -> Move down in list - - # More advanced "Pixel-based" mapping to index - # We map the total pixel offset (start_y + scroll_y_offset - dy) to an index - # This allows variable row heights natural feeling - - # 1. Calculate theoretical pixel position - # We want to move the "originally selected item" by -dy pixels relative to the view center? - # Or simpler: The "selection cursor" moves by -dy pixels. - - # Current selection top in pixels - if 0 <= self.touch_start_idx < len(self.items): - start_item_y = self.items[self.touch_start_idx]['_y'] - - # Target Y for the selection top - target_y = start_item_y - dy - - # Find index at target_y - # Wescan items to find which one contains target_y - # But to fix the "clamping" issue with sections: - # If the index found is a section, or we are crossing a section... - - found_idx = -1 - for i, it in enumerate(self.items): - if it['_y'] <= target_y < it['_y'] + it['_h']: - found_idx = i - break - - if found_idx == -1: - if target_y < 0: found_idx = 0 - else: found_idx = len(self.items) - 1 - - # Apply Clamping Logic: - # If found_idx is a SECTION, we check where in the section we are. - # User wants: "consider the selection to enter the next section first item - # only when the scroll amount is above the title height." - - # Implementation interpretation: - # If we land on a section header (found_idx is Section), - # we should STAY on the *previous* item unless we are past the section header? - # Or STAY on the section header (But sections aren't selectable...)? - # "Selectable" logic usually skips sections. - - # Refined logic: Drag maps to a pixel Y. That pixel Y lands on an item. - # If it lands on a Section: - # If we came from above (moving down), we need to drag PAST the section entirely to select the item below it. - # If we came from below (moving up), we need to drag ABOVE the section entirely to select item above. - - # Effectively, the "active area" for the section header does not trigger selection change until passed? - # Let's try: if match is section, map to previous valid item if (target_y < section_center) else next valid item? - # User said: "only when the scroll amount is above the title height" - - if self.items[found_idx].get('type') == 'section': - # We are hovering over a section - # Check overlap - sec_y = self.items[found_idx]['_y'] - sec_h = self.items[found_idx]['_h'] - overlap = target_y - sec_y - - # If we are dragging DOWN (dy < 0, target_y increasing), we are moving to next section - if dy < 0: - # We are moving down the list. - # Only jump to next item if we are really deep into the section or past it? - # Actually, if we are conceptually "dragging the paper", moving finger UP (negative dy) - # means we want to see lower items. - pass - - # Logic: - # If we land on section, look at adjacent items. - # If we are closer to the top of section, pick item above. - # If we are closer to bottom, pick item below. - # "Glitch: separator ... flicker between the two voices" - - # Let's enforce a "dead zone" corresponding to the section height. - # If target_y falls within a section's vertical space, keep the selection - # on the *previously selected item* (from start of drag or previous frame) - # UNLESS we have crossed it? - - # Simpler: Don't select the section. Select the adjacent valid item based on direction, - # BUT require the "target_y" to be fully into the valid item's space. - - # If target_y is in section -> Don't change selection from what it would be if we were at the edge? - - if self.selected_index < found_idx: - # We were above, moving down. - # Stay above until we are purely past the section? - # effectively, for the section to be skipped, target_y must be >= sec_y + sec_h - # But found_idx says we are < sec_y + sec_h. - # So stay at found_idx - 1 (or whatever was valid above) - final_idx = found_idx - 1 - else: - # We were below, moving up. - # Stay below until target_y < sec_y - final_idx = found_idx + 1 - - # Boundary checks - final_idx = max(0, min(len(self.items)-1, final_idx)) - - else: - final_idx = found_idx - - if 0 <= final_idx < len(self.items) and self.items[final_idx].get('type') != 'section': - self.selected_index = final_idx - self.ensure_visible() - - # 3. Touch Release - if touch_up: - if self.touch_start_time != 0: - # Valid release sequence - ret = None - - if not self.is_dragging and not self.long_press_triggered: - # Click Candidate - local_y = touch_up.y - self.y + self.scroll_y - release_idx = self.get_index_at(local_y) - - # Logic: If release is on same item as start, it's a click. - if release_idx == self.touch_initial_item_idx and release_idx >= 0: - if self.items[release_idx].get('type') != 'section': - # Ensure selection updates to the clicked item - self.selected_index = release_idx - self.ensure_visible() - ret = ('click', release_idx, self.items[release_idx]) - - # Reset - self.touch_start_time = 0 - self.is_dragging = False - return ret - - # If touch_up happened but we weren't tracking, ignore it (stray event) - - # 4. Long Press - if self.touch_start_time != 0 and not self.is_dragging and not self.long_press_triggered: - if now - self.touch_start_time > self.long_press_delay: - self.long_press_triggered = True - # Trigger on current selection or initial? - # Usually initial item - if 0 <= self.touch_initial_item_idx < len(self.items): - it = self.items[self.touch_initial_item_idx] - if it.get('type') != 'section': - self.selected_index = self.touch_initial_item_idx - return ('long', self.touch_initial_item_idx, it) - - # 5. Keys - for e in events: - if e.type == KEYEV_DOWN or (e.type == KEYEV_HOLD and e.key in [KEY_UP, KEY_DOWN]): - if e.key == KEY_UP: - self.select_next(self.selected_index - 1, -1) - elif e.key == KEY_DOWN: - self.select_next(self.selected_index + 1, 1) - elif e.key == KEY_EXE: - if self.selected_index >= 0: - return ('click', self.selected_index, self.items[self.selected_index]) - - return None - - def get_index_at(self, y): - # Linear scan is sufficient for likely list sizes (<500) - # Binary search could be used since _y is sorted - for i, it in enumerate(self.items): - if it['_y'] <= y < it['_y'] + it['_h']: - return i - return -1 - - def draw_item(self, x, y, item, is_selected): - # Can be overridden by subclass or assigned - t = self.theme - h = item['_h'] - - if item.get('type') == 'section': - drect(x, y, x + self.w, y + h, t['key_spec']) - drect_border(x, y, x + self.w, y + h, C_NONE, 1, t['key_spec']) - dtext_opt(x + 10, y + h//2, t['txt_dim'], C_NONE, DTEXT_LEFT, DTEXT_MIDDLE, str(item['text']), -1) - else: - bg = t['hl'] if is_selected else t['modal_bg'] - drect(x, y, x + self.w, y + h, bg) - drect_border(x, y, x + self.w, y + h, C_NONE, 1, t['key_spec']) - - x_off = 20 - if item.get('checked'): - self.draw_check(x + 10, y + (h-20)//2, t) - x_off = 40 - - dtext_opt(x + x_off, y + h//2, t['txt'], C_NONE, DTEXT_LEFT, DTEXT_MIDDLE, str(item['text']), -1) - - # Draw Arrow if requested - if item.get('arrow'): - ar_x = x + self.w - 15 - ar_y = y + h//2 - c = t['txt_dim'] - dline(ar_x - 4, ar_y - 4, ar_x, ar_y, c) - dline(ar_x - 4, ar_y + 4, ar_x, ar_y, c) - - def draw(self): - t = self.theme - drect(self.x, self.y, self.x + self.w, self.y + self.h, t['modal_bg']) - - # Lazy Rendering: Find start index - start_y = self.scroll_y - end_y = self.scroll_y + self.h - - # Simple scan to find start (optimize later if needed) - start_idx = 0 - for i, it in enumerate(self.items): - if it['_y'] + it['_h'] > start_y: - start_idx = i - break - - # Draw visible items - for i in range(start_idx, len(self.items)): - it = self.items[i] - if it['_y'] >= end_y: break # Stop if below view - - item_y = self.y + it['_y'] - self.scroll_y - self.draw_item(self.x, item_y, it, (i == self.selected_index)) - - # Scrollbar - if self.max_scroll > 0: - sb_w = 4 - ratio = self.h / (self.total_h if self.total_h > 0 else 1) - thumb_h = max(20, int(self.h * ratio)) - - scroll_ratio = self.scroll_y / self.max_scroll - thumb_y = self.y + int(scroll_ratio * (self.h - thumb_h)) - - sb_x = self.x + self.w - sb_w - 2 - drect(sb_x, thumb_y, sb_x + sb_w, thumb_y + thumb_h, t['accent']) - - def draw_check(self, x, y, t): - drect(x, y, x+20, y+20, t['accent']) - c = t['check'] - dline(x+4, y+10, x+8, y+14, c) - dline(x+8, y+14, x+15, y+5, c) - -# ============================================================================= -# KEYBOARD WIDGET -# ============================================================================= - -LAYOUTS = { - 'qwerty': [list("1234567890"), list("qwertyuiop"), list("asdfghjkl:"), list("zxcvbnm,._")], - 'azerty': [list("1234567890"), list("azertyuiop"), list("qsdfghjklm"), list("wxcvbn,._:")], - 'qwertz': [list("1234567890"), list("qwertzuiop"), list("asdfghjkl:"), list("yxcvbnm,._")], - 'abc': [list("1234567890"), list("abcdefghij"), list("klmnopqrst"), list("uvwxyz,._:")] -} - -LAYOUT_SYM = [ - list("1234567890"), - list("@#$_&-+()/"), - list("=\\<*\"':;!?"), - list("{}[]^~`|<>") -] - -class Keyboard: - def __init__(self, default_tab=0, enable_tabs=True, numpad_opts=None, theme='light', layout='qwerty'): - self.y = SCREEN_H - KBD_H - self.visible = True - self.current_tab = default_tab - self.enable_tabs = enable_tabs - self.shift = False - self.tabs = ["ABC", "Sym", "Math"] - self.last_key = None - self.numpad_opts = numpad_opts if numpad_opts else {'float': True, 'neg': True} - - self.theme: dict = get_theme(theme) - self.layout_alpha = LAYOUTS.get(layout, LAYOUTS['qwerty']) - if layout != 'qwerty': - self.tabs[0] = layout.upper() if len(layout) <= 3 else "Txt" - - def draw_key(self, x, y, w, h, label, is_special=False, is_pressed=False, is_accent=False): - t = self.theme - # Background - if is_pressed: bg = t['hl'] - elif is_accent: bg = t['accent'] - elif is_special: bg = t['key_spec'] - else: bg = t['key_bg'] - - txt_col = t['txt_acc'] if is_accent else t['txt'] - # Soft Border using secondary color - border_col = t['key_spec'] - - drect(x + 1, y + 1, x + w - 1, y + h - 1, bg) - drect_border(x, y, x + w, y + h, C_NONE, 1, border_col) - dtext_opt(x + w//2, y + h//2, txt_col, C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, label, -1) - - def draw_tabs(self): - t = self.theme - tab_w = SCREEN_W // 3 - border_col = t['key_spec'] - for i, tab_name in enumerate(self.tabs): - tx = i * tab_w - is_active = (i == self.current_tab) - bg = t['kbd_bg'] if is_active else t['key_spec'] - drect(tx, self.y, tx + tab_w, self.y + TAB_H, bg) - drect_border(tx, self.y, tx + tab_w, self.y + TAB_H, C_NONE, 1, border_col) - if is_active: - drect(tx + 1, self.y + TAB_H - 1, tx + tab_w - 1, self.y + TAB_H + 1, t['kbd_bg']) - dtext_opt(tx + tab_w//2, self.y + TAB_H//2, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, tab_name, -1) - - def draw_grid(self): - layout = LAYOUT_SYM if self.current_tab == 1 else self.layout_alpha - grid_y = self.y + TAB_H - row_h = 45 - for r, row in enumerate(layout): - count = len(row) - kw = SCREEN_W // count - for c, char in enumerate(row): - kx = c * kw - ky = grid_y + r * row_h - label = char.upper() if (self.current_tab == 0 and self.shift) else char - is_pressed = (self.last_key == label) - self.draw_key(kx, ky, kw, row_h, label, False, is_pressed) - - # Bottom Control Row - bot_y = grid_y + 4 * row_h - bot_h = row_h - self.draw_key(0, bot_y, 50, bot_h, "CAPS", True, self.shift, False) - self.draw_key(50, bot_y, 50, bot_h, "<-", True, self.last_key == "BACKSPACE", False) - self.draw_key(100, bot_y, 160, bot_h, "Space", False, self.last_key == " ", False) - self.draw_key(260, bot_y, 60, bot_h, "EXE", False, self.last_key == "ENTER", True) - - def update_grid(self, x, y, type): - grid_y = self.y + TAB_H - row_h = 45 - row_idx = (y - grid_y) // row_h - if 0 <= row_idx < 4: - layout = LAYOUT_SYM if self.current_tab == 1 else self.layout_alpha - if row_idx >= len(layout): return None - row_chars = layout[row_idx] - kw = SCREEN_W // len(row_chars) - col_idx = min(len(row_chars)-1, max(0, x // kw)) - char = row_chars[col_idx] - if self.current_tab == 0 and self.shift: char = char.upper() - if type == KEYEV_TOUCH_DOWN: self.last_key = char - return char - elif row_idx == 4: - cmd = None - if x < 50: - if type == KEYEV_TOUCH_DOWN: self.shift = not self.shift - elif x < 100: cmd = "BACKSPACE" - elif x < 260: cmd = " " - else: cmd = "ENTER" - if type == KEYEV_TOUCH_DOWN: self.last_key = cmd - return cmd - return None - - def get_math_rects(self): - keys = [] - start_y = self.y + TAB_H - total_h = KBD_H - TAB_H - row_h = total_h // 4 - side_w = 50 - center_w = SCREEN_W - (side_w * 2) - numpad_w = center_w // 3 - for i, char in enumerate(["+", "-", "*", "/"]): - keys.append((0, start_y + i*row_h, side_w, row_h, char, False, True, False)) - r_chars = [("%", False, True, False), (" ", False, True, False), ("<-", "BACKSPACE", True, False), ("EXE", "ENTER", False, True)] - for i, (disp, val, spec, acc) in enumerate(r_chars): - keys.append((SCREEN_W - side_w, start_y + i*row_h, side_w, row_h, disp, val, spec, acc)) - nums = [["1","2","3"], ["4","5","6"], ["7","8","9"]] - for r in range(3): - for c in range(3): - keys.append((side_w + c*numpad_w, start_y + r*row_h, numpad_w, row_h, nums[r][c], False, False, False)) - y_bot = start_y + 3*row_h - unit_w = center_w // 6 - bot_row = [",", "#", "0", "=", "."] - widths = [1, 1, 2, 1, 1] - cur_x = side_w - for i, char in enumerate(bot_row): - w = widths[i] * unit_w - if i == len(bot_row) - 1: w = (side_w + center_w) - cur_x - keys.append((cur_x, y_bot, w, row_h, char, False, False, False)) - cur_x += w - return keys - - def get_numpad_rects(self): - keys = [] - start_y = self.y - total_h = KBD_H - row_h = total_h // 4 - action_w = 80 - digit_w = (SCREEN_W - action_w) // 3 - keys.append((SCREEN_W - action_w, start_y, action_w, row_h, "<-", "BACKSPACE", True, False)) - keys.append((SCREEN_W - action_w, start_y + row_h, action_w, row_h*3, "EXE", "ENTER", False, True)) - nums = [["1","2","3"], ["4","5","6"], ["7","8","9"]] - for r in range(3): - for c in range(3): - keys.append((c*digit_w, start_y + r*row_h, digit_w, row_h, nums[r][c], False, False, False)) - y_bot = start_y + 3*row_h - bot_keys = [] - if self.numpad_opts['neg']: bot_keys.append("-") - bot_keys.append("0") - if self.numpad_opts['float']: bot_keys.append(".") - if len(bot_keys) > 0: - bw = (SCREEN_W - action_w) // len(bot_keys) - cur_x = 0 - for i, k in enumerate(bot_keys): - w = bw - if i == len(bot_keys) - 1: w = (SCREEN_W - action_w) - cur_x - keys.append((cur_x, y_bot, w, row_h, k, False, False, False)) - cur_x += w - return keys - - def draw_keys_from_rects(self, rects): - for x, y, w, h, label, val, is_spec, is_acc in rects: - check_val = val if val is not False else label - is_pressed = (self.last_key == check_val) - self.draw_key(x, y, w, h, label, is_spec, is_pressed, is_acc) - - def update_keys_from_rects(self, rects, x, y, type): - for rx, ry, rw, rh, label, val, is_spec, is_acc in rects: - if rx <= x < rx + rw and ry <= y < ry + rh: - ret = val if val is not False else label - if type == KEYEV_TOUCH_DOWN: self.last_key = ret - return ret - return None - - def draw(self): - if not self.visible: return - t = self.theme - drect(0, self.y, SCREEN_W, SCREEN_H, t['kbd_bg']) - dhline(self.y, t['key_spec']) - if self.enable_tabs: - self.draw_tabs() - if self.current_tab == 2: - self.draw_keys_from_rects(self.get_math_rects()) - else: - self.draw_grid() - else: - self.draw_keys_from_rects(self.get_numpad_rects()) - - def update(self, ev): - # We only set visual feedback on TOUCH DOWN - if ev.type == KEYEV_TOUCH_DOWN: - self.last_key = None # Clear previous visual press - - if not self.visible: return None - - x, y = ev.x, ev.y - if y < self.y: return None - - # Only process taps on tabs, ignore drags - if self.enable_tabs and y < self.y + TAB_H: - if ev.type == KEYEV_TOUCH_DOWN: - tab_w = SCREEN_W // 3 - self.current_tab = min(2, max(0, x // tab_w)) - return None - - # Determine active update method - method = None - if not self.enable_tabs: method = lambda t: self.update_keys_from_rects(self.get_numpad_rects(), x, y, t) - elif self.current_tab == 2: method = lambda t: self.update_keys_from_rects(self.get_math_rects(), x, y, t) - else: method = lambda t: self.update_grid(x, y, t) - - return method(ev.type) - -# ============================================================================= -# LIST PICKER WIDGET -# ============================================================================= - -class ListPicker: - def __init__(self, options, prompt="Select:", theme="light", multi=False, touch_mode=KEYEV_TOUCH_DOWN): - self.options = options - self.prompt = prompt - self.theme_name = theme if isinstance(theme, str) else 'light' - self.theme: dict = get_theme(theme) - self.multi = multi - self.touch_mode = touch_mode - self.selected_indices = set() if multi else {0} - - self.header_h = PICK_HEADER_H - self.footer_h = PICK_FOOTER_H - self.view_h = SCREEN_H - self.header_h - self.footer_h - self.btn_w = 60 - self.last_action = None - - # Initialize ListView - # Convert options to dicts if needed, or ListView handles strings - # We need to inject 'checked' state for multi-select - self.lv_items = [] - for i, opt in enumerate(options): - it = {'text': str(opt), 'type': 'item', 'idx': i} - if multi and i in self.selected_indices: it['checked'] = True - self.lv_items.append(it) - - rect = (0, self.header_h, SCREEN_W, self.view_h) - self.list_view = ListView(rect, self.lv_items, row_h=PICK_ITEM_H, theme=self.theme_name) - if not multi and len(self.lv_items) > 0: - self.list_view.selected_index = 0 - - def draw_nav_btn(self, x, w, h, type, is_pressed): - t = self.theme - bg = t['hl'] if is_pressed else t['key_spec'] - y = SCREEN_H - h - - drect(x, y, x + w, SCREEN_H, bg) - drect_border(x, y, x + w, SCREEN_H, C_NONE, 1, t['key_spec']) - - cx, cy = x + w//2, y + h//2 - col = t['txt'] - - if type == "UP": - dpoly([cx, cy-5, cx-5, cy+5, cx+5, cy+5], col, C_NONE) - elif type == "DOWN": - dpoly([cx, cy+5, cx-5, cy-5, cx+5, cy-5], col, C_NONE) - - def draw_close_icon(self, x, y, sz, col): - dline(x, y, x+sz, y+sz, col) - dline(x, y+sz, x+sz, y, col) - dline(x+1, y, x+sz+1, y+sz, col) - dline(x+1, y+sz, x+sz+1, y, col) - - def draw(self): - t = self.theme - dclear(t['modal_bg']) - - # ListView (Draw first so Header/Footer cover any spill) - # Sync multi-select checkmarks - if self.multi: - for it in self.list_view.items: - it['checked'] = (it['idx'] in self.selected_indices) - - self.list_view.draw() - - # Header - drect(0, 0, SCREEN_W, self.header_h, t['accent']) - dtext_opt(SCREEN_W//2, self.header_h//2, t['txt_acc'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, self.prompt, -1) - self.draw_close_icon(15, 15, 10, t['txt_acc']) - - # Footer - fy = SCREEN_H - self.footer_h - drect(0, fy, SCREEN_W, SCREEN_H, t['key_spec']) - dhline(fy, t['key_spec']) - - self.draw_nav_btn(0, self.btn_w, self.footer_h, "UP", self.last_action == "PAGE_UP") - self.draw_nav_btn(SCREEN_W - self.btn_w, self.btn_w, self.footer_h, "DOWN", self.last_action == "PAGE_DOWN") - - ok_pressed = (self.last_action == "OK") - ok_bg = t['hl'] if ok_pressed else t['key_spec'] - ok_rect_x = self.btn_w - ok_rect_w = SCREEN_W - 2 * self.btn_w - drect(ok_rect_x, fy, ok_rect_x + ok_rect_w, SCREEN_H, ok_bg) - drect_border(ok_rect_x, fy, ok_rect_x + ok_rect_w, SCREEN_H, C_NONE, 1, t['key_spec']) - - label = "OK" if self.multi else "Select" - dtext_opt(SCREEN_W//2, fy + self.footer_h//2, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, label, -1) - - def run(self): - # Clear any lingering events - clearevents() - cleareventflips() - - touch_latched = False - - while True: - self.draw() - dupdate() - cleareventflips() - - ev = pollevent() - events = [] - while ev.type != KEYEV_NONE: - events.append(ev) - ev = pollevent() - - if keypressed(KEY_EXIT) or keypressed(KEY_DEL): - return None - - # --- Footer / Nav Keys --- - key_pg_up = keypressed(KEY_LEFT) - key_pg_dn = keypressed(KEY_RIGHT) - - # Pass events to ListView first - # We filter out touches that are in header/footer to prevent ListView from acting on them - lv_events = [] - footer_touch = None - header_touch = None - - for e in events: - if e.type in [KEYEV_TOUCH_DOWN, KEYEV_TOUCH_UP, KEYEV_TOUCH_DRAG]: - if e.y < self.header_h: - if e.type == KEYEV_TOUCH_DOWN: header_touch = e # Capture down for header - elif e.type == KEYEV_TOUCH_UP: header_touch = e - elif e.y >= SCREEN_H - self.footer_h: - if e.type == KEYEV_TOUCH_DOWN: footer_touch = e - elif e.type == KEYEV_TOUCH_UP: footer_touch = e - else: - lv_events.append(e) # Pass to list view - else: - lv_events.append(e) # Pass keys - - action = self.list_view.update(lv_events) - - if action: - type, idx, item = action - if type == 'click': - if self.multi: - real_idx = item['idx'] - if real_idx in self.selected_indices: self.selected_indices.remove(real_idx) - else: self.selected_indices.add(real_idx) - else: - return self.options[item['idx']] - - if key_pg_up: - self.list_view.scroll_y = max(0, self.list_view.scroll_y - self.list_view.h) - self.list_view.clamp_scroll() - if key_pg_dn: - self.list_view.scroll_y = min(self.list_view.max_scroll, self.list_view.scroll_y + self.list_view.h) - self.list_view.clamp_scroll() - - # Handle Footer/Header Touches - # Check Latch for DOWN mode - ignore_action = (self.touch_mode == KEYEV_TOUCH_DOWN and touch_latched) - - # Header - if header_touch and header_touch.type == self.touch_mode: - if not ignore_action and header_touch.x < 40: - clearevents() - cleareventflips() - return None - - # Footer - if footer_touch: - if footer_touch.type == self.touch_mode and not ignore_action: - if footer_touch.x < self.btn_w: self.last_action = "PAGE_UP" - elif footer_touch.x > SCREEN_W - self.btn_w: self.last_action = "PAGE_DOWN" - else: self.last_action = "OK" - - if self.touch_mode == KEYEV_TOUCH_DOWN: touch_latched = True - - # Reset latch on any UP - for e in events: - if e.type == KEYEV_TOUCH_UP: touch_latched = False - - if self.last_action: - if self.last_action == "PAGE_UP": - self.list_view.scroll_y = max(0, self.list_view.scroll_y - self.list_view.h) - self.list_view.clamp_scroll() - elif self.last_action == "PAGE_DOWN": - self.list_view.scroll_y = min(self.list_view.max_scroll, self.list_view.scroll_y + self.list_view.h) - self.list_view.clamp_scroll() - elif self.last_action == "OK": - clearevents() - cleareventflips() - if self.multi: return [self.options[i] for i in sorted(self.selected_indices)] - else: - if self.list_view.selected_index >= 0: - return self.options[self.list_view.items[self.list_view.selected_index]['idx']] - self.last_action = None - - time.sleep(0.01) - - # Enforce scroll bounds after any input - # if self.cursor_idx < self.page_start: self.page_start = self.cursor_idx - # elif self.cursor_idx >= self.page_start + self.items_per_page: self.page_start = self.cursor_idx - self.items_per_page + 1 - - # Small sleep to prevent busy loop eating battery/CPU - time.sleep(0.01) - - -# ============================================================================= -# PUBLIC FUNCTIONS -# ============================================================================= - - -def input(prompt="Input:", type="alpha_numeric", theme="light", layout="qwerty", touch_mode=KEYEV_TOUCH_DOWN): - # Re-implementing input function since it seems missing or was overwritten - t = get_theme(theme) - clearevents() - cleareventflips() - - # Kbd config - start_tab = 0 - enable_tabs = True - numpad_opts = None - - if type.startswith("numeric_"): - enable_tabs = False - allow_float = "int" not in type - allow_neg = "negative" in type - numpad_opts = {'float': allow_float, 'neg': allow_neg} - elif type == "math": start_tab = 2 - - kbd = Keyboard(default_tab=start_tab, enable_tabs=enable_tabs, numpad_opts=numpad_opts, theme=theme, layout=layout) - text = "" - running = True - touch_latched = False - - while running: - dclear(t['modal_bg']) - - # Header - drect(0, 0, SCREEN_W, PICK_HEADER_H, t['accent']) - dtext_opt(SCREEN_W//2, PICK_HEADER_H//2, t['txt_acc'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, prompt, -1) - - # Close Button - dline(15, 15, 25, 25, t['txt_acc']) - dline(25, 15, 15, 25, t['txt_acc']) - dline(16, 15, 26, 25, t['txt_acc']) - dline(26, 15, 16, 25, t['txt_acc']) - - # Input Box - box_y = 60; box_h = 40 - drect_border(10, box_y, SCREEN_W - 10, box_y + box_h, t['hl'], 2, t['txt']) - dtext(15, box_y + 10, t['txt'], text + "_") - - kbd.draw() - dupdate() - cleareventflips() - - ev = pollevent() - events = [] - while ev.type != KEYEV_NONE: - events.append(ev) - ev = pollevent() - - # Physical Keys - if keypressed(KEY_EXIT): return None - if keypressed(KEY_EXE): return text - if keypressed(KEY_DEL) and len(text) > 0: text = text[:-1] - - # Touch & Virtual Kbd - for e in events: - # Latch Reset - if e.type == KEYEV_TOUCH_UP: - touch_latched = False - kbd.last_key = None - - # Close Button (Respect touch_mode) - should_close = (e.type == touch_mode) - if touch_mode == KEYEV_TOUCH_DOWN and touch_latched: should_close = False - - if should_close and e.y < PICK_HEADER_H and e.x < 40: - clearevents() - cleareventflips() - return None - - # Keyboard (Touch Down Only) - if e.type == KEYEV_TOUCH_DOWN and not touch_latched: - # Don't type if touching close button area - if e.y < PICK_HEADER_H and e.x < 40: - pass - else: - touch_latched = True - res = kbd.update(e) - if res: - if res == "ENTER": - clearevents() - cleareventflips() - return text - elif res == "BACKSPACE": text = text[:-1] - elif len(res) == 1: text += res - - time.sleep(0.01) - return text - - -def pick(options, prompt="Select:", theme="light", multi=False, touch_mode=KEYEV_TOUCH_DOWN): - picker = ListPicker(options, prompt, theme, multi, touch_mode=touch_mode) - return picker.run() - -class ConfirmationDialog: - def __init__(self, title, body, ok_text="OK", cancel_text="Cancel", theme="light", touch_mode=KEYEV_TOUCH_DOWN): - self.title = title - self.body = body - self.ok_text = ok_text - self.cancel_text = cancel_text - self.theme = get_theme(theme) - self.touch_mode = touch_mode - self.header_h = 40 - self.footer_h = 45 - self.btn_w = SCREEN_W // 2 - - def draw_btn(self, x, y, w, h, text, pressed): - t = self.theme - bg = t['hl'] if pressed else t['key_spec'] - drect(x, y, x + w, y + h, bg) - drect_border(x, y, x + w, y + h, C_NONE, 1, t['key_spec']) - dtext_opt(x + w//2, y + h//2, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, text, -1) - - def draw(self, btn_ok_pressed, btn_cn_pressed): - t = self.theme - dclear(t['modal_bg']) - - # Header - drect(0, 0, SCREEN_W, self.header_h, t['accent']) - dtext_opt(SCREEN_W//2, self.header_h//2, t['txt_acc'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, self.title, -1) - - # Body - cy = (SCREEN_H - self.header_h - self.footer_h) // 2 + self.header_h - dtext_opt(SCREEN_W//2, cy, t['txt'], C_NONE, DTEXT_CENTER, DTEXT_MIDDLE, self.body, -1) - - # Footer - fy = SCREEN_H - self.footer_h - self.draw_btn(0, fy, self.btn_w, self.footer_h, self.cancel_text, btn_cn_pressed) - self.draw_btn(self.btn_w, fy, self.btn_w, self.footer_h, self.ok_text, btn_ok_pressed) - - def run(self): - clearevents() - cleareventflips() - - touch_latched = False - btn_ok_pressed = False - btn_cn_pressed = False - - while True: - self.draw(btn_ok_pressed, btn_cn_pressed) - dupdate() - cleareventflips() - - # Key Handling - if keypressed(KEY_EXIT) or keypressed(KEY_DEL): return False - if keypressed(KEY_EXE): return True - - # Event Handling - ev = pollevent() - events = [] - while ev.type != KEYEV_NONE: - events.append(ev) - ev = pollevent() - - touch = None - for e in events: - if e.type == KEYEV_TOUCH_DOWN and not touch_latched: - touch_latched = True - touch = e - elif e.type == KEYEV_TOUCH_UP: - touch_latched = False - touch = e - - if touch: - tx, ty = touch.x, touch.y - fy = SCREEN_H - self.footer_h - - is_cancel = (ty >= fy and tx < self.btn_w) - is_ok = (ty >= fy and tx >= self.btn_w) - - if touch.type == KEYEV_TOUCH_DOWN: - if is_cancel: btn_cn_pressed = True - elif is_ok: btn_ok_pressed = True - - if touch.type == self.touch_mode: - if is_cancel: - clearevents() - cleareventflips() - return False - elif is_ok: - clearevents() - cleareventflips() - return True - - if touch.type == KEYEV_TOUCH_UP: - btn_cn_pressed = False - btn_ok_pressed = False - - time.sleep(0.01) - -def ask(title, body, ok_text="OK", cancel_text="Cancel", theme="light", touch_mode=KEYEV_TOUCH_DOWN): - dlg = ConfirmationDialog(title, body, ok_text, cancel_text, theme, touch_mode=touch_mode) - return dlg.run() \ No newline at end of file diff --git a/lib/core/exceptions.hpp b/lib/core/exceptions.hpp index 3c61e1e..b29a6af 100644 --- a/lib/core/exceptions.hpp +++ b/lib/core/exceptions.hpp @@ -19,6 +19,6 @@ * } * @endcode */ -#define try bool __HadError=false; -#define catch __ExitJmp:if(__HadError) -#define throw(x) __HadError=true;goto __ExitJmp; +#define TRY bool __HadError=false; +#define CATCH __ExitJmp:if(__HadError) +#define THROW(x) __HadError=true;goto __ExitJmp; diff --git a/lib/core/touch_event_handler.hpp b/lib/core/touch_event_handler.hpp index 34f581b..9e6c711 100644 --- a/lib/core/touch_event_handler.hpp +++ b/lib/core/touch_event_handler.hpp @@ -25,6 +25,11 @@ struct ActBarHandler { void (*callback)(); }; +struct KeyListener { + Input_Keycode key; + void (*callback)(); +}; + // can be changed to accomodate more events if needed struct TouchHandler touchHandlers[64]; @@ -33,12 +38,15 @@ uint32_t touchHandlersLength = 0; struct ActBarHandler actBarHandlers[6]; uint32_t actBarHandlersLength = 0; +struct KeyListener keyListeners[32]; +uint32_t keyListenersLength = 0; + struct Input_Event event __attribute__((aligned(4))); void checkTouchEvents() { // check if there are no events to check - if (touchHandlersLength == 0 && actBarHandlersLength == 0) { + if (touchHandlersLength == 0 && actBarHandlersLength == 0 && keyListenersLength == 0) { return; } @@ -54,13 +62,20 @@ void checkTouchEvents() { switch (event.type) { case EVENT_KEY: - // for some reason, this is really slow with certain keys, so kept old event handler + if (keyListenersLength > 0 && event.data.key.direction == KEY_PRESSED) { + for (uint32_t i = 0; i < keyListenersLength; i++) { + if (event.data.key.keyCode == keyListeners[i].key) { + (*keyListeners[i].callback)(); + break; + } + } + } break; case EVENT_TOUCH: // check if there are any touch handlers if (touchHandlersLength > 0) { - for (uint8_t i = 0; i < touchHandlersLength; i++) { + for (uint32_t i = 0; i < touchHandlersLength; i++) { if ((uint32_t)event.data.touch_single.p1_x >= touchHandlers[i].minX && (uint32_t)event.data.touch_single.p1_x <= touchHandlers[i].maxX && (uint32_t)event.data.touch_single.p1_y >= touchHandlers[i].minY && (uint32_t)event.data.touch_single.p1_y <= touchHandlers[i].maxY) { // check direction @@ -80,7 +95,7 @@ void checkTouchEvents() { case EVENT_ACTBAR_SETTINGS: // check if there are any act bar handlers if (actBarHandlersLength > 0) { - for (uint8_t i = 0; i < actBarHandlersLength; i++) { + for (uint32_t i = 0; i < actBarHandlersLength; i++) { if (event.type == actBarHandlers[i].type) { (*actBarHandlers[i].callback)(); break; @@ -111,8 +126,38 @@ void addActBarListener(uint16_t type, void (*callback)()) { actBarHandlers[actBarHandlersLength++] = handler; } +void addListener(Input_Keycode key, void (*callback)()) { + KeyListener handler; + handler.key = key; + handler.callback = callback; + keyListeners[keyListenersLength++] = handler; +} + +// Support for Keys1 legacy? +// No, migrating to SDK keycodes. +// But legacy event_handler.hpp defines addListener(Keys1). +// To avoid conflict, I should rename my addListener or update main.cpp to call addKeyListener? +// The error in main.cpp was: `cannot convert 'Input_Keycode' to 'Keys1'`. +// And it referenced `lib/core/event_handler.hpp`. +// `main.cpp` includes `lib/core/event_handler.hpp`. +// It does NOT include `touch_event_handler.hpp` directly? No, it does. +// But `addListener` is ambiguous or `event_handler.hpp`'s version is picked. +// `event_handler.hpp` has `void addListener(Keys1 key...`. +// `touch_event_handler.hpp` has `void addTouchListener...`. +// I should add `addKeyListener` to `touch_event_handler.hpp` and USE IT in `main.cpp` instead of `addListener`. +// And I should likely NOT include `event_handler.hpp` in `main.cpp` if I want to fully migrate. +// But `main.cpp` calls `checkEvents`. +// I'll define `addKeyListener` here. + +void addKeyListener(Input_Keycode key, void (*callback)()) { + KeyListener handler; + handler.key = key; + handler.callback = callback; + keyListeners[keyListenersLength++] = handler; +} + void removeTouchListener(uint32_t minX, uint32_t minY, uint32_t maxX, uint32_t maxY, uint32_t direction = TOUCH_DOWN) { - for (uint8_t i = 0; i < touchHandlersLength; i++) { + for (uint32_t i = 0; i < touchHandlersLength; i++) { if (touchHandlers[i].minX == minX && touchHandlers[i].minY == minY && touchHandlers[i].maxX == maxX && touchHandlers[i].maxY == maxY && touchHandlers[i].direction == direction) { touchHandlers[i] = touchHandlers[touchHandlersLength - 1]; touchHandlersLength--; @@ -121,7 +166,7 @@ void removeTouchListener(uint32_t minX, uint32_t minY, uint32_t maxX, uint32_t m } void removeActBarListener(uint16_t type) { - for (uint8_t i = 0; i < actBarHandlersLength; i++) { + for (uint32_t i = 0; i < actBarHandlersLength; i++) { if (actBarHandlers[i].type == type) { actBarHandlers[i] = actBarHandlers[actBarHandlersLength - 1]; actBarHandlersLength--; diff --git a/main.cpp b/main.cpp index 31c08d0..1100734 100644 --- a/main.cpp +++ b/main.cpp @@ -5,8 +5,8 @@ #include "calc.hpp" #include "lib/draw_functions.hpp" #include "lib/core/exceptions.hpp" -#include "lib/core/event_handler.hpp" -#include "lib/core/touch_event_handler.hpp" +// #include "lib/core/event_handler.hpp" // Removed legacy event handler +#include "lib/core/touch_event_handler.hpp" // New unified handler #include "lib/functions/random.hpp" // shell @@ -44,16 +44,8 @@ void ProcessCommand() { if (terminal->bufferInPos != 0) { terminal->HideCursor(); - // Move to next line history is handled in WriteBuffer('\n') ? - // No, WriteBuffer handles \n. - // If we press Enter, we should just WriteBuffer('\n')? - // But we need to execute the command. - // Copy command char callingArgs[BUF_SIZE]; - // bufferIn is not null terminated by default? - // WriteBuffer adds to bufferIn. - // Let's assume bufferIn has content up to bufferInPos. for (int i = 0; i < terminal->bufferInPos; i++) { callingArgs[i] = terminal->bufferIn[i]; } @@ -214,33 +206,19 @@ void main2() { terminal->Render(); keyboard->Render(); - // Add event listeners - addListener(KEY_CLEAR, endShell); // end the shell - cmd now - addListener(KEY_BACKSPACE, kbBackspace); // remove last character - // addListener(KEY_SHIFT, kbShift); // toggle Shift - handled by virtual keyboard button now? Or keep physical key too? - // keyboard->shift is public, can toggle it. + // Add event listeners (Migrated to SDK keycodes and addKeyListener) + addKeyListener(KEYCODE_POWER_CLEAR, endShell); + addKeyListener(KEYCODE_BACKSPACE, kbBackspace); - addListener(KEYCODE_KEYBOARD, kbToggle); + addKeyListener(KEYCODE_KEYBOARD, kbToggle); // Keyboard Listeners - // addListener(KEY_LEFT, kbLeft); - // addListener(KEY_RIGHT, kbRight); - addListener2(KEY_UP, kbUp); - addListener2(KEY_DOWN, kbDown); - addListener(KEY_EXE, kbEnter); - - // addTouchListener(0, 0, 300, 100, testTouch); // touch listener - // Touch listener covers whole screen for keyboard check? - // VirtualKeyboard::Update checks bounds. - // But `checkTouchEvents` checks bounds before calling callback. - // We should add a listener for the keyboard area. - // But keyboard area size changes or is fixed KBD_H? - // It's KBD_H at bottom. + addKeyListener(KEYCODE_UP, kbUp); + addKeyListener(KEYCODE_DOWN, kbDown); + addKeyListener(KEYCODE_EXE, kbEnter); + addTouchListener(0, height - KBD_H, width, height, HandleTouchForKeyboard, TOUCH_DOWN); addTouchListener(0, height - KBD_H, width, height, HandleTouchForKeyboard, TOUCH_UP); - // Also need TOUCH_HOLD_DRAG? cinput.py uses it. - // addTouchListener(0, height - KBD_H, width, height, HandleTouchForKeyboard, TOUCH_HOLD_DRAG); - // Initialize the shell cpshell_init(); @@ -269,9 +247,8 @@ void main2() { } } - checkEvents(); - checkTouchEvents(); - // Debug_Printf(10,28,true,0,"T X: %i | Y: %i | PX: %i | PY: %i",terminal->bufferCX, terminal->bufferCY, terminal->bufferCX * terminal->xmargin + terminal->bufferOffsetX, terminal->bufferCY * terminal->ymargin + terminal->bufferOffsetY); + // checkEvents(); // Removed legacy event loop + checkTouchEvents(); // Unified event loop LCD_Refresh(); } diff --git a/src/commands/clear.cpp b/src/commands/clear.cpp index cf0d268..b2b24aa 100644 --- a/src/commands/clear.cpp +++ b/src/commands/clear.cpp @@ -14,11 +14,8 @@ extern int clear_main(int, char **) fillScreen(0); // rerender the keyboard keyboard->Render(); - // keyboard highlight - keyboard->Highlight(keyboard->xcursor, keyboard->ycursor); // clear the terminal buffer terminal->ClearBuffer(); - // reset terminal y - terminal->bufferCY = 0; + terminal->Render(); return 0; }; diff --git a/src/commands/help.cpp b/src/commands/help.cpp index 56035fc..b8a177c 100644 --- a/src/commands/help.cpp +++ b/src/commands/help.cpp @@ -23,11 +23,6 @@ extern int help_main(int, char **) char cmds[APPLET_SIZE]; while (a->name[0] != 0) { strcpy(cmds, (a++)->name); - // check if terminal->bufferCX is at the end of the line + 2 for ', ' - if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2)) >= terminal->xmax) { - terminal->WriteBuffer('\n', false); - terminal->ClearBuffer(); - } strcat(cmds, ", "); terminal->WriteChars(cmds, true); } diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index eb6ef55..4ac2909 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -38,18 +38,11 @@ extern int ls_main(int, char **) thisfile.type=findInfoBuf.type==File_FindInfo::EntryTypeDirectory?'D':'F'; //display this strcpy(outBuf, thisfile.fileName); - // check if it will fit on the screen or we are in second half - if ((terminal->bufferCX + (int16_t)strlen(outBuf)) >= terminal->xmax || (terminal->bufferCX + 1) >= (terminal->xmax/2)) { - terminal->WriteBuffer('\n', false); - terminal->ClearBuffer(); - } else if (terminal->bufferCX > 0) { - // change to second half - terminal->bufferCX = (terminal->xmax/2); - } // check if directory uint32_t newColor = thisfile.type=='D'?color(255,0,0):0xFFFF; // red for directories, white for files terminal->SetColor(newColor); terminal->WriteChars(outBuf, true); + terminal->WriteBuffer('\n', false); //save this dirEntry to directory directory[dirFiles++] = thisfile; diff --git a/src/cpshell.cpp b/src/cpshell.cpp index 61ad0e8..65fea31 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -103,11 +103,6 @@ int cpshell_main(int argc, char **argv) char cmds[APPLET_SIZE]; while (a->name[0] != 0) { strcpy(cmds, (a++)->name); - // check if terminal->bufferCX is at the end of the line + 2 for ', ' - if ((terminal->bufferCX + (int16_t)(strlen(a->name) + 2)) >= terminal->xmax) { - terminal->WriteBuffer('\n', false); - terminal->ClearBuffer(); - } strcat(cmds, ", "); terminal->WriteChars(cmds, true); } diff --git a/src/internal.hpp b/src/internal.hpp index 55a55af..76d2b12 100644 --- a/src/internal.hpp +++ b/src/internal.hpp @@ -19,6 +19,12 @@ #define CPS_VERSION "0.1.0" +// UI Constants +#define KBD_H 260 +#define TAB_H 30 +#define SCREEN_W width +#define SCREEN_H height + #define isBlank(ch) (((ch) == ' ') || ((ch) == '\t')) #define isDecimal(ch) (((ch) >= '0') && ((ch) <= '9')) #define isOctal(ch) (((ch) >= '0') && ((ch) <= '7')) diff --git a/src/terminal.hpp b/src/terminal.hpp index 2fab3e6..45fbcf5 100644 --- a/src/terminal.hpp +++ b/src/terminal.hpp @@ -1,10 +1,15 @@ +/** + * @file terminal.hpp + * @author Sean McGinty (newfolderlocation@gmail.com) + * @brief A terminal class for CPShell, which handles all terminal input and output. + * @version 1.1 + * @date 2022-06-09 + */ + #pragma once #include "../calc.hpp" #include "../lib/functions/shapes.hpp" -#include -#include -#include // Terminal Class class Terminal { @@ -18,54 +23,22 @@ class Terminal { void SetFont(uint8_t* font); void SetColor(uint32_t newColor); void SetCursorColor(uint32_t newColor); - void Render(); // Redraw visible lines - void Scroll(int lines); - void ScrollToBottom(); - // buffer for command input char bufferIn[BUF_SIZE]; int8_t bufferInPos = 0; - - // History Buffer - std::vector history; - - // View State - int scrollOffset = 0; // 0 means showing the top. Or maybe 0 means bottom? - // Let's say scrollOffset is the index of the first visible line. - // But we want auto-scroll to end. - // Maybe scrollOffset is "lines from bottom"? - // Let's use: scrollY = index of top visible line. - int scrollY = 0; - - // Dimensions + // buffer cursor position + int16_t bufferCX = 0; + int16_t bufferCY = 0; + int16_t bufferOffsetX = 2; + int16_t bufferOffsetY = 2; int16_t xmargin = 8; // character width - int16_t ymargin = 10; // character height (7x8 font + spacing) + int16_t ymargin = 9; // character height + uint8_t* font; int16_t termWidth = width - xmargin * 2; - // termHeight depends on keyboard visibility? - // User said: "prompt would always be visible". - // When keyboard is visible, terminal area is smaller. - // When hidden, full screen. - int16_t termHeight = height - 40; // Default full screen minus some margin? + int16_t termHeight = 40; int16_t xmax = termWidth / xmargin; - int16_t ymax; // Calculated based on height - - uint8_t* font; uint32_t color = 0xFFFF; // white uint32_t cursorColor = 0xFFFF; // white - - bool keyboardVisible = true; - - Terminal() { - history.push_back(""); // Start with one empty line - UpdateLayout(); - } - - void UpdateLayout() { - int kbdH = keyboardVisible ? KBD_H : 0; - termHeight = height - kbdH; - ymax = termHeight / ymargin; - ScrollToBottom(); - } }; void Terminal::SetFont(uint8_t* font) { @@ -74,148 +47,84 @@ void Terminal::SetFont(uint8_t* font) { void Terminal::ClearBuffer() { this->bufferInPos = 0; - // No, we don't clear history here. Just the current input line buffer. - // But Wait, WriteBuffer writes to `bufferIn` AND history? - // `bufferIn` tracks the *current command being typed*. - // The history vector tracks *displayed lines*. - // When user types, we update the last line of history? - // Or we treat `bufferIn` separate? - // Standard terminal: history contains committed lines + current line being edited. - // Let's sync them. -} - -void Terminal::Scroll(int lines) { - scrollY += lines; - int maxScroll = std::max(0, (int)history.size() - ymax); - if (scrollY < 0) scrollY = 0; - if (scrollY > maxScroll) scrollY = maxScroll; - Render(); -} - -void Terminal::ScrollToBottom() { - int maxScroll = std::max(0, (int)history.size() - ymax); - scrollY = maxScroll; + this->bufferCX = 0; } void Terminal::WriteBuffer(char c, bool hideCursor) { + // hideCursor should only be false from WriteChars as writing to vram is slow if (hideCursor) this->HideCursor(); - - std::string& currentLine = history.back(); - + // check if '\n' if (c == '\n') { - history.push_back(""); - bufferInPos = 0; - ScrollToBottom(); - Render(); // Need to redraw to show scroll + this->ClearBuffer(); + this->bufferCY++; return; } - // Handle Backspace (passed as special char or handled in RemoveLast?) - // RemoveLast calls this? No. - - if (c >= 32 && c <= 126) { - currentLine += c; - bufferIn[bufferInPos++] = c; // Keep bufferIn for command processing - - // Wrap text - if (currentLine.length() >= (size_t)xmax) { - // Move excess to next line? - // Simple wrapping: just let it grow and Renderer handles it? - // Or split? - // Splitting is hard. Let's just break line. - std::string remainder = currentLine.substr(xmax); - currentLine = currentLine.substr(0, xmax); - history.push_back(remainder); + if (this->bufferInPos < (BUF_SIZE - 1)) { + // add the character to the buffer + this->bufferIn[this->bufferInPos] = c; + this->bufferInPos++; + + // buf of chars array that will have c in it + char buf[2] = {c, '\0'}; + + // draw the character + DRAW_FONT(this->font, buf, this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->color, this->termWidth); + + // check for overflow on screen + if (this->bufferCX < this->xmax) { + this->bufferCX++; + } else { + this->bufferCX = 0; + this->bufferCY++; } + } else { + Debug_Printf(1,1,true,0,"Buffer overflow!"); } - - ScrollToBottom(); - Render(); } +/* + * Usage + * char myMessage[] = "Hello World!\nSupports newlines!"; + * Terminal* terminal; + * terminal->WriteChars(myMessage); + */ void Terminal::WriteChars(const char *charArray, bool skipClear) { int len = strlen(charArray); for (int i = 0; i < len; i++) { this->WriteBuffer(charArray[i], false); } - if (!skipClear) { - bufferInPos = 0; // Reset input buffer, but history persists - // memset(bufferIn, 0, BUF_SIZE); - } + // clear the buffer + if (!skipClear) this->ClearBuffer(); } void Terminal::RemoveLast() { - std::string& currentLine = history.back(); - if (currentLine.length() > 0) { - currentLine.pop_back(); - if (bufferInPos > 0) bufferInPos--; - Render(); + if (this->bufferInPos == 0) return; + // remove the active cursor + this->HideCursor(); + // check for wrapping + if (this->bufferCX == 0) { + this->bufferCY--; + this->bufferCX = this->xmax; } else { - // Merge with previous line if empty? - if (history.size() > 1) { - history.pop_back(); - // bufferInPos needs to be restored? - // This is complex if we allow backspacing over newlines. - // For now, simple backspace on current line. - Render(); - } - } -} - -void Terminal::Render() { - // Clear visible area - int visibleH = keyboardVisible ? (height - KBD_H) : height; - drawFilledRectangle(0, 0, width, visibleH, 0x0000); // Black bg - - if (!font) return; - - int startLine = scrollY; - int endLine = std::min((int)history.size(), startLine + ymax + 1); - - for (int i = startLine; i < endLine; i++) { - int screenY = (i - startLine) * ymargin; - if (screenY >= visibleH) break; - - const std::string& line = history[i]; - // const char* text = line.c_str(); // DRAW_FONT takes char*? const char* fix applied. - // Need mutable for DRAW_FONT? No, fixed to const char*. - - DRAW_FONT(font, line.c_str(), 2, screenY, color, width); + this->bufferCX--; } - // Draw cursor if at bottom? - if (scrollY >= (int)history.size() - ymax) { - ShowCursor(); - } + this->bufferInPos--; + // remove the last character from vram + drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, 0); // 0 is always color black / 0,0,0 } // Cursor Show void Terminal::ShowCursor() { - // Find cursor position - // It's at the end of the last line - if (history.empty()) return; - - int lineIdx = history.size() - 1; - // Is it visible? - if (lineIdx < scrollY || lineIdx >= scrollY + ymax) return; - - int screenY = (lineIdx - scrollY) * ymargin; - int screenX = 2 + history.back().length() * xmargin; - - drawFilledRectangle(screenX, screenY, xmargin, ymargin, cursorColor); + // draw the cursor + drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, this->cursorColor); } // Cursor Hide void Terminal::HideCursor() { - // Find cursor position and clear it - if (history.empty()) return; - int lineIdx = history.size() - 1; - if (lineIdx < scrollY || lineIdx >= scrollY + ymax) return; - - int screenY = (lineIdx - scrollY) * ymargin; - int screenX = 2 + history.back().length() * xmargin; - - drawFilledRectangle(screenX, screenY, xmargin, ymargin, 0x0000); + // draw the cursor + drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, 0); // 0 is always color black / 0,0,0 } // Set Color diff --git a/src/virtual_keyboard.hpp b/src/virtual_keyboard.hpp index 3f0ec65..e75b226 100644 --- a/src/virtual_keyboard.hpp +++ b/src/virtual_keyboard.hpp @@ -1,372 +1,132 @@ #pragma once -#include "../calc.hpp" -#include "../lib/functions/shapes.hpp" -#include -#include -#include - -// Constants -#define KBD_H 260 -#define TAB_H 30 -#define SCREEN_W width -#define SCREEN_H height - -// Theme colors (Light theme from cinput.py) -#define C_WHITE 0xFFFF -#define C_BLACK 0x0000 -#define C_LIGHT 0xCE59 // Grey -#define C_DARK 0x0000 - -// Theme dictionary equivalent -struct Theme { - uint16_t modal_bg = C_WHITE; - uint16_t kbd_bg = C_WHITE; - uint16_t key_bg = C_WHITE; - uint16_t key_spec = 0xCE59; // Secondary (Light Grey) ~ RGB(200, 200, 200) - uint16_t key_out = C_BLACK; - uint16_t txt = 0x0000; - uint16_t txt_dim = 0x4208; // Dark Grey - uint16_t accent = 0x0010; // Dark Blue-ish - uint16_t txt_acc = C_WHITE; - uint16_t hl = 0xCE59; -}; - -struct KeyRect { - int16_t x, y, w, h; - const char* label; - const char* val; // if NULL, use label - bool is_spec; - bool is_acc; -}; - +// KeyBoard Class class VirtualKeyboard { -public: - bool visible = true; - int current_tab = 0; // 0: ABC, 1: Sym, 2: Math - bool shift = false; - const char* last_key = nullptr; - uint8_t* font; - Theme theme; - - // Layouts - const char* tabs[3] = {"ABC", "Sym", "Math"}; - - // QWERTY Layout - const char* layout_alpha[4] = { - "1234567890", - "qwertyuiop", - "asdfghjkl:", - "zxcvbnm,._" - }; - - const char* layout_sym[4] = { - "1234567890", - "@#$_&-+()/", - "=\\<*\"':;!?", - "{}[]^~`|<>" - }; - - VirtualKeyboard() { - // Initialize theme - } - - void SetFont(uint8_t* f) { - font = f; - } - - void draw_rect_border(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t border_col) { - drawRectangle(x, y, w, h, border_col); - } - - void draw_text_centered(int16_t cx, int16_t cy, const char* text, uint16_t color) { - if (!font) return; - // Estimate width (8px per char approx) - int len = strlen(text); - int w = len * 8; - int h = 10; - DRAW_FONT(font, text, cx - w/2, cy - h/2, color, 0); - } - - void draw_key(int16_t x, int16_t y, int16_t w, int16_t h, const char* label, bool is_special, bool is_pressed, bool is_accent) { - uint16_t bg; - if (is_pressed) bg = theme.hl; - else if (is_accent) bg = theme.accent; - else if (is_special) bg = theme.key_spec; - else bg = theme.key_bg; - - uint16_t txt_col = is_accent ? theme.txt_acc : theme.txt; - uint16_t border_col = theme.key_spec; - - drawFilledRectangle(x + 1, y + 1, w - 2, h - 2, bg); - draw_rect_border(x, y, w, h, border_col); - draw_text_centered(x + w/2, y + h/2, label, txt_col); - } - - void draw_tabs() { - int16_t y_pos = SCREEN_H - KBD_H; - int16_t tab_w = SCREEN_W / 3; - uint16_t border_col = theme.key_spec; - - for (int i = 0; i < 3; i++) { - int16_t tx = i * tab_w; - bool is_active = (i == current_tab); - uint16_t bg = is_active ? theme.kbd_bg : theme.key_spec; - - drawFilledRectangle(tx, y_pos, tab_w, TAB_H, bg); - draw_rect_border(tx, y_pos, tab_w, TAB_H, border_col); - - // "Active" look (cover bottom border) - if (is_active) { - // drawFilledRectangle(tx + 1, y_pos + TAB_H - 1, tab_w - 2, 2, theme.kbd_bg); - } - - const char* name = tabs[i]; - // If tab 0 and not qwerty (not implemented yet), change name? - draw_text_centered(tx + tab_w/2, y_pos + TAB_H/2, name, theme.txt); - } - } - - void draw_grid() { - int16_t grid_y = (SCREEN_H - KBD_H) + TAB_H; - int16_t row_h = 45; - - const char** layout = (current_tab == 1) ? layout_sym : layout_alpha; - - char labelBuf[2] = {0, 0}; - - for (int r = 0; r < 4; r++) { - const char* row = layout[r]; - int count = strlen(row); - int16_t kw = SCREEN_W / count; - - for (int c = 0; c < count; c++) { - int16_t kx = c * kw; - int16_t ky = grid_y + r * row_h; - - char char_code = row[c]; - if (current_tab == 0 && shift) { - if (char_code >= 'a' && char_code <= 'z') char_code -= 32; - } - - labelBuf[0] = char_code; - - // Check if pressed - bool is_pressed = false; - // We need to know if this specific key is pressed. - // For simplicity, we can rely on immediate redraw or state. - // Assuming last_key stores the char string. - if (last_key && last_key[0] == char_code && last_key[1] == 0) is_pressed = true; - - draw_key(kx, ky, kw, row_h, labelBuf, false, is_pressed, false); - } - } - - // Bottom Control Row - int16_t bot_y = grid_y + 4 * row_h; - int16_t bot_h = row_h; - - bool caps_pressed = shift; - bool back_pressed = (last_key && strcmp(last_key, "BACKSPACE") == 0); - bool space_pressed = (last_key && strcmp(last_key, " ") == 0); - bool enter_pressed = (last_key && strcmp(last_key, "ENTER") == 0); - - draw_key(0, bot_y, 50, bot_h, "CAPS", true, caps_pressed, false); - draw_key(50, bot_y, 50, bot_h, "<-", true, back_pressed, false); - draw_key(100, bot_y, 160, bot_h, "Space", false, space_pressed, false); - draw_key(260, bot_y, 60, bot_h, "EXE", false, enter_pressed, true); - } - - // Helper for Math layout (simplified from cinput.py) - void draw_math() { - int16_t start_y = (SCREEN_H - KBD_H) + TAB_H; - int16_t total_h = KBD_H - TAB_H; - int16_t row_h = total_h / 4; - int16_t side_w = 50; - int16_t center_w = SCREEN_W - (side_w * 2); - int16_t numpad_w = center_w / 3; - - // Left Col: + - * / - const char* ops[] = {"+", "-", "*", "/"}; - for (int i=0; i<4; i++) { - bool pressed = (last_key && strcmp(last_key, ops[i]) == 0); - draw_key(0, start_y + i*row_h, side_w, row_h, ops[i], false, pressed, false); - } - - // Right Col - // %, " ", <-, EXE - // const char* r_labels[] = {"%", " ", "<-", "EXE"}; - // const char* r_vals[] = {"%", " ", "BACKSPACE", "ENTER"}; - // is_spec, is_acc - - // % - draw_key(SCREEN_W - side_w, start_y, side_w, row_h, "%", false, (last_key && strcmp(last_key, "%") == 0), false); - // Space - draw_key(SCREEN_W - side_w, start_y + row_h, side_w, row_h, " ", false, (last_key && strcmp(last_key, " ") == 0), false); - // Backspace - draw_key(SCREEN_W - side_w, start_y + 2*row_h, side_w, row_h, "<-", true, (last_key && strcmp(last_key, "BACKSPACE") == 0), false); - // Enter - draw_key(SCREEN_W - side_w, start_y + 3*row_h, side_w, row_h, "EXE", false, (last_key && strcmp(last_key, "ENTER") == 0), true); - - // Numpad 1-9 - const char* nums[3][3] = {{"1","2","3"}, {"4","5","6"}, {"7","8","9"}}; - for (int r=0; r<3; r++) { - for (int c=0; c<3; c++) { - bool pressed = (last_key && strcmp(last_key, nums[r][c]) == 0); - draw_key(side_w + c*numpad_w, start_y + r*row_h, numpad_w, row_h, nums[r][c], false, pressed, false); - } - } - - // Bottom Row: , # 0 = . - int16_t y_bot = start_y + 3*row_h; - int16_t unit_w = center_w / 6; - const char* bot_row[] = {",", "#", "0", "=", "."}; - int widths[] = {1, 1, 2, 1, 1}; - int16_t cur_x = side_w; - - for (int i=0; i<5; i++) { - int16_t w = widths[i] * unit_w; - if (i == 4) w = (side_w + center_w) - cur_x; - bool pressed = (last_key && strcmp(last_key, bot_row[i]) == 0); - draw_key(cur_x, y_bot, w, row_h, bot_row[i], false, pressed, false); - cur_x += w; - } - } - - void Render() { - if (!visible) return; - - int16_t y_pos = SCREEN_H - KBD_H; - // Background - drawFilledRectangle(0, y_pos, SCREEN_W, KBD_H, theme.kbd_bg); - line(0, y_pos, SCREEN_W, y_pos, theme.key_spec); // Top border line - - draw_tabs(); - - if (current_tab == 2) { - draw_math(); - } else { - draw_grid(); - } - } - - // Returns the key string if pressed, or NULL - const char* Update(uint16_t touch_x, uint16_t touch_y, uint32_t type) { - if (!visible) return nullptr; - int16_t kbd_y = SCREEN_H - KBD_H; - if (touch_y < kbd_y) return nullptr; - - // Reset last key on touch down - if (type == TOUCH_DOWN) last_key = nullptr; - - // Tabs - if (touch_y < kbd_y + TAB_H) { - if (type == TOUCH_DOWN) { - int16_t tab_w = SCREEN_W / 3; - current_tab = touch_x / tab_w; - if (current_tab > 2) current_tab = 2; - Render(); // Redraw immediately - } - return nullptr; - } - - const char* ret = nullptr; - - if (current_tab == 2) { - // Math Layout Hit Test - int16_t start_y = kbd_y + TAB_H; - int16_t total_h = KBD_H - TAB_H; - int16_t row_h = total_h / 4; - int16_t side_w = 50; - int16_t center_w = SCREEN_W - (side_w * 2); - int16_t numpad_w = center_w / 3; - - int row = (touch_y - start_y) / row_h; - if (row < 0 || row > 3) return nullptr; - - // Left Col - if (touch_x < side_w) { - const char* ops[] = {"+", "-", "*", "/"}; - ret = ops[row]; - } - // Right Col - else if (touch_x >= SCREEN_W - side_w) { - const char* vals[] = {"%", " ", "BACKSPACE", "ENTER"}; - ret = vals[row]; - } - // Center - else { - if (row < 3) { - int col = (touch_x - side_w) / numpad_w; - const char* nums[3][3] = {{"1","2","3"}, {"4","5","6"}, {"7","8","9"}}; - if (col >= 0 && col < 3) ret = nums[row][col]; - } else { - // Bottom row - int16_t rel_x = touch_x - side_w; - int16_t unit_w = center_w / 6; - // , # 0 = . widths: 1 1 2 1 1 - if (rel_x < unit_w) ret = ","; - else if (rel_x < unit_w*2) ret = "#"; - else if (rel_x < unit_w*4) ret = "0"; - else if (rel_x < unit_w*5) ret = "="; - else ret = "."; - } - } - - } else { - // Grid Layout Hit Test - int16_t grid_y = kbd_y + TAB_H; - int16_t row_h = 45; - int row_idx = (touch_y - grid_y) / row_h; - - if (row_idx >= 0 && row_idx < 4) { - const char** layout = (current_tab == 1) ? layout_sym : layout_alpha; - const char* row_str = layout[row_idx]; - int count = strlen(row_str); - int16_t kw = SCREEN_W / count; - int col_idx = touch_x / kw; - if (col_idx >= count) col_idx = count - 1; - - static char charStr[2] = {0, 0}; - char char_code = row_str[col_idx]; - if (current_tab == 0 && shift && char_code >= 'a' && char_code <= 'z') { - char_code -= 32; - } - charStr[0] = char_code; - ret = charStr; - - } else if (row_idx == 4) { - if (touch_x < 50) { - if (type == TOUCH_DOWN) { - shift = !shift; - Render(); - } - return nullptr; // Shift toggle doesn't return key char - } else if (touch_x < 100) { - ret = "BACKSPACE"; - } else if (touch_x < 260) { - ret = " "; - } else { - ret = "ENTER"; - } - } - } - - if (type == TOUCH_DOWN) { - last_key = ret; - Render(); // Visual feedback - } else if (type == TOUCH_UP) { - last_key = nullptr; - Render(); // Clear feedback - } - - return ret; - } - - // Toggle Visibility - void Toggle() { - visible = !visible; - // Need to clear screen area if hidden? - // The main loop usually redraws everything, or we might need to trigger a full refresh. - } + public: + void Render(); + void SetFont(uint8_t* font); + void Highlight(uint8_t x, uint8_t y, uint32_t highlightColor = color(255,255,0), bool hide = false); + void ToggleShift(); + bool caps = false; + int16_t x = 8; + int16_t y = 482; + int16_t xmargin = 10; + int16_t ymargin = 10; + uint8_t* font; + int8_t xcursor = 0; + int8_t ycursor = 0; + int8_t yfix = 0; + const char lower[27] = "abcdefghijklmnopqrstuvwxyz"; + const char characters[67] = "1234567890=!@#$%^&*_+-ABCDEFGHIJKLM\\|;:',./?NOPQRSTUVWXYZ[]{}()\"~`"; + const char specials[23] = "[SPACE] [BACK] [ENTER]"; + char activeKey[2] = ""; + int32_t keyColor = color(255, 255, 255); }; + +void VirtualKeyboard::ToggleShift() { + // call render to update the keyboard + // will need something to only update the qwerty keys + + // set the color to black so that we cannot see the text + this->keyColor = color(0, 0, 0); + this->Render(); + this->caps = !this->caps; + // set back to white + this->keyColor = color(255, 255, 255); + this->Render(); + // render highlighted key + this->Highlight(this->xcursor, this->ycursor); + +} + +// x is between 0 and 21 (inclusive) +// y is between 0 and 2 (inclusive) +void VirtualKeyboard::Highlight(uint8_t xOffset = 0, uint8_t yOffset = 0, uint32_t highlightColor, bool hide) { + // KEYBOARD + // 1 2 3 4 5 6 7 8 9 0 = ! @ # $ % ^ & * _ + - + // A B C D E F G H I J K L M \ | ; : ' , . / ? + // N O P Q R S T U V W X Y Z [ ] { } ( ) " ~ ` + // [ S P A C E ] [ B A C K ] [ E N T E R ] + + // get active key + // check if within first 13 chars on y=1,2 + if (xOffset < 13 && !this->caps && yOffset != 0 && yOffset != 3) { + if (yOffset == 1) { + this->activeKey[0] = this->lower[xOffset]; + } else { + this->activeKey[0] = this->lower[xOffset + 13]; + } + } else { + this->activeKey[0] = this->characters[xOffset + (yOffset * 22)]; + } + + if (yOffset != 3) { + DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * xOffset, this->y + this->ymargin * yOffset, highlightColor, 0); + + // draw rectangle around the character + + if (hide) { + highlightColor = color(0, 0, 0); + } + line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * yOffset - 2, highlightColor); + line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); + line(this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); + line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * (yOffset + 1) - 1, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); + } else { + // else specials + char specialChar[2] = ""; + + if (xOffset == 0) { + for (int i = 0; i < 7; i++) { + specialChar[0] = this->specials[i]; + DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i), this->y + this->ymargin * yOffset, highlightColor, 0); + } + } else if (xOffset == 1) { + for (int i = 8; i < 14; i++) { + specialChar[0] = this->specials[i]; + DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i - 1), this->y + this->ymargin * yOffset, highlightColor, 0); + } + } else if (xOffset == 2) { + for (int i = 15; i < 22; i++) { + specialChar[0] = this->specials[i]; + DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i - 2), this->y + this->ymargin * yOffset, highlightColor, 0); + } + } + } +} + +void VirtualKeyboard::SetFont(uint8_t* font) { + this->font = font; +} + +void VirtualKeyboard::Render() { + + // render normals + for (int i = 0; i < 66; i++) { + this->activeKey[0] = this->characters[i]; + // if in top row + if (i < 22) { + DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * i, this->y, this->keyColor, 0); + // if in 2nd row + } else if (i < 44) { + // check for left 13 keys + if (i < 35 && !this->caps) { + this->activeKey[0] = this->lower[i - 22]; + } + DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * (i - 22), this->y + this->ymargin, this->keyColor, 0); + // 3rd + } else { + // check for left 13 keys + if (i < 57 && !this->caps) { + this->activeKey[0] = this->lower[i - 44 + 13]; + } + DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * (i - 44), this->y + this->ymargin * 2, this->keyColor, 0); + } + } + // render specials + for (int i = 0; i < 23; i++) { + this->activeKey[0] = this->specials[i]; + DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * i, this->y + this->ymargin * 3, this->keyColor, 0); + } + +} From 52d6e5d066686f615a55c00de870df2ab3771e4c Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Thu, 26 Feb 2026 02:04:51 +0000 Subject: [PATCH 21/23] Fix CI build errors: re-apply lost changes for keyboard, terminal, and internal headers Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/core/touch_event_handler.hpp | 25 +------------------------ src/commands/ls.cpp | 2 +- src/internal.hpp | 8 ++++++-- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/lib/core/touch_event_handler.hpp b/lib/core/touch_event_handler.hpp index 9e6c711..99a19d3 100644 --- a/lib/core/touch_event_handler.hpp +++ b/lib/core/touch_event_handler.hpp @@ -126,29 +126,6 @@ void addActBarListener(uint16_t type, void (*callback)()) { actBarHandlers[actBarHandlersLength++] = handler; } -void addListener(Input_Keycode key, void (*callback)()) { - KeyListener handler; - handler.key = key; - handler.callback = callback; - keyListeners[keyListenersLength++] = handler; -} - -// Support for Keys1 legacy? -// No, migrating to SDK keycodes. -// But legacy event_handler.hpp defines addListener(Keys1). -// To avoid conflict, I should rename my addListener or update main.cpp to call addKeyListener? -// The error in main.cpp was: `cannot convert 'Input_Keycode' to 'Keys1'`. -// And it referenced `lib/core/event_handler.hpp`. -// `main.cpp` includes `lib/core/event_handler.hpp`. -// It does NOT include `touch_event_handler.hpp` directly? No, it does. -// But `addListener` is ambiguous or `event_handler.hpp`'s version is picked. -// `event_handler.hpp` has `void addListener(Keys1 key...`. -// `touch_event_handler.hpp` has `void addTouchListener...`. -// I should add `addKeyListener` to `touch_event_handler.hpp` and USE IT in `main.cpp` instead of `addListener`. -// And I should likely NOT include `event_handler.hpp` in `main.cpp` if I want to fully migrate. -// But `main.cpp` calls `checkEvents`. -// I'll define `addKeyListener` here. - void addKeyListener(Input_Keycode key, void (*callback)()) { KeyListener handler; handler.key = key; @@ -165,7 +142,7 @@ void removeTouchListener(uint32_t minX, uint32_t minY, uint32_t maxX, uint32_t m } } -void removeActBarListener(uint16_t type) { +void removeActBarListener(uint32_t type) { for (uint32_t i = 0; i < actBarHandlersLength; i++) { if (actBarHandlers[i].type == type) { actBarHandlers[i] = actBarHandlers[actBarHandlersLength - 1]; diff --git a/src/commands/ls.cpp b/src/commands/ls.cpp index 4ac2909..3e1d01e 100644 --- a/src/commands/ls.cpp +++ b/src/commands/ls.cpp @@ -47,7 +47,7 @@ extern int ls_main(int, char **) directory[dirFiles++] = thisfile; //serch the next - ret = File_FindNext(findHandle, (char_const16_t*)(char_const16_t*)fileName, &findInfoBuf); + ret = File_FindNext(findHandle, fileName, &findInfoBuf); } File_FindClose(findHandle); diff --git a/src/internal.hpp b/src/internal.hpp index 76d2b12..5e355b4 100644 --- a/src/internal.hpp +++ b/src/internal.hpp @@ -1,5 +1,9 @@ -/* - * Ported from BusyBox main internal header file on 0_32 branch +/** + * @file internal.hpp + * @author Sean McGinty (newfolderlocation@gmail.com) + * @brief Ported from BusyBox main internal header file on 0_32 branch + * @version 1.1 + * @date 2022-06-09 */ #ifndef _INTERNAL_H_ From a9c6ab4062b6e761463e5e5e705606b083762ab3 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:01:00 +0000 Subject: [PATCH 22/23] Fix CI build errors: re-apply lost code for keyboard/terminal Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/terminal.hpp | 219 ++++++++++++----- src/virtual_keyboard.hpp | 491 +++++++++++++++++++++++++++++---------- 2 files changed, 519 insertions(+), 191 deletions(-) diff --git a/src/terminal.hpp b/src/terminal.hpp index 45fbcf5..eb163d8 100644 --- a/src/terminal.hpp +++ b/src/terminal.hpp @@ -1,15 +1,11 @@ -/** - * @file terminal.hpp - * @author Sean McGinty (newfolderlocation@gmail.com) - * @brief A terminal class for CPShell, which handles all terminal input and output. - * @version 1.1 - * @date 2022-06-09 - */ - #pragma once #include "../calc.hpp" #include "../lib/functions/shapes.hpp" +#include +#include +#include +#include "internal.hpp" // Terminal Class class Terminal { @@ -23,108 +19,205 @@ class Terminal { void SetFont(uint8_t* font); void SetColor(uint32_t newColor); void SetCursorColor(uint32_t newColor); + void Render(); // Redraw visible lines + void Scroll(int lines); + void ScrollToBottom(); + void UpdateLayout(); + // buffer for command input char bufferIn[BUF_SIZE]; int8_t bufferInPos = 0; - // buffer cursor position - int16_t bufferCX = 0; - int16_t bufferCY = 0; - int16_t bufferOffsetX = 2; - int16_t bufferOffsetY = 2; + + // History Buffer + std::vector history; + + // View State + int scrollOffset = 0; // 0 means showing the top. Or maybe 0 means bottom? + // Let's say scrollOffset is the index of the first visible line. + // But we want auto-scroll to end. + // Maybe scrollOffset is "lines from bottom"? + // Let's use: scrollY = index of top visible line. + int scrollY = 0; + + // Dimensions int16_t xmargin = 8; // character width - int16_t ymargin = 9; // character height - uint8_t* font; + int16_t ymargin = 10; // character height (7x8 font + spacing) int16_t termWidth = width - xmargin * 2; - int16_t termHeight = 40; + // termHeight depends on keyboard visibility? + // User said: "prompt would always be visible". + // When keyboard is visible, terminal area is smaller. + // When hidden, full screen. + int16_t termHeight = height - 40; // Default full screen minus some margin? int16_t xmax = termWidth / xmargin; + int16_t ymax; // Calculated based on height + + uint8_t* font; uint32_t color = 0xFFFF; // white uint32_t cursorColor = 0xFFFF; // white + + bool keyboardVisible = true; + + Terminal() { + history.push_back(""); // Start with one empty line + UpdateLayout(); + } }; +void Terminal::UpdateLayout() { + int kbdH = keyboardVisible ? KBD_H : 0; + termHeight = height - kbdH; + ymax = termHeight / ymargin; + ScrollToBottom(); +} + void Terminal::SetFont(uint8_t* font) { this->font = font; } void Terminal::ClearBuffer() { this->bufferInPos = 0; - this->bufferCX = 0; + // No, we don't clear history here. Just the current input line buffer. + // But Wait, WriteBuffer writes to `bufferIn` AND history? + // `bufferIn` tracks the *current command being typed*. + // The history vector tracks *displayed lines*. + // When user types, we update the last line of history? + // Or we treat `bufferIn` separate? + // Standard terminal: history contains committed lines + current line being edited. + // Let's sync them. +} + +void Terminal::Scroll(int lines) { + scrollY += lines; + int maxScroll = std::max(0, (int)history.size() - ymax); + if (scrollY < 0) scrollY = 0; + if (scrollY > maxScroll) scrollY = maxScroll; + Render(); +} + +void Terminal::ScrollToBottom() { + int maxScroll = std::max(0, (int)history.size() - ymax); + scrollY = maxScroll; } void Terminal::WriteBuffer(char c, bool hideCursor) { - // hideCursor should only be false from WriteChars as writing to vram is slow if (hideCursor) this->HideCursor(); - // check if '\n' + + std::string& currentLine = history.back(); + if (c == '\n') { - this->ClearBuffer(); - this->bufferCY++; + history.push_back(""); + bufferInPos = 0; + ScrollToBottom(); + Render(); // Need to redraw to show scroll return; } - if (this->bufferInPos < (BUF_SIZE - 1)) { - // add the character to the buffer - this->bufferIn[this->bufferInPos] = c; - this->bufferInPos++; - - // buf of chars array that will have c in it - char buf[2] = {c, '\0'}; - - // draw the character - DRAW_FONT(this->font, buf, this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->color, this->termWidth); - - // check for overflow on screen - if (this->bufferCX < this->xmax) { - this->bufferCX++; - } else { - this->bufferCX = 0; - this->bufferCY++; + // Handle Backspace (passed as special char or handled in RemoveLast?) + // RemoveLast calls this? No. + + if (c >= 32 && c <= 126) { + currentLine += c; + bufferIn[bufferInPos++] = c; // Keep bufferIn for command processing + + // Wrap text + if (currentLine.length() >= (size_t)xmax) { + // Move excess to next line? + // Simple wrapping: just let it grow and Renderer handles it? + // Or split? + // Splitting is hard. Let's just break line. + std::string remainder = currentLine.substr(xmax); + currentLine = currentLine.substr(0, xmax); + history.push_back(remainder); } - } else { - Debug_Printf(1,1,true,0,"Buffer overflow!"); } + + ScrollToBottom(); + Render(); } -/* - * Usage - * char myMessage[] = "Hello World!\nSupports newlines!"; - * Terminal* terminal; - * terminal->WriteChars(myMessage); - */ void Terminal::WriteChars(const char *charArray, bool skipClear) { int len = strlen(charArray); for (int i = 0; i < len; i++) { this->WriteBuffer(charArray[i], false); } - // clear the buffer - if (!skipClear) this->ClearBuffer(); + if (!skipClear) { + bufferInPos = 0; // Reset input buffer, but history persists + // memset(bufferIn, 0, BUF_SIZE); + } } void Terminal::RemoveLast() { - if (this->bufferInPos == 0) return; - // remove the active cursor - this->HideCursor(); - // check for wrapping - if (this->bufferCX == 0) { - this->bufferCY--; - this->bufferCX = this->xmax; + std::string& currentLine = history.back(); + if (currentLine.length() > 0) { + currentLine.pop_back(); + if (bufferInPos > 0) bufferInPos--; + Render(); } else { - this->bufferCX--; + // Merge with previous line if empty? + if (history.size() > 1) { + history.pop_back(); + // bufferInPos needs to be restored? + // This is complex if we allow backspacing over newlines. + // For now, simple backspace on current line. + Render(); + } + } +} + +void Terminal::Render() { + // Clear visible area + int visibleH = keyboardVisible ? (height - KBD_H) : height; + drawFilledRectangle(0, 0, width, visibleH, 0x0000); // Black bg + + if (!font) return; + + int startLine = scrollY; + int endLine = std::min((int)history.size(), startLine + ymax + 1); + + for (int i = startLine; i < endLine; i++) { + int screenY = (i - startLine) * ymargin; + if (screenY >= visibleH) break; + + const std::string& line = history[i]; + // const char* text = line.c_str(); // DRAW_FONT takes char*? const char* fix applied. + // Need mutable for DRAW_FONT? No, fixed to const char*. + + DRAW_FONT(font, line.c_str(), 2, screenY, color, width); } - this->bufferInPos--; - // remove the last character from vram - drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, 0); // 0 is always color black / 0,0,0 + // Draw cursor if at bottom? + if (scrollY >= (int)history.size() - ymax) { + ShowCursor(); + } } // Cursor Show void Terminal::ShowCursor() { - // draw the cursor - drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, this->cursorColor); + // Find cursor position + // It's at the end of the last line + if (history.empty()) return; + + int lineIdx = history.size() - 1; + // Is it visible? + if (lineIdx < scrollY || lineIdx >= scrollY + ymax) return; + + int screenY = (lineIdx - scrollY) * ymargin; + int screenX = 2 + history.back().length() * xmargin; + + drawFilledRectangle(screenX, screenY, xmargin, ymargin, cursorColor); } // Cursor Hide void Terminal::HideCursor() { - // draw the cursor - drawFilledRectangle(this->bufferOffsetX + this->bufferCX * this->xmargin, this->bufferOffsetY + this->bufferCY * this->ymargin, this->xmargin, this->ymargin, 0); // 0 is always color black / 0,0,0 + // Find cursor position and clear it + if (history.empty()) return; + int lineIdx = history.size() - 1; + if (lineIdx < scrollY || lineIdx >= scrollY + ymax) return; + + int screenY = (lineIdx - scrollY) * ymargin; + int screenX = 2 + history.back().length() * xmargin; + + drawFilledRectangle(screenX, screenY, xmargin, ymargin, 0x0000); } // Set Color diff --git a/src/virtual_keyboard.hpp b/src/virtual_keyboard.hpp index e75b226..1135a23 100644 --- a/src/virtual_keyboard.hpp +++ b/src/virtual_keyboard.hpp @@ -1,132 +1,367 @@ #pragma once -// KeyBoard Class -class VirtualKeyboard { - public: - void Render(); - void SetFont(uint8_t* font); - void Highlight(uint8_t x, uint8_t y, uint32_t highlightColor = color(255,255,0), bool hide = false); - void ToggleShift(); - bool caps = false; - int16_t x = 8; - int16_t y = 482; - int16_t xmargin = 10; - int16_t ymargin = 10; - uint8_t* font; - int8_t xcursor = 0; - int8_t ycursor = 0; - int8_t yfix = 0; - const char lower[27] = "abcdefghijklmnopqrstuvwxyz"; - const char characters[67] = "1234567890=!@#$%^&*_+-ABCDEFGHIJKLM\\|;:',./?NOPQRSTUVWXYZ[]{}()\"~`"; - const char specials[23] = "[SPACE] [BACK] [ENTER]"; - char activeKey[2] = ""; - int32_t keyColor = color(255, 255, 255); +#include "../calc.hpp" +#include "../lib/functions/shapes.hpp" +#include +#include +#include +#include "internal.hpp" + +// Theme colors (Light theme from cinput.py) +#define C_WHITE 0xFFFF +#define C_BLACK 0x0000 +#define C_LIGHT 0xCE59 // Grey +#define C_DARK 0x0000 + +// Theme dictionary equivalent +struct Theme { + uint16_t modal_bg = C_WHITE; + uint16_t kbd_bg = C_WHITE; + uint16_t key_bg = C_WHITE; + uint16_t key_spec = 0xCE59; // Secondary (Light Grey) ~ RGB(200, 200, 200) + uint16_t key_out = C_BLACK; + uint16_t txt = 0x0000; + uint16_t txt_dim = 0x4208; // Dark Grey + uint16_t accent = 0x0010; // Dark Blue-ish + uint16_t txt_acc = C_WHITE; + uint16_t hl = 0xCE59; +}; + +struct KeyRect { + int16_t x, y, w, h; + const char* label; + const char* val; // if NULL, use label + bool is_spec; + bool is_acc; }; -void VirtualKeyboard::ToggleShift() { - // call render to update the keyboard - // will need something to only update the qwerty keys - - // set the color to black so that we cannot see the text - this->keyColor = color(0, 0, 0); - this->Render(); - this->caps = !this->caps; - // set back to white - this->keyColor = color(255, 255, 255); - this->Render(); - // render highlighted key - this->Highlight(this->xcursor, this->ycursor); - -} - -// x is between 0 and 21 (inclusive) -// y is between 0 and 2 (inclusive) -void VirtualKeyboard::Highlight(uint8_t xOffset = 0, uint8_t yOffset = 0, uint32_t highlightColor, bool hide) { - // KEYBOARD - // 1 2 3 4 5 6 7 8 9 0 = ! @ # $ % ^ & * _ + - - // A B C D E F G H I J K L M \ | ; : ' , . / ? - // N O P Q R S T U V W X Y Z [ ] { } ( ) " ~ ` - // [ S P A C E ] [ B A C K ] [ E N T E R ] - - // get active key - // check if within first 13 chars on y=1,2 - if (xOffset < 13 && !this->caps && yOffset != 0 && yOffset != 3) { - if (yOffset == 1) { - this->activeKey[0] = this->lower[xOffset]; - } else { - this->activeKey[0] = this->lower[xOffset + 13]; - } - } else { - this->activeKey[0] = this->characters[xOffset + (yOffset * 22)]; - } - - if (yOffset != 3) { - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * xOffset, this->y + this->ymargin * yOffset, highlightColor, 0); - - // draw rectangle around the character - - if (hide) { - highlightColor = color(0, 0, 0); - } - line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * yOffset - 2, highlightColor); - line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); - line(this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * yOffset - 2, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); - line(this->x + this->xmargin * xOffset - 2, this->y + this->ymargin * (yOffset + 1) - 1, this->x + this->xmargin * (xOffset + 1) - 1, this->y + this->ymargin * (yOffset + 1) - 1, highlightColor); - } else { - // else specials - char specialChar[2] = ""; - - if (xOffset == 0) { - for (int i = 0; i < 7; i++) { - specialChar[0] = this->specials[i]; - DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i), this->y + this->ymargin * yOffset, highlightColor, 0); - } - } else if (xOffset == 1) { - for (int i = 8; i < 14; i++) { - specialChar[0] = this->specials[i]; - DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i - 1), this->y + this->ymargin * yOffset, highlightColor, 0); - } - } else if (xOffset == 2) { - for (int i = 15; i < 22; i++) { - specialChar[0] = this->specials[i]; - DRAW_FONT(this->font, specialChar, this->x + this->xmargin * (xOffset + i - 2), this->y + this->ymargin * yOffset, highlightColor, 0); - } - } - } -} - -void VirtualKeyboard::SetFont(uint8_t* font) { - this->font = font; -} - -void VirtualKeyboard::Render() { - - // render normals - for (int i = 0; i < 66; i++) { - this->activeKey[0] = this->characters[i]; - // if in top row - if (i < 22) { - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * i, this->y, this->keyColor, 0); - // if in 2nd row - } else if (i < 44) { - // check for left 13 keys - if (i < 35 && !this->caps) { - this->activeKey[0] = this->lower[i - 22]; - } - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * (i - 22), this->y + this->ymargin, this->keyColor, 0); - // 3rd - } else { - // check for left 13 keys - if (i < 57 && !this->caps) { - this->activeKey[0] = this->lower[i - 44 + 13]; - } - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * (i - 44), this->y + this->ymargin * 2, this->keyColor, 0); - } - } - // render specials - for (int i = 0; i < 23; i++) { - this->activeKey[0] = this->specials[i]; - DRAW_FONT(this->font, this->activeKey, this->x + this->xmargin * i, this->y + this->ymargin * 3, this->keyColor, 0); - } - -} +class VirtualKeyboard { +public: + bool visible = true; + int current_tab = 0; // 0: ABC, 1: Sym, 2: Math + bool shift = false; + const char* last_key = nullptr; + uint8_t* font; + Theme theme; + + // Layouts + const char* tabs[3] = {"ABC", "Sym", "Math"}; + + // QWERTY Layout + const char* layout_alpha[4] = { + "1234567890", + "qwertyuiop", + "asdfghjkl:", + "zxcvbnm,._" + }; + + const char* layout_sym[4] = { + "1234567890", + "@#$_&-+()/", + "=\\<*\"':;!?", + "{}[]^~`|<>" + }; + + VirtualKeyboard() { + // Initialize theme + } + + void SetFont(uint8_t* f) { + font = f; + } + + void draw_rect_border(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t border_col) { + drawRectangle(x, y, w, h, border_col); + } + + void draw_text_centered(int16_t cx, int16_t cy, const char* text, uint16_t color) { + if (!font) return; + // Estimate width (8px per char approx) + int len = strlen(text); + int w = len * 8; + int h = 10; + DRAW_FONT(font, text, cx - w/2, cy - h/2, color, 0); + } + + void draw_key(int16_t x, int16_t y, int16_t w, int16_t h, const char* label, bool is_special, bool is_pressed, bool is_accent) { + uint16_t bg; + if (is_pressed) bg = theme.hl; + else if (is_accent) bg = theme.accent; + else if (is_special) bg = theme.key_spec; + else bg = theme.key_bg; + + uint16_t txt_col = is_accent ? theme.txt_acc : theme.txt; + uint16_t border_col = theme.key_spec; + + drawFilledRectangle(x + 1, y + 1, w - 2, h - 2, bg); + draw_rect_border(x, y, w, h, border_col); + draw_text_centered(x + w/2, y + h/2, label, txt_col); + } + + void draw_tabs() { + int16_t y_pos = SCREEN_H - KBD_H; + int16_t tab_w = SCREEN_W / 3; + uint16_t border_col = theme.key_spec; + + for (int i = 0; i < 3; i++) { + int16_t tx = i * tab_w; + bool is_active = (i == current_tab); + uint16_t bg = is_active ? theme.kbd_bg : theme.key_spec; + + drawFilledRectangle(tx, y_pos, tab_w, TAB_H, bg); + draw_rect_border(tx, y_pos, tab_w, TAB_H, border_col); + + // "Active" look (cover bottom border) + if (is_active) { + // drawFilledRectangle(tx + 1, y_pos + TAB_H - 1, tab_w - 2, 2, theme.kbd_bg); + } + + const char* name = tabs[i]; + // If tab 0 and not qwerty (not implemented yet), change name? + draw_text_centered(tx + tab_w/2, y_pos + TAB_H/2, name, theme.txt); + } + } + + void draw_grid() { + int16_t grid_y = (SCREEN_H - KBD_H) + TAB_H; + int16_t row_h = 45; + + const char** layout = (current_tab == 1) ? layout_sym : layout_alpha; + + char labelBuf[2] = {0, 0}; + + for (int r = 0; r < 4; r++) { + const char* row = layout[r]; + int count = strlen(row); + int16_t kw = SCREEN_W / count; + + for (int c = 0; c < count; c++) { + int16_t kx = c * kw; + int16_t ky = grid_y + r * row_h; + + char char_code = row[c]; + if (current_tab == 0 && shift) { + if (char_code >= 'a' && char_code <= 'z') char_code -= 32; + } + + labelBuf[0] = char_code; + + // Check if pressed + bool is_pressed = false; + // We need to know if this specific key is pressed. + // For simplicity, we can rely on immediate redraw or state. + // Assuming last_key stores the char string. + if (last_key && last_key[0] == char_code && last_key[1] == 0) is_pressed = true; + + draw_key(kx, ky, kw, row_h, labelBuf, false, is_pressed, false); + } + } + + // Bottom Control Row + int16_t bot_y = grid_y + 4 * row_h; + int16_t bot_h = row_h; + + bool caps_pressed = shift; + bool back_pressed = (last_key && strcmp(last_key, "BACKSPACE") == 0); + bool space_pressed = (last_key && strcmp(last_key, " ") == 0); + bool enter_pressed = (last_key && strcmp(last_key, "ENTER") == 0); + + draw_key(0, bot_y, 50, bot_h, "CAPS", true, caps_pressed, false); + draw_key(50, bot_y, 50, bot_h, "<-", true, back_pressed, false); + draw_key(100, bot_y, 160, bot_h, "Space", false, space_pressed, false); + draw_key(260, bot_y, 60, bot_h, "EXE", false, enter_pressed, true); + } + + // Helper for Math layout (simplified from cinput.py) + void draw_math() { + int16_t start_y = (SCREEN_H - KBD_H) + TAB_H; + int16_t total_h = KBD_H - TAB_H; + int16_t row_h = total_h / 4; + int16_t side_w = 50; + int16_t center_w = SCREEN_W - (side_w * 2); + int16_t numpad_w = center_w / 3; + + // Left Col: + - * / + const char* ops[] = {"+", "-", "*", "/"}; + for (int i=0; i<4; i++) { + bool pressed = (last_key && strcmp(last_key, ops[i]) == 0); + draw_key(0, start_y + i*row_h, side_w, row_h, ops[i], false, pressed, false); + } + + // Right Col + // %, " ", <-, EXE + // const char* r_labels[] = {"%", " ", "<-", "EXE"}; + // const char* r_vals[] = {"%", " ", "BACKSPACE", "ENTER"}; + // is_spec, is_acc + + // % + draw_key(SCREEN_W - side_w, start_y, side_w, row_h, "%", false, (last_key && strcmp(last_key, "%") == 0), false); + // Space + draw_key(SCREEN_W - side_w, start_y + row_h, side_w, row_h, " ", false, (last_key && strcmp(last_key, " ") == 0), false); + // Backspace + draw_key(SCREEN_W - side_w, start_y + 2*row_h, side_w, row_h, "<-", true, (last_key && strcmp(last_key, "BACKSPACE") == 0), false); + // Enter + draw_key(SCREEN_W - side_w, start_y + 3*row_h, side_w, row_h, "EXE", false, (last_key && strcmp(last_key, "ENTER") == 0), true); + + // Numpad 1-9 + const char* nums[3][3] = {{"1","2","3"}, {"4","5","6"}, {"7","8","9"}}; + for (int r=0; r<3; r++) { + for (int c=0; c<3; c++) { + bool pressed = (last_key && strcmp(last_key, nums[r][c]) == 0); + draw_key(side_w + c*numpad_w, start_y + r*row_h, numpad_w, row_h, nums[r][c], false, pressed, false); + } + } + + // Bottom Row: , # 0 = . + int16_t y_bot = start_y + 3*row_h; + int16_t unit_w = center_w / 6; + const char* bot_row[] = {",", "#", "0", "=", "."}; + int widths[] = {1, 1, 2, 1, 1}; + int16_t cur_x = side_w; + + for (int i=0; i<5; i++) { + int16_t w = widths[i] * unit_w; + if (i == 4) w = (side_w + center_w) - cur_x; + bool pressed = (last_key && strcmp(last_key, bot_row[i]) == 0); + draw_key(cur_x, y_bot, w, row_h, bot_row[i], false, pressed, false); + cur_x += w; + } + } + + void Render() { + if (!visible) return; + + int16_t y_pos = SCREEN_H - KBD_H; + // Background + drawFilledRectangle(0, y_pos, SCREEN_W, KBD_H, theme.kbd_bg); + line(0, y_pos, SCREEN_W, y_pos, theme.key_spec); // Top border line + + draw_tabs(); + + if (current_tab == 2) { + draw_math(); + } else { + draw_grid(); + } + } + + // Returns the key string if pressed, or NULL + const char* Update(uint16_t touch_x, uint16_t touch_y, uint32_t type) { + if (!visible) return nullptr; + int16_t kbd_y = SCREEN_H - KBD_H; + if (touch_y < kbd_y) return nullptr; + + // Reset last key on touch down + if (type == TOUCH_DOWN) last_key = nullptr; + + // Tabs + if (touch_y < kbd_y + TAB_H) { + if (type == TOUCH_DOWN) { + int16_t tab_w = SCREEN_W / 3; + current_tab = touch_x / tab_w; + if (current_tab > 2) current_tab = 2; + Render(); // Redraw immediately + } + return nullptr; + } + + const char* ret = nullptr; + + if (current_tab == 2) { + // Math Layout Hit Test + int16_t start_y = kbd_y + TAB_H; + int16_t total_h = KBD_H - TAB_H; + int16_t row_h = total_h / 4; + int16_t side_w = 50; + int16_t center_w = SCREEN_W - (side_w * 2); + int16_t numpad_w = center_w / 3; + + int row = (touch_y - start_y) / row_h; + if (row < 0 || row > 3) return nullptr; + + // Left Col + if (touch_x < side_w) { + const char* ops[] = {"+", "-", "*", "/"}; + ret = ops[row]; + } + // Right Col + else if (touch_x >= SCREEN_W - side_w) { + const char* vals[] = {"%", " ", "BACKSPACE", "ENTER"}; + ret = vals[row]; + } + // Center + else { + if (row < 3) { + int col = (touch_x - side_w) / numpad_w; + const char* nums[3][3] = {{"1","2","3"}, {"4","5","6"}, {"7","8","9"}}; + if (col >= 0 && col < 3) ret = nums[row][col]; + } else { + // Bottom row + int16_t rel_x = touch_x - side_w; + int16_t unit_w = center_w / 6; + // , # 0 = . widths: 1 1 2 1 1 + if (rel_x < unit_w) ret = ","; + else if (rel_x < unit_w*2) ret = "#"; + else if (rel_x < unit_w*4) ret = "0"; + else if (rel_x < unit_w*5) ret = "="; + else ret = "."; + } + } + + } else { + // Grid Layout Hit Test + int16_t grid_y = kbd_y + TAB_H; + int16_t row_h = 45; + int row_idx = (touch_y - grid_y) / row_h; + + if (row_idx >= 0 && row_idx < 4) { + const char** layout = (current_tab == 1) ? layout_sym : layout_alpha; + const char* row_str = layout[row_idx]; + int count = strlen(row_str); + int16_t kw = SCREEN_W / count; + int col_idx = touch_x / kw; + if (col_idx >= count) col_idx = count - 1; + + static char charStr[2] = {0, 0}; + char char_code = row_str[col_idx]; + if (current_tab == 0 && shift && char_code >= 'a' && char_code <= 'z') { + char_code -= 32; + } + charStr[0] = char_code; + ret = charStr; + + } else if (row_idx == 4) { + if (touch_x < 50) { + if (type == TOUCH_DOWN) { + shift = !shift; + Render(); + } + return nullptr; // Shift toggle doesn't return key char + } else if (touch_x < 100) { + ret = "BACKSPACE"; + } else if (touch_x < 260) { + ret = " "; + } else { + ret = "ENTER"; + } + } + } + + if (type == TOUCH_DOWN) { + last_key = ret; + Render(); // Visual feedback + } else if (type == TOUCH_UP) { + last_key = nullptr; + Render(); // Clear feedback + } + + return ret; + } + + // Toggle Visibility + void Toggle() { + visible = !visible; + // Need to clear screen area if hidden? + // The main loop usually redraws everything, or we might need to trigger a full refresh. + } +}; From c2d33dd19f9c7c090a5b9bac8a6b4e1d87c052f8 Mon Sep 17 00:00:00 2001 From: TheRainbowPhoenix <13310559+TheRainbowPhoenix@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:37:27 +0000 Subject: [PATCH 23/23] Add migration notes and fix remaining build issues Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- MIGRATION_NOTES.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 MIGRATION_NOTES.md diff --git a/MIGRATION_NOTES.md b/MIGRATION_NOTES.md new file mode 100644 index 0000000..50ad9f8 --- /dev/null +++ b/MIGRATION_NOTES.md @@ -0,0 +1,37 @@ +# Hollyhock V2 to V3 Migration Notes + +## Build System +* **Makefile**: Update toolchain to `sh4a_nofpueb-elf-gcc/g++`. +* **Flags**: Use `-flto=auto`, `-ffat-lto-objects`, `-gdwarf-5`, `-O2`. +* **Sources**: Ensure Makefile picks up sources correctly (e.g., recursive search or explicit list). Be careful with "unity builds" (files including other .cpp files); only compile the entry point file to avoid duplicate symbols. +* **Linker**: Remove custom `linker.ld`. The SDK handles linking. + +## Entry Point & Init +* **main()**: Signature is `int main() { ... return 0; }`. Remove `extern "C"` if using C++. +* **calcInit()**: Removed. Initialization is automatic. +* **calcEnd()**: Removed. Cleanup is automatic. +* **VRAM**: `width`, `height`, and `vram` pointer are compile-time constants in ``. Do not redefine them. For PC ports, wrap custom definitions in `#ifdef PC`. + +## Headers & API +* **Headers**: Use C-style headers (e.g., `` instead of `.hpp`). +* **Prefixes**: APIs now use prefixes. `open` -> `File_Open`, `memset` -> `Mem_Memset`. +* **Constants**: Constants are often enums. `OPEN_READ` -> `FILE_OPEN_READ`. Check SDK headers for exact names. +* **Standard Lib**: Use ``, `` instead of ``, `` if working with global namespace C functions. Avoid renaming standard macros like `try`/`catch`. + +## File I/O +* **Paths**: Paths are `const char_const16_t*` (UTF-16). +* **Strings**: Use `char16_t` arrays and `u"path"` literals. Cast to `(const char_const16_t*)` when calling SDK functions. +* **Alignment**: File path buffers passed to SDK functions (like `File_FindFirst`) MUST be 4-byte aligned on SuperH. Use `__attribute__((aligned(4)))`. +* **Structs**: `File_FindInfo` uses scoped enums like `File_FindInfo::EntryTypeDirectory` (or just `EntryTypeDirectory` depending on SDK version/macros). Check scoping. + +## Input +* **Polling**: Use `GetInput` instead of `GetKey`. +* **Struct**: `struct Input_Event` MUST be 4-byte aligned. Use `__attribute__((aligned(4)))`. Misalignment causes crashes (address error) at runtime. +* **Keycodes**: Use `KEYCODE_*` enums. +* **Touch**: Coordinates in `Input_Event` are `int32_t`. Cast to `uint32_t` if comparing with unsigned bounds to avoid compiler warnings. + +## Troubleshooting +* **Crashes**: If it crashes in an SDK function (e.g., `GetInput`, `File_Find...`), check ALIGNMENT of the structs/buffers passed to it. +* **Linker Errors**: "Multiple definition of ..." usually means a conflict with SDK symbols (e.g., `fillScreen`). Rename your function or wrap in `#ifdef PC`. +* **Compile Errors**: "Did you mean...?" implies the symbol exists but maybe signature mismatch or missing namespace. Check SDK headers. +* **Warnings**: Suppress `warn_unused_result` by checking return value `if (func() < 0) {}` rather than `(void)func()`.