├── .gitignore ├── main ├── CMakeLists.txt ├── sdcard.h ├── input.h ├── display.h ├── ugui │ ├── README.md │ ├── ugui_config.h │ ├── LICENSE.md │ └── ugui.h ├── sdcard.c ├── input.c ├── display.c └── main.c ├── partitions.csv ├── CMakeLists.txt ├── tools ├── patches │ ├── esp-idf-sdcard-patch.diff │ ├── esp-idf-enable-exfat.diff │ └── esp-idf-partition-patch.diff ├── pack.py └── mkfw.py ├── README.md └── sdkconfig.defaults /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | sdkconfig 4 | sdkconfig.old 5 | *.fw 6 | *.img 7 | *.exe 8 | *.gch 9 | *.zip -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCDIRS ". ugui") 2 | set(COMPONENT_ADD_INCLUDEDIRS ".") 3 | register_component() 4 | component_compile_options(-DPROJECT_VER="${PROJECT_VER}") 5 | -------------------------------------------------------------------------------- /main/sdcard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_err.h" 4 | 5 | #define SDCARD_BASE_PATH "/sd" 6 | 7 | esp_err_t odroid_sdcard_open(void); 8 | esp_err_t odroid_sdcard_close(void); 9 | esp_err_t odroid_sdcard_format(int fs_type); 10 | 11 | int odroid_sdcard_files_get(const char* path, const char* extension, char*** filesOut); 12 | void odroid_sdcard_files_free(char** files, int count); 13 | -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild 2 | # Name, Type, SubType, Offset, Size 3 | mfw_nvs, data, nvs, 0x9000, 0x4000 4 | otadata, data, ota, 0xd000, 0x2000 5 | phy_init, data, phy, 0xf000, 0x1000 6 | mfw_app, app, factory, 0x10000, 0xD0000 7 | mfw_data, data, 0xFE, 0xE0000, 0x20000 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | set(EXTRA_COMPONENT_DIRS "components") 4 | 5 | execute_process( 6 | COMMAND git rev-parse --short HEAD 7 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 8 | OUTPUT_VARIABLE GITREV 9 | OUTPUT_STRIP_TRAILING_WHITESPACE 10 | ) 11 | 12 | string(TIMESTAMP TODAY "%Y%m%d") 13 | 14 | set(PROJECT_VER "${TODAY}-${GITREV}") 15 | 16 | project(odroid-go-multi-firmware) 17 | -------------------------------------------------------------------------------- /main/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum 6 | { 7 | ODROID_INPUT_UP = 0, 8 | ODROID_INPUT_RIGHT, 9 | ODROID_INPUT_DOWN, 10 | ODROID_INPUT_LEFT, 11 | ODROID_INPUT_SELECT, 12 | ODROID_INPUT_START, 13 | ODROID_INPUT_A, 14 | ODROID_INPUT_B, 15 | ODROID_INPUT_MENU, 16 | ODROID_INPUT_VOLUME, 17 | 18 | ODROID_INPUT_MAX 19 | }; 20 | 21 | void input_init(void); 22 | uint32_t input_read_raw(); 23 | int input_wait_for_button_press(int ticks); 24 | -------------------------------------------------------------------------------- /tools/patches/esp-idf-sdcard-patch.diff: -------------------------------------------------------------------------------- 1 | diff --git a/components/driver/sdspi_host.c b/components/driver/sdspi_host.c 2 | index e823ee7fa..79aef3130 100644 3 | --- a/components/driver/sdspi_host.c 4 | +++ b/components/driver/sdspi_host.c 5 | @@ -623,6 +623,8 @@ static esp_err_t poll_cmd_response(int slot, sdspi_hw_cmd_t *cmd) 6 | static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd, 7 | uint8_t *data, uint32_t rx_length) 8 | { 9 | + go_idle_clockout(slot); 10 | + 11 | bool need_stop_command = rx_length > SDSPI_MAX_DATA_LEN; 12 | spi_transaction_t* t_command = get_transaction(slot); 13 | *t_command = (spi_transaction_t) { -------------------------------------------------------------------------------- /tools/patches/esp-idf-enable-exfat.diff: -------------------------------------------------------------------------------- 1 | diff --git a/components/fatfs/src/ffconf.h b/components/fatfs/src/ffconf.h 2 | index 79792beef..0339ccef2 100644 3 | --- a/components/fatfs/src/ffconf.h 4 | +++ b/components/fatfs/src/ffconf.h 5 | @@ -252,7 +252,7 @@ 6 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 7 | 8 | 9 | -#define FF_FS_EXFAT 0 10 | +#define FF_FS_EXFAT (FF_USE_LFN >= 1) 11 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 12 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 13 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 14 | -------------------------------------------------------------------------------- /main/display.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef TARGET_MRGC_G32 4 | #define LCD_PIN_NUM_MISO GPIO_NUM_NC 5 | #define LCD_PIN_NUM_MOSI GPIO_NUM_23 6 | #define LCD_PIN_NUM_CLK GPIO_NUM_18 7 | #define LCD_PIN_NUM_CS GPIO_NUM_5 8 | #define LCD_PIN_NUM_DC GPIO_NUM_12 9 | #define LCD_PIN_NUM_BCKL GPIO_NUM_27 10 | 11 | #define SCREEN_OFFSET_TOP 28 12 | #define SCREEN_WIDTH 240 13 | #define SCREEN_HEIGHT 224 14 | #else 15 | #define LCD_PIN_NUM_MISO GPIO_NUM_19 16 | #define LCD_PIN_NUM_MOSI GPIO_NUM_23 17 | #define LCD_PIN_NUM_CLK GPIO_NUM_18 18 | #define LCD_PIN_NUM_CS GPIO_NUM_5 19 | #define LCD_PIN_NUM_DC GPIO_NUM_21 20 | #define LCD_PIN_NUM_BCKL GPIO_NUM_14 21 | 22 | #define SCREEN_OFFSET_TOP 0 23 | #define SCREEN_WIDTH 320 24 | #define SCREEN_HEIGHT 240 25 | #endif 26 | 27 | void ili9341_init(void); 28 | void ili9341_deinit(void); 29 | void ili9341_writeLE(const uint16_t *buffer); 30 | void ili9341_writeBE(const uint16_t *buffer); 31 | -------------------------------------------------------------------------------- /tools/patches/esp-idf-partition-patch.diff: -------------------------------------------------------------------------------- 1 | diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c 2 | index 400329199..1b6ab8be8 100644 3 | --- a/components/spi_flash/partition.c 4 | +++ b/components/spi_flash/partition.c 5 | @@ -56,6 +56,25 @@ static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list = 6 | static _lock_t s_partition_list_lock; 7 | 8 | 9 | +void esp_partition_reload_table() 10 | +{ 11 | + if (!SLIST_EMPTY(&s_partition_list)) 12 | + { 13 | + _lock_acquire(&s_partition_list_lock); 14 | + 15 | + // Remove all entries 16 | + while(!SLIST_EMPTY(&s_partition_list)) 17 | + { 18 | + partition_list_item_t* item = SLIST_FIRST(&s_partition_list); 19 | + SLIST_REMOVE_HEAD(&s_partition_list, next); 20 | + 21 | + free(item); 22 | + } 23 | + 24 | + _lock_release(&s_partition_list_lock); 25 | + } 26 | +} 27 | + 28 | esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, 29 | esp_partition_subtype_t subtype, const char* label) 30 | { 31 | -------------------------------------------------------------------------------- /tools/pack.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import datetime 3 | import sys 4 | import os 5 | 6 | PROJECT_NAME = "odroid-go-multi-firmware" 7 | PROJECT_VER = datetime.datetime.now().strftime("%Y%m%d") 8 | PROJECT_BIN = "build/%s.bin" % PROJECT_NAME 9 | PROJECT_IMG = "%s-%s.img" % (PROJECT_NAME, PROJECT_VER) 10 | 11 | if not os.path.isfile(PROJECT_BIN): 12 | exit("You should run `idf.py .build` first!") 13 | 14 | def readfile(filepath): 15 | with open(filepath, "rb") as f: 16 | return f.read() 17 | 18 | 19 | print("Creating img file %s...\n" % PROJECT_IMG) 20 | 21 | # TO DO: Read the partitions.csv instead... 22 | with open(PROJECT_IMG, "wb") as fp: 23 | fp.write(b"\xFF" * 0x10000) 24 | fp.seek(0x1000) 25 | fp.write(readfile("build/bootloader/bootloader.bin")) 26 | fp.seek(0x8000) 27 | fp.write(readfile("build/partition_table/partition-table.bin")) 28 | fp.seek(0xF000) 29 | fp.write(readfile("build/phy_init_data.bin")) 30 | fp.seek(0x10000) 31 | fp.write(readfile(PROJECT_BIN)) 32 | 33 | 34 | print("Creating the .fw file...") 35 | 36 | subprocess.run([ 37 | sys.executable, 38 | "tools/mkfw.py", 39 | ("%s-%s.fw" % (PROJECT_NAME, PROJECT_VER)), "Multi-fw installer", PROJECT_BIN, # BIN as tile :) 40 | "1", "231", "0", "payload", PROJECT_IMG, # Put payload first because we might overwrite that part during install 41 | "0", "0", "0", "installer", PROJECT_BIN, # 42 | ], check=True) 43 | 44 | print("all done!") 45 | -------------------------------------------------------------------------------- /main/ugui/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | ## What is µGUI? 3 | µGUI is a free and open source graphic library for embedded systems. It is platform-independent 4 | and can be easily ported to almost any microcontroller system. As long as the display is capable 5 | of showing graphics, µGUI is not restricted to a certain display technology. Therefore, display 6 | technologies such as LCD, TFT, E-Paper, LED or OLED are supported. The whole module 7 | consists of three files: **ugui.c**, **ugui.h** and **ugui_config.h**. 8 | 9 | ## µGUI Features 10 | * µGUI supports any color, grayscale or monochrome display 11 | * µGUI supports any display resolution 12 | * µGUI supports multiple different displays 13 | * µGUI supports any touch screen technology (e.g. AR, PCAP) 14 | * µGUI supports windows and objects (e.g. button, textbox) 15 | * µGUI supports platform-specific hardware acceleration 16 | * 16 different fonts available 17 | * cyrillic fonts supported 18 | * TrueType font converter available ([https://github.com/AriZuu](https://github.com/AriZuu)) 19 | * integrated and free scalable system console 20 | * basic geometric functions (e.g. line, circle, frame etc.) 21 | * can be easily ported to almost any microcontroller system 22 | * no risky dynamic memory allocation required 23 | 24 | ## µGUI Requirements 25 | µGUI is platform-independent, so there is no need to use a certain embedded system. In order to 26 | use µGUI, only two requirements are necessary: 27 | * a C-function which is able to control pixels of the target display. 28 | * integer types for the target platform have to be adjusted in ugui_config.h. 29 | -------------------------------------------------------------------------------- /main/ugui/ugui_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __UGUI_CONFIG_H 2 | #define __UGUI_CONFIG_H 3 | 4 | #include "stdint.h" 5 | 6 | /* -------------------------------------------------------------------------------- */ 7 | /* -- CONFIG SECTION -- */ 8 | /* -------------------------------------------------------------------------------- */ 9 | 10 | //#define USE_MULTITASKING 11 | 12 | /* Enable color mode */ 13 | //#define USE_COLOR_RGB888 // RGB = 0xFF,0xFF,0xFF 14 | #define USE_COLOR_RGB565 // RGB = 0bRRRRRGGGGGGBBBBB 15 | 16 | /* Enable needed fonts here */ 17 | #define USE_FONT_4X6 18 | #define USE_FONT_5X8 19 | #define USE_FONT_5X12 20 | #define USE_FONT_6X8 21 | #define USE_FONT_6X10 22 | #define USE_FONT_7X12 23 | #define USE_FONT_8X8 24 | //#define USE_FONT_8X12_CYRILLIC 25 | #define USE_FONT_8X12 26 | #define USE_FONT_8X12 27 | #define USE_FONT_8X14 28 | #define USE_FONT_10X16 29 | #define USE_FONT_12X16 30 | #define USE_FONT_12X20 31 | #define USE_FONT_16X26 32 | #define USE_FONT_22X36 33 | #define USE_FONT_24X40 34 | #define USE_FONT_32X53 35 | 36 | /* Specify platform-dependent integer types here */ 37 | 38 | #define __UG_FONT_DATA const 39 | typedef uint8_t UG_U8; 40 | typedef int8_t UG_S8; 41 | typedef uint16_t UG_U16; 42 | typedef int16_t UG_S16; 43 | typedef uint32_t UG_U32; 44 | typedef int32_t UG_S32; 45 | 46 | 47 | /* Example for dsPIC33 48 | typedef unsigned char UG_U8; 49 | typedef signed char UG_S8; 50 | typedef unsigned int UG_U16; 51 | typedef signed int UG_S16; 52 | typedef unsigned long int UG_U32; 53 | typedef signed long int UG_S32; 54 | */ 55 | 56 | /* -------------------------------------------------------------------------------- */ 57 | /* -------------------------------------------------------------------------------- */ 58 | 59 | 60 | /* Feature enablers */ 61 | #define USE_PRERENDER_EVENT 62 | #define USE_POSTRENDER_EVENT 63 | 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /tools/mkfw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys, math, zlib, struct 3 | 4 | def readfile(filepath): 5 | try: 6 | with open(filepath, "rb") as f: 7 | return f.read() 8 | except FileNotFoundError as err: 9 | exit("\nERROR: Unable to open partition file '%s' !\n" % err.filename) 10 | 11 | if len(sys.argv) < 4: 12 | exit("usage: mkfw.py output_file.fw 'description' tile.raw type subtype size label file.bin " 13 | "[type subtype size label file.bin, ...]") 14 | 15 | fw_name = sys.argv[1] 16 | 17 | fw_data = struct.pack( 18 | "<24s40s8256s", b"ODROIDGO_FIRMWARE_V00_01", sys.argv[2].encode(), readfile(sys.argv[3]) 19 | ) 20 | 21 | fw_size = 0 22 | fw_part = 0 23 | fw_next_ota = 16 # First OTA partition 24 | pos = 4 25 | 26 | while pos < len(sys.argv): 27 | partype = int(sys.argv[pos + 0], 0) 28 | subtype = int(sys.argv[pos + 1], 0) 29 | size = int(sys.argv[pos + 2], 0) 30 | label = sys.argv[pos + 3] 31 | filename = sys.argv[pos + 4] 32 | pos += 5 33 | 34 | if partype == 0: 35 | if not subtype: 36 | subtype = fw_next_ota 37 | fw_next_ota = subtype + 1 38 | 39 | data = readfile(filename) 40 | real_size = max(size, math.ceil(len(data) / 0x10000) * 0x10000) 41 | usage = len(data) / real_size * 100 42 | 43 | print("[%d]: type=%d, subtype=%d, size=%d (%d%% used), label=%s" 44 | % (fw_part, partype, subtype, real_size, usage, label)) 45 | 46 | if real_size > size and size != 0: 47 | print(" > WARNING: Partition smaller than file (+%d bytes), increasing size to %d" 48 | % (len(data) - size, real_size)) 49 | 50 | fw_data += struct.pack("https://github.com/Sermus) 20 | // for giving valuable suggestions, reporting bugs and adding several new features. 21 | // Andrey also put a lot of work in the implementaion of anti-aliased font support. 22 | // 23 | // Mikhail Podkur (-->https://github.com/MikhailPodkur) 24 | // for adding cyrillic 8x12 font, checkbox feature and RGB565 support. 25 | // 26 | // Gustavo Denardin 27 | // for giving valuable suggestions regarding real-time os support. 28 | // 29 | // Samuel Kleiser 30 | // for reporting bugs and giving examples how to improve µGUI. 31 | /* -------------------------------------------------------------------------------- */ 32 | /* -- REVISION HISTORY -- */ 33 | /* -------------------------------------------------------------------------------- */ 34 | // Dec 20, 2015 V0.31 Checkbox component with all funtions added. 35 | // Cyrillic font 8x12 added. 36 | // RGB565 color schema added. 37 | // Windows components font could be getted from current GUI by default 38 | // Mar 18, 2015 V0.3 Driver support added. 39 | // Window and object support added. 40 | // Touch support added. 41 | // Fixed some minor bugs. 42 | // 43 | // Oct 20, 2014 V0.2 Function UG_DrawRoundFrame() added. 44 | // Function UG_FillRoundFrame() added. 45 | // Function UG_DrawArc() added. 46 | // Fixed some minor bugs. 47 | // 48 | // Oct 11, 2014 V0.1 First release. 49 | /* -------------------------------------------------------------------------------- */ 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Odroid-go-multi-firmware is an improvement of the official Odroid GO firmware. It allows you to keep multiple applications installed in the flash and switch instantly between them. You can think of it as a multi-boot loader. 3 | 4 | 5 | # Usage 6 | Holding **B** while powering up the device will bring you to the boot menu. 7 | 8 | 9 | # Installation 10 | 11 | _Note: There is no risk in flashing your Odroid GO. You can always recover and return to the stock firmware by following ODROID's guide (method 2 below)._ 12 | 13 | ### Method 1: Self-installer 14 | 15 | This is the easiest method, no need for drivers or flashing software. But it isn't as well tested. 16 | 17 | 1. Copy the file `odroid-go-multi-firmware-XXX.fw` to your sdcard in the folder `/odroid/firmware` 18 | 2. Power up your device while holding B 19 | 3. Flash the self-installer and run it 20 | 21 | 22 | ### Method 2: USB flashing 23 | 24 | Follow these instructions: https://wiki.odroid.com/odroid_go/firmware_update but use the .img provided here. 25 | 26 | _Note: If you are familiar with esptool, you just have to flash `odroid-go-multi-firmware-XXX.fw` to offset 0x0._ 27 | 28 | 29 | # Compilation 30 | The official esp-idf version 3.1 or newer is required and to improve SD Card support you should this patch: 31 | 32 | - tools/patches/esp-idf-sdcard-patch.diff 33 | 34 | _Note: Those patches do not introduce breaking changes to non-GO (standard ESP32) projects and can safely be applied to your global esp-idf installation._ 35 | 36 | ## Build Steps: 37 | 1. Compile firmware: `idf.py build` 38 | 2. And then: 39 | - To produce image files: `python tools/pack.py` 40 | - To flash and debug: `idf.py flash monitor` 41 | 42 | 43 | # Technical information 44 | 45 | ### Creating .fw files 46 | The mkfw.py tool is used to package your application in a .fw file. 47 | 48 | Usage: 49 | `mkfw.py output_file.fw 'description' tile.raw type subtype size label file.bin [type subtype size label file.bin, ...]` 50 | 51 | - tile.raw must be a RAW RGB565 86x48 image 52 | 53 | - [type](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#type) is 0 for application and 1 for data 54 | - [subtype](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#subtype) can be set to 0 for auto 55 | - [size](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#offset-size) must be equal to or bigger than your app's .bin and a multiple of 65536. Can be set to 0 for auto 56 | - [label](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#name-field) is a label for your own usage 57 | - file.bin contains the partition's data 58 | 59 | ### .fw format: 60 | ``` 61 | Header: 62 | "ODROIDGO_FIRMWARE_V00_01" 24 bytes 63 | Firmware Description 40 bytes 64 | RAW565 86x48 tile 8256 bytes 65 | Partition [, ...]: 66 | Type 1 byte 67 | Subtype 1 byte 68 | Padding 2 bytes 69 | Label 16 bytes 70 | Flags 4 bytes 71 | Size 4 bytes 72 | Data length 4 bytes 73 | Data bytes 74 | Footer: 75 | CRC32 4 bytes 76 | ``` 77 | 78 | # Questions 79 | 80 | > **Q: How does it work?** 81 | > 82 | > A: When you select an application to boot, the ESP32's partition table is rewritten to make it seem like the app is the only thing there. That way any existing, unmodified, Odroid-GO applications will work. The firmware keeps track of what is installed and where. The app will then boot every time you power up the system, until you change it. 83 | -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # 2 | # Bootloader config 3 | # 4 | CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y 5 | CONFIG_LOG_BOOTLOADER_LEVEL=3 6 | CONFIG_BOOTLOADER_SPI_WP_PIN=7 7 | CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y 8 | CONFIG_BOOTLOADER_FACTORY_RESET=y 9 | CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=33 10 | CONFIG_BOOTLOADER_OTA_DATA_ERASE=y 11 | CONFIG_BOOTLOADER_DATA_FACTORY_RESET="" 12 | CONFIG_BOOTLOADER_APP_TEST= 13 | CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=32 14 | CONFIG_BOOTLOADER_HOLD_TIME_GPIO=0 15 | CONFIG_BOOTLOADER_WDT_ENABLE= 16 | CONFIG_APP_ROLLBACK_ENABLE= 17 | 18 | # 19 | # Serial flasher config 20 | # 21 | CONFIG_ESPTOOLPY_PORT="COM4" 22 | CONFIG_ESPTOOLPY_BAUD_921600B=y 23 | CONFIG_ESPTOOLPY_BAUD=921600 24 | CONFIG_ESPTOOLPY_COMPRESSED=y 25 | CONFIG_FLASHMODE_QIO=y 26 | CONFIG_FLASHMODE_QOUT= 27 | CONFIG_FLASHMODE_DIO= 28 | CONFIG_FLASHMODE_DOUT= 29 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 30 | CONFIG_ESPTOOLPY_FLASHFREQ_80M=y 31 | CONFIG_ESPTOOLPY_FLASHFREQ="80m" 32 | CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y 33 | CONFIG_ESPTOOLPY_FLASHSIZE="16MB" 34 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 35 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 36 | CONFIG_ESPTOOLPY_BEFORE_NORESET= 37 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 38 | CONFIG_ESPTOOLPY_AFTER_RESET=y 39 | CONFIG_ESPTOOLPY_AFTER_NORESET= 40 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 41 | CONFIG_MONITOR_BAUD_115200B=y 42 | CONFIG_MONITOR_BAUD=115200 43 | 44 | # 45 | # Partition Table 46 | # 47 | CONFIG_PARTITION_TABLE_SINGLE_APP= 48 | CONFIG_PARTITION_TABLE_TWO_OTA= 49 | CONFIG_PARTITION_TABLE_CUSTOM=y 50 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 51 | CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" 52 | CONFIG_PARTITION_TABLE_OFFSET=0x8000 53 | CONFIG_PARTITION_TABLE_MD5= 54 | 55 | # 56 | # Compiler options 57 | # 58 | CONFIG_OPTIMIZATION_LEVEL_DEBUG= 59 | CONFIG_OPTIMIZATION_LEVEL_RELEASE=y 60 | 61 | # 62 | # Bluetooth 63 | # 64 | CONFIG_BT_ENABLED= 65 | CONFIG_BT_RESERVE_DRAM=0 66 | 67 | # 68 | # ADC configuration 69 | # 70 | CONFIG_ADC_FORCE_XPD_FSM= 71 | CONFIG_ADC2_DISABLE_DAC=y 72 | 73 | # 74 | # ESP32-specific 75 | # 76 | CONFIG_IDF_TARGET_ESP32=y 77 | CONFIG_ESP32_DEFAULT_CPU_FREQ_80= 78 | CONFIG_ESP32_DEFAULT_CPU_FREQ_160= 79 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y 80 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 81 | CONFIG_SPIRAM_SUPPORT=y 82 | 83 | # 84 | # SPI RAM config 85 | # 86 | CONFIG_SPIRAM_BOOT_INIT=y 87 | CONFIG_SPIRAM_IGNORE_NOTFOUND=y 88 | CONFIG_SPIRAM_USE_MEMMAP= 89 | CONFIG_SPIRAM_USE_CAPS_ALLOC= 90 | CONFIG_SPIRAM_USE_MALLOC=y 91 | CONFIG_SPIRAM_TYPE_AUTO=y 92 | CONFIG_SPIRAM_TYPE_ESPPSRAM32= 93 | CONFIG_SPIRAM_TYPE_ESPPSRAM64= 94 | # CONFIG_SPIRAM_SIZE=-1 95 | CONFIG_SPIRAM_SPEED_40M= 96 | CONFIG_SPIRAM_SPEED_80M=y 97 | CONFIG_SPIRAM_MEMTEST=n 98 | CONFIG_SPIRAM_CACHE_WORKAROUND=y 99 | CONFIG_SPIRAM_BANKSWITCH_ENABLE= 100 | CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 101 | CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 102 | CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY= 103 | CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY= 104 | CONFIG_SPIRAM_OCCUPY_HSPI_HOST= 105 | CONFIG_SPIRAM_OCCUPY_VSPI_HOST=y 106 | 107 | 108 | CONFIG_MAIN_TASK_STACK_SIZE=12288 109 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y 110 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= 111 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= 112 | CONFIG_NEWLIB_NANO_FORMAT=y 113 | CONFIG_CONSOLE_UART_DEFAULT=y 114 | CONFIG_CONSOLE_UART_CUSTOM= 115 | CONFIG_CONSOLE_UART_NONE= 116 | CONFIG_CONSOLE_UART_NUM=0 117 | CONFIG_CONSOLE_UART_BAUDRATE=115200 118 | CONFIG_ESP32_PANIC_PRINT_HALT=y 119 | CONFIG_ESP32_PANIC_PRINT_REBOOT= 120 | CONFIG_ESP32_PANIC_SILENT_REBOOT= 121 | CONFIG_ESP32_PANIC_GDBSTUB= 122 | CONFIG_ESP32_DEBUG_OCDAWARE= 123 | CONFIG_ESP32_DEBUG_STUBS_ENABLE= 124 | CONFIG_INT_WDT= 125 | CONFIG_TASK_WDT= 126 | CONFIG_ESP32_XTAL_FREQ_40=y 127 | CONFIG_ESP32_XTAL_FREQ_26= 128 | CONFIG_ESP32_XTAL_FREQ_AUTO= 129 | CONFIG_ESP32_XTAL_FREQ=40 130 | CONFIG_DISABLE_BASIC_ROM_CONSOLE= 131 | CONFIG_NO_BLOBS= 132 | CONFIG_ESP_TIMER_PROFILING= 133 | CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS= 134 | CONFIG_ESP_ERR_TO_NAME_LOOKUP=y 135 | 136 | # 137 | # PHY 138 | # 139 | CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y 140 | CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION=y 141 | CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 142 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 143 | 144 | # 145 | # Power Management 146 | # 147 | CONFIG_PM_ENABLE= 148 | 149 | # 150 | # ADC-Calibration 151 | # 152 | CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y 153 | CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y 154 | CONFIG_ADC_CAL_LUT_ENABLE=y 155 | 156 | # 157 | # FAT Filesystem support 158 | # 159 | CONFIG_FATFS_CODEPAGE_DYNAMIC= 160 | CONFIG_FATFS_CODEPAGE_437=y 161 | CONFIG_FATFS_CODEPAGE=437 162 | CONFIG_FATFS_LFN_NONE= 163 | CONFIG_FATFS_LFN_HEAP=y 164 | CONFIG_FATFS_LFN_STACK= 165 | CONFIG_FATFS_MAX_LFN=255 166 | CONFIG_FATFS_API_ENCODING_ANSI_OEM=y 167 | CONFIG_FATFS_API_ENCODING_UTF_16= 168 | CONFIG_FATFS_API_ENCODING_UTF_8= 169 | CONFIG_FATFS_FS_LOCK=0 170 | CONFIG_FATFS_TIMEOUT_MS=10000 171 | CONFIG_FATFS_PER_FILE_CACHE=y 172 | CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y 173 | 174 | # 175 | # FreeRTOS 176 | # 177 | CONFIG_FREERTOS_UNICORE= 178 | CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF 179 | CONFIG_FREERTOS_CORETIMER_0=y 180 | CONFIG_FREERTOS_CORETIMER_1= 181 | CONFIG_FREERTOS_HZ=100 182 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y 183 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE= 184 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL= 185 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y 186 | CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK= 187 | CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y 188 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 189 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y 190 | CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= 191 | CONFIG_FREERTOS_ASSERT_DISABLE= 192 | CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 193 | CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 194 | CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 195 | CONFIG_FREERTOS_USE_TRACE_FACILITY= 196 | CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= 197 | CONFIG_FREERTOS_DEBUG_INTERNALS= 198 | CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y 199 | CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE= 200 | 201 | # 202 | # Log output 203 | # 204 | CONFIG_LOG_DEFAULT_LEVEL_INFO=y 205 | CONFIG_LOG_DEFAULT_LEVEL=3 206 | CONFIG_LOG_COLORS=y 207 | 208 | # 209 | # SPI Flash driver 210 | # 211 | CONFIG_SPI_FLASH_VERIFY_WRITE= 212 | CONFIG_SPI_FLASH_ENABLE_COUNTERS= 213 | CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y 214 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS= 215 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS= 216 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED=y 217 | CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y 218 | CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 219 | CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 220 | -------------------------------------------------------------------------------- /main/sdcard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "sdcard.h" 17 | 18 | extern esp_err_t ff_diskio_get_drive(BYTE* out_pdrv); 19 | extern void ff_diskio_register_sdmmc(unsigned char pdrv, sdmmc_card_t* card); 20 | 21 | #ifdef TARGET_MRGC_G32 22 | #define DECLARE_SDCARD_CONFIG() \ 23 | sdmmc_host_t host_config = SDMMC_HOST_DEFAULT(); \ 24 | host_config.flags = SDMMC_HOST_FLAG_1BIT; \ 25 | host_config.max_freq_khz = SDMMC_FREQ_HIGHSPEED; \ 26 | sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); \ 27 | slot_config.width = 1; 28 | #else 29 | #define DECLARE_SDCARD_CONFIG() \ 30 | sdmmc_host_t host_config = SDSPI_HOST_DEFAULT(); \ 31 | host_config.slot = HSPI_HOST; \ 32 | host_config.max_freq_khz = SDMMC_FREQ_DEFAULT; \ 33 | sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); \ 34 | slot_config.gpio_miso = GPIO_NUM_19; \ 35 | slot_config.gpio_mosi = GPIO_NUM_23; \ 36 | slot_config.gpio_sck = GPIO_NUM_18; \ 37 | slot_config.gpio_cs = GPIO_NUM_22; \ 38 | //slot_config.dma_channel = 2; 39 | #endif 40 | 41 | 42 | inline static void swap(char** a, char** b) 43 | { 44 | char* t = *a; 45 | *a = *b; 46 | *b = t; 47 | } 48 | 49 | static int strcicmp(char const *a, char const *b) 50 | { 51 | for (;; a++, b++) 52 | { 53 | int d = tolower((int)*a) - tolower((int)*b); 54 | if (d != 0 || !*a) return d; 55 | } 56 | } 57 | 58 | static int partition (char* arr[], int low, int high) 59 | { 60 | char* pivot = arr[high]; 61 | int i = (low - 1); 62 | 63 | for (int j = low; j <= high- 1; j++) 64 | { 65 | if (strcicmp(arr[j], pivot) < 0) 66 | { 67 | i++; 68 | swap(&arr[i], &arr[j]); 69 | } 70 | } 71 | swap(&arr[i + 1], &arr[high]); 72 | return (i + 1); 73 | } 74 | 75 | static void quick_sort(char* arr[], int low, int high) 76 | { 77 | if (low < high) 78 | { 79 | int pi = partition(arr, low, high); 80 | 81 | quick_sort(arr, low, pi - 1); 82 | quick_sort(arr, pi + 1, high); 83 | } 84 | } 85 | 86 | static void sort_files(char** files, int count) 87 | { 88 | if (count > 1) 89 | { 90 | quick_sort(files, 0, count - 1); 91 | } 92 | } 93 | 94 | 95 | int odroid_sdcard_files_get(const char* path, const char* extension, char*** filesOut) 96 | { 97 | const int MAX_FILES = 1024; 98 | 99 | int count = 0; 100 | char** result = (char**)malloc(MAX_FILES * sizeof(void*)); 101 | if (!result) abort(); 102 | 103 | 104 | DIR *dir = opendir(path); 105 | if( dir == NULL ) 106 | { 107 | ESP_LOGE(__func__, "opendir failed."); 108 | return 0; 109 | } 110 | 111 | int extensionLength = strlen(extension); 112 | if (extensionLength < 1) abort(); 113 | 114 | struct dirent *entry; 115 | while ((entry = readdir(dir))) 116 | { 117 | size_t len = strlen(entry->d_name); 118 | 119 | if (len < extensionLength) 120 | continue; 121 | 122 | if (entry->d_name[0] == '.') 123 | continue; 124 | 125 | if (strcasecmp(extension, &entry->d_name[len - extensionLength]) != 0) 126 | continue; 127 | 128 | if (!(result[count++] = strdup(entry->d_name))) 129 | abort(); 130 | 131 | if (count >= MAX_FILES) 132 | break; 133 | } 134 | 135 | closedir(dir); 136 | 137 | sort_files(result, count); 138 | 139 | *filesOut = result; 140 | return count; 141 | } 142 | 143 | void odroid_sdcard_files_free(char** files, int count) 144 | { 145 | for (int i = 0; i < count; ++i) 146 | { 147 | free(files[i]); 148 | } 149 | 150 | free(files); 151 | } 152 | 153 | esp_err_t odroid_sdcard_open(void) 154 | { 155 | DECLARE_SDCARD_CONFIG(); 156 | 157 | esp_vfs_fat_sdmmc_mount_config_t mount_config = { 158 | .format_if_mount_failed = false, 159 | .max_files = 5, 160 | }; 161 | 162 | esp_err_t ret = esp_vfs_fat_sdmmc_mount(SDCARD_BASE_PATH, &host_config, &slot_config, &mount_config, NULL); 163 | 164 | if (ret == ESP_OK || ret == ESP_ERR_INVALID_STATE) 165 | { 166 | ret = ESP_OK; 167 | } 168 | else 169 | { 170 | ESP_LOGE(__func__, "esp_vfs_fat_sdmmc_mount failed (%d)", ret); 171 | } 172 | 173 | return ret; 174 | } 175 | 176 | esp_err_t odroid_sdcard_close(void) 177 | { 178 | esp_err_t ret = esp_vfs_fat_sdmmc_unmount(); 179 | 180 | if (ret != ESP_OK) 181 | { 182 | ESP_LOGE(__func__, "esp_vfs_fat_sdmmc_unmount failed (%d)", ret); 183 | } 184 | 185 | return ret; 186 | } 187 | 188 | esp_err_t odroid_sdcard_format(int fs_type) 189 | { 190 | esp_err_t err = ESP_FAIL; 191 | const char *errmsg = "success!"; 192 | sdmmc_card_t card; 193 | void *buffer = malloc(4096); 194 | DWORD partitions[] = {100, 0, 0, 0}; 195 | BYTE drive = 0xFF; 196 | 197 | DECLARE_SDCARD_CONFIG(); 198 | 199 | if (buffer == NULL) { 200 | return false; 201 | } 202 | 203 | odroid_sdcard_close(); 204 | 205 | err = ff_diskio_get_drive(&drive); 206 | if (drive == 0xFF) { 207 | errmsg = "ff_diskio_get_drive() failed"; 208 | goto _cleanup; 209 | } 210 | 211 | err = (*host_config.init)(); 212 | if (err != ESP_OK) { 213 | errmsg = "host_config.init() failed"; 214 | goto _cleanup; 215 | } 216 | 217 | #ifdef TARGET_MRGC_G32 218 | err = sdmmc_host_init_slot(host_config.slot, &slot_config); 219 | #else 220 | err = sdspi_host_init_slot(host_config.slot, &slot_config); 221 | #endif 222 | 223 | if (err != ESP_OK) { 224 | errmsg = "sdspi_host_init_slot() failed"; 225 | goto _cleanup; 226 | } 227 | 228 | err = sdmmc_card_init(&host_config, &card); 229 | if (err != ESP_OK) { 230 | errmsg = "sdmmc_card_init() failed"; 231 | goto _cleanup; 232 | } 233 | 234 | ff_diskio_register_sdmmc(drive, &card); 235 | 236 | ESP_LOGI(__func__, "partitioning card %d", drive); 237 | if (f_fdisk(drive, partitions, buffer) != FR_OK) { 238 | errmsg = "f_fdisk() failed"; 239 | err = ESP_FAIL; 240 | goto _cleanup; 241 | } 242 | 243 | ESP_LOGI(__func__, "formatting card %d", drive); 244 | char path[3] = {(char)('0' + drive), ':', 0}; 245 | if (f_mkfs(path, fs_type ? FM_EXFAT : FM_FAT32, 0, buffer, 4096) != FR_OK) { 246 | errmsg = "f_mkfs() failed"; 247 | err = ESP_FAIL; 248 | goto _cleanup; 249 | } 250 | 251 | err = ESP_OK; 252 | 253 | _cleanup: 254 | 255 | if (err == ESP_OK) { 256 | ESP_LOGI(__func__, "%s", errmsg); 257 | } else { 258 | ESP_LOGE(__func__, "%s (%d)", errmsg, err); 259 | } 260 | 261 | free(buffer); 262 | host_config.deinit(); 263 | ff_diskio_register_sdmmc(drive, NULL); 264 | 265 | return err; 266 | } 267 | -------------------------------------------------------------------------------- /main/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "input.h" 10 | 11 | #ifdef TARGET_MRGC_G32 12 | #define TRY(x) if ((err = (x)) != ESP_OK) { goto fail; } 13 | 14 | static bool rg_i2c_read(uint8_t addr, int reg, void *read_data, size_t read_len) 15 | { 16 | esp_err_t err = ESP_FAIL; 17 | 18 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 19 | if (reg >= 0) 20 | { 21 | TRY(i2c_master_start(cmd)); 22 | TRY(i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true)); 23 | TRY(i2c_master_write_byte(cmd, (uint8_t)reg, true)); 24 | } 25 | TRY(i2c_master_start(cmd)); 26 | TRY(i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, true)); 27 | TRY(i2c_master_read(cmd, read_data, read_len, I2C_MASTER_LAST_NACK)); 28 | TRY(i2c_master_stop(cmd)); 29 | TRY(i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(500))); 30 | i2c_cmd_link_delete(cmd); 31 | return true; 32 | 33 | fail: 34 | ESP_LOGE(__func__, "Read from 0x%02x failed. reg=%d, err=0x%x\n", addr, reg, err); 35 | return false; 36 | } 37 | #else 38 | #define ODROID_GAMEPAD_IO_X ADC1_CHANNEL_6 39 | #define ODROID_GAMEPAD_IO_Y ADC1_CHANNEL_7 40 | #define ODROID_GAMEPAD_IO_SELECT GPIO_NUM_27 41 | #define ODROID_GAMEPAD_IO_START GPIO_NUM_39 42 | #define ODROID_GAMEPAD_IO_A GPIO_NUM_32 43 | #define ODROID_GAMEPAD_IO_B GPIO_NUM_33 44 | #define ODROID_GAMEPAD_IO_MENU GPIO_NUM_13 45 | #define ODROID_GAMEPAD_IO_VOLUME GPIO_NUM_0 46 | #endif 47 | 48 | static uint32_t gamepad_state = 0; 49 | 50 | 51 | uint32_t input_read_raw(void) 52 | { 53 | uint32_t state = 0; 54 | 55 | #ifdef TARGET_MRGC_G32 56 | uint8_t data[5]; 57 | if (rg_i2c_read(0x20, -1, &data, 5)) 58 | { 59 | int buttons = ~((data[2] << 8) | data[1]); 60 | 61 | if (buttons & (1 << 2)) state |= (1 << ODROID_INPUT_UP); 62 | if (buttons & (1 << 3)) state |= (1 << ODROID_INPUT_DOWN); 63 | if (buttons & (1 << 4)) state |= (1 << ODROID_INPUT_LEFT); 64 | if (buttons & (1 << 5)) state |= (1 << ODROID_INPUT_RIGHT); 65 | if (buttons & (1 << 8)) state |= (1 << ODROID_INPUT_MENU); 66 | // if (buttons & (1 << 0)) state |= (1 << ODROID_INPUT_OPTION); 67 | if (buttons & (1 << 1)) state |= (1 << ODROID_INPUT_SELECT); 68 | if (buttons & (1 << 0)) state |= (1 << ODROID_INPUT_START); 69 | if (buttons & (1 << 6)) state |= (1 << ODROID_INPUT_A); 70 | if (buttons & (1 << 7)) state |= (1 << ODROID_INPUT_B); 71 | } 72 | #else 73 | int joyX = adc1_get_raw(ODROID_GAMEPAD_IO_X); 74 | int joyY = adc1_get_raw(ODROID_GAMEPAD_IO_Y); 75 | 76 | if (joyX > 2048 + 1024) 77 | state |= (1 << ODROID_INPUT_LEFT); 78 | else if (joyX > 1024) 79 | state |= (1 << ODROID_INPUT_RIGHT); 80 | 81 | if (joyY > 2048 + 1024) 82 | state |= (1 << ODROID_INPUT_UP); 83 | else if (joyY > 1024) 84 | state |= (1 << ODROID_INPUT_DOWN); 85 | 86 | state |= (!gpio_get_level(ODROID_GAMEPAD_IO_SELECT)) ? (1 << ODROID_INPUT_SELECT) : 0; 87 | state |= (!gpio_get_level(ODROID_GAMEPAD_IO_START)) ? (1 << ODROID_INPUT_START) : 0; 88 | state |= (!gpio_get_level(ODROID_GAMEPAD_IO_A)) ? (1 << ODROID_INPUT_A) : 0; 89 | state |= (!gpio_get_level(ODROID_GAMEPAD_IO_B)) ? (1 << ODROID_INPUT_B) : 0; 90 | state |= (!gpio_get_level(ODROID_GAMEPAD_IO_MENU)) ? (1 << ODROID_INPUT_MENU) : 0; 91 | state |= (!gpio_get_level(ODROID_GAMEPAD_IO_VOLUME)) ? (1 << ODROID_INPUT_VOLUME) : 0; 92 | #endif 93 | 94 | return state; 95 | } 96 | 97 | int input_wait_for_button_press(int ticks) 98 | { 99 | uint32_t previousState = gamepad_state; 100 | uint32_t timeout = xTaskGetTickCount() + ticks; 101 | 102 | while (true) 103 | { 104 | uint32_t state = gamepad_state; 105 | 106 | for (int i = 0; i < ODROID_INPUT_MAX; i++) 107 | { 108 | if (!(previousState & (1 << i)) && (state & (1 << i))) { 109 | return i; 110 | } 111 | } 112 | 113 | if (ticks > 0 && timeout < xTaskGetTickCount()) { 114 | break; 115 | } 116 | 117 | previousState = state; 118 | vTaskDelay(pdMS_TO_TICKS(10)); 119 | } 120 | 121 | return -1; 122 | } 123 | 124 | static void input_task(void *arg) 125 | { 126 | uint8_t debounce[ODROID_INPUT_MAX]; 127 | 128 | // Initialize state 129 | for (int i = 0; i < ODROID_INPUT_MAX; ++i) 130 | { 131 | debounce[i] = 0xff; 132 | } 133 | 134 | while (1) 135 | { 136 | // Read hardware 137 | uint32_t state = input_read_raw(); 138 | 139 | // Debounce 140 | for (int i = 0; i < ODROID_INPUT_MAX; ++i) 141 | { 142 | debounce[i] <<= 1; 143 | debounce[i] |= (state >> i) & 1; 144 | switch (debounce[i] & 0x03) 145 | { 146 | case 0x00: 147 | gamepad_state &= ~(1 << i); 148 | break; 149 | 150 | case 0x03: 151 | gamepad_state |= (1 << i); 152 | break; 153 | 154 | default: 155 | // ignore 156 | break; 157 | } 158 | } 159 | 160 | vTaskDelay(pdMS_TO_TICKS(10)); 161 | } 162 | 163 | vTaskDelete(NULL); 164 | } 165 | 166 | void input_init(void) 167 | { 168 | #ifdef TARGET_MRGC_G32 169 | const i2c_config_t i2c_config = { 170 | .mode = I2C_MODE_MASTER, 171 | .sda_io_num = GPIO_NUM_21, 172 | .sda_pullup_en = GPIO_PULLUP_ENABLE, 173 | .scl_io_num = GPIO_NUM_22, 174 | .scl_pullup_en = GPIO_PULLUP_ENABLE, 175 | .master.clk_speed = 200000, 176 | }; 177 | esp_err_t err = ESP_FAIL; 178 | 179 | TRY(i2c_param_config(I2C_NUM_0, &i2c_config)); 180 | TRY(i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0)); 181 | ESP_LOGI(__func__, "I2C driver ready (SDA:%d SCL:%d).\n", i2c_config.sda_io_num, i2c_config.scl_io_num); 182 | fail: 183 | #else 184 | gpio_set_direction(ODROID_GAMEPAD_IO_SELECT, GPIO_MODE_INPUT); 185 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_SELECT, GPIO_PULLUP_ONLY); 186 | 187 | gpio_set_direction(ODROID_GAMEPAD_IO_START, GPIO_MODE_INPUT); 188 | 189 | gpio_set_direction(ODROID_GAMEPAD_IO_A, GPIO_MODE_INPUT); 190 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_A, GPIO_PULLUP_ONLY); 191 | 192 | gpio_set_direction(ODROID_GAMEPAD_IO_B, GPIO_MODE_INPUT); 193 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_B, GPIO_PULLUP_ONLY); 194 | 195 | adc1_config_width(ADC_WIDTH_12Bit); 196 | adc1_config_channel_atten(ODROID_GAMEPAD_IO_X, ADC_ATTEN_11db); 197 | adc1_config_channel_atten(ODROID_GAMEPAD_IO_Y, ADC_ATTEN_11db); 198 | 199 | gpio_set_direction(ODROID_GAMEPAD_IO_MENU, GPIO_MODE_INPUT); 200 | gpio_set_pull_mode(ODROID_GAMEPAD_IO_MENU, GPIO_PULLUP_ONLY); 201 | 202 | gpio_set_direction(ODROID_GAMEPAD_IO_VOLUME, GPIO_MODE_INPUT); 203 | #endif 204 | 205 | // Start background polling 206 | xTaskCreatePinnedToCore(&input_task, "input_task", 1024 * 2, NULL, 5, NULL, 1); 207 | 208 | ESP_LOGI(__func__, "done."); 209 | } 210 | -------------------------------------------------------------------------------- /main/display.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "display.h" 11 | 12 | static spi_device_handle_t spi; 13 | static DMA_ATTR uint16_t dma_buffer[SCREEN_WIDTH]; 14 | 15 | static const struct { 16 | uint8_t cmd; 17 | uint8_t data[16]; 18 | uint8_t databytes; 19 | } ili_init_cmds[] = { 20 | {0x01, {0}, 0x80}, 21 | {0x3A, {0x55}, 1}, // Pixel Format Set RGB565 22 | #ifdef TARGET_MRGC_G32 23 | {0x36, {(0x00|0x00|0x00)}, 1}, 24 | {0xB1, {0x00, 0x10}, 2}, // Frame Rate Control (1B=70, 1F=61, 10=119) 25 | {0xB2, {0x0c, 0x0c, 0x00, 0x33, 0x33}, 5}, 26 | {0xB7, {0x35}, 1}, 27 | {0xBB, {0x24}, 1}, 28 | {0xC0, {0x2C}, 1}, 29 | {0xC2, {0x01, 0xFF}, 2}, 30 | {0xC3, {0x11}, 1}, 31 | {0xC4, {0x20}, 1}, 32 | {0xC6, {0x0f}, 1}, 33 | {0xD0, {0xA4, 0xA1}, 2}, 34 | {0xE0, {0xD0, 0x00, 0x03, 0x09, 0x13, 0x1C, 0x3A, 0x55, 0x48, 0x18, 0x12, 0x0E, 0x19, 0x1E}, 14}, 35 | {0xE1, {0xD0, 0x00, 0x03, 0x09, 0x05, 0x25, 0x3A, 0x55, 0x50, 0x3D, 0x1C, 0x1D, 0x1D, 0x1E}, 14}, 36 | #else 37 | {0xCF, {0x00, 0xc3, 0x30}, 3}, 38 | {0xED, {0x64, 0x03, 0x12, 0x81}, 4}, 39 | {0xE8, {0x85, 0x00, 0x78}, 3}, 40 | {0xCB, {0x39, 0x2c, 0x00, 0x34, 0x02}, 5}, 41 | {0xF7, {0x20}, 1}, 42 | {0xEA, {0x00, 0x00}, 2}, 43 | {0xC0, {0x1B}, 1}, //Power control //VRH[5:0] 44 | {0xC1, {0x12}, 1}, //Power control //SAP[2:0];BT[3:0] 45 | {0xC5, {0x32, 0x3C}, 2}, //VCM control 46 | {0xC7, {0x91}, 1}, //VCM control2 47 | {0x36, {(0x20 | 0x80 | 0x08)}, 1}, // Memory Access Control 48 | {0xB1, {0x00, 0x1B}, 2}, // Frame Rate Control (1B=70, 1F=61, 10=119) 49 | {0xB6, {0x0A, 0xA2}, 2}, // Display Function Control 50 | {0xF6, {0x01, 0x30}, 2}, 51 | {0xF2, {0x00}, 1}, // 3Gamma Function Disable 52 | {0x26, {0x01}, 1}, //Gamma curve selected 53 | {0xE0, {0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00}, 15}, 54 | {0XE1, {0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F}, 15}, 55 | #endif 56 | {0x11, {0}, 0x80}, //Exit Sleep 57 | {0x29, {0}, 0x80}, //Display on 58 | }; 59 | 60 | 61 | static void ili_cmd(spi_device_handle_t spi, const uint8_t cmd) 62 | { 63 | spi_transaction_t t = { 64 | .length = 8, // In bits 65 | .tx_buffer = &cmd, 66 | .user = (void*)0, // DC line 67 | }; 68 | esp_err_t ret = spi_device_transmit(spi, &t); 69 | assert(ret==ESP_OK); 70 | } 71 | 72 | static void ili_data(spi_device_handle_t spi, const void *data, int len) 73 | { 74 | spi_transaction_t t = { 75 | .length = len * 8, // In bits 76 | .tx_buffer = data, 77 | .user = (void*)1, // DC Line 78 | }; 79 | esp_err_t ret = spi_device_transmit(spi, &t); 80 | assert(ret==ESP_OK); 81 | } 82 | 83 | static void ili_spi_pre_transfer_callback(spi_transaction_t *t) 84 | { 85 | gpio_set_level(LCD_PIN_NUM_DC, (int)t->user & 1); 86 | } 87 | 88 | void ili9341_writeLE(const uint16_t *buffer) 89 | { 90 | const int left = 0; 91 | const int top = SCREEN_OFFSET_TOP; 92 | const int width = SCREEN_WIDTH; 93 | const int height = SCREEN_HEIGHT; 94 | 95 | uint8_t tx_data[4]; 96 | 97 | tx_data[0] = (left) >> 8; //Start Col High 98 | tx_data[1] = (left) & 0xff; //Start Col Low 99 | tx_data[2] = (left + width - 1) >> 8; //End Col High 100 | tx_data[3] = (left + width - 1) & 0xff; //End Col Low 101 | ili_cmd(spi, 0x2A); 102 | ili_data(spi, tx_data, 4); 103 | 104 | tx_data[0] = top >> 8; //Start page high 105 | tx_data[1] = top & 0xff; //start page low 106 | tx_data[2] = (top + height - 1)>>8; //end page high 107 | tx_data[3] = (top + height - 1)&0xff; //end page low 108 | ili_cmd(spi, 0x2B); 109 | ili_data(spi, tx_data, 4); 110 | 111 | ili_cmd(spi, 0x2C); 112 | 113 | for (int y = 0; y < height; y++) 114 | { 115 | for (int i = 0; i < width; ++i) 116 | { 117 | uint16_t pixel = buffer[y * width + i]; 118 | dma_buffer[i] = pixel << 8 | pixel >> 8; 119 | } 120 | ili_data(spi, dma_buffer, width * 2); 121 | } 122 | } 123 | 124 | void ili9341_deinit() 125 | { 126 | spi_bus_remove_device(spi); 127 | gpio_reset_pin(LCD_PIN_NUM_DC); 128 | gpio_reset_pin(LCD_PIN_NUM_BCKL); 129 | } 130 | 131 | void ili9341_init() 132 | { 133 | esp_err_t ret; 134 | 135 | ESP_LOGI(__func__, "LCD: backlight init..."); 136 | 137 | ledc_timer_config(&(ledc_timer_config_t){ 138 | .duty_resolution = LEDC_TIMER_13_BIT, 139 | .freq_hz = 5000, 140 | .speed_mode = LEDC_LOW_SPEED_MODE, 141 | .timer_num = LEDC_TIMER_0, 142 | }); 143 | ledc_channel_config(&(ledc_channel_config_t){ 144 | .channel = LEDC_CHANNEL_0, 145 | .duty = 0x1fff, 146 | .gpio_num = LCD_PIN_NUM_BCKL, 147 | .intr_type = LEDC_INTR_FADE_END, 148 | .speed_mode = LEDC_LOW_SPEED_MODE, 149 | .timer_sel = LEDC_TIMER_0, 150 | }); 151 | ledc_fade_func_install(0); 152 | ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0x1fff, 500); 153 | ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT); 154 | 155 | 156 | ESP_LOGI(__func__, "LCD: spi init..."); 157 | 158 | PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[LCD_PIN_NUM_DC], PIN_FUNC_GPIO); 159 | gpio_set_direction(LCD_PIN_NUM_DC, GPIO_MODE_OUTPUT); 160 | gpio_set_level(LCD_PIN_NUM_DC, 1); 161 | 162 | // Initialize SPI 163 | spi_bus_config_t buscfg = { 164 | .miso_io_num = LCD_PIN_NUM_MISO, 165 | .mosi_io_num = LCD_PIN_NUM_MOSI, 166 | .sclk_io_num = LCD_PIN_NUM_CLK, 167 | .quadwp_io_num = GPIO_NUM_NC, 168 | .quadhd_io_num = GPIO_NUM_NC, 169 | }; 170 | 171 | spi_device_interface_config_t devcfg = { 172 | .clock_speed_hz = SPI_MASTER_FREQ_40M, 173 | .mode = 0, 174 | .spics_io_num = LCD_PIN_NUM_CS, 175 | .queue_size = 4, 176 | .pre_cb = ili_spi_pre_transfer_callback, 177 | .flags = SPI_DEVICE_NO_DUMMY, 178 | }; 179 | 180 | ret = spi_bus_initialize(HSPI_HOST, &buscfg, 1); 181 | //assert(ret==ESP_OK); 182 | 183 | ret = spi_bus_add_device(HSPI_HOST, &devcfg, &spi); 184 | assert(ret==ESP_OK); 185 | 186 | for (int cmd = 0; cmd < sizeof(ili_init_cmds)/sizeof(ili_init_cmds[0]); cmd++) 187 | { 188 | size_t datalen = ili_init_cmds[cmd].databytes & 0x7f; 189 | ili_cmd(spi, ili_init_cmds[cmd].cmd); 190 | if (datalen > 0) 191 | { 192 | memcpy(dma_buffer, ili_init_cmds[cmd].data, datalen); 193 | ili_data(spi, dma_buffer, datalen); 194 | } 195 | if (ili_init_cmds[cmd].databytes & 0x80) 196 | vTaskDelay(pdMS_TO_TICKS(100)); 197 | } 198 | 199 | // Flood the lcd's framebuffer 200 | ili_cmd(spi, 0x2A); 201 | ili_data(spi, (uint8_t[]){0, 0, 0xFF, 0xFF}, 4); 202 | ili_cmd(spi, 0x2B); 203 | ili_data(spi, (uint8_t[]){0, 0, 0xFF, 0xFF}, 4); 204 | ili_cmd(spi, 0x2C); 205 | memset(dma_buffer, 0, SCREEN_WIDTH * 2); 206 | for (int p = 0; p < 320 * 240; p += SCREEN_WIDTH) 207 | ili_data(spi, dma_buffer, SCREEN_WIDTH * 2); 208 | 209 | ESP_LOGI(__func__, "LCD Initialized."); 210 | } 211 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR >= 4 16 | #include 17 | #else 18 | #include 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "sdcard.h" 27 | #include "display.h" 28 | #include "input.h" 29 | 30 | #include "ugui/ugui.h" 31 | 32 | 33 | #define MFW_NVS_PARTITION "mfw_nvs" 34 | #define MFW_DATA_PARTITION "mfw_data" 35 | 36 | #ifndef PROJECT_VER 37 | #define PROJECT_VER "n/a" 38 | #endif 39 | 40 | #define APP_TABLE_MAGIC 0x1207 41 | #define APP_NVS_SIZE 0x3000 42 | 43 | #define FLASH_BLOCK_SIZE (64 * 1024) 44 | #define ERASE_BLOCK_SIZE (4 * 1024) 45 | 46 | #define LIST_SORT_OFFSET 0b0000 47 | #define LIST_SORT_SEQUENCE 0b0010 48 | #define LIST_SORT_DESCRIPTION 0b0100 49 | #define LIST_SORT_MAX 0b0101 50 | #define LIST_SORT_DIR_ASC 0b0000 51 | #define LIST_SORT_DIR_DESC 0b0001 52 | 53 | #define FIRMWARE_PARTS_MAX (20) 54 | #define FIRMWARE_TILE_WIDTH (86) 55 | #define FIRMWARE_TILE_HEIGHT (48) 56 | 57 | #define BATTERY_VMAX (4.20f) 58 | #define BATTERY_VMIN (3.30f) 59 | 60 | #define ITEM_COUNT ((SCREEN_HEIGHT-32)/52) 61 | 62 | #define ALIGN_ADDRESS(val, alignment) (((val & (alignment-1)) != 0) ? (val & ~(alignment-1)) + alignment : val) 63 | #define SET_STATUS_LED(on) gpio_set_level(GPIO_NUM_2, on); 64 | #define RG_MIN(a, b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b);_a < _b ? _a : _b; }) 65 | #define RG_MAX(a, b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b);_a > _b ? _a : _b; }) 66 | 67 | #ifdef TARGET_MRGC_G32 68 | #define HEADER_LENGTH 22 69 | #define HEADER_V00_01 "ESPLAY_FIRMWARE_V00_01" 70 | #define FIRMWARE_PATH SDCARD_BASE_PATH "/espgbc/firmware" 71 | #else 72 | #define HEADER_LENGTH 24 73 | #define HEADER_V00_01 "ODROIDGO_FIRMWARE_V00_01" 74 | #define FIRMWARE_PATH SDCARD_BASE_PATH "/odroid/firmware" 75 | #endif 76 | 77 | typedef struct 78 | { 79 | uint8_t type; 80 | uint8_t subtype; 81 | uint8_t _reserved0; 82 | uint8_t _reserved1; 83 | char label[16]; 84 | uint32_t flags; 85 | uint32_t length; 86 | uint32_t dataLength; 87 | } odroid_partition_t; // __attribute__((packed)) 88 | 89 | typedef struct 90 | { 91 | uint16_t magic; 92 | uint16_t flags; 93 | uint32_t startOffset; 94 | uint32_t endOffset; 95 | char description[40]; 96 | char filename[40]; 97 | uint16_t tile[FIRMWARE_TILE_WIDTH * FIRMWARE_TILE_HEIGHT]; 98 | odroid_partition_t parts[FIRMWARE_PARTS_MAX]; 99 | uint8_t parts_count; 100 | uint8_t _reserved0; 101 | uint16_t installSeq; 102 | } odroid_app_t; 103 | 104 | typedef struct 105 | { 106 | struct __attribute__((packed)) 107 | { 108 | char version[HEADER_LENGTH]; 109 | char description[40]; 110 | uint16_t tile[FIRMWARE_TILE_WIDTH * FIRMWARE_TILE_HEIGHT]; 111 | } header; 112 | odroid_partition_t parts[FIRMWARE_PARTS_MAX]; 113 | uint8_t parts_count; 114 | size_t flashSize; 115 | size_t fileSize; 116 | size_t dataOffset; 117 | uint32_t checksum; 118 | } odroid_fw_t; 119 | 120 | typedef struct 121 | { 122 | size_t offset; 123 | size_t size; 124 | } odroid_flash_block_t; 125 | 126 | typedef struct 127 | { 128 | long id; 129 | char label[32]; 130 | bool enabled; 131 | } dialog_option_t; 132 | 133 | static odroid_app_t *apps; 134 | static int apps_count = -1; 135 | static int apps_max = 4; 136 | static int apps_seq = 0; 137 | static int firstAppOffset = 0x100000; // We scan the table to find the real value but this is a reasonable default 138 | static uint16_t fb[SCREEN_WIDTH * SCREEN_HEIGHT]; 139 | static UG_GUI gui; 140 | static esp_err_t sdcardret; 141 | static nvs_handle nvs_h; 142 | 143 | static float read_battery(void); 144 | 145 | static void pset(UG_S16 x, UG_S16 y, UG_COLOR color) 146 | { 147 | fb[y * SCREEN_WIDTH + x] = color; 148 | } 149 | 150 | static void UpdateDisplay(void) 151 | { 152 | ili9341_writeLE(fb); 153 | } 154 | 155 | static void DisplayCenter(int top, const char *str) 156 | { 157 | const int maxlen = SCREEN_WIDTH / (gui.font.char_width + 1); 158 | int left = RG_MAX(0, (SCREEN_WIDTH - strlen(str) * (gui.font.char_width + 1)) / 2); 159 | int height = gui.font.char_height + 4 + 4; 160 | char tempstring[128] = {0}; 161 | 162 | UG_FillFrame(0, top, SCREEN_WIDTH-1, top + height - 1, UG_GetBackcolor()); 163 | UG_PutString(left, top + 4 , strncpy(tempstring, str, maxlen)); 164 | } 165 | 166 | static void DisplayPage(const char *title, const char *footer) 167 | { 168 | UG_FillFrame(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, C_WHITE); 169 | UG_FontSelect(&FONT_8X8); 170 | UG_SetBackcolor(C_MIDNIGHT_BLUE); 171 | UG_SetForecolor(C_WHITE); 172 | DisplayCenter(0, title); 173 | UG_SetForecolor(C_LIGHT_GRAY); 174 | DisplayCenter(SCREEN_HEIGHT - 16, footer); 175 | } 176 | 177 | static void DisplayIndicators(int page, int totalPages) 178 | { 179 | char tempstring[128]; 180 | 181 | UG_FontSelect(&FONT_8X8); 182 | UG_SetForecolor(0x8C51); 183 | 184 | // Page indicator 185 | sprintf(tempstring, "%d/%d", page, totalPages); 186 | UG_PutString(4, 4, tempstring); 187 | 188 | // Battery indicator 189 | int percent = (read_battery() - BATTERY_VMIN) / (BATTERY_VMAX - BATTERY_VMIN) * 100.f; 190 | sprintf(tempstring, "%d%%", RG_MIN(100, RG_MAX(0, percent))); 191 | UG_PutString(SCREEN_WIDTH - (9 * strlen(tempstring)) - 4, 4, tempstring); 192 | } 193 | 194 | static void DisplayError(const char *message) 195 | { 196 | UG_FontSelect(&FONT_8X12); 197 | UG_SetForecolor(C_RED); 198 | UG_SetBackcolor(C_WHITE); 199 | DisplayCenter((SCREEN_HEIGHT / 2) - (12 / 2), message); 200 | UpdateDisplay(); 201 | } 202 | 203 | static void DisplayMessage(const char *message) 204 | { 205 | UG_FontSelect(&FONT_8X12); 206 | UG_SetForecolor(C_BLACK); 207 | UG_SetBackcolor(C_WHITE); 208 | DisplayCenter((SCREEN_HEIGHT / 2) + 8 + (12 / 2) + 16, message); 209 | UpdateDisplay(); 210 | } 211 | 212 | static void DisplayNotification(const char *message) 213 | { 214 | UG_FontSelect(&FONT_8X8); 215 | UG_SetForecolor(C_WHITE); 216 | UG_SetBackcolor(C_BLUE); 217 | DisplayCenter(SCREEN_HEIGHT - 16, message); 218 | UpdateDisplay(); 219 | } 220 | 221 | static void DisplayProgress(int percent) 222 | { 223 | const int WIDTH = 200; 224 | const int HEIGHT = 12; 225 | const int FILL_WIDTH = WIDTH * ((percent > 100 ? 100 : percent) / 100.0f); 226 | int left = (SCREEN_WIDTH / 2) - (WIDTH / 2); 227 | int top = (SCREEN_HEIGHT / 2) - (HEIGHT / 2) + 16; 228 | UG_FillFrame(left - 1, top - 1, left + WIDTH + 1, top + HEIGHT + 1, C_WHITE); 229 | UG_DrawFrame(left - 1, top - 1, left + WIDTH + 1, top + HEIGHT + 1, C_BLACK); 230 | if (FILL_WIDTH > 0) 231 | UG_FillFrame(left, top, left + FILL_WIDTH, top + HEIGHT, C_GREEN); 232 | } 233 | 234 | static void DisplayFooter(const char *message) 235 | { 236 | int left = (SCREEN_WIDTH / 2) - (strlen(message) * 9 / 2); 237 | int top = SCREEN_HEIGHT - (16 * 2) - 8; 238 | UG_FontSelect(&FONT_8X12); 239 | UG_SetForecolor(C_BLACK); 240 | UG_SetBackcolor(C_WHITE); 241 | UG_FillFrame(0, top, SCREEN_WIDTH-1, top + 12, C_WHITE); 242 | UG_PutString(left, top, message); 243 | } 244 | 245 | static void DisplayHeader(const char *message) 246 | { 247 | int left = (SCREEN_WIDTH / 2) - (strlen(message) * 9 / 2); 248 | int top = (16 + 8); 249 | UG_FontSelect(&FONT_8X12); 250 | UG_SetForecolor(C_BLACK); 251 | UG_SetBackcolor(C_WHITE); 252 | UG_FillFrame(0, top, SCREEN_WIDTH-1, top + 12, C_WHITE); 253 | UG_PutString(left, top, message); 254 | } 255 | 256 | static void DisplayRow(int line, const char *line1, const char *line2, uint16_t color, const uint16_t *tile, bool selected) 257 | { 258 | const int margin = SCREEN_WIDTH > 240 ? 6 : 2; 259 | const int itemHeight = 52; 260 | const int textLeft = margin + FIRMWARE_TILE_WIDTH + margin; 261 | const int top = 16 + (line * itemHeight) - 1; 262 | 263 | UG_FontSelect(&FONT_8X12); 264 | UG_SetBackcolor(selected ? C_YELLOW : C_WHITE); 265 | UG_FillFrame(0, top + 2, SCREEN_WIDTH-1, top + itemHeight - 1 - 1, UG_GetBackcolor()); 266 | UG_SetForecolor(C_BLACK); 267 | UG_PutString(textLeft, top + 2 + 2 + 7, line1); 268 | UG_SetForecolor(color); 269 | UG_PutString(textLeft, top + 2 + 2 + 23, line2); 270 | 271 | if (tile) // Draw Tile at the end 272 | { 273 | for (int i = 0 ; i < FIRMWARE_TILE_HEIGHT; ++i) 274 | for (int j = 0; j < FIRMWARE_TILE_WIDTH; ++j) 275 | UG_DrawPixel(margin + j, top + 2 + i, tile[i * FIRMWARE_TILE_WIDTH + j]); 276 | } 277 | } 278 | 279 | //--------------- 280 | static float read_battery(void) 281 | { 282 | static esp_adc_cal_characteristics_t adc_cal; 283 | static float batteryVoltage = -1; 284 | 285 | if (batteryVoltage < 0) 286 | { 287 | adc1_config_width(ADC_WIDTH_BIT_12); 288 | adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11); 289 | esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_cal); 290 | batteryVoltage = esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_0), &adc_cal) * 2.f / 1000.f; 291 | } 292 | 293 | batteryVoltage += esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_0), &adc_cal) * 2.f / 1000.f; 294 | batteryVoltage /= 2; 295 | 296 | return batteryVoltage; 297 | } 298 | 299 | static void panic_abort(const char *reason) 300 | { 301 | ESP_LOGE(__func__, "Panic: %s", reason); 302 | DisplayError(reason); 303 | int level = 0; 304 | while (true) { 305 | SET_STATUS_LED(level); 306 | level = !level; 307 | vTaskDelay(100 / portTICK_PERIOD_MS); 308 | } 309 | } 310 | 311 | static void *safe_alloc(size_t size) 312 | { 313 | void *ptr = malloc(size); 314 | if (!ptr) 315 | panic_abort("MEMORY ALLOCATION ERROR"); 316 | return ptr; 317 | } 318 | 319 | static void cleanup_and_restart(void) 320 | { 321 | gpio_set_direction(GPIO_NUM_2, GPIO_MODE_INPUT); 322 | odroid_sdcard_close(); 323 | nvs_close(nvs_h); 324 | nvs_flash_deinit_partition(MFW_NVS_PARTITION); 325 | ili9341_writeLE(memset(fb, 0, sizeof(fb))); 326 | ili9341_deinit(); 327 | esp_restart(); 328 | } 329 | 330 | 331 | static int sort_app_table_by_offset(const void * a, const void * b) 332 | { 333 | if ( (*(odroid_app_t*)a).startOffset < (*(odroid_app_t*)b).startOffset ) return -1; 334 | if ( (*(odroid_app_t*)a).startOffset > (*(odroid_app_t*)b).startOffset ) return 1; 335 | return 0; 336 | } 337 | 338 | static int sort_app_table_by_sequence(const void * a, const void * b) 339 | { 340 | return (*(odroid_app_t*)a).installSeq - (*(odroid_app_t*)b).installSeq; 341 | } 342 | 343 | static int sort_app_table_by_alphabet(const void * a, const void * b) 344 | { 345 | return strcasecmp((*(odroid_app_t*)a).description, (*(odroid_app_t*)b).description); 346 | } 347 | 348 | static void sort_app_table(int newMode) 349 | { 350 | switch(newMode & ~1) { 351 | case LIST_SORT_SEQUENCE: 352 | qsort(apps, apps_count, sizeof(odroid_app_t), &sort_app_table_by_sequence); 353 | break; 354 | case LIST_SORT_DESCRIPTION: 355 | qsort(apps, apps_count, sizeof(odroid_app_t), &sort_app_table_by_alphabet); 356 | break; 357 | case LIST_SORT_OFFSET: 358 | default: 359 | qsort(apps, apps_count, sizeof(odroid_app_t), &sort_app_table_by_offset); 360 | break; 361 | } 362 | 363 | if (newMode & 1) { // Reverse array. Very inefficient. 364 | odroid_app_t *tmp = safe_alloc(sizeof(odroid_app_t)); 365 | int i = apps_count - 1, j = 0; 366 | while (i > j) 367 | { 368 | memcpy(tmp, &apps[i], sizeof(odroid_app_t)); 369 | memcpy(&apps[i], &apps[j], sizeof(odroid_app_t)); 370 | memcpy(&apps[j], tmp, sizeof(odroid_app_t)); 371 | i--; 372 | j++; 373 | } 374 | free(tmp); 375 | } 376 | } 377 | 378 | 379 | static void read_app_table(void) 380 | { 381 | const esp_partition_t *app_table_part = esp_partition_find_first( 382 | ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, MFW_DATA_PARTITION); 383 | 384 | if (!app_table_part) 385 | { 386 | panic_abort("NO APP TABLE ERROR"); 387 | } 388 | else if (!apps) 389 | { 390 | apps = safe_alloc(app_table_part->size); 391 | } 392 | 393 | apps_max = (app_table_part->size / sizeof(odroid_app_t)); 394 | apps_count = 0; 395 | apps_seq = 0; 396 | 397 | if (esp_partition_read(app_table_part, 0, apps, app_table_part->size) != ESP_OK) 398 | { 399 | panic_abort("APP TABLE READ ERROR"); 400 | } 401 | 402 | for (int i = 0; i < apps_max; i++) 403 | { 404 | if (apps[i].magic != APP_TABLE_MAGIC) 405 | break; 406 | if (apps[i].installSeq >= apps_seq) 407 | apps_seq = apps[i].installSeq + 1; 408 | apps_count++; 409 | } 410 | 411 | //64K align the address (https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/partition-tables.html#offset-size) 412 | firstAppOffset = app_table_part->address + app_table_part->size; 413 | firstAppOffset = ALIGN_ADDRESS(firstAppOffset, FLASH_BLOCK_SIZE); 414 | 415 | ESP_LOGI(__func__, "Read app table (%d apps)", apps_count); 416 | } 417 | 418 | 419 | static void write_app_table() 420 | { 421 | const esp_partition_t *app_table_part = esp_partition_find_first( 422 | ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, MFW_DATA_PARTITION); 423 | 424 | if (!apps || !app_table_part) 425 | { 426 | panic_abort("NO APP TABLE ERROR"); 427 | } 428 | 429 | for (int i = apps_count; i < apps_max; ++i) 430 | { 431 | memset(&apps[i], 0xff, sizeof(odroid_app_t)); 432 | } 433 | 434 | if (esp_partition_erase_range(app_table_part, 0, app_table_part->size) != ESP_OK) 435 | { 436 | panic_abort("APP TABLE ERASE ERROR"); 437 | } 438 | 439 | if (esp_partition_write(app_table_part, 0, apps, app_table_part->size) != ESP_OK) 440 | { 441 | panic_abort("APP TABLE WRITE ERROR"); 442 | } 443 | 444 | ESP_LOGI(__func__, "Written app table (%d apps)", apps_count); 445 | } 446 | 447 | 448 | static void write_partition_table(const odroid_app_t *app) 449 | { 450 | esp_partition_info_t partitionTable[ESP_PARTITION_TABLE_MAX_ENTRIES]; 451 | size_t nextPart = 0; 452 | 453 | if (spi_flash_read(ESP_PARTITION_TABLE_OFFSET, &partitionTable, sizeof(partitionTable)) != ESP_OK) 454 | { 455 | panic_abort("PART TABLE READ ERROR"); 456 | } 457 | 458 | // Keep only the valid system partitions 459 | for (int i = 0; i < ESP_PARTITION_TABLE_MAX_ENTRIES; ++i) 460 | { 461 | esp_partition_info_t *part = &partitionTable[i]; 462 | if (part->magic == 0xFFFF) 463 | break; 464 | if (part->magic != ESP_PARTITION_MAGIC) 465 | continue; 466 | if (part->pos.offset >= firstAppOffset) 467 | continue; 468 | partitionTable[nextPart++] = *part; 469 | ESP_LOGI(__func__, "Keeping partition #%d '%s'", nextPart - 1, part->label); 470 | } 471 | 472 | // Append app's partitions, if any 473 | if (app) 474 | { 475 | size_t flashOffset = app->startOffset; 476 | 477 | for (int i = 0; i < app->parts_count && i < ESP_PARTITION_TABLE_MAX_ENTRIES; ++i) 478 | { 479 | esp_partition_info_t* part = &partitionTable[nextPart++]; 480 | part->magic = ESP_PARTITION_MAGIC; 481 | part->type = app->parts[i].type; 482 | part->subtype = app->parts[i].subtype; 483 | part->pos.offset = flashOffset; 484 | part->pos.size = app->parts[i].length; 485 | memcpy(&part->label, app->parts[i].label, 16); 486 | part->flags = app->parts[i].flags; 487 | 488 | flashOffset += app->parts[i].length; 489 | 490 | ESP_LOGI(__func__, "Added partition #%d '%s'", nextPart - 1, part->label); 491 | } 492 | } 493 | 494 | // We must fill the rest with 0xFF, the boot loader checks magic = 0xFFFF, type = 0xFF, subtype = 0xFF 495 | while (nextPart < ESP_PARTITION_TABLE_MAX_ENTRIES) 496 | { 497 | memset(&partitionTable[nextPart++], 0xFF, sizeof(esp_partition_info_t)); 498 | } 499 | 500 | if (spi_flash_erase_range(ESP_PARTITION_TABLE_OFFSET, ERASE_BLOCK_SIZE) != ESP_OK) 501 | { 502 | panic_abort("PART TABLE ERASE ERROR"); 503 | } 504 | 505 | if (spi_flash_write(ESP_PARTITION_TABLE_OFFSET, partitionTable, sizeof(partitionTable)) != ESP_OK) 506 | { 507 | panic_abort("PART TABLE WRITE ERROR"); 508 | } 509 | 510 | // esp_partition_reload_table(); 511 | } 512 | 513 | 514 | static void boot_application(odroid_app_t *app) 515 | { 516 | ESP_LOGI(__func__, "Booting application."); 517 | 518 | if (app) 519 | { 520 | DisplayMessage("Updating partitions ..."); 521 | write_partition_table(app); 522 | } 523 | 524 | DisplayMessage("Setting boot partition ..."); 525 | 526 | // This is the correct way of doing things, but we must patch esp-idf to allow us to reload the partition table 527 | // So, now, instead we just write the OTA partition ourselves. It is always the same (boot app OTA+0). 528 | #if 0 529 | const esp_partition_t *partition = esp_partition_find_first( 530 | ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL); 531 | 532 | if (!partition) 533 | { 534 | DisplayError("NO BOOT PART ERROR"); 535 | panic_abort(); 536 | } 537 | 538 | if (esp_ota_set_boot_partition(partition) != ESP_OK) 539 | { 540 | DisplayError("BOOT SET ERROR"); 541 | panic_abort(); 542 | } 543 | #else 544 | uint32_t ota_data[8] = {1, 0, 0, 0, 0, 0, 0xFFFFFFFFU, 0x4743989A}; 545 | 546 | if (spi_flash_erase_range(0xD000, 0x1000) != ESP_OK 547 | || spi_flash_write(0xD000, &ota_data, sizeof(ota_data)) != ESP_OK) 548 | { 549 | panic_abort("BOOT SET ERROR"); 550 | } 551 | #endif 552 | 553 | cleanup_and_restart(); 554 | } 555 | 556 | 557 | static void defrag_flash(void) 558 | { 559 | size_t nextStartOffset = firstAppOffset; 560 | size_t totalBytesToMove = 0; 561 | size_t totalBytesMoved = 0; 562 | char tempstring[128]; 563 | 564 | sort_app_table(LIST_SORT_OFFSET); 565 | 566 | // First loop to get total for the progress bar 567 | for (int i = 0; i < apps_count; i++) 568 | { 569 | if (apps[i].startOffset > nextStartOffset) 570 | { 571 | totalBytesToMove += (apps[i].endOffset - apps[i].startOffset); 572 | } else { 573 | nextStartOffset = apps[i].endOffset + 1; 574 | } 575 | } 576 | 577 | sprintf(tempstring, "Moving: %.2f MB", (float)totalBytesToMove / 1024 / 1024); 578 | DisplayPage("Defragmenting flash", tempstring); 579 | DisplayHeader("Making some space..."); 580 | UpdateDisplay(); 581 | 582 | void *dataBuffer = safe_alloc(FLASH_BLOCK_SIZE); 583 | 584 | for (int i = 0; i < apps_count; i++) 585 | { 586 | if (apps[i].startOffset > nextStartOffset) 587 | { 588 | SET_STATUS_LED(1); 589 | 590 | size_t app_size = apps[i].endOffset - apps[i].startOffset; 591 | size_t newOffset = nextStartOffset, oldOffset = apps[i].startOffset; 592 | // move 593 | for (size_t i = 0; i < app_size; i += FLASH_BLOCK_SIZE) 594 | { 595 | ESP_LOGI(__func__, "Moving 0x%x to 0x%x", oldOffset + i, newOffset + i); 596 | 597 | DisplayMessage("Defragmenting ... (E)"); 598 | spi_flash_erase_range(newOffset + i, FLASH_BLOCK_SIZE); 599 | 600 | DisplayMessage("Defragmenting ... (R)"); 601 | spi_flash_read(oldOffset + i, dataBuffer, FLASH_BLOCK_SIZE); 602 | 603 | DisplayMessage("Defragmenting ... (W)"); 604 | spi_flash_write(newOffset + i, dataBuffer, FLASH_BLOCK_SIZE); 605 | 606 | totalBytesMoved += FLASH_BLOCK_SIZE; 607 | 608 | DisplayProgress((float) totalBytesMoved / totalBytesToMove * 100.0); 609 | } 610 | 611 | apps[i].startOffset = newOffset; 612 | apps[i].endOffset = newOffset + app_size; 613 | 614 | SET_STATUS_LED(0); 615 | } 616 | 617 | nextStartOffset = apps[i].endOffset + 1; 618 | } 619 | 620 | free(dataBuffer); 621 | 622 | write_app_table(); 623 | } 624 | 625 | 626 | static void find_free_blocks(odroid_flash_block_t **blocks, size_t *count, size_t *totalFreeSpace) 627 | { 628 | size_t flashSize = spi_flash_get_chip_size(); 629 | size_t previousBlockEnd = firstAppOffset; 630 | 631 | *blocks = safe_alloc(sizeof(odroid_flash_block_t) * 32); 632 | *totalFreeSpace = 0; 633 | *count = 0; 634 | 635 | sort_app_table(LIST_SORT_OFFSET); 636 | 637 | for (int i = 0; i < apps_count; i++) 638 | { 639 | size_t free_space = apps[i].startOffset - previousBlockEnd; 640 | 641 | if (free_space > 0) { 642 | odroid_flash_block_t *block = &(*blocks)[(*count)++]; 643 | block->offset = previousBlockEnd; 644 | block->size = free_space; 645 | *totalFreeSpace += block->size; 646 | ESP_LOGI(__func__, "Found free block: %d 0x%x %d", i, block->offset, free_space / 1024); 647 | } 648 | 649 | previousBlockEnd = apps[i].endOffset + 1; 650 | } 651 | 652 | if (((int)flashSize - previousBlockEnd) > 0) { 653 | odroid_flash_block_t *block = &(*blocks)[(*count)++]; 654 | block->offset = previousBlockEnd; 655 | block->size = (flashSize - previousBlockEnd); 656 | *totalFreeSpace += block->size; 657 | ESP_LOGI(__func__, "Found free block: end 0x%x %d", block->offset, block->size / 1024); 658 | } 659 | } 660 | 661 | static int find_free_block(size_t size, bool defragIfNeeded) 662 | { 663 | odroid_flash_block_t *blocks; 664 | size_t count, totalFreeSpace; 665 | 666 | find_free_blocks(&blocks, &count, &totalFreeSpace); 667 | 668 | int result = -1; 669 | 670 | for (int i = 0; i < count; i++) 671 | { 672 | if (blocks[i].size >= size) { 673 | result = blocks[i].offset; 674 | break; 675 | } 676 | } 677 | 678 | if (result < 0 && totalFreeSpace >= size) { 679 | defrag_flash(); 680 | result = find_free_block(size, false); 681 | } 682 | 683 | free(blocks); 684 | return result; 685 | } 686 | 687 | 688 | static odroid_fw_t *firmware_get_info(const char *filename) 689 | { 690 | odroid_fw_t *outData = safe_alloc(sizeof(odroid_fw_t)); 691 | 692 | FILE* file = fopen(filename, "rb"); 693 | if (!file) 694 | goto firmware_get_info_err; 695 | 696 | size_t file_size; 697 | 698 | fseek(file, 0, SEEK_END); 699 | file_size = ftell(file); 700 | fseek(file, 0, SEEK_SET); 701 | 702 | if (!fread(&outData->header, sizeof(outData->header), 1, file)) 703 | { 704 | goto firmware_get_info_err; 705 | } 706 | 707 | if (memcmp(HEADER_V00_01, outData->header.version, HEADER_LENGTH) != 0) 708 | { 709 | goto firmware_get_info_err; 710 | } 711 | 712 | outData->header.description[sizeof(outData->header.description) - 1] = 0; 713 | outData->parts_count = 0; 714 | outData->flashSize = 0; 715 | outData->dataOffset = ftell(file); 716 | outData->fileSize = file_size; 717 | 718 | while (ftell(file) < (file_size - 4)) 719 | { 720 | // Partition information 721 | odroid_partition_t *part = &outData->parts[outData->parts_count]; 722 | 723 | if (fread(part, sizeof(odroid_partition_t), 1, file) != 1) 724 | goto firmware_get_info_err; 725 | 726 | // Check if dataLength is valid 727 | if (ftell(file) + part->dataLength > file_size || part->dataLength > part->length) 728 | goto firmware_get_info_err; 729 | 730 | // Check partition subtype 731 | if (part->type == 0xff) 732 | goto firmware_get_info_err; 733 | 734 | // 4KB align the partition length, this is needed for erasing 735 | part->length = ALIGN_ADDRESS(part->length, ERASE_BLOCK_SIZE); 736 | 737 | outData->flashSize += part->length; 738 | outData->parts_count++; 739 | 740 | fseek(file, part->dataLength, SEEK_CUR); 741 | } 742 | 743 | if (outData->parts_count >= FIRMWARE_PARTS_MAX) 744 | goto firmware_get_info_err; 745 | 746 | fseek(file, file_size - sizeof(outData->checksum), SEEK_SET); 747 | fread(&outData->checksum, sizeof(outData->checksum), 1, file); 748 | 749 | // We try to steal some unused space if possible, otherwise we might waste up to 48K 750 | odroid_partition_t *part = &outData->parts[outData->parts_count - 1]; 751 | if (part->type == ESP_PARTITION_TYPE_APP && (part->length - part->dataLength) >= APP_NVS_SIZE) { 752 | ESP_LOGI(__func__, "Found room for NVS partition, reducing last partition size by %d", APP_NVS_SIZE); 753 | part->length -= APP_NVS_SIZE; 754 | outData->flashSize -= APP_NVS_SIZE; 755 | } 756 | // Add an application-specific NVS partition. 757 | odroid_partition_t *nvs_part = &outData->parts[outData->parts_count]; 758 | strcpy(nvs_part->label, "nvs"); 759 | nvs_part->dataLength = 0; 760 | nvs_part->length = APP_NVS_SIZE; 761 | nvs_part->type = ESP_PARTITION_TYPE_DATA; 762 | nvs_part->subtype = ESP_PARTITION_SUBTYPE_DATA_NVS; 763 | outData->flashSize += nvs_part->length; 764 | outData->parts_count++; 765 | 766 | fclose(file); 767 | return outData; 768 | 769 | firmware_get_info_err: 770 | free(outData); 771 | fclose(file); 772 | return NULL; 773 | } 774 | 775 | 776 | static void flash_firmware(const char *fullPath) 777 | { 778 | odroid_app_t *app = memset(&apps[apps_count], 0x00, sizeof(*app)); 779 | odroid_fw_t *fw = firmware_get_info(fullPath); 780 | void *dataBuffer = safe_alloc(FLASH_BLOCK_SIZE); 781 | char tempstring[128]; 782 | 783 | ESP_LOGI(__func__, "Flashing file: %s", fullPath); 784 | 785 | sort_app_table(LIST_SORT_OFFSET); 786 | DisplayPage("Install Application", "Destination: Pending"); 787 | DisplayFooter("[B] Go Back"); 788 | UpdateDisplay(); 789 | SET_STATUS_LED(0); 790 | 791 | if (!fw) 792 | { 793 | DisplayError("INVALID FIRMWARE FILE"); // To do: Make it show what is invalid 794 | while (input_wait_for_button_press(-1) != ODROID_INPUT_B); 795 | free(dataBuffer), free(fw); 796 | return; 797 | } 798 | 799 | int currentFlashAddress = find_free_block(fw->flashSize, true); 800 | if (currentFlashAddress == -1) 801 | { 802 | DisplayError("NOT ENOUGH FREE SPACE"); 803 | while (input_wait_for_button_press(-1) != ODROID_INPUT_B); 804 | free(dataBuffer), free(fw); 805 | return; 806 | } 807 | 808 | strncpy(app->description, fw->header.description, sizeof(app->description)-1); 809 | strncpy(app->filename, strrchr(fullPath, '/'), sizeof(app->filename)-1); 810 | memcpy(app->tile, fw->header.tile, sizeof(app->tile)); 811 | memcpy(app->parts, fw->parts, sizeof(app->parts)); 812 | app->parts_count = fw->parts_count; 813 | 814 | ESP_LOGI(__func__, "Destination: 0x%x", currentFlashAddress); 815 | ESP_LOGI(__func__, "Description: '%s'", app->description); 816 | 817 | sprintf(tempstring, "Destination: 0x%x", currentFlashAddress); 818 | DisplayPage("Install Application", tempstring); 819 | DisplayHeader(app->description); 820 | DisplayMessage("[START]"); 821 | DisplayFooter("[B] Cancel"); 822 | 823 | int tileLeft = (SCREEN_WIDTH / 2) - (FIRMWARE_TILE_WIDTH / 2); 824 | int tileTop = (16 + 16 + 16); 825 | 826 | for (int i = 0 ; i < FIRMWARE_TILE_HEIGHT; ++i) 827 | for (int j = 0; j < FIRMWARE_TILE_WIDTH; ++j) 828 | UG_DrawPixel(tileLeft + j, tileTop + i, app->tile[i * FIRMWARE_TILE_WIDTH + j]); 829 | 830 | UG_DrawFrame(tileLeft - 1, tileTop - 1, tileLeft + FIRMWARE_TILE_WIDTH, tileTop + FIRMWARE_TILE_HEIGHT, C_BLACK); 831 | UpdateDisplay(); 832 | 833 | while (1) 834 | { 835 | int btn = input_wait_for_button_press(-1); 836 | if (btn == ODROID_INPUT_START) break; 837 | if (btn == ODROID_INPUT_B) return; 838 | } 839 | 840 | DisplayMessage("Verifying ..."); 841 | DisplayFooter(""); 842 | UpdateDisplay(); 843 | 844 | SET_STATUS_LED(1); 845 | 846 | FILE *file = fopen(fullPath, "rb"); 847 | if (file == NULL) 848 | { 849 | panic_abort("FILE OPEN ERROR"); 850 | } 851 | 852 | uint32_t checksum = 0; 853 | while (true) 854 | { 855 | size_t count = fread(dataBuffer, 1, FLASH_BLOCK_SIZE, file); 856 | if (ftell(file) == fw->fileSize) 857 | { 858 | count -= 4; 859 | } 860 | 861 | checksum = crc32_le(checksum, dataBuffer, count); 862 | 863 | if (count < FLASH_BLOCK_SIZE) break; 864 | } 865 | 866 | if (checksum != fw->checksum) 867 | { 868 | ESP_LOGE(__func__, "Checksum mismatch: expected: %#010x, computed:%#010x", fw->checksum, checksum); 869 | panic_abort("CHECKSUM MISMATCH ERROR"); 870 | } 871 | ESP_LOGI(__func__, "Checksum OK: %#010x", checksum); 872 | 873 | // restore location to end of description 874 | fseek(file, fw->dataOffset, SEEK_SET); 875 | 876 | app->magic = APP_TABLE_MAGIC; 877 | app->startOffset = currentFlashAddress; 878 | 879 | // Copy the firmware 880 | for (int i = 0; i < app->parts_count; i++) 881 | { 882 | odroid_partition_t *slot = &app->parts[i]; 883 | 884 | // Skip header, firmware_get_info prepared everything for us 885 | fseek(file, sizeof(odroid_partition_t), SEEK_CUR); 886 | 887 | SET_STATUS_LED(0); 888 | 889 | // Erase target partition space 890 | sprintf(tempstring, "Erasing ... (%d/%d)", i+1, app->parts_count); 891 | ESP_LOGI(__func__, "%s", tempstring); 892 | 893 | DisplayProgress(0); 894 | DisplayMessage(tempstring); 895 | 896 | int eraseBlocks = slot->length / ERASE_BLOCK_SIZE; 897 | if (eraseBlocks * ERASE_BLOCK_SIZE < slot->length) ++eraseBlocks; 898 | 899 | if (spi_flash_erase_range(currentFlashAddress, eraseBlocks * ERASE_BLOCK_SIZE) != ESP_OK) 900 | { 901 | ESP_LOGE(__func__, "spi_flash_erase_range failed. eraseBlocks=%d", eraseBlocks); 902 | panic_abort("ERASE ERROR"); 903 | } 904 | 905 | if (slot->dataLength > 0) 906 | { 907 | size_t nextEntry = ftell(file) + slot->dataLength; 908 | 909 | SET_STATUS_LED(1); 910 | 911 | // Write data 912 | int totalCount = 0; 913 | for (int offset = 0; offset < slot->dataLength; offset += FLASH_BLOCK_SIZE) 914 | { 915 | sprintf(tempstring, "Writing (%d/%d)", i+1, app->parts_count); 916 | ESP_LOGI(__func__, "%s", tempstring); 917 | DisplayProgress((float)offset / (float)(slot->dataLength - FLASH_BLOCK_SIZE) * 100.0f); 918 | DisplayMessage(tempstring); 919 | 920 | // read 921 | size_t count = fread(dataBuffer, 1, FLASH_BLOCK_SIZE, file); 922 | if (count <= 0) 923 | { 924 | panic_abort("DATA READ ERROR"); 925 | } 926 | 927 | if (offset + count >= slot->dataLength) 928 | { 929 | count = slot->dataLength - offset; 930 | } 931 | 932 | // flash 933 | if (spi_flash_write(currentFlashAddress + offset, dataBuffer, count) != ESP_OK) 934 | { 935 | ESP_LOGE(__func__, "spi_flash_write failed. address=%#08x", currentFlashAddress + offset); 936 | panic_abort("WRITE ERROR"); 937 | } 938 | 939 | totalCount += count; 940 | } 941 | 942 | SET_STATUS_LED(0); 943 | 944 | if (totalCount != slot->dataLength) 945 | { 946 | ESP_LOGE(__func__, "Size mismatch: length=%#08x, totalCount=%#08x", slot->dataLength, totalCount); 947 | panic_abort("DATA SIZE ERROR"); 948 | } 949 | 950 | fseek(file, nextEntry, SEEK_SET); 951 | // TODO: verify 952 | } 953 | 954 | // Notify OK 955 | ESP_LOGI(__func__, "Partition(%d): OK. Length=%#08x", i, slot->length); 956 | currentFlashAddress += slot->length; 957 | } 958 | 959 | fclose(file); 960 | free(fw); 961 | free(dataBuffer); 962 | 963 | // 64K align our endOffset 964 | app->endOffset = ALIGN_ADDRESS(currentFlashAddress, FLASH_BLOCK_SIZE) - 1; 965 | 966 | // Remember the install order, for display sorting 967 | app->installSeq = apps_seq++; 968 | 969 | // Write app table 970 | apps_count++; // Everything went well, acknowledge the new app 971 | write_app_table(); 972 | 973 | DisplayMessage("Ready !"); 974 | DisplayFooter("[B] Go Back | [A] Boot"); 975 | UpdateDisplay(); 976 | 977 | while (1) 978 | { 979 | int btn = input_wait_for_button_press(-1); 980 | if (btn == ODROID_INPUT_START) break; 981 | if (btn == ODROID_INPUT_A) break; 982 | if (btn == ODROID_INPUT_B) return; 983 | } 984 | 985 | boot_application(app); 986 | } 987 | 988 | 989 | static char *ui_choose_file(const char *path) 990 | { 991 | char tempstring[128]; 992 | 993 | // Check SD card 994 | if (sdcardret != ESP_OK) 995 | { 996 | DisplayPage("Error", "Error"); 997 | DisplayError("SD CARD ERROR"); 998 | vTaskDelay(200); 999 | return NULL; 1000 | } 1001 | 1002 | char **files = NULL; 1003 | char *result = NULL; 1004 | int fileCount = odroid_sdcard_files_get(path, ".fw", &files); 1005 | int currentItem = 0; 1006 | 1007 | ESP_LOGI(__func__, "fileCount=%d", fileCount); 1008 | 1009 | while (true) 1010 | { 1011 | int page = (currentItem / ITEM_COUNT) * ITEM_COUNT; 1012 | size_t count, totalFreeSpace; 1013 | odroid_flash_block_t *blocks; 1014 | 1015 | find_free_blocks(&blocks, &count, &totalFreeSpace); 1016 | free(blocks); 1017 | 1018 | sprintf(tempstring, "Free space: %.2fMB (%d block)", (double)totalFreeSpace / 1024 / 1024, count); 1019 | 1020 | DisplayPage("Select a file", tempstring); 1021 | DisplayIndicators(page / ITEM_COUNT + 1, (int)ceil((double)fileCount / ITEM_COUNT)); 1022 | 1023 | for (int line = 0; line < ITEM_COUNT && (page + line) < fileCount; ++line) 1024 | { 1025 | char *fileName = files[page + line]; 1026 | bool selected = (page + line) == currentItem; 1027 | 1028 | sprintf(tempstring, "%s/%s", FIRMWARE_PATH, fileName); 1029 | 1030 | odroid_fw_t *fw = firmware_get_info(tempstring); 1031 | if (fw) { 1032 | sprintf(tempstring, "%.2f MB", (float)fw->flashSize / 1024 / 1024); 1033 | DisplayRow(line, fileName, tempstring, C_GRAY, fw->header.tile, selected); 1034 | } else { 1035 | DisplayRow(line, fileName, "Invalid firmware", C_RED, NULL, selected); 1036 | } 1037 | free(fw); 1038 | } 1039 | 1040 | if (fileCount == 0) 1041 | DisplayMessage("SD Card Empty"); 1042 | 1043 | UpdateDisplay(); 1044 | 1045 | // Wait for input but refresh display after 1000 ticks if no input 1046 | int btn = input_wait_for_button_press(1000); 1047 | 1048 | if (fileCount > 0) 1049 | { 1050 | if (btn == ODROID_INPUT_DOWN) 1051 | { 1052 | if (++currentItem >= fileCount) currentItem = 0; 1053 | } 1054 | else if (btn == ODROID_INPUT_UP) 1055 | { 1056 | if (--currentItem < 0) currentItem = fileCount - 1; 1057 | } 1058 | else if (btn == ODROID_INPUT_RIGHT) 1059 | { 1060 | if (page + ITEM_COUNT < fileCount) currentItem = page + ITEM_COUNT; 1061 | else currentItem = 0; 1062 | } 1063 | else if (btn == ODROID_INPUT_LEFT) 1064 | { 1065 | if (page - ITEM_COUNT >= 0) currentItem = page - ITEM_COUNT; 1066 | else currentItem = (fileCount - 1) / ITEM_COUNT * ITEM_COUNT; 1067 | } 1068 | else if (btn == ODROID_INPUT_A) 1069 | { 1070 | size_t fullPathLength = strlen(path) + 1 + strlen(files[currentItem]) + 1; 1071 | char *fullPath = safe_alloc(fullPathLength); 1072 | 1073 | strcpy(fullPath, path); 1074 | strcat(fullPath, "/"); 1075 | strcat(fullPath, files[currentItem]); 1076 | 1077 | result = fullPath; 1078 | break; 1079 | } 1080 | } 1081 | 1082 | if (btn == ODROID_INPUT_B) 1083 | { 1084 | break; 1085 | } 1086 | } 1087 | 1088 | odroid_sdcard_files_free(files, fileCount); 1089 | 1090 | return result; 1091 | } 1092 | 1093 | static int ui_choose_dialog(dialog_option_t *options, int optionCount, bool cancellable) 1094 | { 1095 | const int border = 3; 1096 | const int itemWidth = 190; 1097 | const int itemHeight = 20; 1098 | const int width = itemWidth + (border * 2); 1099 | const int height = ((optionCount+1) * itemHeight) + (border * 2); 1100 | int currentItem = 0; 1101 | 1102 | while (true) 1103 | { 1104 | int top = (SCREEN_HEIGHT - height) / 2; 1105 | int left = (SCREEN_WIDTH - width) / 2; 1106 | 1107 | UG_FillFrame(left, top, left + width, top + height, C_BLUE); 1108 | UG_FillFrame(left + border, top + border, left + width - border, top + height - border, C_WHITE); 1109 | 1110 | top += border; 1111 | left += border; 1112 | 1113 | for (int i = 0; i < optionCount; i++) { 1114 | int fg = (i == currentItem) ? C_WHITE : C_BLACK; 1115 | int bg = (i == currentItem) ? C_BLUE : C_WHITE; 1116 | 1117 | if (!options[i].enabled) { 1118 | fg = C_GRAY; 1119 | } 1120 | 1121 | UG_SetForecolor(fg); 1122 | UG_SetBackcolor(bg); 1123 | UG_FillFrame(left, top, left + itemWidth, top + itemHeight, bg); 1124 | UG_FontSelect(&FONT_8X12); 1125 | UG_PutString(left + 2, top + 3, (const char*)options[i].label); 1126 | 1127 | top += itemHeight; 1128 | } 1129 | 1130 | // Display version at the bottom 1131 | UG_SetForecolor(C_GRAY); 1132 | UG_SetBackcolor(C_WHITE); 1133 | UG_FontSelect(&FONT_8X8); 1134 | UG_PutString(left + 2, top + 2, "Multi-firmware build:\n " PROJECT_VER); 1135 | UpdateDisplay(); 1136 | 1137 | int btn = input_wait_for_button_press(-1); 1138 | 1139 | if (btn == ODROID_INPUT_DOWN) 1140 | { 1141 | if (++currentItem >= optionCount) currentItem = 0; 1142 | } 1143 | else if (btn == ODROID_INPUT_UP) 1144 | { 1145 | if (--currentItem < 0) currentItem = optionCount - 1; 1146 | } 1147 | else if (btn == ODROID_INPUT_A) 1148 | { 1149 | if (options[currentItem].enabled) { 1150 | return options[currentItem].id; 1151 | } 1152 | } 1153 | else if (btn == ODROID_INPUT_B) 1154 | { 1155 | if (cancellable) break; 1156 | } 1157 | } 1158 | 1159 | return -1; 1160 | } 1161 | 1162 | static void ui_draw_app_page(int currentItem) 1163 | { 1164 | int page = (currentItem / ITEM_COUNT) * ITEM_COUNT; 1165 | char tempstring[128]; 1166 | 1167 | DisplayPage("MULTI-FIRMWARE", "[MENU] Menu | [A] Boot App"); 1168 | DisplayIndicators(page / ITEM_COUNT + 1, (int)ceil((double)apps_count / ITEM_COUNT)); 1169 | 1170 | for (int line = 0; line < ITEM_COUNT && (page + line) < apps_count; ++line) 1171 | { 1172 | odroid_app_t *app = &apps[page + line]; 1173 | sprintf(tempstring, "0x%x - 0x%x", app->startOffset, app->endOffset); 1174 | DisplayRow(line, app->description, tempstring, C_GRAY, app->tile, (page + line) == currentItem); 1175 | } 1176 | 1177 | if (apps_count == 0) 1178 | DisplayMessage("No apps have been flashed yet!"); 1179 | 1180 | UpdateDisplay(); 1181 | } 1182 | 1183 | 1184 | static void start_normal(void) 1185 | { 1186 | char tempstring[128]; 1187 | int displayOrder = 0; 1188 | int currentItem = 0; 1189 | int queuedBtn = -1; 1190 | 1191 | nvs_flash_init_partition(MFW_NVS_PARTITION); 1192 | if (nvs_open("settings", NVS_READWRITE, &nvs_h) != ESP_OK) { 1193 | nvs_flash_erase(); 1194 | nvs_open("settings", NVS_READWRITE, &nvs_h); 1195 | } 1196 | nvs_get_i32(nvs_h, "display_order", &displayOrder); 1197 | 1198 | read_app_table(); 1199 | sort_app_table(displayOrder); 1200 | 1201 | while (true) 1202 | { 1203 | ui_draw_app_page(currentItem); 1204 | 1205 | int page = (currentItem / ITEM_COUNT) * ITEM_COUNT; 1206 | 1207 | // Wait for input but refresh display after 1000 ticks if no input 1208 | int btn = (queuedBtn != -1) ? queuedBtn : input_wait_for_button_press(1000); 1209 | queuedBtn = -1; 1210 | 1211 | if (apps_count > 0) 1212 | { 1213 | if (btn == ODROID_INPUT_DOWN) 1214 | { 1215 | if (++currentItem >= apps_count) currentItem = 0; 1216 | } 1217 | else if (btn == ODROID_INPUT_UP) 1218 | { 1219 | if (--currentItem < 0) currentItem = apps_count - 1; 1220 | } 1221 | else if (btn == ODROID_INPUT_RIGHT) 1222 | { 1223 | if (page + ITEM_COUNT < apps_count) currentItem = page + ITEM_COUNT; 1224 | else currentItem = 0; 1225 | } 1226 | else if (btn == ODROID_INPUT_LEFT) 1227 | { 1228 | if (page - ITEM_COUNT >= 0) currentItem = page - ITEM_COUNT; 1229 | else currentItem = (apps_count - 1) / ITEM_COUNT * ITEM_COUNT; 1230 | } 1231 | else if (btn == ODROID_INPUT_A) 1232 | { 1233 | DisplayPage("MULTI-FIRMWARE", PROJECT_VER); 1234 | boot_application(apps + currentItem); 1235 | } 1236 | else if (btn == ODROID_INPUT_SELECT) 1237 | { 1238 | do { 1239 | if ((++displayOrder) > LIST_SORT_MAX) 1240 | displayOrder = (displayOrder & 1); 1241 | 1242 | sort_app_table(displayOrder); 1243 | ui_draw_app_page(currentItem); 1244 | 1245 | char descriptions[][16] = {"OFFSET", "INSTALL", "NAME"}; 1246 | char order[][5] = {"ASC", "DESC"}; 1247 | sprintf(tempstring, "NOW SORTING BY %s %s", descriptions[(displayOrder >> 1)], order[displayOrder & 1]); 1248 | 1249 | DisplayNotification(tempstring); 1250 | queuedBtn = input_wait_for_button_press(200); 1251 | } 1252 | while (queuedBtn == ODROID_INPUT_SELECT); 1253 | 1254 | nvs_set_i32(nvs_h, "display_order", displayOrder); 1255 | nvs_commit(nvs_h); 1256 | } 1257 | } 1258 | 1259 | if (btn == ODROID_INPUT_MENU || btn == ODROID_INPUT_START) 1260 | { 1261 | dialog_option_t options[] = { 1262 | {0, "Install from SD Card", true}, 1263 | {1, "Erase selected app", apps_count > 0}, 1264 | {2, "Erase selected NVS", apps_count > 0}, 1265 | {3, "Erase all apps", apps_count > 0}, 1266 | {4, "Format SD Card", true}, 1267 | {5, "Restart System", true} 1268 | }; 1269 | 1270 | odroid_app_t *app = &apps[currentItem]; 1271 | char *fileName; 1272 | size_t offset; 1273 | 1274 | switch (ui_choose_dialog(options, 6, true)) 1275 | { 1276 | case 0: // Install from SD Card 1277 | if ((fileName = ui_choose_file(FIRMWARE_PATH))) { 1278 | flash_firmware(fileName); 1279 | free(fileName); 1280 | } 1281 | break; 1282 | case 1: // Remove selected app 1283 | memmove(app, app + 1, (apps_max - currentItem) * sizeof(odroid_app_t)); 1284 | apps_count--; 1285 | write_app_table(); 1286 | break; 1287 | case 2: // Erase selected app's NVS 1288 | offset = app->startOffset; 1289 | for (int i = 0; i < app->parts_count; i++) 1290 | { 1291 | odroid_partition_t *part = &app->parts[i]; 1292 | if (part->type == 1 && part->subtype == ESP_PARTITION_SUBTYPE_DATA_NVS) 1293 | { 1294 | if (spi_flash_erase_range(offset, part->length) == ESP_OK) 1295 | DisplayNotification("Operation successful!"); 1296 | else 1297 | DisplayNotification("An error has occurred!"); 1298 | } 1299 | offset += part->length; 1300 | } 1301 | queuedBtn = input_wait_for_button_press(200); 1302 | break; 1303 | case 3: // Erase all apps 1304 | memset(apps, 0xFF, apps_max * sizeof(odroid_app_t)); 1305 | apps_count = 0; 1306 | currentItem = 0; 1307 | app = &apps[0]; 1308 | write_app_table(); 1309 | write_partition_table(NULL); 1310 | break; 1311 | case 4: // Format SD Card 1312 | DisplayPage("Format SD Card", PROJECT_VER); 1313 | DisplayMessage("Press start to begin"); 1314 | if (input_wait_for_button_press(50000) != ODROID_INPUT_START) { 1315 | break; 1316 | } 1317 | DisplayMessage("Formatting... (be patient)"); 1318 | sdcardret = odroid_sdcard_format(0); 1319 | if (sdcardret == ESP_OK) { 1320 | sdcardret = odroid_sdcard_open(); 1321 | } 1322 | if (sdcardret == ESP_OK) { 1323 | char path[32] = SDCARD_BASE_PATH "/odroid"; 1324 | mkdir(path, 0777); 1325 | strcat(path, "/firmware"); 1326 | mkdir(path, 0777); 1327 | DisplayMessage("Card formatted!"); 1328 | } else { 1329 | DisplayError("Format failed!"); 1330 | } 1331 | input_wait_for_button_press(50000); 1332 | break; 1333 | case 5: // Restart 1334 | cleanup_and_restart(); 1335 | break; 1336 | } 1337 | 1338 | sort_app_table(displayOrder); 1339 | } 1340 | else if (btn == ODROID_INPUT_B) 1341 | { 1342 | DisplayNotification("Press B again to reboot."); 1343 | queuedBtn = input_wait_for_button_press(100); 1344 | if (queuedBtn == ODROID_INPUT_B) { 1345 | boot_application(NULL); 1346 | } 1347 | } 1348 | } 1349 | } 1350 | 1351 | 1352 | static void start_install(void) 1353 | { 1354 | DisplayPage("Multi-firmware Installer", ""); 1355 | 1356 | for (int i = 5; i > 0; --i) 1357 | { 1358 | char tempstring[128]; 1359 | sprintf(tempstring, "Installing in %d seconds...", i); 1360 | DisplayMessage(tempstring); 1361 | vTaskDelay(pdMS_TO_TICKS(1000)); 1362 | } 1363 | 1364 | DisplayMessage("Installing..."); 1365 | 1366 | const esp_partition_t *payload = esp_partition_find_first( 1367 | ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "payload"); 1368 | 1369 | // We could fall back to copying the current partition and creating the partition table 1370 | // ourselves but we'd be missing the calibration data. Might handle this later... 1371 | if (!payload) 1372 | { 1373 | panic_abort("CORRUPT INSTALLER ERROR"); 1374 | } 1375 | 1376 | SET_STATUS_LED(1); 1377 | 1378 | size_t size = ALIGN_ADDRESS(payload->size, FLASH_BLOCK_SIZE); 1379 | void *data = safe_alloc(size); 1380 | 1381 | // We must copy the data to RAM first because our data address space is full, can't mmap 1382 | if (spi_flash_read(payload->address, data, payload->size) != ESP_OK) 1383 | { 1384 | panic_abort("PART TABLE WRITE ERROR"); 1385 | } 1386 | 1387 | // It would be nicer to do the erase/write in blocks to be able to show progress 1388 | // but, because of the shared SPI bus, I think it is safer to do it in one go. 1389 | 1390 | if (spi_flash_erase_range(0x0, size) != ESP_OK) 1391 | { 1392 | panic_abort("PART TABLE ERASE ERROR"); 1393 | } 1394 | 1395 | if (spi_flash_write(0x0, data, size) != ESP_OK) 1396 | { 1397 | panic_abort("PART TABLE WRITE ERROR"); 1398 | } 1399 | 1400 | // The above code will clear the ota partition, no need to set boot app 1401 | cleanup_and_restart(); 1402 | } 1403 | 1404 | 1405 | void app_main(void) 1406 | { 1407 | printf("\n\n############### odroid-go-multi-firmware (Ver: "PROJECT_VER") ###############\n\n"); 1408 | 1409 | gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT); 1410 | SET_STATUS_LED(1); 1411 | 1412 | // Has to be before LCD 1413 | sdcardret = odroid_sdcard_open(); 1414 | ili9341_init(); 1415 | input_init(); 1416 | 1417 | UG_Init(&gui, pset, SCREEN_WIDTH, SCREEN_HEIGHT); 1418 | 1419 | SET_STATUS_LED(0); 1420 | 1421 | // Start the installation process if we didn't boot from the factory app partition 1422 | if (esp_ota_get_running_partition()->subtype != ESP_PARTITION_SUBTYPE_APP_FACTORY) 1423 | { 1424 | ESP_LOGI(__func__, "Non-factory startup, launching installer..."); 1425 | start_install(); 1426 | } 1427 | else 1428 | { 1429 | ESP_LOGI(__func__, "Factory startup, launching normally..."); 1430 | start_normal(); 1431 | } 1432 | 1433 | panic_abort("???"); 1434 | } 1435 | -------------------------------------------------------------------------------- /main/ugui/ugui.h: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------------- */ 2 | /* -- µGUI - Generic GUI module (C)Achim Döbler, 2015 -- */ 3 | /* -------------------------------------------------------------------------------- */ 4 | // µGUI is a generic GUI module for embedded systems. 5 | // This is a free software that is open for education, research and commercial 6 | // developments under license policy of following terms. 7 | // 8 | // Copyright (C) 2015, Achim Döbler, all rights reserved. 9 | // URL: http://www.embeddedlightning.com/ 10 | // 11 | // * The µGUI module is a free software and there is NO WARRANTY. 12 | // * No restriction on use. You can use, modify and redistribute it for 13 | // personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 14 | // * Redistributions of source code must retain the above copyright notice. 15 | // 16 | /* -------------------------------------------------------------------------------- */ 17 | #ifndef __UGUI_H 18 | #define __UGUI_H 19 | 20 | #include "ugui_config.h" 21 | 22 | 23 | /* -------------------------------------------------------------------------------- */ 24 | /* -- µGUI FONTS -- */ 25 | /* -- Source: http://www.mikrocontroller.net/user/show/benedikt -- */ 26 | /* -------------------------------------------------------------------------------- */ 27 | typedef enum 28 | { 29 | FONT_TYPE_1BPP, 30 | FONT_TYPE_8BPP 31 | } FONT_TYPE; 32 | 33 | typedef struct 34 | { 35 | unsigned char* p; 36 | FONT_TYPE font_type; 37 | UG_S16 char_width; 38 | UG_S16 char_height; 39 | UG_U16 start_char; 40 | UG_U16 end_char; 41 | UG_U8 *widths; 42 | } UG_FONT; 43 | 44 | #ifdef USE_FONT_4X6 45 | extern const UG_FONT FONT_4X6; 46 | #endif 47 | #ifdef USE_FONT_5X8 48 | extern const UG_FONT FONT_5X8; 49 | #endif 50 | #ifdef USE_FONT_5X12 51 | extern const UG_FONT FONT_5X12; 52 | #endif 53 | #ifdef USE_FONT_6X8 54 | extern const UG_FONT FONT_6X8; 55 | #endif 56 | #ifdef USE_FONT_6X10 57 | extern const UG_FONT FONT_6X10; 58 | #endif 59 | #ifdef USE_FONT_7X12 60 | extern const UG_FONT FONT_7X12; 61 | #endif 62 | #ifdef USE_FONT_8X8 63 | extern const UG_FONT FONT_8X8; 64 | #endif 65 | #ifdef USE_FONT_8X12 66 | extern const UG_FONT FONT_8X12; 67 | #endif 68 | #ifdef USE_FONT_8X12_CYRILLIC 69 | extern const UG_FONT FONT_8X12; 70 | #endif 71 | #ifdef USE_FONT_8X14 72 | extern const UG_FONT FONT_8X14; 73 | #endif 74 | #ifdef USE_FONT_10X16 75 | extern const UG_FONT FONT_10X16; 76 | #endif 77 | #ifdef USE_FONT_12X16 78 | extern const UG_FONT FONT_12X16; 79 | #endif 80 | #ifdef USE_FONT_12X20 81 | extern const UG_FONT FONT_12X20; 82 | #endif 83 | #ifdef USE_FONT_16X26 84 | extern const UG_FONT FONT_16X26; 85 | #endif 86 | #ifdef USE_FONT_22X36 87 | extern const UG_FONT FONT_22X36; 88 | #endif 89 | #ifdef USE_FONT_24X40 90 | extern const UG_FONT FONT_24X40; 91 | #endif 92 | #ifdef USE_FONT_32X53 93 | extern const UG_FONT FONT_32X53; 94 | #endif 95 | 96 | /* -------------------------------------------------------------------------------- */ 97 | /* -- TYPEDEFS -- */ 98 | /* -------------------------------------------------------------------------------- */ 99 | typedef struct S_OBJECT UG_OBJECT; 100 | typedef struct S_WINDOW UG_WINDOW; 101 | typedef UG_S8 UG_RESULT; 102 | #ifdef USE_COLOR_RGB888 103 | typedef UG_U32 UG_COLOR; 104 | #endif 105 | #ifdef USE_COLOR_RGB565 106 | typedef UG_U16 UG_COLOR; 107 | #endif 108 | /* -------------------------------------------------------------------------------- */ 109 | /* -- DEFINES -- */ 110 | /* -------------------------------------------------------------------------------- */ 111 | #ifndef NULL 112 | #define NULL ((void*) 0) 113 | #endif 114 | 115 | /* Alignments */ 116 | #define ALIGN_H_LEFT (1<<0) 117 | #define ALIGN_H_CENTER (1<<1) 118 | #define ALIGN_H_RIGHT (1<<2) 119 | #define ALIGN_V_TOP (1<<3) 120 | #define ALIGN_V_CENTER (1<<4) 121 | #define ALIGN_V_BOTTOM (1<<5) 122 | #define ALIGN_BOTTOM_RIGHT (ALIGN_V_BOTTOM|ALIGN_H_RIGHT) 123 | #define ALIGN_BOTTOM_CENTER (ALIGN_V_BOTTOM|ALIGN_H_CENTER) 124 | #define ALIGN_BOTTOM_LEFT (ALIGN_V_BOTTOM|ALIGN_H_LEFT) 125 | #define ALIGN_CENTER_RIGHT (ALIGN_V_CENTER|ALIGN_H_RIGHT) 126 | #define ALIGN_CENTER (ALIGN_V_CENTER|ALIGN_H_CENTER) 127 | #define ALIGN_CENTER_LEFT (ALIGN_V_CENTER|ALIGN_H_LEFT) 128 | #define ALIGN_TOP_RIGHT (ALIGN_V_TOP|ALIGN_H_RIGHT) 129 | #define ALIGN_TOP_CENTER (ALIGN_V_TOP|ALIGN_H_CENTER) 130 | #define ALIGN_TOP_LEFT (ALIGN_V_TOP|ALIGN_H_LEFT) 131 | 132 | /* Default IDs */ 133 | #define OBJ_ID_0 0 134 | #define OBJ_ID_1 1 135 | #define OBJ_ID_2 2 136 | #define OBJ_ID_3 3 137 | #define OBJ_ID_4 4 138 | #define OBJ_ID_5 5 139 | #define OBJ_ID_6 6 140 | #define OBJ_ID_7 7 141 | #define OBJ_ID_8 8 142 | #define OBJ_ID_9 9 143 | #define OBJ_ID_10 10 144 | #define OBJ_ID_11 11 145 | #define OBJ_ID_12 12 146 | #define OBJ_ID_13 13 147 | #define OBJ_ID_14 14 148 | #define OBJ_ID_15 15 149 | #define OBJ_ID_16 16 150 | #define OBJ_ID_17 17 151 | #define OBJ_ID_18 18 152 | #define OBJ_ID_19 19 153 | 154 | /* -------------------------------------------------------------------------------- */ 155 | /* -- FUNCTION RESULTS -- */ 156 | /* -------------------------------------------------------------------------------- */ 157 | #define UG_RESULT_FAIL -1 158 | #define UG_RESULT_OK 0 159 | 160 | /* -------------------------------------------------------------------------------- */ 161 | /* -- UNIVERSAL STRUCTURES -- */ 162 | /* -------------------------------------------------------------------------------- */ 163 | /* Area structure */ 164 | typedef struct 165 | { 166 | UG_S16 xs; 167 | UG_S16 ys; 168 | UG_S16 xe; 169 | UG_S16 ye; 170 | } UG_AREA; 171 | 172 | /* Text structure */ 173 | typedef struct 174 | { 175 | char* str; 176 | const UG_FONT* font; 177 | UG_AREA a; 178 | UG_COLOR fc; 179 | UG_COLOR bc; 180 | UG_U8 align; 181 | UG_S16 h_space; 182 | UG_S16 v_space; 183 | } UG_TEXT; 184 | 185 | /* -------------------------------------------------------------------------------- */ 186 | /* -- BITMAP -- */ 187 | /* -------------------------------------------------------------------------------- */ 188 | typedef struct 189 | { 190 | void* p; 191 | UG_U16 width; 192 | UG_U16 height; 193 | UG_U8 bpp; 194 | UG_U8 colors; 195 | } UG_BMP; 196 | 197 | #define BMP_BPP_1 (1<<0) 198 | #define BMP_BPP_2 (1<<1) 199 | #define BMP_BPP_4 (1<<2) 200 | #define BMP_BPP_8 (1<<3) 201 | #define BMP_BPP_16 (1<<4) 202 | #define BMP_BPP_32 (1<<5) 203 | #define BMP_RGB888 (1<<0) 204 | #define BMP_RGB565 (1<<1) 205 | #define BMP_RGB555 (1<<2) 206 | 207 | /* -------------------------------------------------------------------------------- */ 208 | /* -- MESSAGE -- */ 209 | /* -------------------------------------------------------------------------------- */ 210 | /* Message structure */ 211 | typedef struct 212 | { 213 | UG_U8 type; 214 | UG_U8 id; 215 | UG_U8 sub_id; 216 | UG_U8 event; 217 | void* src; 218 | } UG_MESSAGE; 219 | 220 | /* Message types */ 221 | #define MSG_TYPE_NONE 0 222 | #define MSG_TYPE_WINDOW 1 223 | #define MSG_TYPE_OBJECT 2 224 | 225 | /* -------------------------------------------------------------------------------- */ 226 | /* -- TOUCH -- */ 227 | /* -------------------------------------------------------------------------------- */ 228 | /* Touch structure */ 229 | typedef struct 230 | { 231 | UG_U8 state; 232 | UG_S16 xp; 233 | UG_S16 yp; 234 | } UG_TOUCH; 235 | 236 | 237 | #define TOUCH_STATE_PRESSED 1 238 | #define TOUCH_STATE_RELEASED 0 239 | 240 | /* -------------------------------------------------------------------------------- */ 241 | /* -- OBJECTS -- */ 242 | /* -------------------------------------------------------------------------------- */ 243 | /* Object structure */ 244 | struct S_OBJECT 245 | { 246 | UG_U8 state; /* object state */ 247 | UG_U8 touch_state; /* object touch state */ 248 | void (*update) (UG_WINDOW*,UG_OBJECT*); /* pointer to object-specific update function */ 249 | UG_AREA a_abs; /* absolute area of the object */ 250 | UG_AREA a_rel; /* relative area of the object */ 251 | UG_U8 type; /* object type */ 252 | UG_U8 id; /* object ID */ 253 | UG_U8 event; /* object-specific events */ 254 | void* data; /* pointer to object-specific data */ 255 | }; 256 | 257 | /* Currently supported objects */ 258 | #define OBJ_TYPE_NONE 0 259 | #define OBJ_TYPE_BUTTON 1 260 | #define OBJ_TYPE_TEXTBOX 2 261 | #define OBJ_TYPE_IMAGE 3 262 | #define OBJ_TYPE_CHECKBOX 4 263 | 264 | /* Standard object events */ 265 | #define OBJ_EVENT_NONE 0 266 | #define OBJ_EVENT_CLICKED 1 267 | #ifdef USE_PRERENDER_EVENT 268 | #define OBJ_EVENT_PRERENDER 2 269 | #endif 270 | #ifdef USE_POSTRENDER_EVENT 271 | #define OBJ_EVENT_POSTRENDER 3 272 | #endif 273 | #define OBJ_EVENT_PRESSED 4 274 | #define OBJ_EVENT_RELEASED 5 275 | 276 | 277 | /* Object states */ 278 | #define OBJ_STATE_FREE (1<<0) 279 | #define OBJ_STATE_VALID (1<<1) 280 | #define OBJ_STATE_BUSY (1<<2) 281 | #define OBJ_STATE_VISIBLE (1<<3) 282 | #define OBJ_STATE_ENABLE (1<<4) 283 | #define OBJ_STATE_UPDATE (1<<5) 284 | #define OBJ_STATE_REDRAW (1<<6) 285 | #define OBJ_STATE_TOUCH_ENABLE (1<<7) 286 | #define OBJ_STATE_INIT (OBJ_STATE_FREE | OBJ_STATE_VALID) 287 | 288 | /* Object touch states */ 289 | #define OBJ_TOUCH_STATE_CHANGED (1<<0) 290 | #define OBJ_TOUCH_STATE_PRESSED_ON_OBJECT (1<<1) 291 | #define OBJ_TOUCH_STATE_PRESSED_OUTSIDE_OBJECT (1<<2) 292 | #define OBJ_TOUCH_STATE_RELEASED_ON_OBJECT (1<<3) 293 | #define OBJ_TOUCH_STATE_RELEASED_OUTSIDE_OBJECT (1<<4) 294 | #define OBJ_TOUCH_STATE_IS_PRESSED_ON_OBJECT (1<<5) 295 | #define OBJ_TOUCH_STATE_IS_PRESSED (1<<6) 296 | #define OBJ_TOUCH_STATE_CLICK_ON_OBJECT (1<<7) 297 | #define OBJ_TOUCH_STATE_INIT 0 298 | 299 | /* -------------------------------------------------------------------------------- */ 300 | /* -- WINDOW -- */ 301 | /* -------------------------------------------------------------------------------- */ 302 | /* Title structure */ 303 | typedef struct 304 | { 305 | char* str; 306 | const UG_FONT* font; 307 | UG_S8 h_space; 308 | UG_S8 v_space; 309 | UG_U8 align; 310 | UG_COLOR fc; 311 | UG_COLOR bc; 312 | UG_COLOR ifc; 313 | UG_COLOR ibc; 314 | UG_U8 height; 315 | } UG_TITLE; 316 | 317 | /* Window structure */ 318 | struct S_WINDOW 319 | { 320 | UG_U8 objcnt; 321 | UG_OBJECT* objlst; 322 | UG_U8 state; 323 | UG_COLOR fc; 324 | UG_COLOR bc; 325 | UG_S16 xs; 326 | UG_S16 ys; 327 | UG_S16 xe; 328 | UG_S16 ye; 329 | UG_U8 style; 330 | UG_TITLE title; 331 | void (*cb)( UG_MESSAGE* ); 332 | }; 333 | 334 | /* Window states */ 335 | #define WND_STATE_FREE (1<<0) 336 | #define WND_STATE_VALID (1<<1) 337 | #define WND_STATE_BUSY (1<<2) 338 | #define WND_STATE_VISIBLE (1<<3) 339 | #define WND_STATE_ENABLE (1<<4) 340 | #define WND_STATE_UPDATE (1<<5) 341 | #define WND_STATE_REDRAW_TITLE (1<<6) 342 | 343 | /* Window styles */ 344 | #define WND_STYLE_2D (0<<0) 345 | #define WND_STYLE_3D (1<<0) 346 | #define WND_STYLE_HIDE_TITLE (0<<1) 347 | #define WND_STYLE_SHOW_TITLE (1<<1) 348 | 349 | /* -------------------------------------------------------------------------------- */ 350 | /* -- BUTTON OBJECT -- */ 351 | /* -------------------------------------------------------------------------------- */ 352 | /* Button structure */ 353 | typedef struct 354 | { 355 | UG_U8 state; 356 | UG_U8 style; 357 | UG_COLOR fc; 358 | UG_COLOR bc; 359 | UG_COLOR afc; 360 | UG_COLOR abc; 361 | const UG_FONT* font; 362 | UG_U8 align; 363 | UG_S8 h_space; 364 | UG_S8 v_space; 365 | char* str; 366 | }UG_BUTTON; 367 | 368 | /* Default button IDs */ 369 | #define BTN_ID_0 OBJ_ID_0 370 | #define BTN_ID_1 OBJ_ID_1 371 | #define BTN_ID_2 OBJ_ID_2 372 | #define BTN_ID_3 OBJ_ID_3 373 | #define BTN_ID_4 OBJ_ID_4 374 | #define BTN_ID_5 OBJ_ID_5 375 | #define BTN_ID_6 OBJ_ID_6 376 | #define BTN_ID_7 OBJ_ID_7 377 | #define BTN_ID_8 OBJ_ID_8 378 | #define BTN_ID_9 OBJ_ID_9 379 | #define BTN_ID_10 OBJ_ID_10 380 | #define BTN_ID_11 OBJ_ID_11 381 | #define BTN_ID_12 OBJ_ID_12 382 | #define BTN_ID_13 OBJ_ID_13 383 | #define BTN_ID_14 OBJ_ID_14 384 | #define BTN_ID_15 OBJ_ID_15 385 | #define BTN_ID_16 OBJ_ID_16 386 | #define BTN_ID_17 OBJ_ID_17 387 | #define BTN_ID_18 OBJ_ID_18 388 | #define BTN_ID_19 OBJ_ID_19 389 | 390 | /* Button states */ 391 | #define BTN_STATE_RELEASED (0<<0) 392 | #define BTN_STATE_PRESSED (1<<0) 393 | #define BTN_STATE_ALWAYS_REDRAW (1<<1) 394 | 395 | /* Button style */ 396 | #define BTN_STYLE_2D (0<<0) 397 | #define BTN_STYLE_3D (1<<0) 398 | #define BTN_STYLE_TOGGLE_COLORS (1<<1) 399 | #define BTN_STYLE_USE_ALTERNATE_COLORS (1<<2) 400 | #define BTN_STYLE_NO_BORDERS (1<<3) 401 | #define BTN_STYLE_NO_FILL (1<<4) 402 | 403 | /* Button events */ 404 | #define BTN_EVENT_CLICKED OBJ_EVENT_CLICKED 405 | 406 | /* -------------------------------------------------------------------------------- */ 407 | /* -- CHECKBOX OBJECT -- */ 408 | /* -------------------------------------------------------------------------------- */ 409 | /* Checkbox structure */ 410 | typedef struct 411 | { 412 | UG_U8 state; 413 | UG_U8 style; 414 | UG_COLOR fc; 415 | UG_COLOR bc; 416 | UG_COLOR afc; 417 | UG_COLOR abc; 418 | const UG_FONT* font; 419 | UG_U8 align; 420 | UG_S8 h_space; 421 | UG_S8 v_space; 422 | char* str; 423 | UG_U8 checked; 424 | }UG_CHECKBOX; 425 | 426 | /* Default checkbox IDs */ 427 | #define CHB_ID_0 OBJ_ID_0 428 | #define CHB_ID_1 OBJ_ID_1 429 | #define CHB_ID_2 OBJ_ID_2 430 | #define CHB_ID_3 OBJ_ID_3 431 | #define CHB_ID_4 OBJ_ID_4 432 | #define CHB_ID_5 OBJ_ID_5 433 | #define CHB_ID_6 OBJ_ID_6 434 | #define CHB_ID_7 OBJ_ID_7 435 | #define CHB_ID_8 OBJ_ID_8 436 | #define CHB_ID_9 OBJ_ID_9 437 | #define CHB_ID_10 OBJ_ID_10 438 | #define CHB_ID_11 OBJ_ID_11 439 | #define CHB_ID_12 OBJ_ID_12 440 | #define CHB_ID_13 OBJ_ID_13 441 | #define CHB_ID_14 OBJ_ID_14 442 | #define CHB_ID_15 OBJ_ID_15 443 | #define CHB_ID_16 OBJ_ID_16 444 | #define CHB_ID_17 OBJ_ID_17 445 | #define CHB_ID_18 OBJ_ID_18 446 | #define CHB_ID_19 OBJ_ID_19 447 | 448 | /* Checkbox states */ 449 | #define CHB_STATE_RELEASED (0<<0) 450 | #define CHB_STATE_PRESSED (1<<0) 451 | #define CHB_STATE_ALWAYS_REDRAW (1<<1) 452 | 453 | /* Checkbox style */ 454 | #define CHB_STYLE_2D (0<<0) 455 | #define CHB_STYLE_3D (1<<0) 456 | #define CHB_STYLE_TOGGLE_COLORS (1<<1) 457 | #define CHB_STYLE_USE_ALTERNATE_COLORS (1<<2) 458 | #define CHB_STYLE_NO_BORDERS (1<<3) 459 | #define CHB_STYLE_NO_FILL (1<<4) 460 | 461 | /* Checkbox events */ 462 | #define CHB_EVENT_CLICKED OBJ_EVENT_CLICKED 463 | 464 | 465 | /* -------------------------------------------------------------------------------- */ 466 | /* -- TEXTBOX OBJECT -- */ 467 | /* -------------------------------------------------------------------------------- */ 468 | /* Textbox structure */ 469 | typedef struct 470 | { 471 | char* str; 472 | const UG_FONT* font; 473 | UG_U8 style; 474 | UG_COLOR fc; 475 | UG_COLOR bc; 476 | UG_U8 align; 477 | UG_S8 h_space; 478 | UG_S8 v_space; 479 | } UG_TEXTBOX; 480 | 481 | /* Default textbox IDs */ 482 | #define TXB_ID_0 OBJ_ID_0 483 | #define TXB_ID_1 OBJ_ID_1 484 | #define TXB_ID_2 OBJ_ID_2 485 | #define TXB_ID_3 OBJ_ID_3 486 | #define TXB_ID_4 OBJ_ID_4 487 | #define TXB_ID_5 OBJ_ID_5 488 | #define TXB_ID_6 OBJ_ID_6 489 | #define TXB_ID_7 OBJ_ID_7 490 | #define TXB_ID_8 OBJ_ID_8 491 | #define TXB_ID_9 OBJ_ID_9 492 | #define TXB_ID_10 OBJ_ID_10 493 | #define TXB_ID_11 OBJ_ID_11 494 | #define TXB_ID_12 OBJ_ID_12 495 | #define TXB_ID_13 OBJ_ID_13 496 | #define TXB_ID_14 OBJ_ID_14 497 | #define TXB_ID_15 OBJ_ID_15 498 | #define TXB_ID_16 OBJ_ID_16 499 | #define TXB_ID_17 OBJ_ID_17 500 | #define TXB_ID_18 OBJ_ID_18 501 | #define TXB_ID_19 OBJ_ID_19 502 | 503 | /* -------------------------------------------------------------------------------- */ 504 | /* -- IMAGE OBJECT -- */ 505 | /* -------------------------------------------------------------------------------- */ 506 | /* Image structure */ 507 | typedef struct 508 | { 509 | void* img; 510 | UG_U8 type; 511 | } UG_IMAGE; 512 | 513 | /* Default image IDs */ 514 | #define IMG_ID_0 OBJ_ID_0 515 | #define IMG_ID_1 OBJ_ID_1 516 | #define IMG_ID_2 OBJ_ID_2 517 | #define IMG_ID_3 OBJ_ID_3 518 | #define IMG_ID_4 OBJ_ID_4 519 | #define IMG_ID_5 OBJ_ID_5 520 | #define IMG_ID_6 OBJ_ID_6 521 | #define IMG_ID_7 OBJ_ID_7 522 | #define IMG_ID_8 OBJ_ID_8 523 | #define IMG_ID_9 OBJ_ID_9 524 | #define IMG_ID_10 OBJ_ID_10 525 | #define IMG_ID_11 OBJ_ID_11 526 | #define IMG_ID_12 OBJ_ID_12 527 | #define IMG_ID_13 OBJ_ID_13 528 | #define IMG_ID_14 OBJ_ID_14 529 | #define IMG_ID_15 OBJ_ID_15 530 | #define IMG_ID_16 OBJ_ID_16 531 | #define IMG_ID_17 OBJ_ID_17 532 | #define IMG_ID_18 OBJ_ID_18 533 | #define IMG_ID_19 OBJ_ID_19 534 | 535 | /* Image types */ 536 | #define IMG_TYPE_BMP (1<<0) 537 | 538 | /* -------------------------------------------------------------------------------- */ 539 | /* -- µGUI DRIVER -- */ 540 | /* -------------------------------------------------------------------------------- */ 541 | typedef struct 542 | { 543 | void* driver; 544 | UG_U8 state; 545 | } UG_DRIVER; 546 | 547 | #define DRIVER_REGISTERED (1<<0) 548 | #define DRIVER_ENABLED (1<<1) 549 | 550 | /* Supported drivers */ 551 | #define NUMBER_OF_DRIVERS 3 552 | #define DRIVER_DRAW_LINE 0 553 | #define DRIVER_FILL_FRAME 1 554 | #define DRIVER_FILL_AREA 2 555 | 556 | /* -------------------------------------------------------------------------------- */ 557 | /* -- µGUI CORE STRUCTURE -- */ 558 | /* -------------------------------------------------------------------------------- */ 559 | typedef struct 560 | { 561 | void (*pset)(UG_S16,UG_S16,UG_COLOR); 562 | UG_S16 x_dim; 563 | UG_S16 y_dim; 564 | UG_TOUCH touch; 565 | UG_WINDOW* next_window; 566 | UG_WINDOW* active_window; 567 | UG_WINDOW* last_window; 568 | struct 569 | { 570 | UG_S16 x_pos; 571 | UG_S16 y_pos; 572 | UG_S16 x_start; 573 | UG_S16 y_start; 574 | UG_S16 x_end; 575 | UG_S16 y_end; 576 | UG_COLOR fore_color; 577 | UG_COLOR back_color; 578 | } console; 579 | UG_FONT font; 580 | UG_S8 char_h_space; 581 | UG_S8 char_v_space; 582 | UG_COLOR fore_color; 583 | UG_COLOR back_color; 584 | UG_COLOR desktop_color; 585 | UG_U8 state; 586 | UG_DRIVER driver[NUMBER_OF_DRIVERS]; 587 | } UG_GUI; 588 | 589 | #define UG_SATUS_WAIT_FOR_UPDATE (1<<0) 590 | 591 | /* -------------------------------------------------------------------------------- */ 592 | /* -- µGUI COLORS -- */ 593 | /* -- Source: http://www.rapidtables.com/web/color/RGB_Color.htm -- */ 594 | /* -------------------------------------------------------------------------------- */ 595 | #ifdef USE_COLOR_RGB565 596 | #define C_MAROON 0x8000 597 | #define C_DARK_RED 0x8800 598 | #define C_BROWN 0xA145 599 | #define C_FIREBRICK 0xB104 600 | #define C_CRIMSON 0xD8A7 601 | #define C_RED 0xF800 602 | #define C_TOMATO 0xFB09 603 | #define C_CORAL 0xFBEA 604 | #define C_INDIAN_RED 0xCAEB 605 | #define C_LIGHT_CORAL 0xEC10 606 | #define C_DARK_SALMON 0xE4AF 607 | #define C_SALMON 0xF40E 608 | #define C_LIGHT_SALMON 0xFD0F 609 | #define C_ORANGE_RED 0xFA20 610 | #define C_DARK_ORANGE 0xFC60 611 | #define C_ORANGE 0xFD20 612 | #define C_GOLD 0xFEA0 613 | #define C_DARK_GOLDEN_ROD 0xB421 614 | #define C_GOLDEN_ROD 0xDD24 615 | #define C_PALE_GOLDEN_ROD 0xEF35 616 | #define C_DARK_KHAKI 0xBDAD 617 | #define C_KHAKI 0xEF31 618 | #define C_OLIVE 0x8400 619 | #define C_YELLOW 0xFFE0 620 | #define C_YELLOW_GREEN 0x9E66 621 | #define C_DARK_OLIVE_GREEN 0x5346 622 | #define C_OLIVE_DRAB 0x6C64 623 | #define C_LAWN_GREEN 0x7FC0 624 | #define C_CHART_REUSE 0x7FE0 625 | #define C_GREEN_YELLOW 0xAFE6 626 | #define C_DARK_GREEN 0x0320 627 | #define C_GREEN 0x07E0 628 | #define C_FOREST_GREEN 0x2444 629 | #define C_LIME 0x07E0 630 | #define C_LIME_GREEN 0x3666 631 | #define C_LIGHT_GREEN 0x9772 632 | #define C_PALE_GREEN 0x97D2 633 | #define C_DARK_SEA_GREEN 0x8DD1 634 | #define C_MEDIUM_SPRING_GREEN 0x07D3 635 | #define C_SPRING_GREEN 0x07EF 636 | #define C_SEA_GREEN 0x344B 637 | #define C_MEDIUM_AQUA_MARINE 0x6675 638 | #define C_MEDIUM_SEA_GREEN 0x3D8E 639 | #define C_LIGHT_SEA_GREEN 0x2595 640 | #define C_DARK_SLATE_GRAY 0x328A 641 | #define C_TEAL 0x0410 642 | #define C_DARK_CYAN 0x0451 643 | #define C_AQUA 0x07FF 644 | #define C_CYAN 0x07FF 645 | #define C_LIGHT_CYAN 0xDFFF 646 | #define C_DARK_TURQUOISE 0x0679 647 | #define C_TURQUOISE 0x46F9 648 | #define C_MEDIUM_TURQUOISE 0x4E99 649 | #define C_PALE_TURQUOISE 0xAF7D 650 | #define C_AQUA_MARINE 0x7FFA 651 | #define C_POWDER_BLUE 0xAEFC 652 | #define C_CADET_BLUE 0x64F3 653 | #define C_STEEL_BLUE 0x4C16 654 | #define C_CORN_FLOWER_BLUE 0x64BD 655 | #define C_DEEP_SKY_BLUE 0x05FF 656 | #define C_DODGER_BLUE 0x249F 657 | #define C_LIGHT_BLUE 0xAEBC 658 | #define C_SKY_BLUE 0x867D 659 | #define C_LIGHT_SKY_BLUE 0x867E 660 | #define C_MIDNIGHT_BLUE 0x18CE 661 | #define C_NAVY 0x0010 662 | #define C_DARK_BLUE 0x0011 663 | #define C_MEDIUM_BLUE 0x0019 664 | #define C_BLUE 0x001F 665 | #define C_ROYAL_BLUE 0x435B 666 | #define C_BLUE_VIOLET 0x897B 667 | #define C_INDIGO 0x4810 668 | #define C_DARK_SLATE_BLUE 0x49F1 669 | #define C_SLATE_BLUE 0x6AD9 670 | #define C_MEDIUM_SLATE_BLUE 0x7B5D 671 | #define C_MEDIUM_PURPLE 0x939B 672 | #define C_DARK_MAGENTA 0x8811 673 | #define C_DARK_VIOLET 0x901A 674 | #define C_DARK_ORCHID 0x9999 675 | #define C_MEDIUM_ORCHID 0xBABA 676 | #define C_PURPLE 0x8010 677 | #define C_THISTLE 0xD5FA 678 | #define C_PLUM 0xDD1B 679 | #define C_VIOLET 0xEC1D 680 | #define C_MAGENTA 0xF81F 681 | #define C_ORCHID 0xDB9A 682 | #define C_MEDIUM_VIOLET_RED 0xC0B0 683 | #define C_PALE_VIOLET_RED 0xDB92 684 | #define C_DEEP_PINK 0xF8B2 685 | #define C_HOT_PINK 0xFB56 686 | #define C_LIGHT_PINK 0xFDB7 687 | #define C_PINK 0xFDF9 688 | #define C_ANTIQUE_WHITE 0xF75A 689 | #define C_BEIGE 0xF7BB 690 | #define C_BISQUE 0xFF18 691 | #define C_BLANCHED_ALMOND 0xFF59 692 | #define C_WHEAT 0xF6F6 693 | #define C_CORN_SILK 0xFFBB 694 | #define C_LEMON_CHIFFON 0xFFD9 695 | #define C_LIGHT_GOLDEN_ROD_YELLOW 0xF7DA 696 | #define C_LIGHT_YELLOW 0xFFFB 697 | #define C_SADDLE_BROWN 0x8A22 698 | #define C_SIENNA 0x9A85 699 | #define C_CHOCOLATE 0xD344 700 | #define C_PERU 0xCC28 701 | #define C_SANDY_BROWN 0xF52C 702 | #define C_BURLY_WOOD 0xDDB0 703 | #define C_TAN 0xD591 704 | #define C_ROSY_BROWN 0xBC71 705 | #define C_MOCCASIN 0xFF16 706 | #define C_NAVAJO_WHITE 0xFEF5 707 | #define C_PEACH_PUFF 0xFED6 708 | #define C_MISTY_ROSE 0xFF1B 709 | #define C_LAVENDER_BLUSH 0xFF7E 710 | #define C_LINEN 0xF77C 711 | #define C_OLD_LACE 0xFFBC 712 | #define C_PAPAYA_WHIP 0xFF7A 713 | #define C_SEA_SHELL 0xFFBD 714 | #define C_MINT_CREAM 0xF7FE 715 | #define C_SLATE_GRAY 0x7412 716 | #define C_LIGHT_SLATE_GRAY 0x7453 717 | #define C_LIGHT_STEEL_BLUE 0xAE1B 718 | #define C_LAVENDER 0xE73E 719 | #define C_FLORAL_WHITE 0xFFDD 720 | #define C_ALICE_BLUE 0xEFBF 721 | #define C_GHOST_WHITE 0xF7BF 722 | #define C_HONEYDEW 0xEFFD 723 | #define C_IVORY 0xFFFD 724 | #define C_AZURE 0xEFFF 725 | #define C_SNOW 0xFFDE 726 | #define C_BLACK 0x0000 727 | #define C_DIM_GRAY 0x6B4D 728 | #define C_GRAY 0x8410 729 | #define C_DARK_GRAY 0xAD55 730 | #define C_SILVER 0xBDF7 731 | #define C_LIGHT_GRAY 0xD69A 732 | #define C_GAINSBORO 0xDEDB 733 | #define C_WHITE_SMOKE 0xF7BE 734 | #define C_WHITE 0xFFFF 735 | #endif 736 | 737 | #ifdef USE_COLOR_RGB888 738 | #define C_MAROON 0x800000 739 | #define C_DARK_RED 0x8B0000 740 | #define C_BROWN 0xA52A2A 741 | #define C_FIREBRICK 0xB22222 742 | #define C_CRIMSON 0xDC143C 743 | #define C_RED 0xFF0000 744 | #define C_TOMATO 0xFF6347 745 | #define C_CORAL 0xFF7F50 746 | #define C_INDIAN_RED 0xCD5C5C 747 | #define C_LIGHT_CORAL 0xF08080 748 | #define C_DARK_SALMON 0xE9967A 749 | #define C_SALMON 0xFA8072 750 | #define C_LIGHT_SALMON 0xFFA07A 751 | #define C_ORANGE_RED 0xFF4500 752 | #define C_DARK_ORANGE 0xFF8C00 753 | #define C_ORANGE 0xFFA500 754 | #define C_GOLD 0xFFD700 755 | #define C_DARK_GOLDEN_ROD 0xB8860B 756 | #define C_GOLDEN_ROD 0xDAA520 757 | #define C_PALE_GOLDEN_ROD 0xEEE8AA 758 | #define C_DARK_KHAKI 0xBDB76B 759 | #define C_KHAKI 0xF0E68C 760 | #define C_OLIVE 0x808000 761 | #define C_YELLOW 0xFFFF00 762 | #define C_YELLOW_GREEN 0x9ACD32 763 | #define C_DARK_OLIVE_GREEN 0x556B2F 764 | #define C_OLIVE_DRAB 0x6B8E23 765 | #define C_LAWN_GREEN 0x7CFC00 766 | #define C_CHART_REUSE 0x7FFF00 767 | #define C_GREEN_YELLOW 0xADFF2F 768 | #define C_DARK_GREEN 0x006400 769 | #define C_GREEN 0x00FF00 770 | #define C_FOREST_GREEN 0x228B22 771 | #define C_LIME 0x00FF00 772 | #define C_LIME_GREEN 0x32CD32 773 | #define C_LIGHT_GREEN 0x90EE90 774 | #define C_PALE_GREEN 0x98FB98 775 | #define C_DARK_SEA_GREEN 0x8FBC8F 776 | #define C_MEDIUM_SPRING_GREEN 0x00FA9A 777 | #define C_SPRING_GREEN 0x00FF7F 778 | #define C_SEA_GREEN 0x2E8B57 779 | #define C_MEDIUM_AQUA_MARINE 0x66CDAA 780 | #define C_MEDIUM_SEA_GREEN 0x3CB371 781 | #define C_LIGHT_SEA_GREEN 0x20B2AA 782 | #define C_DARK_SLATE_GRAY 0x2F4F4F 783 | #define C_TEAL 0x008080 784 | #define C_DARK_CYAN 0x008B8B 785 | #define C_AQUA 0x00FFFF 786 | #define C_CYAN 0x00FFFF 787 | #define C_LIGHT_CYAN 0xE0FFFF 788 | #define C_DARK_TURQUOISE 0x00CED1 789 | #define C_TURQUOISE 0x40E0D0 790 | #define C_MEDIUM_TURQUOISE 0x48D1CC 791 | #define C_PALE_TURQUOISE 0xAFEEEE 792 | #define C_AQUA_MARINE 0x7FFFD4 793 | #define C_POWDER_BLUE 0xB0E0E6 794 | #define C_CADET_BLUE 0x5F9EA0 795 | #define C_STEEL_BLUE 0x4682B4 796 | #define C_CORN_FLOWER_BLUE 0x6495ED 797 | #define C_DEEP_SKY_BLUE 0x00BFFF 798 | #define C_DODGER_BLUE 0x1E90FF 799 | #define C_LIGHT_BLUE 0xADD8E6 800 | #define C_SKY_BLUE 0x87CEEB 801 | #define C_LIGHT_SKY_BLUE 0x87CEFA 802 | #define C_MIDNIGHT_BLUE 0x191970 803 | #define C_NAVY 0x000080 804 | #define C_DARK_BLUE 0x00008B 805 | #define C_MEDIUM_BLUE 0x0000CD 806 | #define C_BLUE 0x0000FF 807 | #define C_ROYAL_BLUE 0x4169E1 808 | #define C_BLUE_VIOLET 0x8A2BE2 809 | #define C_INDIGO 0x4B0082 810 | #define C_DARK_SLATE_BLUE 0x483D8B 811 | #define C_SLATE_BLUE 0x6A5ACD 812 | #define C_MEDIUM_SLATE_BLUE 0x7B68EE 813 | #define C_MEDIUM_PURPLE 0x9370DB 814 | #define C_DARK_MAGENTA 0x8B008B 815 | #define C_DARK_VIOLET 0x9400D3 816 | #define C_DARK_ORCHID 0x9932CC 817 | #define C_MEDIUM_ORCHID 0xBA55D3 818 | #define C_PURPLE 0x800080 819 | #define C_THISTLE 0xD8BFD8 820 | #define C_PLUM 0xDDA0DD 821 | #define C_VIOLET 0xEE82EE 822 | #define C_MAGENTA 0xFF00FF 823 | #define C_ORCHID 0xDA70D6 824 | #define C_MEDIUM_VIOLET_RED 0xC71585 825 | #define C_PALE_VIOLET_RED 0xDB7093 826 | #define C_DEEP_PINK 0xFF1493 827 | #define C_HOT_PINK 0xFF69B4 828 | #define C_LIGHT_PINK 0xFFB6C1 829 | #define C_PINK 0xFFC0CB 830 | #define C_ANTIQUE_WHITE 0xFAEBD7 831 | #define C_BEIGE 0xF5F5DC 832 | #define C_BISQUE 0xFFE4C4 833 | #define C_BLANCHED_ALMOND 0xFFEBCD 834 | #define C_WHEAT 0xF5DEB3 835 | #define C_CORN_SILK 0xFFF8DC 836 | #define C_LEMON_CHIFFON 0xFFFACD 837 | #define C_LIGHT_GOLDEN_ROD_YELLOW 0xFAFAD2 838 | #define C_LIGHT_YELLOW 0xFFFFE0 839 | #define C_SADDLE_BROWN 0x8B4513 840 | #define C_SIENNA 0xA0522D 841 | #define C_CHOCOLATE 0xD2691E 842 | #define C_PERU 0xCD853F 843 | #define C_SANDY_BROWN 0xF4A460 844 | #define C_BURLY_WOOD 0xDEB887 845 | #define C_TAN 0xD2B48C 846 | #define C_ROSY_BROWN 0xBC8F8F 847 | #define C_MOCCASIN 0xFFE4B5 848 | #define C_NAVAJO_WHITE 0xFFDEAD 849 | #define C_PEACH_PUFF 0xFFDAB9 850 | #define C_MISTY_ROSE 0xFFE4E1 851 | #define C_LAVENDER_BLUSH 0xFFF0F5 852 | #define C_LINEN 0xFAF0E6 853 | #define C_OLD_LACE 0xFDF5E6 854 | #define C_PAPAYA_WHIP 0xFFEFD5 855 | #define C_SEA_SHELL 0xFFF5EE 856 | #define C_MINT_CREAM 0xF5FFFA 857 | #define C_SLATE_GRAY 0x708090 858 | #define C_LIGHT_SLATE_GRAY 0x778899 859 | #define C_LIGHT_STEEL_BLUE 0xB0C4DE 860 | #define C_LAVENDER 0xE6E6FA 861 | #define C_FLORAL_WHITE 0xFFFAF0 862 | #define C_ALICE_BLUE 0xF0F8FF 863 | #define C_GHOST_WHITE 0xF8F8FF 864 | #define C_HONEYDEW 0xF0FFF0 865 | #define C_IVORY 0xFFFFF0 866 | #define C_AZURE 0xF0FFFF 867 | #define C_SNOW 0xFFFAFA 868 | #define C_BLACK 0x000000 869 | #define C_DIM_GRAY 0x696969 870 | #define C_GRAY 0x808080 871 | #define C_DARK_GRAY 0xA9A9A9 872 | #define C_SILVER 0xC0C0C0 873 | #define C_LIGHT_GRAY 0xD3D3D3 874 | #define C_GAINSBORO 0xDCDCDC 875 | #define C_WHITE_SMOKE 0xF5F5F5 876 | #define C_WHITE 0xFFFFFF 877 | #endif 878 | 879 | /* -------------------------------------------------------------------------------- */ 880 | /* -- PROTOTYPES -- */ 881 | /* -------------------------------------------------------------------------------- */ 882 | /* Classic functions */ 883 | UG_S16 UG_Init( UG_GUI* g, void (*p)(UG_S16,UG_S16,UG_COLOR), UG_S16 x, UG_S16 y ); 884 | UG_S16 UG_SelectGUI( UG_GUI* g ); 885 | UG_GUI* UG_GetGUI( ); 886 | void UG_FontSelect( const UG_FONT* font ); 887 | void UG_FillScreen( UG_COLOR c ); 888 | void UG_FillFrame( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c ); 889 | void UG_DrawTriangle(UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_U8 h, UG_COLOR c ); 890 | void UG_FillTriangle( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_U8 h, UG_COLOR c ); 891 | void UG_FillRoundFrame( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_S16 r, UG_COLOR c ); 892 | void UG_DrawMesh( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c ); 893 | void UG_DrawFrame( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c ); 894 | void UG_DrawRoundFrame( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_S16 r, UG_COLOR c ); 895 | void UG_DrawPixel( UG_S16 x0, UG_S16 y0, UG_COLOR c ); 896 | void UG_DrawCircle( UG_S16 x0, UG_S16 y0, UG_S16 r, UG_COLOR c ); 897 | void UG_FillCircle( UG_S16 x0, UG_S16 y0, UG_S16 r, UG_COLOR c ); 898 | void UG_DrawArc( UG_S16 x0, UG_S16 y0, UG_S16 r, UG_U8 s, UG_COLOR c ); 899 | void UG_DrawLine( UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c ); 900 | void UG_PutString( UG_S16 x, UG_S16 y, const char* str ); 901 | void UG_PutChar( char chr, UG_S16 x, UG_S16 y, UG_COLOR fc, UG_COLOR bc ); 902 | void UG_ConsolePutString( char* str ); 903 | void UG_ConsoleSetArea( UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye ); 904 | void UG_ConsoleSetForecolor( UG_COLOR c ); 905 | void UG_ConsoleSetBackcolor( UG_COLOR c ); 906 | void UG_SetForecolor( UG_COLOR c ); 907 | void UG_SetBackcolor( UG_COLOR c ); 908 | UG_COLOR UG_GetForecolor( ); 909 | UG_COLOR UG_GetBackcolor( ); 910 | UG_S16 UG_GetXDim( void ); 911 | UG_S16 UG_GetYDim( void ); 912 | void UG_FontSetHSpace( UG_U16 s ); 913 | void UG_FontSetVSpace( UG_U16 s ); 914 | 915 | /* Miscellaneous functions */ 916 | void UG_WaitForUpdate( void ); 917 | void UG_Update( void ); 918 | void UG_DrawBMP( UG_S16 xp, UG_S16 yp, UG_BMP* bmp ); 919 | void UG_TouchUpdate( UG_S16 xp, UG_S16 yp, UG_U8 state ); 920 | 921 | /* Driver functions */ 922 | void UG_DriverRegister( UG_U8 type, void* driver ); 923 | void UG_DriverEnable( UG_U8 type ); 924 | void UG_DriverDisable( UG_U8 type ); 925 | 926 | /* Window functions */ 927 | UG_RESULT UG_WindowCreate( UG_WINDOW* wnd, UG_OBJECT* objlst, UG_U8 objcnt, void (*cb)( UG_MESSAGE* ) ); 928 | UG_RESULT UG_WindowDelete( UG_WINDOW* wnd ); 929 | UG_RESULT UG_WindowShow( UG_WINDOW* wnd ); 930 | UG_RESULT UG_WindowHide( UG_WINDOW* wnd ); 931 | UG_RESULT UG_WindowResize( UG_WINDOW* wnd, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye ); 932 | UG_RESULT UG_WindowAlert( UG_WINDOW* wnd ); 933 | UG_RESULT UG_WindowSetForeColor( UG_WINDOW* wnd, UG_COLOR fc ); 934 | UG_RESULT UG_WindowSetBackColor( UG_WINDOW* wnd, UG_COLOR bc ); 935 | UG_RESULT UG_WindowSetTitleTextColor( UG_WINDOW* wnd, UG_COLOR c ); 936 | UG_RESULT UG_WindowSetTitleColor( UG_WINDOW* wnd, UG_COLOR c ); 937 | UG_RESULT UG_WindowSetTitleInactiveTextColor( UG_WINDOW* wnd, UG_COLOR c ); 938 | UG_RESULT UG_WindowSetTitleInactiveColor( UG_WINDOW* wnd, UG_COLOR c ); 939 | UG_RESULT UG_WindowSetTitleText( UG_WINDOW* wnd, char* str ); 940 | UG_RESULT UG_WindowSetTitleTextFont( UG_WINDOW* wnd, const UG_FONT* font ); 941 | UG_RESULT UG_WindowSetTitleTextHSpace( UG_WINDOW* wnd, UG_S8 hs ); 942 | UG_RESULT UG_WindowSetTitleTextVSpace( UG_WINDOW* wnd, UG_S8 vs ); 943 | UG_RESULT UG_WindowSetTitleTextAlignment( UG_WINDOW* wnd, UG_U8 align ); 944 | UG_RESULT UG_WindowSetTitleHeight( UG_WINDOW* wnd, UG_U8 height ); 945 | UG_RESULT UG_WindowSetXStart( UG_WINDOW* wnd, UG_S16 xs ); 946 | UG_RESULT UG_WindowSetYStart( UG_WINDOW* wnd, UG_S16 ys ); 947 | UG_RESULT UG_WindowSetXEnd( UG_WINDOW* wnd, UG_S16 xe ); 948 | UG_RESULT UG_WindowSetYEnd( UG_WINDOW* wnd, UG_S16 ye ); 949 | UG_RESULT UG_WindowSetStyle( UG_WINDOW* wnd, UG_U8 style ); 950 | UG_COLOR UG_WindowGetForeColor( UG_WINDOW* wnd ); 951 | UG_COLOR UG_WindowGetBackColor( UG_WINDOW* wnd ); 952 | UG_COLOR UG_WindowGetTitleTextColor( UG_WINDOW* wnd ); 953 | UG_COLOR UG_WindowGetTitleColor( UG_WINDOW* wnd ); 954 | UG_COLOR UG_WindowGetTitleInactiveTextColor( UG_WINDOW* wnd ); 955 | UG_COLOR UG_WindowGetTitleInactiveColor( UG_WINDOW* wnd ); 956 | char* UG_WindowGetTitleText( UG_WINDOW* wnd ); 957 | UG_FONT* UG_WindowGetTitleTextFont( UG_WINDOW* wnd ); 958 | UG_S8 UG_WindowGetTitleTextHSpace( UG_WINDOW* wnd ); 959 | UG_S8 UG_WindowGetTitleTextVSpace( UG_WINDOW* wnd ); 960 | UG_U8 UG_WindowGetTitleTextAlignment( UG_WINDOW* wnd ); 961 | UG_U8 UG_WindowGetTitleHeight( UG_WINDOW* wnd ); 962 | UG_S16 UG_WindowGetXStart( UG_WINDOW* wnd ); 963 | UG_S16 UG_WindowGetYStart( UG_WINDOW* wnd ); 964 | UG_S16 UG_WindowGetXEnd( UG_WINDOW* wnd ); 965 | UG_S16 UG_WindowGetYEnd( UG_WINDOW* wnd ); 966 | UG_U8 UG_WindowGetStyle( UG_WINDOW* wnd ); 967 | UG_RESULT UG_WindowGetArea( UG_WINDOW* wnd, UG_AREA* a ); 968 | UG_S16 UG_WindowGetInnerWidth( UG_WINDOW* wnd ); 969 | UG_S16 UG_WindowGetOuterWidth( UG_WINDOW* wnd ); 970 | UG_S16 UG_WindowGetInnerHeight( UG_WINDOW* wnd ); 971 | UG_S16 UG_WindowGetOuterHeight( UG_WINDOW* wnd ); 972 | 973 | /* Button functions */ 974 | UG_RESULT UG_ButtonCreate( UG_WINDOW* wnd, UG_BUTTON* btn, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye ); 975 | UG_RESULT UG_ButtonDelete( UG_WINDOW* wnd, UG_U8 id ); 976 | UG_RESULT UG_ButtonShow( UG_WINDOW* wnd, UG_U8 id ); 977 | UG_RESULT UG_ButtonHide( UG_WINDOW* wnd, UG_U8 id ); 978 | UG_RESULT UG_ButtonSetForeColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR fc ); 979 | UG_RESULT UG_ButtonSetBackColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR bc ); 980 | UG_RESULT UG_ButtonSetAlternateForeColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR afc ); 981 | UG_RESULT UG_ButtonSetAlternateBackColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR abc ); 982 | UG_RESULT UG_ButtonSetText( UG_WINDOW* wnd, UG_U8 id, char* str ); 983 | UG_RESULT UG_ButtonSetFont( UG_WINDOW* wnd, UG_U8 id, const UG_FONT* font ); 984 | UG_RESULT UG_ButtonSetStyle( UG_WINDOW* wnd, UG_U8 id, UG_U8 style ); 985 | UG_RESULT UG_ButtonSetHSpace( UG_WINDOW* wnd, UG_U8 id, UG_S8 hs ); 986 | UG_RESULT UG_ButtonSetVSpace( UG_WINDOW* wnd, UG_U8 id, UG_S8 vs ); 987 | UG_RESULT UG_ButtonSetAlignment( UG_WINDOW* wnd, UG_U8 id, UG_U8 align ); 988 | UG_COLOR UG_ButtonGetForeColor( UG_WINDOW* wnd, UG_U8 id ); 989 | UG_COLOR UG_ButtonGetBackColor( UG_WINDOW* wnd, UG_U8 id ); 990 | UG_COLOR UG_ButtonGetAlternateForeColor( UG_WINDOW* wnd, UG_U8 id ); 991 | UG_COLOR UG_ButtonGetAlternateBackColor( UG_WINDOW* wnd, UG_U8 id ); 992 | char* UG_ButtonGetText( UG_WINDOW* wnd, UG_U8 id ); 993 | UG_FONT* UG_ButtonGetFont( UG_WINDOW* wnd, UG_U8 id ); 994 | UG_U8 UG_ButtonGetStyle( UG_WINDOW* wnd, UG_U8 id ); 995 | UG_S8 UG_ButtonGetHSpace( UG_WINDOW* wnd, UG_U8 id ); 996 | UG_S8 UG_ButtonGetVSpace( UG_WINDOW* wnd, UG_U8 id ); 997 | UG_U8 UG_ButtonGetAlignment( UG_WINDOW* wnd, UG_U8 id ); 998 | 999 | /* Checkbox functions */ 1000 | UG_RESULT UG_CheckboxCreate( UG_WINDOW* wnd, UG_CHECKBOX* btn, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye ); 1001 | UG_RESULT UG_CheckboxDelete( UG_WINDOW* wnd, UG_U8 id ); 1002 | UG_RESULT UG_CheckboxShow( UG_WINDOW* wnd, UG_U8 id ); 1003 | UG_RESULT UG_CheckboxHide( UG_WINDOW* wnd, UG_U8 id ); 1004 | UG_RESULT UG_CheckboxSetCheched( UG_WINDOW* wnd, UG_U8 id, UG_U8 ch ); 1005 | UG_RESULT UG_CheckboxSetForeColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR fc ); 1006 | UG_RESULT UG_CheckboxSetBackColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR bc ); 1007 | UG_RESULT UG_CheckboxSetAlternateForeColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR afc ); 1008 | UG_RESULT UG_CheckboxSetAlternateBackColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR abc ); 1009 | UG_RESULT UG_CheckboxSetText( UG_WINDOW* wnd, UG_U8 id, char* str ); 1010 | UG_RESULT UG_CheckboxSetFont( UG_WINDOW* wnd, UG_U8 id, const UG_FONT* font ); 1011 | UG_RESULT UG_CheckboxSetStyle( UG_WINDOW* wnd, UG_U8 id, UG_U8 style ); 1012 | UG_RESULT UG_CheckboxSetHSpace( UG_WINDOW* wnd, UG_U8 id, UG_S8 hs ); 1013 | UG_RESULT UG_CheckboxSetVSpace( UG_WINDOW* wnd, UG_U8 id, UG_S8 vs ); 1014 | UG_RESULT UG_CheckboxSetAlignment( UG_WINDOW* wnd, UG_U8 id, UG_U8 align ); 1015 | UG_U8 UG_CheckboxGetChecked( UG_WINDOW* wnd, UG_U8 id ); 1016 | UG_COLOR UG_CheckboxGetForeColor( UG_WINDOW* wnd, UG_U8 id ); 1017 | UG_COLOR UG_CheckboxGetBackColor( UG_WINDOW* wnd, UG_U8 id ); 1018 | UG_COLOR UG_CheckboxGetAlternateForeColor( UG_WINDOW* wnd, UG_U8 id ); 1019 | UG_COLOR UG_CheckboxGetAlternateBackColor( UG_WINDOW* wnd, UG_U8 id ); 1020 | char* UG_CheckboxGetText( UG_WINDOW* wnd, UG_U8 id ); 1021 | UG_FONT* UG_CheckboxGetFont( UG_WINDOW* wnd, UG_U8 id ); 1022 | UG_U8 UG_CheckboxGetStyle( UG_WINDOW* wnd, UG_U8 id ); 1023 | UG_S8 UG_CheckboxGetHSpace( UG_WINDOW* wnd, UG_U8 id ); 1024 | UG_S8 UG_CheckboxGetVSpace( UG_WINDOW* wnd, UG_U8 id ); 1025 | UG_U8 UG_CheckboxGetAlignment( UG_WINDOW* wnd, UG_U8 id ); 1026 | 1027 | /* Textbox functions */ 1028 | UG_RESULT UG_TextboxCreate( UG_WINDOW* wnd, UG_TEXTBOX* txb, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye ); 1029 | UG_RESULT UG_TextboxDelete( UG_WINDOW* wnd, UG_U8 id ); 1030 | UG_RESULT UG_TextboxShow( UG_WINDOW* wnd, UG_U8 id ); 1031 | UG_RESULT UG_TextboxHide( UG_WINDOW* wnd, UG_U8 id ); 1032 | UG_RESULT UG_TextboxSetForeColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR fc ); 1033 | UG_RESULT UG_TextboxSetBackColor( UG_WINDOW* wnd, UG_U8 id, UG_COLOR bc ); 1034 | UG_RESULT UG_TextboxSetText( UG_WINDOW* wnd, UG_U8 id, char* str ); 1035 | UG_RESULT UG_TextboxSetFont( UG_WINDOW* wnd, UG_U8 id, const UG_FONT* font ); 1036 | UG_RESULT UG_TextboxSetHSpace( UG_WINDOW* wnd, UG_U8 id, UG_S8 hs ); 1037 | UG_RESULT UG_TextboxSetVSpace( UG_WINDOW* wnd, UG_U8 id, UG_S8 vs ); 1038 | UG_RESULT UG_TextboxSetAlignment( UG_WINDOW* wnd, UG_U8 id, UG_U8 align ); 1039 | UG_COLOR UG_TextboxGetForeColor( UG_WINDOW* wnd, UG_U8 id ); 1040 | UG_COLOR UG_TextboxGetBackColor( UG_WINDOW* wnd, UG_U8 id ); 1041 | char* UG_TextboxGetText( UG_WINDOW* wnd, UG_U8 id ); 1042 | UG_FONT* UG_TextboxGetFont( UG_WINDOW* wnd, UG_U8 id ); 1043 | UG_S8 UG_TextboxGetHSpace( UG_WINDOW* wnd, UG_U8 id ); 1044 | UG_S8 UG_TextboxGetVSpace( UG_WINDOW* wnd, UG_U8 id ); 1045 | UG_U8 UG_TextboxGetAlignment( UG_WINDOW* wnd, UG_U8 id ); 1046 | 1047 | /* Image functions */ 1048 | UG_RESULT UG_ImageCreate( UG_WINDOW* wnd, UG_IMAGE* img, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye ); 1049 | UG_RESULT UG_ImageDelete( UG_WINDOW* wnd, UG_U8 id ); 1050 | UG_RESULT UG_ImageShow( UG_WINDOW* wnd, UG_U8 id ); 1051 | UG_RESULT UG_ImageHide( UG_WINDOW* wnd, UG_U8 id ); 1052 | UG_RESULT UG_ImageSetBMP( UG_WINDOW* wnd, UG_U8 id, const UG_BMP* bmp ); 1053 | 1054 | 1055 | 1056 | #endif 1057 | --------------------------------------------------------------------------------