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..6fe5b09 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.jsontemp_hhk3_sdk/ 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/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()`. 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..48b15fd 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 @@ -31,10 +31,6 @@ void main(){ SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); 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 +41,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 @@ -206,9 +205,10 @@ void vline(int x, int y1, int y2, uint16_t color){ for (int y=y1; y<=y2; y++) setPixel(x,y,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,8 +227,155 @@ 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 ) +} +#endif + +//This is defined in sdk/calc/calc.cpp for the calc... +#ifdef PC +//Draw a line (bresanham line algorithm) +void line(int x1, int y1, int x2, int y2, uint16_t color){ + int8_t ix, iy; + + int dx = (x2>x1 ? (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++) + setPixel(x,y,color); +} + +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 = width * height; + // const uint32_t size = app_width * app_height; // for(uint32_t i = 0; i //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; #endif +#ifdef PC extern int width; extern int height; +#endif void println(const char str[] ); void println(const char str[],int a ); @@ -49,7 +51,7 @@ inline uint16_t color(uint8_t R, uint8_t G, uint8_t B){ ((B>>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 < (int)width && y>=0 && y < (int)height){ #ifdef PC unsigned char pixels[4]; // { A, B, G, R } //Convert 565 colors to RGBA @@ -61,7 +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*)( (uint32_t)vram + ((width*y + x)*2) )) = color; + 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/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 c99ab61..99a19d3 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; @@ -24,20 +25,28 @@ struct ActBarHandler { void (*callback)(); }; +struct KeyListener { + Input_Keycode key; + void (*callback)(); +}; + // 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 InputEvent event; +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; } @@ -48,20 +57,27 @@ 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) { 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++) { - 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) { + 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 if (event.data.touch_single.direction == touchHandlers[i].direction) { (*touchHandlers[i].callback)(); @@ -79,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; @@ -110,8 +126,15 @@ void addActBarListener(uint16_t type, void (*callback)()) { actBarHandlers[actBarHandlersLength++] = handler; } +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--; @@ -119,8 +142,8 @@ 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++) { +void removeActBarListener(uint32_t type) { + for (uint32_t i = 0; i < actBarHandlersLength; i++) { if (actBarHandlers[i].type == type) { actBarHandlers[i] = actBarHandlers[actBarHandlersLength - 1]; actBarHandlersLength--; diff --git a/lib/draw_functions.hpp b/lib/draw_functions.hpp index 9cbd173..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 @@ -25,9 +26,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 +55,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); + 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; - lseek(fd, 0, SEEK_SET); - read(fd, result, w*h*2+4); - close(fd); + if (File_Lseek(fd, 0, FILE_SEEK_SET) < 0) {} + if (File_Read(fd, result, w*h*2+4) < 0) {} + File_Close(fd); return result; } return 0; @@ -91,18 +92,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); + 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; - lseek(fd, 0, SEEK_SET); - read(fd, result, (95*w*h/8)+5); - close(fd); + 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; } return 0; @@ -140,4 +141,3 @@ void draw_font_shader(uint8_t *fontpointer, const char *text, int16_t x, int16_t } } - 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/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 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..1100734 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,12 @@ -#include +#include +#include // main #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 @@ -38,182 +39,137 @@ 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(); + + // Copy command + char callingArgs[BUF_SIZE]; + 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 < 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 < 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 @@ -222,6 +178,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; @@ -231,31 +193,32 @@ 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(); - // 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 + // Initial Render + terminal->Render(); + keyboard->Render(); - // 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 + // Add event listeners (Migrated to SDK keycodes and addKeyListener) + addKeyListener(KEYCODE_POWER_CLEAR, endShell); + addKeyListener(KEYCODE_BACKSPACE, kbBackspace); - 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); + addKeyListener(KEYCODE_KEYBOARD, kbToggle); + + // Keyboard Listeners + 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); // Initialize the shell cpshell_init(); @@ -284,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/cat.cpp b/src/commands/cat.cpp index c07a3cc..f8ed3b7 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) @@ -7,7 +9,7 @@ */ #include "../internal.hpp" -#include +#include extern int cat_main(int argc, char **argv) { @@ -28,42 +30,42 @@ 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]; - struct findInfo findInfoBuf; - int ret = findFirst(wpath, &findHandle, fileName, &findInfoBuf); + char_const16_t fileName[100]; + struct File_FindInfo 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"); 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_FindInfo::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 +75,10 @@ extern int cat_main(int argc, char **argv) // copy memory address uint8_t* addr; - getAddr(fd,0,(const void**)&addr); + if (File_GetAddr(fd,0,(const void**)&addr) < 0) {} // 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..c734714 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) @@ -7,7 +9,7 @@ */ #include "../internal.hpp" -#include +#include extern int cd_main(int argc, char **argv) { @@ -25,32 +27,32 @@ 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); + char_const16_t fileName[100]; + struct File_FindInfo 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"); 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_FindInfo::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) { @@ -58,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; } @@ -89,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/clear.cpp b/src/commands/clear.cpp index 666704b..b2b24aa 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,17 +8,14 @@ * @date 2022-06-06 */ -extern int clear_main(int argc, char **argv) +extern int clear_main(int, char **) { // clear the screen to black 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/credits.cpp b/src/commands/credits.cpp index 6ff884c..7a1c5a8 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, 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 6294ef6..792b65f 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) diff --git a/src/commands/echo.cpp b/src/commands/echo.cpp index cd8fec6..c700cc0 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) diff --git a/src/commands/exit.cpp b/src/commands/exit.cpp index cac3e84..51b3964 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, char **) { shell_running = false; return 0; diff --git a/src/commands/help.cpp b/src/commands/help.cpp index 6c230a0..b8a177c 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, char **) { struct Applet *a = applets; terminal->ClearBuffer(); @@ -21,11 +23,6 @@ extern int help_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 + strlen(a->name) + 2) >= terminal->xmax) { - terminal->WriteBuffer('\n', false); - terminal->ClearBuffer(); - } strcat(cmds, ", "); terminal->WriteChars(cmds, true); } diff --git a/src/commands/history.cpp b/src/commands/history.cpp index deeb380..2323f73 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) @@ -7,50 +9,50 @@ */ #include "../internal.hpp" -#include +#include -// write to history file with int argc, char **argv -extern int history_main(int argc, char **argv) { +// write to history file with int, char ** +extern int history_main(int, char **) { terminal->ClearBuffer(); char outBuf[BUF_SIZE]; // check if history file exists int findHandle; - wchar_t fileName[100]; - struct findInfo findInfoBuf; - int ret = findFirst(g_whistory, &findHandle, fileName, &findInfoBuf); + 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) { // 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_FindInfo::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]; - 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 89509e0..3e1d01e 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) @@ -7,9 +9,10 @@ */ #include "../internal.hpp" -#include +#include +#include -extern int ls_main(int argc, char **argv) +extern int ls_main(int, char **) { // clear buffer terminal->ClearBuffer(); @@ -18,42 +21,35 @@ extern int ls_main(int argc, char **argv) int dirFiles = 0; int findHandle; - wchar_t fileName[100]; + char_const16_t fileName[100] __attribute__((aligned(4))); char outBuf[110]; - struct findInfo findInfoBuf; - int ret = findFirst(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; - 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]; + char_const16_t ch = fileName[i]; thisfile.fileName[i] = ch; } //copy file type - thisfile.type=findInfoBuf.type==findInfoBuf.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 - if ((terminal->bufferCX + 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; //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/commands/osname.cpp b/src/commands/osname.cpp index 80e6922..d9f8cd7 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, char **) { char outBuf[BUF_SIZE]; diff --git a/src/commands/rand.cpp b/src/commands/rand.cpp index d81e662..b5603cd 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) diff --git a/src/commands/test.cpp b/src/commands/test.cpp index c1c2104..924d429 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) diff --git a/src/commands/username.cpp b/src/commands/username.cpp index 34e0110..b9e5333 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) diff --git a/src/cpshell.cpp b/src/cpshell.cpp index 87010ab..65fea31 100644 --- a/src/cpshell.cpp +++ b/src/cpshell.cpp @@ -1,3 +1,6 @@ +#include +#include +#include #pragma once #include "../lib/functions/convert.hpp" @@ -100,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 + strlen(a->name) + 2) >= terminal->xmax) { - terminal->WriteBuffer('\n', false); - terminal->ClearBuffer(); - } strcat(cmds, ", "); terminal->WriteChars(cmds, true); } @@ -149,18 +147,18 @@ 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 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..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_ @@ -19,6 +23,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')) @@ -26,20 +36,22 @@ // Files +#include + struct dirEntry{ char fileName[100]; char type; }; struct dirEntry directory[64]; -char g_path[PATH_LEN]; -wchar_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 -wchar_t g_whistory[32] = L"\\fls0\\usr\\.history"; +char16_t g_whistory[32] __attribute__((aligned(4))) = u"\\fls0\\usr\\.history"; char g_history[32] = "\\fls0\\usr\\.history"; -wchar_t g_wuserprofile[32] = L"\\fls0\\usr\\.profile"; +char16_t g_wuserprofile[32] __attribute__((aligned(4))) = u"\\fls0\\usr\\.profile"; char g_userprofile[32] = "\\fls0\\usr\\.profile"; struct Applet { diff --git a/src/loader.hpp b/src/loader.hpp index 55c392f..ab1f3a8 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; @@ -28,37 +28,37 @@ int load_userprofile() { terminal->ClearBuffer(); int findHandle; - wchar_t fileName[100]; - struct findInfo findInfoBuf; - int ret = findFirst(g_wuserprofile, &findHandle, fileName, &findInfoBuf); + char_const16_t fileName[100]; + struct File_FindInfo 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"); terminal->WriteChars(outBuf); - findClose(findHandle); + File_FindClose(findHandle); load_settings(); return 0; - } else if (findInfoBuf.type == findInfoBuf.EntryTypeDirectory) { + } else if (findInfoBuf.type == File_FindInfo::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); + if (File_GetAddr(fd,0,(const void**)&addr) < 0) {} - ret = close(fd); + ret = File_Close(fd); if (ret < 0) { strcpy(outBuf, "LOAD: File error.\n"); terminal->WriteChars(outBuf); diff --git a/src/terminal.hpp b/src/terminal.hpp index aef21aa..eb163d8 100644 --- a/src/terminal.hpp +++ b/src/terminal.hpp @@ -1,130 +1,223 @@ -/** - * @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 { 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(); 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(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); } - // 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/utilities.hpp b/src/utilities.hpp index 3916400..0244fbc 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() @@ -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,37 +42,37 @@ 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); - 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 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 ret = open(path, 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"); } 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, (File_Whence)whence); return safe_internal(ret, "An error occurred calling lseek.\n"); } @@ -83,40 +83,40 @@ 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); + char_const16_t fileName[100]; + struct File_FindInfo 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 - 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_FindInfo::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/src/virtual_keyboard.hpp b/src/virtual_keyboard.hpp index aa82035..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. + } +}; 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