├── .gitignore ├── fapulator ├── theseus │ ├── u8g2.h │ ├── memory.c │ ├── applications │ │ ├── gui │ │ │ ├── font │ │ │ │ ├── fonts.h │ │ │ │ ├── u8g2_font_render.h │ │ │ │ ├── u8g2_font_render.c │ │ │ │ └── fonts.cpp │ │ │ └── canvas.cpp │ │ └── input │ │ │ └── input.c │ ├── furi_hal_resources.h │ ├── check.h │ └── core │ │ ├── mutex.cpp │ │ ├── timer.cpp │ │ ├── message_queue.cpp │ │ ├── thread.cpp │ │ └── event_flag.cpp ├── hal │ ├── hal.h │ ├── input.h │ └── display.h ├── flipper │ ├── furi │ │ ├── furi.h │ │ └── core │ │ │ ├── mutex.h │ │ │ ├── timer.h │ │ │ ├── event_flag.h │ │ │ ├── base.h │ │ │ ├── record.h │ │ │ ├── pubsub.h │ │ │ ├── valuemutex.c │ │ │ ├── message_queue.h │ │ │ ├── core_defines.h │ │ │ ├── log.h │ │ │ ├── pubsub.c │ │ │ ├── record.c │ │ │ ├── valuemutex.h │ │ │ └── thread.h │ ├── lib │ │ ├── mlib │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── m-genint.h │ │ │ ├── m-mempool.h │ │ │ └── m-atomic.h │ │ └── toolbox │ │ │ └── m_cstr_dup.h │ └── applications │ │ ├── gui │ │ ├── view_port_i.h │ │ ├── canvas_i.h │ │ ├── gui_i.h │ │ ├── view_port.h │ │ ├── gui.h │ │ ├── view_port.c │ │ └── canvas.h │ │ └── input │ │ └── input.h ├── main.cpp └── hal.cpp ├── .gitattributes ├── README.md ├── .vscode ├── c_cpp_properties.json ├── tasks.json ├── launch.json └── settings.json ├── CMakeLists.txt ├── .clang-format └── app ├── keypad_test.c └── snake_game.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store -------------------------------------------------------------------------------- /fapulator/theseus/u8g2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef void* u8g2_t; -------------------------------------------------------------------------------- /fapulator/theseus/memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /fapulator/hal/hal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "display.h" 5 | #include "input.h" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FAPulator 2 | Flipper Zero source level emulator for Flipper Applications (FAP's) 3 | 4 | ## How to... 5 | You are on your own, this is proof of concept stage. 6 | -------------------------------------------------------------------------------- /fapulator/theseus/applications/gui/font/fonts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | extern const uint8_t u8g2_font_helvB08_tr[]; 5 | extern const uint8_t u8g2_font_haxrcorp4089_tr[]; 6 | extern const uint8_t u8g2_font_profont11_mr[]; 7 | extern const uint8_t u8g2_font_profont22_tn[]; -------------------------------------------------------------------------------- /fapulator/theseus/furi_hal_resources.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | typedef enum { 6 | InputKeyUp, 7 | InputKeyDown, 8 | InputKeyRight, 9 | InputKeyLeft, 10 | InputKeyOk, 11 | InputKeyBack, 12 | InputKeyMAX, /**< Special value */ 13 | } InputKey; -------------------------------------------------------------------------------- /fapulator/hal/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef void (*InputCallback)(InputEvent* input_event, void* context); 9 | 10 | void hal_input_add_callback(InputCallback callback, void* context); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif -------------------------------------------------------------------------------- /fapulator/flipper/furi/furi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "core/core_defines.h" 8 | #include "core/event_flag.h" 9 | #include "core/log.h" 10 | #include "core/message_queue.h" 11 | #include "core/mutex.h" 12 | #include "core/pubsub.h" 13 | #include "core/record.h" 14 | #include "core/timer.h" 15 | #include "core/thread.h" 16 | #include "core/valuemutex.h" -------------------------------------------------------------------------------- /fapulator/hal/display.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | static constexpr size_t DISPLAY_WIDTH = 128; 7 | static constexpr size_t DISPLAY_HEIGHT = 64; 8 | 9 | class DisplayBuffer { 10 | public: 11 | void set_pixel(size_t x, size_t y, bool value); 12 | void fill(bool value); 13 | }; 14 | 15 | DisplayBuffer* get_display_buffer(); 16 | void commit_display_buffer(bool redraw); -------------------------------------------------------------------------------- /fapulator/theseus/check.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #define furi_crash(message) 9 | #define furi_halt(message) 10 | 11 | #define furi_check(__e) \ 12 | do { \ 13 | if(!(__e)) { \ 14 | abort(); \ 15 | } \ 16 | } while(0) 17 | 18 | #define furi_assert(__e) 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /fapulator/flipper/lib/mlib/.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | depend 11 | *.depend 12 | 13 | # Libraries 14 | *.lib 15 | *.a 16 | *.la 17 | *.lo 18 | 19 | # Shared objects (inc. Windows DLLs) 20 | *.dll 21 | *.so 22 | *.so.* 23 | *.dylib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | *.i*86 30 | *.x86_64 31 | *.hex 32 | 33 | # Debug files 34 | *.dSYM/ 35 | *.su 36 | 37 | # Others 38 | other/ 39 | a*.dat 40 | config 41 | *.log 42 | .vscode 43 | .vs 44 | tmp-serial.* 45 | *~ 46 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "macFrameworkPath": [], 10 | "compilerPath": "/usr/bin/gcc", 11 | "intelliSenseMode": "gcc-arm", 12 | "compileCommands": "${workspaceFolder}/build/compile_commands.json", 13 | "configurationProvider": "ms-vscode.cmake-tools", 14 | "cStandard": "c11", 15 | "cppStandard": "c++23" 16 | } 17 | ], 18 | "version": 4 19 | } -------------------------------------------------------------------------------- /fapulator/flipper/lib/toolbox/m_cstr_dup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define M_INIT_DUP(a) ((a) = strdup("")) 5 | #define M_INIT_SET_DUP(a, b) ((a) = strdup(b)) 6 | #define M_SET_DUP(a, b) (M_CHECK_DEFAULT_TYPE(a), free((void*)a), (a) = strdup(b)) 7 | #define M_CLEAR_DUP(a) (free((void*)a)) 8 | 9 | #define M_CSTR_DUP_OPLIST \ 10 | (INIT(M_INIT_DUP), \ 11 | INIT_SET(M_INIT_SET_DUP), \ 12 | SET(M_SET_DUP), \ 13 | CLEAR(M_CLEAR_DUP), \ 14 | HASH(m_core_cstr_hash), \ 15 | EQUAL(M_CSTR_EQUAL), \ 16 | CMP(strcmp), \ 17 | TYPE(const char*)) 18 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "shell", 8 | "label": "Build", 9 | "command": "cmake -DCMAKE_BUILD_TYPE=Debug -S. -B build && cd build && make", 10 | "group": "build" 11 | }, 12 | { 13 | "type": "shell", 14 | "label": "Build and Run", 15 | "dependsOn": "Build", 16 | "command": "cd build && ./fapulator", 17 | "group": "build" 18 | }, 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/fapulator", 12 | "args": [], 13 | "stopAtEntry": true, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "lldb", 18 | "preLaunchTask": "Build" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /fapulator/theseus/core/mutex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | FuriMutex* furi_mutex_alloc(FuriMutexType type) { 5 | return (FuriMutex*)new std::timed_mutex(); 6 | } 7 | 8 | void furi_mutex_free(FuriMutex* instance) { 9 | delete(std::timed_mutex*)instance; 10 | } 11 | 12 | FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { 13 | std::timed_mutex* mutex = (std::timed_mutex*)instance; 14 | if(timeout == FuriWaitForever) { 15 | mutex->lock(); 16 | return FuriStatusOk; 17 | } else { 18 | if(mutex->try_lock_for(std::chrono::milliseconds(timeout))) { 19 | return FuriStatusOk; 20 | } else { 21 | return FuriStatusErrorTimeout; 22 | } 23 | } 24 | } 25 | 26 | FuriStatus furi_mutex_release(FuriMutex* instance) { 27 | std::timed_mutex* mutex = (std::timed_mutex*)instance; 28 | mutex->unlock(); 29 | return FuriStatusOk; 30 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | project("fapulator" LANGUAGES C CXX ASM) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_STANDARD 20) 8 | set(CXX_STANDARD_REQUIRED ON) 9 | 10 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") 12 | 13 | list(APPEND CMAKE_PREFIX_PATH "/opt/homebrew/Cellar/qt@5/5.15.6/") 14 | 15 | set(CMAKE_AUTOMOC ON) 16 | 17 | find_package(Qt5 COMPONENTS Core Widgets REQUIRED) 18 | 19 | include_directories("${CMAKE_SOURCE_DIR}/app") 20 | include_directories("${CMAKE_SOURCE_DIR}/fapulator") 21 | include_directories("${CMAKE_SOURCE_DIR}/fapulator/flipper") 22 | include_directories("${CMAKE_SOURCE_DIR}/fapulator/flipper/furi") 23 | include_directories("${CMAKE_SOURCE_DIR}/fapulator/flipper/lib") 24 | include_directories("${CMAKE_SOURCE_DIR}/fapulator/flipper/applications") 25 | include_directories("${CMAKE_SOURCE_DIR}/fapulator/theseus") 26 | 27 | file(GLOB_RECURSE CORE_SOURCES 28 | "app/*.c" 29 | "app/*.cpp" 30 | "fapulator/*.c" 31 | "fapulator/*.cpp" 32 | ) 33 | 34 | add_executable("${PROJECT_NAME}" ${CORE_SOURCES}) 35 | 36 | target_link_libraries(${PROJECT_NAME} Qt5::Widgets) -------------------------------------------------------------------------------- /fapulator/flipper/applications/gui/view_port_i.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file view_port_i.h 3 | * GUI: internal ViewPort API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "gui_i.h" 9 | #include "view_port.h" 10 | 11 | struct ViewPort { 12 | Gui* gui; 13 | bool is_enabled; 14 | ViewPortOrientation orientation; 15 | 16 | uint8_t width; 17 | uint8_t height; 18 | 19 | ViewPortDrawCallback draw_callback; 20 | void* draw_callback_context; 21 | 22 | ViewPortInputCallback input_callback; 23 | void* input_callback_context; 24 | }; 25 | 26 | /** Set GUI reference. 27 | * 28 | * To be used by GUI, called upon view_port tree insert 29 | * 30 | * @param view_port ViewPort instance 31 | * @param gui gui instance pointer 32 | */ 33 | void view_port_gui_set(ViewPort* view_port, Gui* gui); 34 | 35 | /** Process draw call. Calls draw callback. 36 | * 37 | * To be used by GUI, called on tree redraw. 38 | * 39 | * @param view_port ViewPort instance 40 | * @param canvas canvas to draw at 41 | */ 42 | void view_port_draw(ViewPort* view_port, Canvas* canvas); 43 | 44 | /** Process input. Calls input callback. 45 | * 46 | * To be used by GUI, called on input dispatch. 47 | * 48 | * @param view_port ViewPort instance 49 | * @param event pointer to input event 50 | */ 51 | void view_port_input(ViewPort* view_port, InputEvent* event); 52 | -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/mutex.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mutex.h 3 | * FuriMutex 4 | */ 5 | #pragma once 6 | 7 | #include "base.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | typedef enum { 14 | FuriMutexTypeNormal, 15 | FuriMutexTypeRecursive, 16 | } FuriMutexType; 17 | 18 | typedef void FuriMutex; 19 | 20 | /** Allocate FuriMutex 21 | * 22 | * @param[in] type The mutex type 23 | * 24 | * @return pointer to FuriMutex instance 25 | */ 26 | FuriMutex* furi_mutex_alloc(FuriMutexType type); 27 | 28 | /** Free FuriMutex 29 | * 30 | * @param instance The pointer to FuriMutex instance 31 | */ 32 | void furi_mutex_free(FuriMutex* instance); 33 | 34 | /** Acquire mutex 35 | * 36 | * @param instance The pointer to FuriMutex instance 37 | * @param[in] timeout The timeout 38 | * 39 | * @return The furi status. 40 | */ 41 | FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout); 42 | 43 | /** Release mutex 44 | * 45 | * @param instance The pointer to FuriMutex instance 46 | * 47 | * @return The furi status. 48 | */ 49 | FuriStatus furi_mutex_release(FuriMutex* instance); 50 | 51 | /** Get mutex owner thread id 52 | * 53 | * @param instance The pointer to FuriMutex instance 54 | * 55 | * @return The furi thread identifier. 56 | */ 57 | // FuriThreadId furi_mutex_get_owner(FuriMutex* instance); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /fapulator/flipper/lib/mlib/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017-2021, Patrick Pelissier 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /fapulator/flipper/applications/input/input.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file input.h 3 | * Input: main API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #define RECORD_INPUT_EVENTS "input_events" 15 | 16 | /** Input Types 17 | * Some of them are physical events and some logical 18 | */ 19 | typedef enum { 20 | InputTypePress, /**< Press event, emitted after debounce */ 21 | InputTypeRelease, /**< Release event, emitted after debounce */ 22 | InputTypeShort, /**< Short event, emitted after InputTypeRelease done withing INPUT_LONG_PRESS interval */ 23 | InputTypeLong, /**< Long event, emmited after INPUT_LONG_PRESS interval, asynchronouse to InputTypeRelease */ 24 | InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */ 25 | InputTypeMAX, /**< Special value for exceptional */ 26 | } InputType; 27 | 28 | /** Input Event, dispatches with FuriPubSub */ 29 | typedef struct { 30 | uint32_t sequence; 31 | InputKey key; 32 | InputType type; 33 | } InputEvent; 34 | 35 | /** Get human readable input key name 36 | * @param key - InputKey 37 | * @return string 38 | */ 39 | const char* input_get_key_name(InputKey key); 40 | 41 | /** Get human readable input type name 42 | * @param type - InputType 43 | * @return string 44 | */ 45 | const char* input_get_type_name(InputType type); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | -------------------------------------------------------------------------------- /fapulator/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TAG "LoaderSrv" 6 | 7 | extern "C" int32_t gui_srv(void* p); 8 | extern "C" int32_t input_srv(void* p); 9 | extern void hal_pre_init(void); 10 | extern void hal_post_init(void); 11 | 12 | typedef struct { 13 | const FuriThreadCallback app; 14 | const char* name; 15 | const size_t stack_size; 16 | } FlipperApplication; 17 | 18 | static bool start_application(const FlipperApplication* application, const char* arguments) { 19 | FURI_LOG_I(TAG, "Starting: %s", application->name); 20 | 21 | FuriThread* thread = furi_thread_alloc(); 22 | furi_thread_set_name(thread, application->name); 23 | furi_thread_set_stack_size(thread, application->stack_size); 24 | furi_thread_set_callback(thread, application->app); 25 | furi_thread_start(thread); 26 | 27 | return true; 28 | } 29 | 30 | extern "C" int32_t snake_game_app(void* p); 31 | extern "C" int32_t keypad_test_app(void* p); 32 | 33 | static FlipperApplication applications[] = { 34 | {input_srv, "InputService", 1024 * 4}, 35 | {gui_srv, "GuiService", 1024 * 4}, 36 | // {snake_game_app, "Snake Game", 1024 * 4}, 37 | {keypad_test_app, "Keypad Test", 1024 * 4}, 38 | }; 39 | 40 | int main(int argc, char** argv) { 41 | hal_pre_init(); 42 | 43 | for(size_t i = 0; i < sizeof(applications) / sizeof(FlipperApplication); i++) { 44 | start_application(&applications[i], NULL); 45 | } 46 | 47 | hal_post_init(); 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core/base.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | typedef void (*FuriTimerCallback)(void* context); 10 | 11 | typedef enum { 12 | FuriTimerTypeOnce = 0, ///< One-shot timer. 13 | FuriTimerTypePeriodic = 1 ///< Repeating timer. 14 | } FuriTimerType; 15 | 16 | typedef void FuriTimer; 17 | 18 | /** Allocate timer 19 | * 20 | * @param[in] func The callback function 21 | * @param[in] type The timer type 22 | * @param context The callback context 23 | * 24 | * @return The pointer to FuriTimer instance 25 | */ 26 | FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context); 27 | 28 | /** Free timer 29 | * 30 | * @param instance The pointer to FuriTimer instance 31 | */ 32 | void furi_timer_free(FuriTimer* instance); 33 | 34 | /** Start timer 35 | * 36 | * @param instance The pointer to FuriTimer instance 37 | * @param[in] ticks The ticks 38 | * 39 | * @return The furi status. 40 | */ 41 | FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks); 42 | 43 | /** Stop timer 44 | * 45 | * @param instance The pointer to FuriTimer instance 46 | * 47 | * @return The furi status. 48 | */ 49 | FuriStatus furi_timer_stop(FuriTimer* instance); 50 | 51 | /** Is timer running 52 | * 53 | * @param instance The pointer to FuriTimer instance 54 | * 55 | * @return 0: not running, 1: running 56 | */ 57 | uint32_t furi_timer_is_running(FuriTimer* instance); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /fapulator/theseus/applications/input/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | const char* input_get_key_name(InputKey key) { 6 | switch(key) { 7 | case InputKeyUp: 8 | return "Up"; 9 | case InputKeyDown: 10 | return "Down"; 11 | case InputKeyRight: 12 | return "Right"; 13 | case InputKeyLeft: 14 | return "Left"; 15 | case InputKeyOk: 16 | return "Ok"; 17 | case InputKeyBack: 18 | return "Back"; 19 | default: 20 | return "Unknown"; 21 | } 22 | } 23 | 24 | const char* input_get_type_name(InputType type) { 25 | switch(type) { 26 | case InputTypePress: 27 | return "Press"; 28 | case InputTypeRelease: 29 | return "Release"; 30 | case InputTypeShort: 31 | return "Short"; 32 | case InputTypeLong: 33 | return "Long"; 34 | case InputTypeRepeat: 35 | return "Repeat"; 36 | default: 37 | return "Unknown"; 38 | } 39 | } 40 | 41 | typedef struct { 42 | FuriPubSub* event_pubsub; 43 | } Input; 44 | 45 | static Input* input = NULL; 46 | #define INPUT_THREAD_FLAG_ISR 0x00000001 47 | 48 | static void input_callback(InputEvent* input_event, void* context) { 49 | Input* input = context; 50 | furi_pubsub_publish(input->event_pubsub, input_event); 51 | } 52 | 53 | int32_t input_srv(void* p) { 54 | input = malloc(sizeof(Input)); 55 | input->event_pubsub = furi_pubsub_alloc(); 56 | furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); 57 | hal_input_add_callback(input_callback, input); 58 | 59 | while(1) { 60 | furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever); 61 | } 62 | } -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/event_flag.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file event_flag.h 3 | * Furi Event Flag 4 | */ 5 | #pragma once 6 | 7 | #include "base.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | typedef void FuriEventFlag; 14 | 15 | /** Allocate FuriEventFlag 16 | * 17 | * @return pointer to FuriEventFlag 18 | */ 19 | FuriEventFlag* furi_event_flag_alloc(); 20 | 21 | /** Deallocate FuriEventFlag 22 | * 23 | * @param instance pointer to FuriEventFlag 24 | */ 25 | void furi_event_flag_free(FuriEventFlag* instance); 26 | 27 | /** Set flags 28 | * 29 | * @param instance pointer to FuriEventFlag 30 | * @param[in] flags The flags 31 | * 32 | * @return Resulting flags or error (FuriStatus) 33 | */ 34 | uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags); 35 | 36 | /** Clear flags 37 | * 38 | * @param instance pointer to FuriEventFlag 39 | * @param[in] flags The flags 40 | * 41 | * @return Resulting flags or error (FuriStatus) 42 | */ 43 | uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags); 44 | 45 | /** Get flags 46 | * 47 | * @param instance pointer to FuriEventFlag 48 | * 49 | * @return Resulting flags 50 | */ 51 | uint32_t furi_event_flag_get(FuriEventFlag* instance); 52 | 53 | /** Wait flags 54 | * 55 | * @param instance pointer to FuriEventFlag 56 | * @param[in] flags The flags 57 | * @param[in] options The option flags 58 | * @param[in] timeout The timeout 59 | * 60 | * @return Resulting flags or error (FuriStatus) 61 | */ 62 | uint32_t furi_event_flag_wait( 63 | FuriEventFlag* instance, 64 | uint32_t flags, 65 | uint32_t options, 66 | uint32_t timeout); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef enum { 12 | FuriWaitForever = 0xFFFFFFFFU, 13 | } FuriWait; 14 | 15 | typedef enum { 16 | FuriFlagWaitAny = 0x00000000U, ///< Wait for any flag (default). 17 | FuriFlagWaitAll = 0x00000001U, ///< Wait for all flags. 18 | FuriFlagNoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for. 19 | 20 | FuriFlagError = 0x80000000U, ///< Error indicator. 21 | FuriFlagErrorUnknown = 0xFFFFFFFFU, ///< FuriStatusError (-1). 22 | FuriFlagErrorTimeout = 0xFFFFFFFEU, ///< FuriStatusErrorTimeout (-2). 23 | FuriFlagErrorResource = 0xFFFFFFFDU, ///< FuriStatusErrorResource (-3). 24 | FuriFlagErrorParameter = 0xFFFFFFFCU, ///< FuriStatusErrorParameter (-4). 25 | FuriFlagErrorISR = 0xFFFFFFFAU, ///< FuriStatusErrorISR (-6). 26 | } FuriFlag; 27 | 28 | typedef enum { 29 | FuriStatusOk = 0, ///< Operation completed successfully. 30 | FuriStatusError = 31 | -1, ///< Unspecified RTOS error: run-time error but no other error message fits. 32 | FuriStatusErrorTimeout = -2, ///< Operation not completed within the timeout period. 33 | FuriStatusErrorResource = -3, ///< Resource not available. 34 | FuriStatusErrorParameter = -4, ///< Parameter error. 35 | FuriStatusErrorNoMemory = 36 | -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation. 37 | FuriStatusErrorISR = 38 | -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines. 39 | FuriStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization. 40 | } FuriStatus; 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/record.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file record.h 3 | * Furi: record API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "core_defines.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | /** Initialize record storage For internal use only. 16 | */ 17 | void furi_record_init(); 18 | 19 | /** Check if record exists 20 | * 21 | * @param name record name 22 | * @note Thread safe. Create and destroy must be executed from the same 23 | * thread. 24 | */ 25 | bool furi_record_exists(const char* name); 26 | 27 | /** Create record 28 | * 29 | * @param name record name 30 | * @param data data pointer 31 | * @note Thread safe. Create and destroy must be executed from the same 32 | * thread. 33 | */ 34 | void furi_record_create(const char* name, void* data); 35 | 36 | /** Destroy record 37 | * 38 | * @param name record name 39 | * 40 | * @return true if successful, false if still have holders or thread is not 41 | * owner. 42 | * @note Thread safe. Create and destroy must be executed from the same 43 | * thread. 44 | */ 45 | bool furi_record_destroy(const char* name); 46 | 47 | /** Open record 48 | * 49 | * @param name record name 50 | * 51 | * @return pointer to the record 52 | * @note Thread safe. Open and close must be executed from the same 53 | * thread. Suspends caller thread till record is available 54 | */ 55 | FURI_RETURNS_NONNULL void* furi_record_open(const char* name); 56 | 57 | /** Close record 58 | * 59 | * @param name record name 60 | * @note Thread safe. Open and close must be executed from the same 61 | * thread. 62 | */ 63 | void furi_record_close(const char* name); 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /fapulator/theseus/core/timer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class TimerInstance { 5 | private: 6 | std::thread thread; 7 | bool continued; 8 | FuriTimerCallback callback; 9 | void* context; 10 | uint32_t ticks; 11 | 12 | void run() { 13 | do { 14 | std::this_thread::sleep_for(std::chrono::milliseconds(ticks)); 15 | callback(context); 16 | } while(continued); 17 | } 18 | 19 | public: 20 | TimerInstance(FuriTimerCallback callback, void* context) 21 | : callback(callback) 22 | , context(context) { 23 | continued = true; 24 | } 25 | 26 | ~TimerInstance() { 27 | continued = false; 28 | thread.join(); 29 | } 30 | 31 | void start(uint32_t ticks) { 32 | this->ticks = ticks; 33 | thread = std::thread(&TimerInstance::run, this); 34 | } 35 | 36 | void stop() { 37 | continued = false; 38 | thread.join(); 39 | } 40 | 41 | bool is_running() { 42 | return continued; 43 | } 44 | }; 45 | 46 | FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { 47 | return new TimerInstance(func, context); 48 | } 49 | 50 | void furi_timer_free(FuriTimer* instance) { 51 | TimerInstance* timer = (TimerInstance*)instance; 52 | delete timer; 53 | } 54 | 55 | FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { 56 | TimerInstance* timer = (TimerInstance*)instance; 57 | timer->start(ticks); 58 | return FuriStatusOk; 59 | } 60 | 61 | FuriStatus furi_timer_stop(FuriTimer* instance) { 62 | TimerInstance* timer = (TimerInstance*)instance; 63 | timer->stop(); 64 | return FuriStatusOk; 65 | } 66 | 67 | uint32_t furi_timer_is_running(FuriTimer* instance) { 68 | TimerInstance* timer = (TimerInstance*)instance; 69 | return timer->is_running(); 70 | } -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/pubsub.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pubsub.h 3 | * FuriPubSub 4 | */ 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** FuriPubSub Callback type */ 12 | typedef void (*FuriPubSubCallback)(const void* message, void* context); 13 | 14 | /** FuriPubSub type */ 15 | typedef struct FuriPubSub FuriPubSub; 16 | 17 | /** FuriPubSubSubscription type */ 18 | typedef struct FuriPubSubSubscription FuriPubSubSubscription; 19 | 20 | /** Allocate FuriPubSub 21 | * 22 | * Reentrable, Not threadsafe, one owner 23 | * 24 | * @return pointer to FuriPubSub instance 25 | */ 26 | FuriPubSub* furi_pubsub_alloc(); 27 | 28 | /** Free FuriPubSub 29 | * 30 | * @param pubsub FuriPubSub instance 31 | */ 32 | void furi_pubsub_free(FuriPubSub* pubsub); 33 | 34 | /** Subscribe to FuriPubSub 35 | * 36 | * Threadsafe, Reentrable 37 | * 38 | * @param pubsub pointer to FuriPubSub instance 39 | * @param[in] callback The callback 40 | * @param callback_context The callback context 41 | * 42 | * @return pointer to FuriPubSubSubscription instance 43 | */ 44 | FuriPubSubSubscription* 45 | furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context); 46 | 47 | /** Unsubscribe from FuriPubSub 48 | * 49 | * No use of `pubsub_subscription` allowed after call of this method 50 | * Threadsafe, Reentrable. 51 | * 52 | * @param pubsub pointer to FuriPubSub instance 53 | * @param pubsub_subscription pointer to FuriPubSubSubscription instance 54 | */ 55 | void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription); 56 | 57 | /** Publish message to FuriPubSub 58 | * 59 | * Threadsafe, Reentrable. 60 | * 61 | * @param pubsub pointer to FuriPubSub instance 62 | * @param message message pointer to publish 63 | */ 64 | void furi_pubsub_publish(FuriPubSub* pubsub, void* message); 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/valuemutex.c: -------------------------------------------------------------------------------- 1 | #include "valuemutex.h" 2 | 3 | #include 4 | 5 | bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) { 6 | // mutex without name, 7 | // no attributes (unfortunatly robust mutex is not supported by FreeRTOS), 8 | // with dynamic memory allocation 9 | valuemutex->mutex = furi_mutex_alloc(FuriMutexTypeNormal); 10 | if(valuemutex->mutex == NULL) return false; 11 | 12 | valuemutex->value = value; 13 | valuemutex->size = size; 14 | 15 | return true; 16 | } 17 | 18 | bool delete_mutex(ValueMutex* valuemutex) { 19 | if(furi_mutex_acquire(valuemutex->mutex, FuriWaitForever) == FuriStatusOk) { 20 | furi_mutex_free(valuemutex->mutex); 21 | return true; 22 | } else { 23 | return false; 24 | } 25 | } 26 | 27 | void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout) { 28 | if(furi_mutex_acquire(valuemutex->mutex, timeout) == FuriStatusOk) { 29 | return valuemutex->value; 30 | } else { 31 | return NULL; 32 | } 33 | } 34 | 35 | bool release_mutex(ValueMutex* valuemutex, const void* value) { 36 | if(value != valuemutex->value) return false; 37 | 38 | if(furi_mutex_release(valuemutex->mutex) != FuriStatusOk) return false; 39 | 40 | return true; 41 | } 42 | 43 | bool read_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout) { 44 | void* value = acquire_mutex(valuemutex, timeout); 45 | if(value == NULL || len > valuemutex->size) return false; 46 | memcpy(data, value, len > 0 ? len : valuemutex->size); 47 | if(!release_mutex(valuemutex, value)) return false; 48 | 49 | return true; 50 | } 51 | 52 | bool write_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout) { 53 | void* value = acquire_mutex(valuemutex, timeout); 54 | if(value == NULL || len > valuemutex->size) return false; 55 | memcpy(value, data, len > 0 ? len : valuemutex->size); 56 | if(!release_mutex(valuemutex, value)) return false; 57 | 58 | return true; 59 | } 60 | -------------------------------------------------------------------------------- /fapulator/theseus/applications/gui/font/u8g2_font_render.h: -------------------------------------------------------------------------------- 1 | /* 2 | * u8g2_font_render.h 3 | * 4 | * Created on: Nov 27, 2020 5 | * Author: quard 6 | */ 7 | 8 | #ifndef INC_U8G2_FONT_RENDER_H_ 9 | #define INC_U8G2_FONT_RENDER_H_ 10 | 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #define U8G2_FONT_HEADER_SIZE 23 19 | 20 | #define U8G2FontRender_OK 0x01 21 | #define U8G2FontRender_ERR 0x02 22 | 23 | #define pgm_read(adr) (*(const uint8_t*)(adr)) 24 | 25 | typedef void (*fnDrawPixel)(uint8_t x, uint8_t y, void* context); 26 | 27 | typedef struct { 28 | uint8_t number_of_glyphs : 8; 29 | uint8_t bounding_box_mode : 8; 30 | uint8_t zero_bit_width : 8; 31 | uint8_t one_bit_width : 8; 32 | 33 | uint8_t glyph_width : 8; 34 | uint8_t glyph_height : 8; 35 | uint8_t glyph_x_offset : 8; 36 | uint8_t glyph_y_offset : 8; 37 | uint8_t glyph_pitch : 8; 38 | 39 | int8_t bounding_box_width : 8; 40 | int8_t bounding_box_height : 8; 41 | int8_t bounding_box_x_offset : 8; 42 | int8_t bounding_box_y_offset : 8; 43 | 44 | int8_t ascent_A : 8; 45 | int8_t descent_g : 8; 46 | int8_t ascent_parentheses : 8; 47 | int8_t descent_parentheses : 8; 48 | 49 | uint16_t offset_A : 16; 50 | uint16_t offset_a : 16; 51 | uint16_t offset_0x100 : 16; 52 | } U8G2FontHeader_t; 53 | 54 | typedef struct { 55 | uint8_t character : 8; 56 | uint8_t next_glypth : 8; 57 | 58 | uint8_t width; 59 | uint8_t height; 60 | int8_t x_offset; 61 | int8_t y_offset; 62 | int8_t pitch; 63 | 64 | uint8_t bit_pos; 65 | const uint8_t* data; 66 | } U8G2FontGlyph_t; 67 | 68 | typedef struct { 69 | U8G2FontHeader_t header; 70 | 71 | const uint8_t* data; 72 | 73 | fnDrawPixel drawFgPixel; 74 | fnDrawPixel drawBgPixel; 75 | void* context; 76 | } U8G2FontRender_t; 77 | 78 | U8G2FontRender_t U8G2FontRender( 79 | const uint8_t* data, 80 | fnDrawPixel drawFgPixel, 81 | fnDrawPixel drawBgPixel, 82 | void* context); 83 | U8G2FontHeader_t U8G2FontRender_ParseHeader(U8G2FontRender_t* font); 84 | void U8G2FontRender_PrintChar(U8G2FontRender_t* font, uint8_t* x, uint8_t y, char chr); 85 | void U8G2FontRender_Print(U8G2FontRender_t* font, uint8_t x, uint8_t y, const char* str); 86 | 87 | #ifdef __cplusplus 88 | } 89 | #endif 90 | 91 | #endif /* INC_U8G2_FONT_RENDER_H_ */ -------------------------------------------------------------------------------- /fapulator/flipper/applications/gui/canvas_i.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file canvas_i.h 3 | * GUI: internal Canvas API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "canvas.h" 9 | #include 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** Canvas structure 17 | */ 18 | struct Canvas { 19 | u8g2_t fb; 20 | CanvasOrientation orientation; 21 | uint8_t offset_x; 22 | uint8_t offset_y; 23 | uint8_t width; 24 | uint8_t height; 25 | }; 26 | 27 | /** Allocate memory and initialize canvas 28 | * 29 | * @return Canvas instance 30 | */ 31 | Canvas* canvas_init(); 32 | 33 | /** Free canvas memory 34 | * 35 | * @param canvas Canvas instance 36 | */ 37 | void canvas_free(Canvas* canvas); 38 | 39 | /** Reset canvas drawing tools configuration 40 | * 41 | * @param canvas Canvas instance 42 | */ 43 | void canvas_reset(Canvas* canvas); 44 | 45 | /** Commit canvas. Send buffer to display 46 | * 47 | * @param canvas Canvas instance 48 | */ 49 | void canvas_commit(Canvas* canvas); 50 | 51 | /** Get canvas buffer. 52 | * 53 | * @param canvas Canvas instance 54 | * 55 | * @return pointer to buffer 56 | */ 57 | uint8_t* canvas_get_buffer(Canvas* canvas); 58 | 59 | /** Get canvas buffer size. 60 | * 61 | * @param canvas Canvas instance 62 | * 63 | * @return size of canvas in bytes 64 | */ 65 | size_t canvas_get_buffer_size(Canvas* canvas); 66 | 67 | /** Set drawing region relative to real screen buffer 68 | * 69 | * @param canvas Canvas instance 70 | * @param offset_x x coordinate offset 71 | * @param offset_y y coordinate offset 72 | * @param width width 73 | * @param height height 74 | */ 75 | void canvas_frame_set( 76 | Canvas* canvas, 77 | uint8_t offset_x, 78 | uint8_t offset_y, 79 | uint8_t width, 80 | uint8_t height); 81 | 82 | /** Set canvas orientation 83 | * 84 | * @param canvas Canvas instance 85 | * @param orientation CanvasOrientation 86 | */ 87 | void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation); 88 | 89 | /** Get canvas orientation 90 | * 91 | * @param canvas Canvas instance 92 | * 93 | * @return CanvasOrientation 94 | */ 95 | CanvasOrientation canvas_get_orientation(const Canvas* canvas); 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | -------------------------------------------------------------------------------- /fapulator/flipper/applications/gui/gui_i.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gui_i.h 3 | * GUI: main API internals 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "gui.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "canvas.h" 16 | #include "canvas_i.h" 17 | #include "view_port.h" 18 | #include "view_port_i.h" 19 | 20 | #define GUI_DISPLAY_WIDTH 128 21 | #define GUI_DISPLAY_HEIGHT 64 22 | 23 | #define GUI_STATUS_BAR_X 0 24 | #define GUI_STATUS_BAR_Y 0 25 | #define GUI_STATUS_BAR_WIDTH GUI_DISPLAY_WIDTH 26 | /* 0-1 pixels for upper thin frame 27 | * 2-9 pixels for icons (battery, sd card, etc) 28 | * 10-12 pixels for lower bold line */ 29 | #define GUI_STATUS_BAR_HEIGHT 13 30 | /* icon itself area (battery, sd card, etc) excluding frame. 31 | * painted 2 pixels below GUI_STATUS_BAR_X. 32 | */ 33 | #define GUI_STATUS_BAR_WORKAREA_HEIGHT 8 34 | 35 | #define GUI_WINDOW_X 0 36 | #define GUI_WINDOW_Y GUI_STATUS_BAR_HEIGHT 37 | #define GUI_WINDOW_WIDTH GUI_DISPLAY_WIDTH 38 | #define GUI_WINDOW_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_WINDOW_Y) 39 | 40 | #define GUI_THREAD_FLAG_DRAW (1 << 0) 41 | #define GUI_THREAD_FLAG_INPUT (1 << 1) 42 | #define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT) 43 | 44 | ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); 45 | 46 | typedef struct { 47 | GuiCanvasCommitCallback callback; 48 | void* context; 49 | } CanvasCallbackPair; 50 | 51 | ARRAY_DEF(CanvasCallbackPairArray, CanvasCallbackPair, M_POD_OPLIST); 52 | 53 | #define M_OPL_CanvasCallbackPairArray_t() ARRAY_OPLIST(CanvasCallbackPairArray, M_POD_OPLIST) 54 | 55 | ALGO_DEF(CanvasCallbackPairArray, CanvasCallbackPairArray_t); 56 | 57 | /** Gui structure */ 58 | struct Gui { 59 | // Thread and lock 60 | FuriThreadId thread_id; 61 | FuriMutex* mutex; 62 | 63 | // Layers and Canvas 64 | bool lockdown; 65 | ViewPortArray_t layers[GuiLayerMAX]; 66 | Canvas* canvas; 67 | CanvasCallbackPairArray_t canvas_callback_pair; 68 | 69 | // Input 70 | FuriMessageQueue* input_queue; 71 | FuriPubSub* input_events; 72 | uint8_t ongoing_input; 73 | ViewPort* ongoing_input_view_port; 74 | }; 75 | 76 | ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); 77 | 78 | /** Update GUI, request redraw 79 | * 80 | * @param gui Gui instance 81 | */ 82 | void gui_update(Gui* gui); 83 | 84 | void gui_input_events_callback(const void* value, void* ctx); 85 | 86 | void gui_lock(Gui* gui); 87 | 88 | void gui_unlock(Gui* gui); 89 | -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/message_queue.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file message_queue.h 3 | * FuriMessageQueue 4 | */ 5 | #pragma once 6 | 7 | #include "core/base.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | typedef void FuriMessageQueue; 14 | 15 | /** Allocate furi message queue 16 | * 17 | * @param[in] msg_count The message count 18 | * @param[in] msg_size The message size 19 | * 20 | * @return pointer to FuriMessageQueue instance 21 | */ 22 | FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size); 23 | 24 | /** Free queue 25 | * 26 | * @param instance pointer to FuriMessageQueue instance 27 | */ 28 | void furi_message_queue_free(FuriMessageQueue* instance); 29 | 30 | /** Put message into queue 31 | * 32 | * @param instance pointer to FuriMessageQueue instance 33 | * @param[in] msg_ptr The message pointer 34 | * @param[in] timeout The timeout 35 | * @param[in] msg_prio The message prio 36 | * 37 | * @return The furi status. 38 | */ 39 | FuriStatus 40 | furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout); 41 | 42 | /** Get message from queue 43 | * 44 | * @param instance pointer to FuriMessageQueue instance 45 | * @param msg_ptr The message pointer 46 | * @param msg_prio The message prioority 47 | * @param[in] timeout The timeout 48 | * 49 | * @return The furi status. 50 | */ 51 | FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout); 52 | 53 | /** Get queue capacity 54 | * 55 | * @param instance pointer to FuriMessageQueue instance 56 | * 57 | * @return capacity in object count 58 | */ 59 | uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance); 60 | 61 | /** Get message size 62 | * 63 | * @param instance pointer to FuriMessageQueue instance 64 | * 65 | * @return Message size in bytes 66 | */ 67 | uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance); 68 | 69 | /** Get message count in queue 70 | * 71 | * @param instance pointer to FuriMessageQueue instance 72 | * 73 | * @return Message count 74 | */ 75 | uint32_t furi_message_queue_get_count(FuriMessageQueue* instance); 76 | 77 | /** Get queue available space 78 | * 79 | * @param instance pointer to FuriMessageQueue instance 80 | * 81 | * @return Message count 82 | */ 83 | uint32_t furi_message_queue_get_space(FuriMessageQueue* instance); 84 | 85 | /** Reset queue 86 | * 87 | * @param instance pointer to FuriMessageQueue instance 88 | * 89 | * @return The furi status. 90 | */ 91 | FuriStatus furi_message_queue_reset(FuriMessageQueue* instance); 92 | 93 | #ifdef __cplusplus 94 | } 95 | #endif 96 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | AlignAfterOpenBracket: AlwaysBreak 3 | AlignConsecutiveAssignments: false 4 | AlignConsecutiveDeclarations: false 5 | AlignEscapedNewlines: Left 6 | AlignOperands: true 7 | AlignTrailingComments: false 8 | AllowAllParametersOfDeclarationOnNextLine: false 9 | AllowShortBlocksOnASingleLine: Never 10 | AllowShortCaseLabelsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: None 12 | AllowShortIfStatementsOnASingleLine: true 13 | AllowShortLoopsOnASingleLine: true 14 | AlwaysBreakAfterDefinitionReturnType: None 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: false 17 | AlwaysBreakTemplateDeclarations: false 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BreakBeforeBinaryOperators: None 21 | BreakBeforeBraces: Attach 22 | BreakBeforeTernaryOperators: false 23 | BreakConstructorInitializers: BeforeComma 24 | BreakStringLiterals: false 25 | ColumnLimit: 99 26 | CompactNamespaces: false 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 28 | ConstructorInitializerIndentWidth: 4 29 | ContinuationIndentWidth: 4 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: false 32 | DisableFormat: false 33 | ExperimentalAutoDetectBinPacking: false 34 | FixNamespaceComments: false 35 | 36 | IncludeBlocks: Preserve 37 | IncludeCategories: 38 | - Regex: '.*' 39 | Priority: 1 40 | IncludeIsMainRegex: '(Test)?$' 41 | IndentCaseLabels: false 42 | IndentPPDirectives: None 43 | IndentWidth: 4 44 | IndentWrappedFunctionNames: true 45 | JavaScriptQuotes: Leave 46 | JavaScriptWrapImports: true 47 | KeepEmptyLinesAtTheStartOfBlocks: false 48 | MacroBlockBegin: '' 49 | MacroBlockEnd: '' 50 | MaxEmptyLinesToKeep: 1 51 | NamespaceIndentation: None 52 | ObjCBinPackProtocolList: Auto 53 | ObjCBlockIndentWidth: 4 54 | ObjCSpaceAfterProperty: true 55 | ObjCSpaceBeforeProtocolList: true 56 | 57 | # Taken from git's rules 58 | PenaltyBreakAssignment: 10 59 | PenaltyBreakBeforeFirstCallParameter: 30 60 | PenaltyBreakComment: 10 61 | PenaltyBreakFirstLessLess: 0 62 | PenaltyBreakString: 10 63 | PenaltyExcessCharacter: 100 64 | PenaltyReturnTypeOnItsOwnLine: 60 65 | 66 | PointerAlignment: Left 67 | ReflowComments: false 68 | SortIncludes: false 69 | SortUsingDeclarations: false 70 | SpaceAfterCStyleCast: false 71 | SpaceAfterTemplateKeyword: true 72 | SpaceBeforeAssignmentOperators: true 73 | SpaceBeforeCtorInitializerColon: true 74 | SpaceBeforeInheritanceColon: true 75 | SpaceBeforeParens: Never 76 | SpaceBeforeRangeBasedForLoopColon: true 77 | SpaceInEmptyParentheses: false 78 | SpacesBeforeTrailingComments: 1 79 | SpacesInAngles: false 80 | SpacesInContainerLiterals: false 81 | SpacesInCStyleCastParentheses: false 82 | SpacesInParentheses: false 83 | SpacesInSquareBrackets: false 84 | Standard: Cpp03 85 | TabWidth: 4 86 | UseTab: Never -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/core_defines.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #define FURI_RETURNS_NONNULL __attribute__((returns_nonnull)) 8 | 9 | #ifndef MAX 10 | #define MAX(a, b) \ 11 | ({ \ 12 | __typeof__(a) _a = (a); \ 13 | __typeof__(b) _b = (b); \ 14 | _a > _b ? _a : _b; \ 15 | }) 16 | #endif 17 | 18 | #ifndef MIN 19 | #define MIN(a, b) \ 20 | ({ \ 21 | __typeof__(a) _a = (a); \ 22 | __typeof__(b) _b = (b); \ 23 | _a < _b ? _a : _b; \ 24 | }) 25 | #endif 26 | 27 | #ifndef ROUND_UP_TO 28 | #define ROUND_UP_TO(a, b) \ 29 | ({ \ 30 | __typeof__(a) _a = (a); \ 31 | __typeof__(b) _b = (b); \ 32 | _a / _b + !!(_a % _b); \ 33 | }) 34 | #endif 35 | 36 | #ifndef CLAMP 37 | #define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) 38 | #endif 39 | 40 | #ifndef COUNT_OF 41 | #define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) 42 | #endif 43 | 44 | #ifndef FURI_SWAP 45 | #define FURI_SWAP(x, y) \ 46 | do { \ 47 | typeof(x) SWAP = x; \ 48 | x = y; \ 49 | y = SWAP; \ 50 | } while(0) 51 | #endif 52 | 53 | #ifndef PLACE_IN_SECTION 54 | #define PLACE_IN_SECTION(x) __attribute__((section(x))) 55 | #endif 56 | 57 | #ifndef ALIGN 58 | #define ALIGN(n) __attribute__((aligned(n))) 59 | #endif 60 | 61 | #ifndef __weak 62 | #define __weak __attribute__((weak)) 63 | #endif 64 | 65 | #ifndef UNUSED 66 | #define UNUSED(X) (void)(X) 67 | #endif 68 | 69 | #ifndef STRINGIFY 70 | #define STRINGIFY(x) #x 71 | #endif 72 | 73 | #ifndef TOSTRING 74 | #define TOSTRING(x) STRINGIFY(x) 75 | #endif 76 | 77 | #ifndef REVERSE_BYTES_U32 78 | #define REVERSE_BYTES_U32(x) \ 79 | ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ 80 | (((x)&0xFF000000) >> 24)) 81 | #endif 82 | 83 | #ifndef FURI_BIT 84 | #define FURI_BIT(x, n) (((x) >> (n)) & 1) 85 | #endif 86 | 87 | #ifndef FURI_BIT_SET 88 | #define FURI_BIT_SET(x, n) \ 89 | ({ \ 90 | __typeof__(x) _x = (1); \ 91 | (x) |= (_x << (n)); \ 92 | }) 93 | #endif 94 | 95 | #ifndef FURI_BIT_CLEAR 96 | #define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n))) 97 | #endif 98 | 99 | #define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") 100 | 101 | #ifdef __GNUC__ 102 | #define _ATTRIBUTE(attrs) __attribute__(attrs) 103 | #else 104 | #define _ATTRIBUTE(attrs) 105 | #endif 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "chrono": "cpp", 4 | "array": "cpp", 5 | "atomic": "cpp", 6 | "bit": "cpp", 7 | "*.tcc": "cpp", 8 | "bitset": "cpp", 9 | "cctype": "cpp", 10 | "clocale": "cpp", 11 | "cmath": "cpp", 12 | "compare": "cpp", 13 | "concepts": "cpp", 14 | "condition_variable": "cpp", 15 | "cstdarg": "cpp", 16 | "cstddef": "cpp", 17 | "cstdint": "cpp", 18 | "cstdio": "cpp", 19 | "cstdlib": "cpp", 20 | "ctime": "cpp", 21 | "cwchar": "cpp", 22 | "cwctype": "cpp", 23 | "deque": "cpp", 24 | "unordered_map": "cpp", 25 | "vector": "cpp", 26 | "exception": "cpp", 27 | "algorithm": "cpp", 28 | "functional": "cpp", 29 | "iterator": "cpp", 30 | "memory": "cpp", 31 | "memory_resource": "cpp", 32 | "numeric": "cpp", 33 | "optional": "cpp", 34 | "random": "cpp", 35 | "ratio": "cpp", 36 | "string": "cpp", 37 | "string_view": "cpp", 38 | "system_error": "cpp", 39 | "tuple": "cpp", 40 | "type_traits": "cpp", 41 | "utility": "cpp", 42 | "initializer_list": "cpp", 43 | "iosfwd": "cpp", 44 | "limits": "cpp", 45 | "mutex": "cpp", 46 | "new": "cpp", 47 | "ostream": "cpp", 48 | "ranges": "cpp", 49 | "stdexcept": "cpp", 50 | "stop_token": "cpp", 51 | "streambuf": "cpp", 52 | "thread": "cpp", 53 | "typeinfo": "cpp", 54 | "input.h": "c", 55 | "canvas.h": "c", 56 | "furi_hal_resources.h": "c", 57 | "__mutex_base": "cpp", 58 | "ios": "c", 59 | "variant": "c", 60 | "istream": "cpp", 61 | "locale": "cpp", 62 | "__functional_base": "cpp", 63 | "__config": "cpp", 64 | "__bit_reference": "cpp", 65 | "__bits": "cpp", 66 | "__debug": "cpp", 67 | "__errc": "cpp", 68 | "__hash_table": "cpp", 69 | "__locale": "cpp", 70 | "__node_handle": "cpp", 71 | "__nullptr": "cpp", 72 | "__split_buffer": "cpp", 73 | "__string": "cpp", 74 | "__threading_support": "cpp", 75 | "__tree": "cpp", 76 | "__tuple": "cpp", 77 | "complex": "cpp", 78 | "cstring": "cpp", 79 | "forward_list": "cpp", 80 | "iomanip": "cpp", 81 | "iostream": "cpp", 82 | "list": "cpp", 83 | "map": "cpp", 84 | "queue": "cpp", 85 | "set": "cpp", 86 | "sstream": "cpp", 87 | "unordered_set": "cpp", 88 | "view_port_i.h": "c", 89 | "gui_i.h": "c", 90 | "__memory": "cpp", 91 | "furi.h": "c" 92 | } 93 | } -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file log.h 3 | * Furi Logging system 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include "core_defines.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | typedef enum { 17 | FuriLogLevelDefault = 0, 18 | FuriLogLevelNone = 1, 19 | FuriLogLevelError = 2, 20 | FuriLogLevelWarn = 3, 21 | FuriLogLevelInfo = 4, 22 | FuriLogLevelDebug = 5, 23 | FuriLogLevelTrace = 6, 24 | } FuriLogLevel; 25 | 26 | #define FURI_LOG_CLR(clr) "\033[0;" clr "m" 27 | #define FURI_LOG_CLR_RESET "\033[0m" 28 | 29 | #define FURI_LOG_CLR_BLACK "30" 30 | #define FURI_LOG_CLR_RED "31" 31 | #define FURI_LOG_CLR_GREEN "32" 32 | #define FURI_LOG_CLR_BROWN "33" 33 | #define FURI_LOG_CLR_BLUE "34" 34 | #define FURI_LOG_CLR_PURPLE "35" 35 | 36 | #define FURI_LOG_CLR_E FURI_LOG_CLR(FURI_LOG_CLR_RED) 37 | #define FURI_LOG_CLR_W FURI_LOG_CLR(FURI_LOG_CLR_BROWN) 38 | #define FURI_LOG_CLR_I FURI_LOG_CLR(FURI_LOG_CLR_GREEN) 39 | #define FURI_LOG_CLR_D FURI_LOG_CLR(FURI_LOG_CLR_BLUE) 40 | #define FURI_LOG_CLR_T FURI_LOG_CLR(FURI_LOG_CLR_PURPLE) 41 | 42 | typedef void (*FuriLogPuts)(const char* data); 43 | typedef uint32_t (*FuriLogTimestamp)(void); 44 | 45 | /** Initialize logging */ 46 | void furi_log_init(); 47 | 48 | /** Print log record 49 | * 50 | * @param level 51 | * @param tag 52 | * @param format 53 | * @param ... 54 | */ 55 | void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) 56 | _ATTRIBUTE((__format__(__printf__, 3, 4))); 57 | 58 | /** Set log level 59 | * 60 | * @param[in] level The level 61 | */ 62 | void furi_log_set_level(FuriLogLevel level); 63 | 64 | /** Get log level 65 | * 66 | * @return The furi log level. 67 | */ 68 | FuriLogLevel furi_log_get_level(); 69 | 70 | /** Set log output callback 71 | * 72 | * @param[in] puts The puts callback 73 | */ 74 | void furi_log_set_puts(FuriLogPuts puts); 75 | 76 | /** Set timestamp callback 77 | * 78 | * @param[in] timestamp The timestamp callback 79 | */ 80 | void furi_log_set_timestamp(FuriLogTimestamp timestamp); 81 | 82 | /** Log methods 83 | * 84 | * @param tag The application tag 85 | * @param format The format 86 | * @param ... VA Args 87 | */ 88 | #define FURI_LOG_E(tag, format, ...) \ 89 | furi_log_print_format(FuriLogLevelError, tag, format, ##__VA_ARGS__) 90 | #define FURI_LOG_W(tag, format, ...) \ 91 | furi_log_print_format(FuriLogLevelWarn, tag, format, ##__VA_ARGS__) 92 | #define FURI_LOG_I(tag, format, ...) \ 93 | furi_log_print_format(FuriLogLevelInfo, tag, format, ##__VA_ARGS__) 94 | #define FURI_LOG_D(tag, format, ...) \ 95 | furi_log_print_format(FuriLogLevelDebug, tag, format, ##__VA_ARGS__) 96 | #define FURI_LOG_T(tag, format, ...) \ 97 | furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__) 98 | 99 | #ifdef __cplusplus 100 | } 101 | #endif 102 | -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/pubsub.c: -------------------------------------------------------------------------------- 1 | #include "pubsub.h" 2 | #include "check.h" 3 | #include "mutex.h" 4 | 5 | #include 6 | 7 | struct FuriPubSubSubscription { 8 | FuriPubSubCallback callback; 9 | void* callback_context; 10 | }; 11 | 12 | LIST_DEF(FuriPubSubSubscriptionList, FuriPubSubSubscription, M_POD_OPLIST); 13 | 14 | struct FuriPubSub { 15 | FuriPubSubSubscriptionList_t items; 16 | FuriMutex* mutex; 17 | }; 18 | 19 | FuriPubSub* furi_pubsub_alloc() { 20 | FuriPubSub* pubsub = malloc(sizeof(FuriPubSub)); 21 | memset(pubsub, 0, sizeof(FuriPubSub)); 22 | 23 | pubsub->mutex = furi_mutex_alloc(FuriMutexTypeNormal); 24 | furi_assert(pubsub->mutex); 25 | 26 | FuriPubSubSubscriptionList_init(pubsub->items); 27 | 28 | return pubsub; 29 | } 30 | 31 | void furi_pubsub_free(FuriPubSub* pubsub) { 32 | furi_assert(pubsub); 33 | 34 | furi_check(FuriPubSubSubscriptionList_size(pubsub->items) == 0); 35 | 36 | FuriPubSubSubscriptionList_clear(pubsub->items); 37 | 38 | furi_mutex_free(pubsub->mutex); 39 | 40 | free(pubsub); 41 | } 42 | 43 | FuriPubSubSubscription* 44 | furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context) { 45 | furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk); 46 | // put uninitialized item to the list 47 | FuriPubSubSubscription* item = FuriPubSubSubscriptionList_push_raw(pubsub->items); 48 | 49 | // initialize item 50 | item->callback = callback; 51 | item->callback_context = callback_context; 52 | 53 | furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk); 54 | 55 | return item; 56 | } 57 | 58 | void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription) { 59 | furi_assert(pubsub); 60 | furi_assert(pubsub_subscription); 61 | 62 | furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk); 63 | bool result = false; 64 | 65 | // iterate over items 66 | FuriPubSubSubscriptionList_it_t it; 67 | for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); 68 | FuriPubSubSubscriptionList_next(it)) { 69 | const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); 70 | 71 | // if the iterator is equal to our element 72 | if(item == pubsub_subscription) { 73 | FuriPubSubSubscriptionList_remove(pubsub->items, it); 74 | result = true; 75 | break; 76 | } 77 | } 78 | 79 | furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk); 80 | furi_check(result); 81 | } 82 | 83 | void furi_pubsub_publish(FuriPubSub* pubsub, void* message) { 84 | furi_check(furi_mutex_acquire(pubsub->mutex, FuriWaitForever) == FuriStatusOk); 85 | 86 | // iterate over subscribers 87 | FuriPubSubSubscriptionList_it_t it; 88 | for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); 89 | FuriPubSubSubscriptionList_next(it)) { 90 | const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); 91 | item->callback(message, item->callback_context); 92 | } 93 | 94 | furi_check(furi_mutex_release(pubsub->mutex) == FuriStatusOk); 95 | } 96 | -------------------------------------------------------------------------------- /fapulator/flipper/applications/gui/view_port.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file view_port.h 3 | * GUI: ViewPort API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "canvas.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | typedef struct ViewPort ViewPort; 16 | 17 | typedef enum { 18 | ViewPortOrientationHorizontal, 19 | ViewPortOrientationHorizontalFlip, 20 | ViewPortOrientationVertical, 21 | ViewPortOrientationVerticalFlip, 22 | ViewPortOrientationMAX, /**< Special value, don't use it */ 23 | } ViewPortOrientation; 24 | 25 | /** ViewPort Draw callback 26 | * @warning called from GUI thread 27 | */ 28 | typedef void (*ViewPortDrawCallback)(Canvas* canvas, void* context); 29 | 30 | /** ViewPort Input callback 31 | * @warning called from GUI thread 32 | */ 33 | typedef void (*ViewPortInputCallback)(InputEvent* event, void* context); 34 | 35 | /** ViewPort allocator 36 | * 37 | * always returns view_port or stops system if not enough memory. 38 | * 39 | * @return ViewPort instance 40 | */ 41 | ViewPort* view_port_alloc(); 42 | 43 | /** ViewPort deallocator 44 | * 45 | * Ensure that view_port was unregistered in GUI system before use. 46 | * 47 | * @param view_port ViewPort instance 48 | */ 49 | void view_port_free(ViewPort* view_port); 50 | 51 | /** Set view_port width. 52 | * 53 | * Will be used to limit canvas drawing area and autolayout feature. 54 | * 55 | * @param view_port ViewPort instance 56 | * @param width wanted width, 0 - auto. 57 | */ 58 | void view_port_set_width(ViewPort* view_port, uint8_t width); 59 | uint8_t view_port_get_width(ViewPort* view_port); 60 | 61 | /** Set view_port height. 62 | * 63 | * Will be used to limit canvas drawing area and autolayout feature. 64 | * 65 | * @param view_port ViewPort instance 66 | * @param height wanted height, 0 - auto. 67 | */ 68 | void view_port_set_height(ViewPort* view_port, uint8_t height); 69 | uint8_t view_port_get_height(ViewPort* view_port); 70 | 71 | /** Enable or disable view_port rendering. 72 | * 73 | * @param view_port ViewPort instance 74 | * @param enabled Indicates if enabled 75 | * @warning automatically dispatches update event 76 | */ 77 | void view_port_enabled_set(ViewPort* view_port, bool enabled); 78 | bool view_port_is_enabled(ViewPort* view_port); 79 | 80 | /** ViewPort event callbacks 81 | * 82 | * @param view_port ViewPort instance 83 | * @param callback appropriate callback function 84 | * @param context context to pass to callback 85 | */ 86 | void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context); 87 | void view_port_input_callback_set( 88 | ViewPort* view_port, 89 | ViewPortInputCallback callback, 90 | void* context); 91 | 92 | /** Emit update signal to GUI system. 93 | * 94 | * Rendering will happen later after GUI system process signal. 95 | * 96 | * @param view_port ViewPort instance 97 | */ 98 | void view_port_update(ViewPort* view_port); 99 | 100 | /** Set ViewPort orientation. 101 | * 102 | * @param view_port ViewPort instance 103 | * @param orientation display orientation, horizontal or vertical. 104 | */ 105 | void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation); 106 | ViewPortOrientation view_port_get_orientation(const ViewPort* view_port); 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif 111 | -------------------------------------------------------------------------------- /fapulator/flipper/applications/gui/gui.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gui.h 3 | * GUI: main API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "view_port.h" 10 | #include "canvas.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** Gui layers */ 17 | typedef enum { 18 | GuiLayerDesktop, /**< Desktop layer for internal use. Like fullscreen but with status bar */ 19 | 20 | GuiLayerWindow, /**< Window layer, status bar is shown */ 21 | 22 | GuiLayerStatusBarLeft, /**< Status bar left-side layer, auto-layout */ 23 | GuiLayerStatusBarRight, /**< Status bar right-side layer, auto-layout */ 24 | 25 | GuiLayerFullscreen, /**< Fullscreen layer, no status bar */ 26 | 27 | GuiLayerMAX /**< Don't use or move, special value */ 28 | } GuiLayer; 29 | 30 | /** Gui Canvas Commit Callback */ 31 | typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context); 32 | 33 | #define RECORD_GUI "gui" 34 | 35 | typedef struct Gui Gui; 36 | 37 | /** Add view_port to view_port tree 38 | * 39 | * @remark thread safe 40 | * 41 | * @param gui Gui instance 42 | * @param view_port ViewPort instance 43 | * @param[in] layer GuiLayer where to place view_port 44 | */ 45 | void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer); 46 | 47 | /** Remove view_port from rendering tree 48 | * 49 | * @remark thread safe 50 | * 51 | * @param gui Gui instance 52 | * @param view_port ViewPort instance 53 | */ 54 | void gui_remove_view_port(Gui* gui, ViewPort* view_port); 55 | 56 | /** Send ViewPort to the front 57 | * 58 | * Places selected ViewPort to the top of the drawing stack 59 | * 60 | * @param gui Gui instance 61 | * @param view_port ViewPort instance 62 | */ 63 | void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port); 64 | 65 | /** Send ViewPort to the back 66 | * 67 | * Places selected ViewPort to the bottom of the drawing stack 68 | * 69 | * @param gui Gui instance 70 | * @param view_port ViewPort instance 71 | */ 72 | void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port); 73 | 74 | /** Add gui canvas commit callback 75 | * 76 | * This callback will be called upon Canvas commit Callback dispatched from GUI 77 | * thread and is time critical 78 | * 79 | * @param gui Gui instance 80 | * @param callback GuiCanvasCommitCallback 81 | * @param context GuiCanvasCommitCallback context 82 | */ 83 | void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context); 84 | 85 | /** Remove gui canvas commit callback 86 | * 87 | * @param gui Gui instance 88 | * @param callback GuiCanvasCommitCallback 89 | * @param context GuiCanvasCommitCallback context 90 | */ 91 | void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context); 92 | 93 | /** Get gui canvas frame buffer size 94 | * * 95 | * @param gui Gui instance 96 | * @return size_t size of frame buffer in bytes 97 | */ 98 | size_t gui_get_framebuffer_size(Gui* gui); 99 | 100 | /** Set lockdown mode 101 | * 102 | * When lockdown mode is enabled, only GuiLayerDesktop is shown. 103 | * This feature prevents services from showing sensitive information when flipper is locked. 104 | * 105 | * @param gui Gui instance 106 | * @param lockdown bool, true if enabled 107 | */ 108 | void gui_set_lockdown(Gui* gui, bool lockdown); 109 | 110 | #ifdef __cplusplus 111 | } 112 | #endif 113 | -------------------------------------------------------------------------------- /fapulator/theseus/core/message_queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // TODO: add queue size limit and timeout for send 6 | 7 | class QueueInstance { 8 | private: 9 | uint32_t msg_size; 10 | 11 | mutable std::mutex mutex; 12 | std::queue queue; 13 | std::condition_variable notifier; 14 | 15 | void push(void* msg) { 16 | void* msg_copy = malloc(msg_size); 17 | memcpy(msg_copy, msg, msg_size); 18 | queue.push(msg_copy); 19 | } 20 | 21 | void pop(void* msg) { 22 | void* msg_copy = queue.front(); 23 | memcpy(msg, msg_copy, msg_size); 24 | free(msg_copy); 25 | queue.pop(); 26 | } 27 | 28 | void pop_and_free() { 29 | void* msg_copy = queue.front(); 30 | free(msg_copy); 31 | queue.pop(); 32 | } 33 | 34 | public: 35 | QueueInstance(uint32_t _msg_size) { 36 | msg_size = _msg_size; 37 | } 38 | 39 | ~QueueInstance() { 40 | reset(); 41 | } 42 | 43 | FuriStatus send(void* msg) { 44 | { 45 | std::unique_lock lock(mutex); 46 | push(msg); 47 | } 48 | notifier.notify_one(); 49 | return FuriStatusOk; 50 | } 51 | 52 | FuriStatus receive(void* msg) { 53 | std::unique_lock lock(mutex); 54 | while(queue.empty()) { 55 | notifier.wait(lock); 56 | } 57 | pop(msg); 58 | return FuriStatusOk; 59 | } 60 | 61 | FuriStatus receive(void* msg, std::chrono::milliseconds timeout) { 62 | std::unique_lock lock(mutex); 63 | FuriStatus status = FuriStatusErrorTimeout; 64 | if(notifier.wait_for(lock, timeout, [this] { return !queue.empty(); })) { 65 | pop(msg); 66 | status = FuriStatusOk; 67 | } 68 | return status; 69 | } 70 | 71 | uint32_t get_msg_count() { 72 | std::unique_lock lock(mutex); 73 | return queue.size() / sizeof(void*); 74 | } 75 | 76 | uint32_t get_free_space() { 77 | std::unique_lock lock(mutex); 78 | return UINT32_MAX; 79 | } 80 | 81 | uint32_t get_msg_size() { 82 | return msg_size; 83 | } 84 | 85 | FuriStatus reset() { 86 | std::unique_lock lock(mutex); 87 | while(!queue.empty()) { 88 | pop_and_free(); 89 | } 90 | return FuriStatusOk; 91 | } 92 | }; 93 | 94 | FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { 95 | (void)msg_count; 96 | return (FuriMessageQueue*)new QueueInstance(msg_size); 97 | } 98 | 99 | void furi_message_queue_free(FuriMessageQueue* instance) { 100 | delete(QueueInstance*)instance; 101 | } 102 | 103 | FuriStatus 104 | furi_message_queue_put(FuriMessageQueue* instance, const void* msg_ptr, uint32_t timeout) { 105 | (void)timeout; 106 | QueueInstance* queue = (QueueInstance*)instance; 107 | return queue->send((void*)msg_ptr); 108 | } 109 | 110 | FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout) { 111 | QueueInstance* queue = (QueueInstance*)instance; 112 | if(timeout == FuriWaitForever) { 113 | return queue->receive(msg_ptr); 114 | } else { 115 | return queue->receive(msg_ptr, std::chrono::milliseconds(timeout)); 116 | } 117 | } 118 | 119 | uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) { 120 | QueueInstance* queue = (QueueInstance*)instance; 121 | return queue->get_free_space(); 122 | } 123 | 124 | uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) { 125 | QueueInstance* queue = (QueueInstance*)instance; 126 | return queue->get_msg_size(); 127 | } 128 | 129 | uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { 130 | QueueInstance* queue = (QueueInstance*)instance; 131 | return queue->get_msg_count(); 132 | } 133 | 134 | uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { 135 | QueueInstance* queue = (QueueInstance*)instance; 136 | return queue->get_free_space(); 137 | } 138 | 139 | FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { 140 | QueueInstance* queue = (QueueInstance*)instance; 141 | return queue->reset(); 142 | } -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/record.c: -------------------------------------------------------------------------------- 1 | #include "record.h" 2 | #include "check.h" 3 | #include "mutex.h" 4 | #include "event_flag.h" 5 | 6 | #include 7 | #include 8 | 9 | #define FURI_RECORD_FLAG_READY (0x1) 10 | 11 | typedef struct { 12 | FuriEventFlag* flags; 13 | void* data; 14 | size_t holders_count; 15 | } FuriRecordData; 16 | 17 | DICT_DEF2(FuriRecordDataDict, const char*, M_CSTR_DUP_OPLIST, FuriRecordData, M_POD_OPLIST) 18 | 19 | typedef struct { 20 | FuriMutex* mutex; 21 | FuriRecordDataDict_t records; 22 | } FuriRecord; 23 | 24 | static FuriRecord* furi_record = NULL; 25 | 26 | static FuriRecordData* furi_record_get(const char* name) { 27 | return FuriRecordDataDict_get(furi_record->records, name); 28 | } 29 | 30 | static void furi_record_put(const char* name, FuriRecordData* record_data) { 31 | FuriRecordDataDict_set_at(furi_record->records, name, *record_data); 32 | } 33 | 34 | static void furi_record_erase(const char* name, FuriRecordData* record_data) { 35 | furi_event_flag_free(record_data->flags); 36 | FuriRecordDataDict_erase(furi_record->records, name); 37 | } 38 | 39 | void furi_record_init() { 40 | furi_record = malloc(sizeof(FuriRecord)); 41 | memset(furi_record, 0, sizeof(FuriRecord)); 42 | 43 | furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal); 44 | furi_check(furi_record->mutex); 45 | FuriRecordDataDict_init(furi_record->records); 46 | } 47 | 48 | static FuriRecordData* furi_record_data_get_or_create(const char* name) { 49 | furi_assert(furi_record); 50 | FuriRecordData* record_data = furi_record_get(name); 51 | if(!record_data) { 52 | FuriRecordData new_record; 53 | new_record.flags = furi_event_flag_alloc(); 54 | new_record.data = NULL; 55 | new_record.holders_count = 0; 56 | furi_record_put(name, &new_record); 57 | record_data = furi_record_get(name); 58 | } 59 | return record_data; 60 | } 61 | 62 | static void furi_record_lock() { 63 | furi_check(furi_mutex_acquire(furi_record->mutex, FuriWaitForever) == FuriStatusOk); 64 | } 65 | 66 | static void furi_record_unlock() { 67 | furi_check(furi_mutex_release(furi_record->mutex) == FuriStatusOk); 68 | } 69 | 70 | bool furi_record_exists(const char* name) { 71 | furi_assert(furi_record); 72 | furi_assert(name); 73 | 74 | bool ret = false; 75 | 76 | furi_record_lock(); 77 | ret = (furi_record_get(name) != NULL); 78 | furi_record_unlock(); 79 | 80 | return ret; 81 | } 82 | 83 | void furi_record_create(const char* name, void* data) { 84 | furi_assert(furi_record); 85 | 86 | furi_record_lock(); 87 | 88 | // Get record data and fill it 89 | FuriRecordData* record_data = furi_record_data_get_or_create(name); 90 | furi_assert(record_data->data == NULL); 91 | record_data->data = data; 92 | furi_event_flag_set(record_data->flags, FURI_RECORD_FLAG_READY); 93 | 94 | furi_record_unlock(); 95 | } 96 | 97 | bool furi_record_destroy(const char* name) { 98 | furi_assert(furi_record); 99 | 100 | bool ret = false; 101 | 102 | furi_record_lock(); 103 | 104 | FuriRecordData* record_data = furi_record_get(name); 105 | furi_assert(record_data); 106 | if(record_data->holders_count == 0) { 107 | furi_record_erase(name, record_data); 108 | ret = true; 109 | } 110 | 111 | furi_record_unlock(); 112 | 113 | return ret; 114 | } 115 | 116 | void* furi_record_open(const char* name) { 117 | furi_assert(furi_record); 118 | 119 | furi_record_lock(); 120 | 121 | FuriRecordData* record_data = furi_record_data_get_or_create(name); 122 | record_data->holders_count++; 123 | 124 | furi_record_unlock(); 125 | 126 | // Wait for record to become ready 127 | furi_check( 128 | furi_event_flag_wait( 129 | record_data->flags, 130 | FURI_RECORD_FLAG_READY, 131 | FuriFlagWaitAny | FuriFlagNoClear, 132 | FuriWaitForever) == FURI_RECORD_FLAG_READY); 133 | 134 | return record_data->data; 135 | } 136 | 137 | void furi_record_close(const char* name) { 138 | furi_assert(furi_record); 139 | 140 | furi_record_lock(); 141 | 142 | FuriRecordData* record_data = furi_record_get(name); 143 | furi_assert(record_data); 144 | record_data->holders_count--; 145 | 146 | furi_record_unlock(); 147 | } 148 | -------------------------------------------------------------------------------- /fapulator/theseus/core/thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static std::mutex thread_map_mutex; 9 | static std::map thread_map; 10 | 11 | void thread_map_push(std::thread::id id, FuriThread* thread) { 12 | std::lock_guard lock(thread_map_mutex); 13 | thread_map[id] = thread; 14 | } 15 | 16 | FuriThread* thread_map_get(std::thread::id id) { 17 | std::lock_guard lock(thread_map_mutex); 18 | return thread_map[id]; 19 | } 20 | 21 | void thread_map_erase(std::thread::id id) { 22 | std::lock_guard lock(thread_map_mutex); 23 | thread_map.erase(id); 24 | } 25 | 26 | class ThreadInstance { 27 | private: 28 | std::thread thread; 29 | std::string name; 30 | FuriThreadCallback callback; 31 | FuriEventFlag* event_flag; 32 | size_t stack_size; 33 | FuriThread* thread_ptr; 34 | 35 | static void furi_thread_body(void* context) { 36 | ThreadInstance* instance = (ThreadInstance*)context; 37 | thread_map_push(std::this_thread::get_id(), instance->thread_ptr); 38 | 39 | instance->callback(NULL); 40 | thread_map_erase(std::this_thread::get_id()); 41 | } 42 | 43 | public: 44 | ThreadInstance(FuriThread* _thread_ptr) { 45 | event_flag = furi_event_flag_alloc(); 46 | thread_ptr = _thread_ptr; 47 | } 48 | 49 | ~ThreadInstance() { 50 | furi_event_flag_free(event_flag); 51 | } 52 | 53 | void set_name(const char* name) { 54 | this->name = name; 55 | } 56 | 57 | void set_callback(FuriThreadCallback callback) { 58 | this->callback = callback; 59 | } 60 | 61 | void set_stack_size(size_t stack_size) { 62 | this->stack_size = stack_size; 63 | } 64 | 65 | void start() { 66 | thread = std::thread(furi_thread_body, this); 67 | } 68 | 69 | uint32_t flags_clear(uint32_t flags) { 70 | return furi_event_flag_clear(event_flag, flags); 71 | } 72 | 73 | uint32_t flags_set(uint32_t flags) { 74 | return furi_event_flag_set(event_flag, flags); 75 | } 76 | 77 | uint32_t flags_get() { 78 | return furi_event_flag_get(event_flag); 79 | } 80 | 81 | uint32_t flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { 82 | return furi_event_flag_wait(event_flag, flags, options, timeout); 83 | } 84 | }; 85 | 86 | struct FuriThread { 87 | ThreadInstance* instance; 88 | }; 89 | 90 | FuriThread* furi_thread_alloc() { 91 | FuriThread* thread = new FuriThread(); 92 | thread->instance = new ThreadInstance(thread); 93 | return thread; 94 | } 95 | 96 | void furi_thread_free(FuriThread* thread) { 97 | delete thread->instance; 98 | delete thread; 99 | } 100 | 101 | void furi_thread_set_name(FuriThread* thread, const char* name) { 102 | thread->instance->set_name(name); 103 | } 104 | 105 | void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback) { 106 | thread->instance->set_callback(callback); 107 | } 108 | 109 | void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) { 110 | thread->instance->set_stack_size(stack_size); 111 | } 112 | 113 | void furi_thread_start(FuriThread* thread) { 114 | thread->instance->start(); 115 | } 116 | 117 | FuriThreadId furi_thread_get_current_id() { 118 | std::thread::id id = std::this_thread::get_id(); 119 | FuriThread* thread = thread_map_get(id); 120 | return (FuriThreadId)thread; 121 | } 122 | 123 | uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) { 124 | FuriThread* thread = (FuriThread*)thread_id; 125 | return thread->instance->flags_set(flags); 126 | } 127 | 128 | uint32_t furi_thread_flags_clear(uint32_t flags) { 129 | std::thread::id id = std::this_thread::get_id(); 130 | FuriThread* thread = thread_map_get(id); 131 | return thread->instance->flags_clear(flags); 132 | } 133 | 134 | uint32_t furi_thread_flags_get(void) { 135 | std::thread::id id = std::this_thread::get_id(); 136 | FuriThread* thread = thread_map_get(id); 137 | return thread->instance->flags_get(); 138 | } 139 | 140 | uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) { 141 | std::thread::id id = std::this_thread::get_id(); 142 | FuriThread* thread = thread_map_get(id); 143 | return thread->instance->flags_wait(flags, options, timeout); 144 | } -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/valuemutex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "mutex.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * == ValueMutex == 13 | 14 | * The most simple concept is ValueMutex. 15 | * It is wrapper around mutex and value pointer. 16 | * You can take and give mutex to work with value and read and write value. 17 | */ 18 | 19 | typedef struct { 20 | void* value; 21 | size_t size; 22 | FuriMutex* mutex; 23 | } ValueMutex; 24 | 25 | /** 26 | * Creates ValueMutex. 27 | */ 28 | bool init_mutex(ValueMutex* valuemutex, void* value, size_t size); 29 | 30 | /** 31 | * Free resources allocated by `init_mutex`. 32 | * This function doesn't free the memory occupied by `ValueMutex` itself. 33 | */ 34 | bool delete_mutex(ValueMutex* valuemutex); 35 | 36 | /** 37 | * Call for work with data stored in mutex. 38 | * @return pointer to data if success, NULL otherwise. 39 | */ 40 | void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout); 41 | 42 | /** 43 | * Helper: infinitly wait for mutex 44 | */ 45 | static inline void* acquire_mutex_block(ValueMutex* valuemutex) { 46 | return acquire_mutex(valuemutex, FuriWaitForever); 47 | } 48 | 49 | /** 50 | * With statement for value mutex, acts as lambda 51 | * @param name a resource name, const char* 52 | * @param function_body a (){} lambda declaration, 53 | * executed within you parent function context. 54 | */ 55 | #define with_value_mutex(value_mutex, function_body) \ 56 | { \ 57 | void* p = acquire_mutex_block(value_mutex); \ 58 | furi_check(p); \ 59 | ({ void __fn__ function_body __fn__; })(p); \ 60 | release_mutex(value_mutex, p); \ 61 | } 62 | 63 | /** 64 | * Release mutex after end of work with data. 65 | * Call `release_mutex` and pass ValueData instance and pointer to data. 66 | */ 67 | bool release_mutex(ValueMutex* valuemutex, const void* value); 68 | 69 | /** 70 | * Instead of take-access-give sequence you can use `read_mutex` and `write_mutex` functions. 71 | * Both functions return true in case of success, false otherwise. 72 | */ 73 | bool read_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout); 74 | 75 | bool write_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout); 76 | 77 | inline static bool write_mutex_block(ValueMutex* valuemutex, void* data, size_t len) { 78 | return write_mutex(valuemutex, data, len, FuriWaitForever); 79 | } 80 | 81 | inline static bool read_mutex_block(ValueMutex* valuemutex, void* data, size_t len) { 82 | return read_mutex(valuemutex, data, len, FuriWaitForever); 83 | } 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | 89 | /* 90 | 91 | Usage example 92 | 93 | ```C 94 | // MANIFEST 95 | // name="example-provider-app" 96 | // stack=128 97 | 98 | void provider_app(void* _p) { 99 | // create record with mutex 100 | uint32_t example_value = 0; 101 | ValueMutex example_mutex; 102 | // call `init_mutex`. 103 | if(!init_mutex(&example_mutex, (void*)&example_value, sizeof(uint32_t))) { 104 | printf("critical error\n"); 105 | flapp_exit(NULL); 106 | } 107 | 108 | furi_record_create("provider/example", (void*)&example_mutex); 109 | 110 | // we are ready to provide record to other apps 111 | flapp_ready(); 112 | 113 | // get value and increment it 114 | while(1) { 115 | uint32_t* value = acquire_mutex(&example_mutex, OsWaitForever); 116 | if(value != NULL) { 117 | value++; 118 | } 119 | release_mutex(&example_mutex, value); 120 | 121 | furi_delay_ms(100); 122 | } 123 | } 124 | 125 | // MANIFEST 126 | // name="example-consumer-app" 127 | // stack=128 128 | // require="example-provider-app" 129 | void consumer_app(void* _p) { 130 | // this app run after flapp_ready call in all requirements app 131 | 132 | // open mutex value 133 | ValueMutex* counter_mutex = furi_record_open("provider/example"); 134 | if(counter_mutex == NULL) { 135 | printf("critical error\n"); 136 | flapp_exit(NULL); 137 | } 138 | 139 | // continously read value every 1s 140 | uint32_t counter; 141 | while(1) { 142 | if(read_mutex(counter_mutex, &counter, sizeof(counter), OsWaitForever)) { 143 | printf("counter value: %d\n", counter); 144 | } 145 | 146 | furi_delay_ms(1000); 147 | } 148 | } 149 | ``` 150 | */ 151 | -------------------------------------------------------------------------------- /fapulator/theseus/core/event_flag.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class EventFlagInstance { 5 | private: 6 | mutable std::mutex mutex; 7 | std::condition_variable notifier; 8 | uint32_t flags = 0; 9 | 10 | public: 11 | EventFlagInstance() { 12 | } 13 | 14 | ~EventFlagInstance() { 15 | reset(); 16 | } 17 | 18 | FuriStatus set(uint32_t flags) { 19 | { 20 | std::unique_lock lock(mutex); 21 | this->flags |= flags; 22 | } 23 | notifier.notify_all(); 24 | return FuriStatusOk; 25 | } 26 | 27 | uint32_t clear(uint32_t flags) { 28 | uint32_t flags_before = 0; 29 | { 30 | std::unique_lock lock(mutex); 31 | flags_before = this->flags; 32 | this->flags &= ~flags; 33 | } 34 | notifier.notify_all(); 35 | return flags_before; 36 | } 37 | 38 | FuriStatus wait( 39 | uint32_t flags, 40 | uint32_t* flags_out, 41 | uint32_t timeout, 42 | bool clear_on_exit, 43 | bool wait_all) { 44 | std::unique_lock lock(mutex); 45 | if(wait_all) { 46 | while((this->flags & flags) != flags) { 47 | if(timeout == 0) { 48 | return FuriStatusErrorTimeout; 49 | } 50 | if(timeout == FuriWaitForever) { 51 | notifier.wait(lock); 52 | } else { 53 | if(notifier.wait_for(lock, std::chrono::milliseconds(timeout)) == 54 | std::cv_status::timeout) { 55 | return FuriStatusErrorTimeout; 56 | } 57 | } 58 | } 59 | } else { 60 | while((this->flags & flags) == 0) { 61 | if(timeout == 0) { 62 | return FuriStatusErrorTimeout; 63 | } 64 | if(timeout == FuriWaitForever) { 65 | notifier.wait(lock); 66 | } else { 67 | if(notifier.wait_for(lock, std::chrono::milliseconds(timeout)) == 68 | std::cv_status::timeout) { 69 | return FuriStatusErrorTimeout; 70 | } 71 | } 72 | } 73 | } 74 | 75 | *flags_out = this->flags; 76 | if(clear_on_exit) { 77 | this->flags &= ~flags; 78 | } 79 | return FuriStatusOk; 80 | } 81 | 82 | uint32_t get() { 83 | std::unique_lock lock(mutex); 84 | return flags; 85 | } 86 | 87 | FuriStatus reset() { 88 | { 89 | std::unique_lock lock(mutex); 90 | flags = 0; 91 | } 92 | notifier.notify_all(); 93 | return FuriStatusOk; 94 | } 95 | }; 96 | 97 | FuriEventFlag* furi_event_flag_alloc() { 98 | EventFlagInstance* instance = new EventFlagInstance(); 99 | return reinterpret_cast(instance); 100 | } 101 | 102 | void furi_event_flag_free(FuriEventFlag* event_flag) { 103 | EventFlagInstance* instance = reinterpret_cast(event_flag); 104 | delete instance; 105 | } 106 | 107 | uint32_t furi_event_flag_set(FuriEventFlag* event_flag, uint32_t flags) { 108 | EventFlagInstance* instance = reinterpret_cast(event_flag); 109 | return instance->set(flags); 110 | } 111 | 112 | uint32_t furi_event_flag_clear(FuriEventFlag* event_flag, uint32_t flags) { 113 | EventFlagInstance* instance = reinterpret_cast(event_flag); 114 | return instance->clear(flags); 115 | } 116 | 117 | uint32_t furi_event_flag_get(FuriEventFlag* event_flag) { 118 | EventFlagInstance* instance = reinterpret_cast(event_flag); 119 | return instance->get(); 120 | } 121 | 122 | uint32_t furi_event_flag_wait( 123 | FuriEventFlag* event_flag, 124 | uint32_t flags, 125 | uint32_t options, 126 | uint32_t timeout) { 127 | EventFlagInstance* instance = reinterpret_cast(event_flag); 128 | uint32_t flags_out = 0; 129 | bool wait_all = false; 130 | bool clear_on_exit = true; 131 | 132 | if(options & FuriFlagWaitAll) { 133 | wait_all = true; 134 | } 135 | 136 | if(options & FuriFlagNoClear) { 137 | clear_on_exit = false; 138 | } 139 | 140 | // TODO: return raw flags and move options logic to instance 141 | FuriStatus status = instance->wait(flags, &flags_out, timeout, clear_on_exit, wait_all); 142 | 143 | if(wait_all) { 144 | if((flags & flags_out) != flags) { 145 | if(timeout > 0U) { 146 | flags_out = (uint32_t)FuriStatusErrorTimeout; 147 | } else { 148 | flags_out = (uint32_t)FuriStatusErrorResource; 149 | } 150 | } 151 | } else { 152 | if((flags & flags_out) == 0U) { 153 | if(timeout > 0U) { 154 | flags_out = (uint32_t)FuriStatusErrorTimeout; 155 | } else { 156 | flags_out = (uint32_t)FuriStatusErrorResource; 157 | } 158 | } 159 | } 160 | 161 | return flags_out; 162 | } -------------------------------------------------------------------------------- /app/keypad_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TAG "KeypadTest" 6 | 7 | typedef struct { 8 | 9 | bool press[5]; 10 | uint16_t up; 11 | uint16_t down; 12 | uint16_t left; 13 | uint16_t right; 14 | uint16_t ok; 15 | } KeypadTestState; 16 | 17 | static void keypad_test_reset_state(KeypadTestState* state) { 18 | state->left = 0; 19 | state->right = 0; 20 | state->up = 0; 21 | state->down = 0; 22 | state->ok = 0; 23 | } 24 | 25 | static void keypad_test_render_callback(Canvas* canvas, void* ctx) { 26 | KeypadTestState* state = (KeypadTestState*)acquire_mutex((ValueMutex*)ctx, 25); 27 | canvas_clear(canvas); 28 | char strings[5][20]; 29 | 30 | snprintf(strings[0], 20, "Ok: %d", state->ok); 31 | snprintf(strings[1], 20, "L: %d", state->left); 32 | snprintf(strings[2], 20, "R: %d", state->right); 33 | snprintf(strings[3], 20, "U: %d", state->up); 34 | snprintf(strings[4], 20, "D: %d", state->down); 35 | 36 | canvas_set_font(canvas, FontPrimary); 37 | canvas_draw_str(canvas, 0, 10, "Keypad test"); 38 | 39 | canvas_set_font(canvas, FontSecondary); 40 | canvas_draw_str(canvas, 0, 24, strings[1]); 41 | canvas_draw_str(canvas, 35, 24, strings[2]); 42 | canvas_draw_str(canvas, 0, 36, strings[3]); 43 | canvas_draw_str(canvas, 35, 36, strings[4]); 44 | canvas_draw_str(canvas, 0, 48, strings[0]); 45 | canvas_draw_circle(canvas, 100, 26, 25); 46 | 47 | if(state->press[0]) canvas_draw_disc(canvas, 118, 26, 5); 48 | if(state->press[1]) canvas_draw_disc(canvas, 82, 26, 5); 49 | if(state->press[2]) canvas_draw_disc(canvas, 100, 8, 5); 50 | if(state->press[3]) canvas_draw_disc(canvas, 100, 44, 5); 51 | if(state->press[4]) canvas_draw_disc(canvas, 100, 26, 5); 52 | 53 | canvas_draw_str(canvas, 10, 63, "[back] - reset, hold to exit"); 54 | 55 | release_mutex((ValueMutex*)ctx, state); 56 | } 57 | 58 | static void keypad_test_input_callback(InputEvent* input_event, void* ctx) { 59 | FuriMessageQueue* event_queue = ctx; 60 | furi_message_queue_put(event_queue, input_event, FuriWaitForever); 61 | } 62 | 63 | int32_t keypad_test_app(void* p) { 64 | UNUSED(p); 65 | FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); 66 | furi_check(event_queue); 67 | 68 | KeypadTestState _state = {{false, false, false, false, false}, 0, 0, 0, 0, 0}; 69 | 70 | ValueMutex state_mutex; 71 | if(!init_mutex(&state_mutex, &_state, sizeof(KeypadTestState))) { 72 | FURI_LOG_E(TAG, "cannot create mutex"); 73 | return 0; 74 | } 75 | 76 | ViewPort* view_port = view_port_alloc(); 77 | 78 | view_port_draw_callback_set(view_port, keypad_test_render_callback, &state_mutex); 79 | view_port_input_callback_set(view_port, keypad_test_input_callback, event_queue); 80 | 81 | // Open GUI and register view_port 82 | Gui* gui = furi_record_open(RECORD_GUI); 83 | FURI_LOG_I(TAG, "GUI opened"); 84 | gui_add_view_port(gui, view_port, GuiLayerFullscreen); 85 | 86 | InputEvent event; 87 | while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { 88 | KeypadTestState* state = (KeypadTestState*)acquire_mutex_block(&state_mutex); 89 | FURI_LOG_I( 90 | TAG, 91 | "key: %s type: %s", 92 | input_get_key_name(event.key), 93 | input_get_type_name(event.type)); 94 | 95 | if(event.key == InputKeyRight) { 96 | if(event.type == InputTypePress) { 97 | state->press[0] = true; 98 | } else if(event.type == InputTypeRelease) { 99 | state->press[0] = false; 100 | } else if(event.type == InputTypeShort) { 101 | ++state->right; 102 | } 103 | } else if(event.key == InputKeyLeft) { 104 | if(event.type == InputTypePress) { 105 | state->press[1] = true; 106 | } else if(event.type == InputTypeRelease) { 107 | state->press[1] = false; 108 | } else if(event.type == InputTypeShort) { 109 | ++state->left; 110 | } 111 | } else if(event.key == InputKeyUp) { 112 | if(event.type == InputTypePress) { 113 | state->press[2] = true; 114 | } else if(event.type == InputTypeRelease) { 115 | state->press[2] = false; 116 | } else if(event.type == InputTypeShort) { 117 | ++state->up; 118 | } 119 | } else if(event.key == InputKeyDown) { 120 | if(event.type == InputTypePress) { 121 | state->press[3] = true; 122 | } else if(event.type == InputTypeRelease) { 123 | state->press[3] = false; 124 | } else if(event.type == InputTypeShort) { 125 | ++state->down; 126 | } 127 | } else if(event.key == InputKeyOk) { 128 | if(event.type == InputTypePress) { 129 | state->press[4] = true; 130 | } else if(event.type == InputTypeRelease) { 131 | state->press[4] = false; 132 | } else if(event.type == InputTypeShort) { 133 | ++state->ok; 134 | } 135 | } else if(event.key == InputKeyBack) { 136 | if(event.type == InputTypeLong) { 137 | release_mutex(&state_mutex, state); 138 | break; 139 | } else if(event.type == InputTypeShort) { 140 | keypad_test_reset_state(state); 141 | } 142 | } 143 | 144 | release_mutex(&state_mutex, state); 145 | view_port_update(view_port); 146 | } 147 | 148 | // remove & free all stuff created by app 149 | gui_remove_view_port(gui, view_port); 150 | view_port_free(view_port); 151 | furi_message_queue_free(event_queue); 152 | delete_mutex(&state_mutex); 153 | 154 | furi_record_close(RECORD_GUI); 155 | 156 | return 0; 157 | } 158 | -------------------------------------------------------------------------------- /fapulator/flipper/applications/gui/view_port.c: -------------------------------------------------------------------------------- 1 | #include "view_port_i.h" 2 | 3 | #include 4 | 5 | #include "gui.h" 6 | #include "gui_i.h" 7 | 8 | // TODO add mutex to view_port ops 9 | 10 | _Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count"); 11 | _Static_assert( 12 | (ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 && 13 | ViewPortOrientationVertical == 2 && ViewPortOrientationVerticalFlip == 3), 14 | "Incorrect ViewPortOrientation order"); 15 | _Static_assert(InputKeyMAX == 6, "Incorrect InputKey count"); 16 | _Static_assert( 17 | (InputKeyUp == 0 && InputKeyDown == 1 && InputKeyRight == 2 && InputKeyLeft == 3 && 18 | InputKeyOk == 4 && InputKeyBack == 5), 19 | "Incorrect InputKey order"); 20 | 21 | /** InputKey directional keys mappings for different screen orientations 22 | * 23 | */ 24 | static const InputKey view_port_input_mapping[ViewPortOrientationMAX][InputKeyMAX] = { 25 | {InputKeyUp, 26 | InputKeyDown, 27 | InputKeyRight, 28 | InputKeyLeft, 29 | InputKeyOk, 30 | InputKeyBack}, //ViewPortOrientationHorizontal 31 | {InputKeyDown, 32 | InputKeyUp, 33 | InputKeyLeft, 34 | InputKeyRight, 35 | InputKeyOk, 36 | InputKeyBack}, //ViewPortOrientationHorizontalFlip 37 | {InputKeyRight, 38 | InputKeyLeft, 39 | InputKeyDown, 40 | InputKeyUp, 41 | InputKeyOk, 42 | InputKeyBack}, //ViewPortOrientationVertical 43 | {InputKeyLeft, 44 | InputKeyRight, 45 | InputKeyUp, 46 | InputKeyDown, 47 | InputKeyOk, 48 | InputKeyBack}, //ViewPortOrientationVerticalFlip 49 | }; 50 | 51 | // Remaps directional pad buttons on Flipper based on ViewPort orientation 52 | static void view_port_map_input(InputEvent* event, ViewPortOrientation orientation) { 53 | furi_assert(orientation < ViewPortOrientationMAX && event->key < InputKeyMAX); 54 | event->key = view_port_input_mapping[orientation][event->key]; 55 | } 56 | 57 | static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { 58 | switch(view_port->orientation) { 59 | case ViewPortOrientationHorizontalFlip: 60 | canvas_set_orientation(canvas, CanvasOrientationHorizontalFlip); 61 | break; 62 | case ViewPortOrientationVertical: 63 | canvas_set_orientation(canvas, CanvasOrientationVertical); 64 | break; 65 | case ViewPortOrientationVerticalFlip: 66 | canvas_set_orientation(canvas, CanvasOrientationVerticalFlip); 67 | break; 68 | default: 69 | canvas_set_orientation(canvas, CanvasOrientationHorizontal); 70 | break; 71 | }; 72 | } 73 | 74 | ViewPort* view_port_alloc() { 75 | ViewPort* view_port = malloc(sizeof(ViewPort)); 76 | memset(view_port, 0, sizeof(ViewPort)); 77 | 78 | view_port->orientation = ViewPortOrientationHorizontal; 79 | view_port->is_enabled = true; 80 | return view_port; 81 | } 82 | 83 | void view_port_free(ViewPort* view_port) { 84 | furi_assert(view_port); 85 | furi_check(view_port->gui == NULL); 86 | free(view_port); 87 | } 88 | 89 | void view_port_set_width(ViewPort* view_port, uint8_t width) { 90 | furi_assert(view_port); 91 | view_port->width = width; 92 | } 93 | 94 | uint8_t view_port_get_width(ViewPort* view_port) { 95 | furi_assert(view_port); 96 | return view_port->width; 97 | } 98 | 99 | void view_port_set_height(ViewPort* view_port, uint8_t height) { 100 | furi_assert(view_port); 101 | view_port->height = height; 102 | } 103 | 104 | uint8_t view_port_get_height(ViewPort* view_port) { 105 | furi_assert(view_port); 106 | return view_port->height; 107 | } 108 | 109 | void view_port_enabled_set(ViewPort* view_port, bool enabled) { 110 | furi_assert(view_port); 111 | if(view_port->is_enabled != enabled) { 112 | view_port->is_enabled = enabled; 113 | if(view_port->gui) gui_update(view_port->gui); 114 | } 115 | } 116 | 117 | bool view_port_is_enabled(ViewPort* view_port) { 118 | furi_assert(view_port); 119 | return view_port->is_enabled; 120 | } 121 | 122 | void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) { 123 | furi_assert(view_port); 124 | view_port->draw_callback = callback; 125 | view_port->draw_callback_context = context; 126 | } 127 | 128 | void view_port_input_callback_set( 129 | ViewPort* view_port, 130 | ViewPortInputCallback callback, 131 | void* context) { 132 | furi_assert(view_port); 133 | view_port->input_callback = callback; 134 | view_port->input_callback_context = context; 135 | } 136 | 137 | void view_port_update(ViewPort* view_port) { 138 | furi_assert(view_port); 139 | if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui); 140 | } 141 | 142 | void view_port_gui_set(ViewPort* view_port, Gui* gui) { 143 | furi_assert(view_port); 144 | view_port->gui = gui; 145 | } 146 | 147 | void view_port_draw(ViewPort* view_port, Canvas* canvas) { 148 | furi_assert(view_port); 149 | furi_assert(canvas); 150 | furi_check(view_port->gui); 151 | 152 | if(view_port->draw_callback) { 153 | view_port_setup_canvas_orientation(view_port, canvas); 154 | view_port->draw_callback(canvas, view_port->draw_callback_context); 155 | } 156 | } 157 | 158 | void view_port_input(ViewPort* view_port, InputEvent* event) { 159 | furi_assert(view_port); 160 | furi_assert(event); 161 | furi_check(view_port->gui); 162 | 163 | if(view_port->input_callback) { 164 | ViewPortOrientation orientation = view_port_get_orientation(view_port); 165 | view_port_map_input(event, orientation); 166 | view_port->input_callback(event, view_port->input_callback_context); 167 | } 168 | } 169 | 170 | void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { 171 | furi_assert(view_port); 172 | view_port->orientation = orientation; 173 | } 174 | 175 | ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) { 176 | return view_port->orientation; 177 | } 178 | -------------------------------------------------------------------------------- /fapulator/theseus/applications/gui/font/u8g2_font_render.c: -------------------------------------------------------------------------------- 1 | /* 2 | * u8g2_font_render.c 3 | * 4 | * Created on: Nov 27, 2020 5 | * Author: quard 6 | */ 7 | #include "u8g2_font_render.h" 8 | 9 | uint8_t font_get_unsigned_bits(U8G2FontGlyph_t* glyph, uint8_t count); 10 | int8_t font_get_signed_bits(U8G2FontGlyph_t* glyph, uint8_t count); 11 | uint16_t font_get_start_symbol_search_postition(U8G2FontRender_t* font, char chr); 12 | int8_t font_get_glyph( 13 | U8G2FontRender_t* font, 14 | U8G2FontGlyph_t* glyph, 15 | uint16_t search_position, 16 | char chr); 17 | void font_parse_glyph_header(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph); 18 | uint8_t font_draw_start_x_position(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph); 19 | uint8_t font_draw_start_y_position(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph); 20 | void font_render_glyph(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph, uint8_t x, uint8_t y); 21 | 22 | U8G2FontRender_t U8G2FontRender( 23 | const uint8_t* data, 24 | fnDrawPixel drawFgPixel, 25 | fnDrawPixel drawBgPixel, 26 | void* context) { 27 | U8G2FontRender_t font = { 28 | .data = data, 29 | .drawFgPixel = drawFgPixel, 30 | .drawBgPixel = drawBgPixel, 31 | .context = context, 32 | }; 33 | 34 | font.header = U8G2FontRender_ParseHeader(&font); 35 | 36 | return font; 37 | } 38 | 39 | U8G2FontHeader_t U8G2FontRender_ParseHeader(U8G2FontRender_t* font) { 40 | U8G2FontHeader_t header; 41 | 42 | memcpy(&header, font->data, U8G2_FONT_HEADER_SIZE); 43 | header.offset_A = U8G2_FONT_HEADER_SIZE + (font->data[17] << 8 | font->data[18]); 44 | header.offset_a = U8G2_FONT_HEADER_SIZE + (font->data[19] << 8 | font->data[20]); 45 | header.offset_0x100 = U8G2_FONT_HEADER_SIZE + (font->data[21] << 8 | font->data[22]); 46 | 47 | return header; 48 | } 49 | 50 | void U8G2FontRender_PrintChar(U8G2FontRender_t* font, uint8_t* x, uint8_t y, char chr) { 51 | uint16_t search_position = font_get_start_symbol_search_postition(font, chr); 52 | 53 | U8G2FontGlyph_t glyph; 54 | if(font_get_glyph(font, &glyph, search_position, chr) != U8G2FontRender_OK) { 55 | return; 56 | } 57 | font_parse_glyph_header(font, &glyph); 58 | 59 | font_render_glyph(font, &glyph, *x, y); 60 | 61 | *x += glyph.pitch; 62 | } 63 | 64 | void U8G2FontRender_Print(U8G2FontRender_t* font, uint8_t x, uint8_t y, const char* str) { 65 | while(*str) { 66 | const char* chr = str++; 67 | // if(*chr < 0x100) { 68 | U8G2FontRender_PrintChar(font, &x, y, *chr); 69 | // } 70 | } 71 | } 72 | 73 | uint8_t font_get_unsigned_bits(U8G2FontGlyph_t* glyph, uint8_t count) { 74 | uint8_t val; 75 | uint8_t start = glyph->bit_pos; 76 | uint8_t end = start + count; 77 | 78 | val = pgm_read(glyph->data); 79 | val >>= start; 80 | 81 | if(end >= 8) { 82 | uint8_t cnt = 8 - start; 83 | glyph->data++; 84 | 85 | val |= pgm_read(glyph->data) << (cnt); 86 | 87 | end -= 8; 88 | } 89 | 90 | glyph->bit_pos = end; 91 | 92 | val &= (1U << count) - 1; 93 | 94 | return val; 95 | } 96 | 97 | int8_t font_get_signed_bits(U8G2FontGlyph_t* glyph, uint8_t count) { 98 | int8_t val = (int8_t)font_get_unsigned_bits(glyph, count); 99 | val -= 1 << (count - 1); 100 | 101 | return val; 102 | } 103 | 104 | uint16_t font_get_start_symbol_search_postition(U8G2FontRender_t* font, char chr) { 105 | uint16_t search_position = U8G2_FONT_HEADER_SIZE; 106 | if(chr >= 65 && chr <= 90) { 107 | search_position = font->header.offset_A; 108 | } else if(chr >= 97 && chr <= 122) { 109 | search_position = font->header.offset_a; 110 | } 111 | 112 | return search_position; 113 | } 114 | 115 | int8_t font_get_glyph( 116 | U8G2FontRender_t* font, 117 | U8G2FontGlyph_t* glyph, 118 | uint16_t search_position, 119 | char chr) { 120 | while(1) { 121 | memcpy(glyph, font->data + search_position, 2); 122 | if(glyph->character == chr) { 123 | glyph->data = font->data + search_position + 2; 124 | glyph->bit_pos = 0; 125 | 126 | return U8G2FontRender_OK; 127 | } 128 | 129 | search_position += glyph->next_glypth; 130 | if(glyph->next_glypth == 0) { 131 | break; 132 | } 133 | } 134 | 135 | return U8G2FontRender_ERR; 136 | } 137 | 138 | void font_parse_glyph_header(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph) { 139 | glyph->width = font_get_unsigned_bits(glyph, font->header.glyph_width); 140 | glyph->height = font_get_unsigned_bits(glyph, font->header.glyph_height); 141 | glyph->x_offset = font_get_signed_bits(glyph, font->header.glyph_x_offset); 142 | glyph->y_offset = font_get_signed_bits(glyph, font->header.glyph_y_offset); 143 | glyph->pitch = font_get_signed_bits(glyph, font->header.glyph_pitch); 144 | } 145 | 146 | uint8_t font_draw_start_x_position(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph) { 147 | return glyph->x_offset; 148 | } 149 | 150 | uint8_t font_draw_start_y_position(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph) { 151 | return -glyph->height - glyph->y_offset; 152 | } 153 | 154 | void font_render_glyph(U8G2FontRender_t* font, U8G2FontGlyph_t* glyph, uint8_t x, uint8_t y) { 155 | uint32_t pixels = 0; 156 | uint8_t y_pos = y + font_draw_start_y_position(font, glyph); 157 | uint8_t x_pos = x + font_draw_start_x_position(font, glyph); 158 | while(1) { 159 | uint8_t zeros = font_get_unsigned_bits(glyph, font->header.zero_bit_width); 160 | uint8_t ones = font_get_unsigned_bits(glyph, font->header.one_bit_width); 161 | int8_t repeat = 0; 162 | 163 | while(font_get_unsigned_bits(glyph, 1) == 1) { 164 | repeat++; 165 | } 166 | 167 | for(; repeat >= 0; repeat--) { 168 | for(uint8_t i = 0; i < zeros + ones; i++) { 169 | if(i <= zeros - 1) { 170 | font->drawBgPixel(x_pos, y_pos, font->context); 171 | } else { 172 | font->drawFgPixel(x_pos, y_pos, font->context); 173 | } 174 | x_pos++; 175 | 176 | pixels++; 177 | if(pixels % glyph->width == 0) { 178 | y_pos++; 179 | x_pos = x + font_draw_start_x_position(font, glyph); 180 | } 181 | } 182 | } 183 | 184 | if(pixels >= glyph->width * glyph->height) { 185 | break; 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /fapulator/flipper/lib/mlib/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2021, Patrick Pelissier 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # + Redistributions of source code must retain the above copyright 7 | # notice, this list of conditions and the following disclaimer. 8 | # + Redistributions in binary form must reproduce the above copyright 9 | # notice, this list of conditions and the following disclaimer in the 10 | # documentation and/or other materials provided with the distribution. 11 | # 12 | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 13 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | # DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 16 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | # Environnement variable. To customize if needed. 24 | RM=rm -f 25 | MKDIR=mkdir -p 26 | RMDIR=rmdir 27 | INSTALL=install 28 | INSTALL_PROGRAM=${INSTALL} 29 | INSTALL_DATA=${INSTALL} -m 644 30 | 31 | # Installation directory 32 | PREFIX=/usr/local 33 | DESTDIR= 34 | 35 | # Package name & version. 36 | PACKAGE=m_lib-$(VERSION) 37 | VERSION=0.6.0 38 | 39 | # Define the contain of the distribution tarball 40 | HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tuple.h m-variant.h m-worker.h 41 | DOC1=LICENSE README.md 42 | DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png 43 | EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c 44 | TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcmempool.c tests/test-mconcurrent.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mfuncobj.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmempool.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-mshared.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-shared.c tests/tgen-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/test-mishared.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp 45 | 46 | .PHONY: all test check doc clean distclean depend install uninstall dist 47 | 48 | all: 49 | @echo "Nothing to be done." 50 | 51 | test: check 52 | 53 | check: 54 | cd tests && $(MAKE) check 55 | cd example && $(MAKE) all 56 | 57 | checkall: 58 | cd tests && $(MAKE) checkall 59 | cd example && $(MAKE) all 60 | 61 | html: README.md 62 | markdown < README.md > README.html 63 | 64 | doc: html doc/depend.png 65 | 66 | clean: 67 | cd tests && $(MAKE) clean 68 | cd example && $(MAKE) clean 69 | cd bench && $(MAKE) clean 70 | $(RM) README.html depend.dot 71 | $(RM) -r 'm*lib-$(VERSION)' 72 | $(RM) 'm*lib-$(VERSION).tar.bz2' 73 | 74 | distclean: clean 75 | cd tests && $(MAKE) distclean 76 | 77 | depend: 78 | cd tests && $(MAKE) depend 79 | 80 | doc/depend.png: $(HEADER) 81 | (echo "digraph g { " ; for i in *.h ; do list=$$(grep "include \"" $$i |cut -f2 -d\") ; for j in $$list ; do echo "\"$$i\" -> \"$$j\" ;" ; done ; done ; echo "}" )> depend.dot 82 | dot -Tpng depend.dot -o doc/depend.png 83 | optipng -o7 doc/depend.png 84 | 85 | dist: $(HEADER) $(DOC1) $(DOC2) $(EXAMPLE) $(TEST) Makefile clean versioncheck 86 | $(MKDIR) '$(PACKAGE)' 87 | $(MKDIR) '$(PACKAGE)/doc' 88 | $(MKDIR) '$(PACKAGE)/tests' 89 | $(MKDIR) '$(PACKAGE)/example' 90 | cp $(HEADER) $(DOC1) Makefile '$(PACKAGE)' 91 | cp $(EXAMPLE) '$(PACKAGE)/example' 92 | cp $(TEST) '$(PACKAGE)/tests' 93 | cp $(DOC2) '$(PACKAGE)/doc' 94 | cd '$(PACKAGE)/tests' && $(MAKE) depend 95 | tar jcf '$(PACKAGE).tar.bz2' '$(PACKAGE)' 96 | 97 | distcheck: dist 98 | cd '$(PACKAGE)' && make check 99 | 100 | versioncheck: 101 | @if test "$(VERSION)." != `grep M_CORE_VERSION m-core.h |awk '{printf "%d.",$$3 } END {printf "\n"}'` ; then echo "ERROR: Version mismatch between Makefile & HEADERS" ; exit 2 ; fi 102 | @for i in *.h ; do echo $(HEADER) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing header $$i in Makefile" ; exit 3 ; fi ; done 103 | @for i in doc/* ; do echo $(DOC2) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing document $$i in Makefile" ; exit 4 ; fi ; done 104 | @for i in tests/*.c tests/*.txt tests/*.h ; do echo $(TEST) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing test $$i in Makefile" ; exit 5 ; fi ; done 105 | @for i in example/*.c example/*.json ; do echo $(EXAMPLE) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing example $$i in Makefile" ; exit 6 ; fi ; done 106 | @for i in tests/*.cpp tests/*.hpp ; do echo $(TEST) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing test $$i in Makefile" ; exit 7 ; fi ; done 107 | 108 | install: 109 | $(MKDIR) $(DESTDIR)$(PREFIX)/include/m-lib 110 | $(INSTALL_DATA) $(HEADER) $(DESTDIR)$(PREFIX)/include/m-lib 111 | 112 | uninstall: 113 | for i in $(HEADER) ; do $(RM) $(DESTDIR)$(PREFIX)/include/m-lib/$$i ; done 114 | $(RMDIR) $(DESTDIR)$(PREFIX)/include/m-lib/ 115 | 116 | format: 117 | cd example && $(MAKE) ex-string01.exe 118 | for i in $(HEADER) ; do ./example/ex-string01.exe $$i > tmp.h && mv tmp.h $$i ; done 119 | -------------------------------------------------------------------------------- /fapulator/flipper/furi/core/thread.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file thread.h 3 | * Furi: Furi Thread API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "base.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** FuriThreadState */ 15 | typedef enum { 16 | FuriThreadStateStopped, 17 | FuriThreadStateStarting, 18 | FuriThreadStateRunning, 19 | } FuriThreadState; 20 | 21 | /** FuriThreadPriority */ 22 | typedef enum { 23 | FuriThreadPriorityNone = 0, /**< Uninitialized, choose system default */ 24 | FuriThreadPriorityIdle = 1, /**< Idle priority */ 25 | FuriThreadPriorityLowest = 14, /**< Lowest */ 26 | FuriThreadPriorityLow = 15, /**< Low */ 27 | FuriThreadPriorityNormal = 16, /**< Normal */ 28 | FuriThreadPriorityHigh = 17, /**< High */ 29 | FuriThreadPriorityHighest = 18, /**< Highest */ 30 | FuriThreadPriorityIsr = 32, /**< Deffered Isr (highest possible) */ 31 | } FuriThreadPriority; 32 | 33 | /** FuriThread anonymous structure */ 34 | typedef struct FuriThread FuriThread; 35 | 36 | /** FuriThreadId proxy type to OS low level functions */ 37 | typedef void* FuriThreadId; 38 | 39 | /** FuriThreadCallback Your callback to run in new thread 40 | * @warning never use osThreadExit in FuriThread 41 | */ 42 | typedef int32_t (*FuriThreadCallback)(void* context); 43 | 44 | /** Write to stdout callback 45 | * @param data pointer to data 46 | * @param size data size @warning your handler must consume everything 47 | */ 48 | typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); 49 | 50 | /** FuriThread state change calback called upon thread state change 51 | * @param state new thread state 52 | * @param context callback context 53 | */ 54 | typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context); 55 | 56 | /** Allocate FuriThread 57 | * 58 | * @return FuriThread instance 59 | */ 60 | FuriThread* furi_thread_alloc(); 61 | 62 | /** Release FuriThread 63 | * 64 | * @param thread FuriThread instance 65 | */ 66 | void furi_thread_free(FuriThread* thread); 67 | 68 | /** Set FuriThread name 69 | * 70 | * @param thread FuriThread instance 71 | * @param name string 72 | */ 73 | void furi_thread_set_name(FuriThread* thread, const char* name); 74 | 75 | /** Mark thread as service 76 | * The service cannot be stopped or removed, and cannot exit from the thread body 77 | * 78 | * @param thread 79 | */ 80 | void furi_thread_mark_as_service(FuriThread* thread); 81 | 82 | /** Set FuriThread stack size 83 | * 84 | * @param thread FuriThread instance 85 | * @param stack_size stack size in bytes 86 | */ 87 | void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size); 88 | 89 | /** Set FuriThread callback 90 | * 91 | * @param thread FuriThread instance 92 | * @param callback FuriThreadCallback, called upon thread run 93 | */ 94 | void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback); 95 | 96 | /** Set FuriThread context 97 | * 98 | * @param thread FuriThread instance 99 | * @param context pointer to context for thread callback 100 | */ 101 | void furi_thread_set_context(FuriThread* thread, void* context); 102 | 103 | /** Set FuriThread priority 104 | * 105 | * @param thread FuriThread instance 106 | * @param priority FuriThreadPriority value 107 | */ 108 | void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); 109 | 110 | /** Set FuriThread state change callback 111 | * 112 | * @param thread FuriThread instance 113 | * @param callback state change callback 114 | */ 115 | void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback); 116 | 117 | /** Set FuriThread state change context 118 | * 119 | * @param thread FuriThread instance 120 | * @param context pointer to context 121 | */ 122 | void furi_thread_set_state_context(FuriThread* thread, void* context); 123 | 124 | /** Get FuriThread state 125 | * 126 | * @param thread FuriThread instance 127 | * 128 | * @return thread state from FuriThreadState 129 | */ 130 | FuriThreadState furi_thread_get_state(FuriThread* thread); 131 | 132 | /** Start FuriThread 133 | * 134 | * @param thread FuriThread instance 135 | */ 136 | void furi_thread_start(FuriThread* thread); 137 | 138 | /** Join FuriThread 139 | * 140 | * @param thread FuriThread instance 141 | * 142 | * @return bool 143 | */ 144 | bool furi_thread_join(FuriThread* thread); 145 | 146 | /** Get FreeRTOS FuriThreadId for FuriThread instance 147 | * 148 | * @param thread FuriThread instance 149 | * 150 | * @return FuriThreadId or NULL 151 | */ 152 | FuriThreadId furi_thread_get_id(FuriThread* thread); 153 | 154 | /** Enable heap tracing 155 | * 156 | * @param thread FuriThread instance 157 | */ 158 | void furi_thread_enable_heap_trace(FuriThread* thread); 159 | 160 | /** Disable heap tracing 161 | * 162 | * @param thread FuriThread instance 163 | */ 164 | void furi_thread_disable_heap_trace(FuriThread* thread); 165 | 166 | /** Get thread heap size 167 | * 168 | * @param thread FuriThread instance 169 | * 170 | * @return size in bytes 171 | */ 172 | size_t furi_thread_get_heap_size(FuriThread* thread); 173 | 174 | /** Get thread return code 175 | * 176 | * @param thread FuriThread instance 177 | * 178 | * @return return code 179 | */ 180 | int32_t furi_thread_get_return_code(FuriThread* thread); 181 | 182 | /** Thread releated methods that doesn't involve FuriThread directly */ 183 | 184 | /** Get FreeRTOS FuriThreadId for current thread 185 | * 186 | * @param thread FuriThread instance 187 | * 188 | * @return FuriThreadId or NULL 189 | */ 190 | FuriThreadId furi_thread_get_current_id(); 191 | 192 | /** Get FuriThread instance for current thread 193 | * 194 | * @return FuriThread* 195 | */ 196 | FuriThread* furi_thread_get_current(); 197 | 198 | /** Return control to scheduler */ 199 | void furi_thread_yield(); 200 | 201 | uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags); 202 | 203 | uint32_t furi_thread_flags_clear(uint32_t flags); 204 | 205 | uint32_t furi_thread_flags_get(void); 206 | 207 | uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); 208 | 209 | uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_items); 210 | 211 | const char* furi_thread_get_name(FuriThreadId thread_id); 212 | 213 | uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); 214 | 215 | /** Set STDOUT callback for thread 216 | * 217 | * @param callback callback or NULL to clear 218 | * 219 | * @return true on success, otherwise fail 220 | */ 221 | bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); 222 | 223 | /** Write data to buffered STDOUT 224 | * 225 | * @param data input data 226 | * @param size input data size 227 | * 228 | * @return size_t written data size 229 | */ 230 | size_t furi_thread_stdout_write(const char* data, size_t size); 231 | 232 | /** Flush data to STDOUT 233 | * 234 | * @return int32_t error code 235 | */ 236 | int32_t furi_thread_stdout_flush(); 237 | 238 | /** Suspend thread 239 | * 240 | * @param thread_id thread id 241 | */ 242 | void furi_thread_suspend(FuriThreadId thread_id); 243 | 244 | /** Resume thread 245 | * 246 | * @param thread_id thread id 247 | */ 248 | void furi_thread_resume(FuriThreadId thread_id); 249 | 250 | /** Get thread suspended state 251 | * 252 | * @param thread_id thread id 253 | * @return true if thread is suspended 254 | */ 255 | bool furi_thread_is_suspended(FuriThreadId thread_id); 256 | 257 | #ifdef __cplusplus 258 | } 259 | #endif 260 | -------------------------------------------------------------------------------- /fapulator/flipper/applications/gui/canvas.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file canvas.h 3 | * GUI: Canvas API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | /** Color enumeration */ 16 | typedef enum { 17 | ColorWhite = 0x00, 18 | ColorBlack = 0x01, 19 | } Color; 20 | 21 | /** Fonts enumeration */ 22 | typedef enum { 23 | FontPrimary, 24 | FontSecondary, 25 | FontKeyboard, 26 | FontBigNumbers, 27 | 28 | // Keep last for fonts number calculation 29 | FontTotalNumber, 30 | } Font; 31 | 32 | /** Alignment enumeration */ 33 | typedef enum { 34 | AlignLeft, 35 | AlignRight, 36 | AlignTop, 37 | AlignBottom, 38 | AlignCenter, 39 | } Align; 40 | 41 | /** Canvas Orientation */ 42 | typedef enum { 43 | CanvasOrientationHorizontal, 44 | CanvasOrientationHorizontalFlip, 45 | CanvasOrientationVertical, 46 | CanvasOrientationVerticalFlip, 47 | } CanvasOrientation; 48 | 49 | /** Font Direction */ 50 | typedef enum { 51 | CanvasDirectionLeftToRight, 52 | CanvasDirectionTopToBottom, 53 | CanvasDirectionRightToLeft, 54 | CanvasDirectionBottomToTop, 55 | } CanvasDirection; 56 | 57 | /** Font parameters */ 58 | typedef struct { 59 | uint8_t leading_default; 60 | uint8_t leading_min; 61 | uint8_t height; 62 | uint8_t descender; 63 | } CanvasFontParameters; 64 | 65 | /** Canvas anonymous structure */ 66 | typedef struct Canvas Canvas; 67 | 68 | /** Get Canvas width 69 | * 70 | * @param canvas Canvas instance 71 | * 72 | * @return width in pixels. 73 | */ 74 | uint8_t canvas_width(Canvas* canvas); 75 | 76 | /** Get Canvas height 77 | * 78 | * @param canvas Canvas instance 79 | * 80 | * @return height in pixels. 81 | */ 82 | uint8_t canvas_height(Canvas* canvas); 83 | 84 | /** Get current font height 85 | * 86 | * @param canvas Canvas instance 87 | * 88 | * @return height in pixels. 89 | */ 90 | uint8_t canvas_current_font_height(Canvas* canvas); 91 | 92 | /** Get font parameters 93 | * 94 | * @param canvas Canvas instance 95 | * @param font Font 96 | * 97 | * @return pointer to CanvasFontParameters structure 98 | */ 99 | CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font); 100 | 101 | /** Clear canvas 102 | * 103 | * @param canvas Canvas instance 104 | */ 105 | void canvas_clear(Canvas* canvas); 106 | 107 | /** Set drawing color 108 | * 109 | * @param canvas Canvas instance 110 | * @param color Color 111 | */ 112 | void canvas_set_color(Canvas* canvas, Color color); 113 | 114 | /** Set font swap 115 | * Argument String Rotation Description 116 | * 117 | * @param canvas Canvas instance 118 | * @param dir Direction font 119 | */ 120 | void canvas_set_font_direction(Canvas* canvas, CanvasDirection dir); 121 | 122 | /** Invert drawing color 123 | * 124 | * @param canvas Canvas instance 125 | */ 126 | void canvas_invert_color(Canvas* canvas); 127 | 128 | /** Set drawing font 129 | * 130 | * @param canvas Canvas instance 131 | * @param font Font 132 | */ 133 | void canvas_set_font(Canvas* canvas, Font font); 134 | 135 | /** Draw string at position of baseline defined by x, y. 136 | * 137 | * @param canvas Canvas instance 138 | * @param x anchor point x coordinate 139 | * @param y anchor point y coordinate 140 | * @param str C-string 141 | */ 142 | void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); 143 | 144 | /** Draw aligned string defined by x, y. 145 | * 146 | * Align calculated from position of baseline, string width and ascent (height 147 | * of the glyphs above the baseline) 148 | * 149 | * @param canvas Canvas instance 150 | * @param x anchor point x coordinate 151 | * @param y anchor point y coordinate 152 | * @param horizontal horizontal alignment 153 | * @param vertical vertical alignment 154 | * @param str C-string 155 | */ 156 | void canvas_draw_str_aligned( 157 | Canvas* canvas, 158 | uint8_t x, 159 | uint8_t y, 160 | Align horizontal, 161 | Align vertical, 162 | const char* str); 163 | 164 | /** Get string width 165 | * 166 | * @param canvas Canvas instance 167 | * @param str C-string 168 | * 169 | * @return width in pixels. 170 | */ 171 | uint16_t canvas_string_width(Canvas* canvas, const char* str); 172 | 173 | /** Get glyph width 174 | * 175 | * @param canvas Canvas instance 176 | * @param[in] symbol character 177 | * 178 | * @return width in pixels 179 | */ 180 | uint8_t canvas_glyph_width(Canvas* canvas, char symbol); 181 | 182 | /** Draw bitmap picture at position defined by x,y. 183 | * 184 | * @param canvas Canvas instance 185 | * @param x x coordinate 186 | * @param y y coordinate 187 | * @param width width of bitmap 188 | * @param height height of bitmap 189 | * @param compressed_bitmap_data compressed bitmap data 190 | */ 191 | void canvas_draw_bitmap( 192 | Canvas* canvas, 193 | uint8_t x, 194 | uint8_t y, 195 | uint8_t width, 196 | uint8_t height, 197 | const uint8_t* compressed_bitmap_data); 198 | 199 | /** Draw XBM bitmap 200 | * 201 | * @param canvas Canvas instance 202 | * @param x x coordinate 203 | * @param y y coordinate 204 | * @param w bitmap width 205 | * @param h bitmap height 206 | * @param bitmap pointer to XBM bitmap data 207 | */ 208 | void canvas_draw_xbm( 209 | Canvas* canvas, 210 | uint8_t x, 211 | uint8_t y, 212 | uint8_t w, 213 | uint8_t h, 214 | const uint8_t* bitmap); 215 | 216 | /** Draw dot at x,y 217 | * 218 | * @param canvas Canvas instance 219 | * @param x x coordinate 220 | * @param y y coordinate 221 | */ 222 | void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y); 223 | 224 | /** Draw box of width, height at x,y 225 | * 226 | * @param canvas Canvas instance 227 | * @param x x coordinate 228 | * @param y y coordinate 229 | * @param width box width 230 | * @param height box height 231 | */ 232 | void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); 233 | 234 | /** Draw frame of width, height at x,y 235 | * 236 | * @param canvas Canvas instance 237 | * @param x x coordinate 238 | * @param y y coordinate 239 | * @param width frame width 240 | * @param height frame height 241 | */ 242 | void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); 243 | 244 | /** Draw line from x1,y1 to x2,y2 245 | * 246 | * @param canvas Canvas instance 247 | * @param x1 x1 coordinate 248 | * @param y1 y1 coordinate 249 | * @param x2 x2 coordinate 250 | * @param y2 y2 coordinate 251 | */ 252 | void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); 253 | 254 | /** Draw circle at x,y with radius r 255 | * 256 | * @param canvas Canvas instance 257 | * @param x x coordinate 258 | * @param y y coordinate 259 | * @param r radius 260 | */ 261 | void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); 262 | 263 | /** Draw disc at x,y with radius r 264 | * 265 | * @param canvas Canvas instance 266 | * @param x x coordinate 267 | * @param y y coordinate 268 | * @param r radius 269 | */ 270 | void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); 271 | 272 | /** Draw triangle with given base and height lengths and their intersection coordinate 273 | * 274 | * @param canvas Canvas instance 275 | * @param x x coordinate of base and height intersection 276 | * @param y y coordinate of base and height intersection 277 | * @param base length of triangle side 278 | * @param height length of triangle height 279 | * @param dir CanvasDirection triangle orientation 280 | */ 281 | void canvas_draw_triangle( 282 | Canvas* canvas, 283 | uint8_t x, 284 | uint8_t y, 285 | uint8_t base, 286 | uint8_t height, 287 | CanvasDirection dir); 288 | 289 | /** Draw glyph 290 | * 291 | * @param canvas Canvas instance 292 | * @param x x coordinate 293 | * @param y y coordinate 294 | * @param ch character 295 | */ 296 | void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch); 297 | 298 | /** Set transparency mode 299 | * 300 | * @param canvas Canvas instance 301 | * @param alpha transparency mode 302 | */ 303 | void canvas_set_bitmap_mode(Canvas* canvas, bool alpha); 304 | 305 | /** Draw rounded-corner frame of width, height at x,y, with round value radius 306 | * 307 | * @param canvas Canvas instance 308 | * @param x x coordinate 309 | * @param y y coordinate 310 | * @param width frame width 311 | * @param height frame height 312 | * @param radius frame corner radius 313 | */ 314 | void canvas_draw_rframe( 315 | Canvas* canvas, 316 | uint8_t x, 317 | uint8_t y, 318 | uint8_t width, 319 | uint8_t height, 320 | uint8_t radius); 321 | 322 | /** Draw rounded-corner box of width, height at x,y, with round value raduis 323 | * 324 | * @param canvas Canvas instance 325 | * @param x x coordinate 326 | * @param y y coordinate 327 | * @param width box width 328 | * @param height box height 329 | * @param radius box corner radius 330 | */ 331 | void canvas_draw_rbox( 332 | Canvas* canvas, 333 | uint8_t x, 334 | uint8_t y, 335 | uint8_t width, 336 | uint8_t height, 337 | uint8_t radius); 338 | 339 | #ifdef __cplusplus 340 | } 341 | #endif 342 | -------------------------------------------------------------------------------- /fapulator/flipper/lib/mlib/m-genint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * M*LIB - Integer Generator (GENINT) module 3 | * 4 | * Copyright (c) 2017-2021, Patrick Pelissier 5 | * All rights reserved. 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * + Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * + Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | #ifndef MSTARLIB_GENINT_H 26 | #define MSTARLIB_GENINT_H 27 | 28 | #include "m-core.h" 29 | #include "m-atomic.h" 30 | 31 | M_BEGIN_PROTECTED_CODE 32 | 33 | /* GENINT is an internal container providing unique integers. 34 | It has the following properties: 35 | - it stores integer from [0..N) (N is fixed). 36 | - an integer can have only one occurrence in the container. 37 | - you can atomically push in / pop out integer from this container 38 | provided that it is not already in the container. 39 | - there are no order (like FIFO or stack) 40 | 41 | This can be used to map integers to index of resources in a table. 42 | At most we can support N = 32*64 = 2048 with the master limb usage. 43 | For the typical usage of this container 44 | (mapping hardware or software limited resources), this should be 45 | enough. 46 | */ 47 | 48 | // TO DO: We may want a specialization with constant N value. 49 | 50 | // Define the limb size used by genint 51 | typedef unsigned long long genint_limb_ct; 52 | 53 | /* Define a generator of unique integer (Lock Free) */ 54 | typedef struct genint_s { 55 | unsigned int n; // size of the container 56 | unsigned int max; // number of allocated limb - 1 57 | genint_limb_ct mask0; // mask of the last limb (constant) 58 | genint_limb_ct mask_master; // mask of the master limb that controls others (constant) 59 | atomic_ullong master; // master bitfield (which informs if a limb is full or not) 60 | atomic_ullong *data; // the bitfield which informs if an integer is used or not 61 | } genint_t[1]; 62 | 63 | // Define the max absolute supported value. It should be 2048 on most implementations. 64 | #define GENINT_MAX_ALLOC (M_GEN1NT_LIMBSIZE * (M_GEN1NT_LIMBSIZE - M_GEN1NT_ABA_CPT)) 65 | 66 | // Define the size of a limb in bits. 67 | #define M_GEN1NT_LIMBSIZE ((unsigned)(sizeof(genint_limb_ct) * CHAR_BIT)) 68 | 69 | // Define the contract of a genint 70 | #define M_GEN1NT_CONTRACT(s) do { \ 71 | M_ASSERT (s != NULL); \ 72 | M_ASSERT (s->n > 0 && s->n <= GENINT_MAX_ALLOC); \ 73 | M_ASSERT ((s->max+1) * M_GEN1NT_LIMBSIZE >= s->n); \ 74 | M_ASSERT (s->data != NULL); \ 75 | } while (0) 76 | 77 | // Define the limb one 78 | #define M_GEN1NT_ONE ((genint_limb_ct)1) 79 | 80 | #define M_GEN1NT_FULL_MASK ULLONG_MAX 81 | 82 | // Value returned in case of error (not integer available). 83 | #define GENINT_ERROR (UINT_MAX) 84 | 85 | /* 32 bits of the master mask are kept for handling the ABA problem. 86 | * NOTE: May be too much. 16 bits should be more than enough. TBC 87 | */ 88 | #define M_GEN1NT_ABA_CPT 32 89 | #define M_GEN1NT_ABA_CPT_T uint32_t 90 | 91 | // Set the bit 'i' of the master limb, and increase ABA counter. 92 | #define M_GEN1NT_MASTER_SET(master, i) \ 93 | ((((master)& (~((M_GEN1NT_ONE<< M_GEN1NT_ABA_CPT)-1))) | (M_GEN1NT_ONE << (M_GEN1NT_LIMBSIZE - 1 - i))) \ 94 | |((M_GEN1NT_ABA_CPT_T)((master) + 1))) 95 | 96 | // Reset the bit i of the master limb, and increase ABA counter. 97 | #define M_GEN1NT_MASTER_RESET(master, i) \ 98 | (((master) & (~((M_GEN1NT_ONE<< M_GEN1NT_ABA_CPT)-1)) & ~(M_GEN1NT_ONE << (M_GEN1NT_LIMBSIZE - 1 - i))) \ 99 | |((M_GEN1NT_ABA_CPT_T)((master) + 1))) 100 | 101 | /* Initialize an integer generator (CONSTRUCTOR). 102 | * Initialy, the container is full of all the integers up to 'n-1' 103 | * The typical sequence is to initialize the container, and pop 104 | * the integer from it. Each pop integer is **unique** for all threads, 105 | * meaning it can be used to index global unique resources shared 106 | * for all threads. 107 | */ 108 | static inline void 109 | genint_init(genint_t s, unsigned int n) 110 | { 111 | M_ASSERT (s != NULL && n > 0 && n <= GENINT_MAX_ALLOC); 112 | const size_t alloc = (n + M_GEN1NT_LIMBSIZE - 1) / M_GEN1NT_LIMBSIZE; 113 | const unsigned int index = n % M_GEN1NT_LIMBSIZE; 114 | atomic_ullong *ptr = M_MEMORY_REALLOC (atomic_ullong, NULL, alloc); 115 | if (M_UNLIKELY (ptr == NULL)) { 116 | M_MEMORY_FULL(alloc); 117 | return; 118 | } 119 | s->n = n; 120 | s->data = ptr; 121 | s->max = (unsigned int) (alloc-1); 122 | s->mask0 = (index == 0) ? M_GEN1NT_FULL_MASK : ~((M_GEN1NT_ONE<<(M_GEN1NT_LIMBSIZE-index))-1); 123 | s->mask_master = (((M_GEN1NT_ONE << alloc) - 1) << (M_GEN1NT_LIMBSIZE-alloc)) >> M_GEN1NT_ABA_CPT; 124 | atomic_init (&s->master, (genint_limb_ct)0); 125 | for(unsigned int i = 0; i < alloc; i++) 126 | atomic_init(&s->data[i], (genint_limb_ct)0); 127 | M_GEN1NT_CONTRACT(s); 128 | } 129 | 130 | /* Clear an integer generator (Destructor) */ 131 | static inline void 132 | genint_clear(genint_t s) 133 | { 134 | M_GEN1NT_CONTRACT(s); 135 | M_MEMORY_FREE(s->data); 136 | s->data = NULL; 137 | } 138 | 139 | /* Return the maximum integer that the generator will provide */ 140 | static inline size_t 141 | genint_size(genint_t s) 142 | { 143 | M_GEN1NT_CONTRACT(s); 144 | return s->n; 145 | } 146 | 147 | /* Get an unique integer from the integer generator. 148 | * NOTE: For a typical case, the amortized cost is one CAS per pop. */ 149 | static inline unsigned int 150 | genint_pop(genint_t s) 151 | { 152 | M_GEN1NT_CONTRACT(s); 153 | // First read master to see which limb is not full. 154 | genint_limb_ct master = atomic_load(&s->master); 155 | // While master is not full 156 | while ((master >> M_GEN1NT_ABA_CPT) != s->mask_master) { 157 | // Let's get the index i of the first not full limb according to master. 158 | unsigned int i = m_core_clz64(~master); 159 | M_ASSERT (i < M_GEN1NT_LIMBSIZE); 160 | // Let's compute the mask of this limb representing the limb as being full 161 | genint_limb_ct mask = s->mask0; 162 | mask = (i == s->max) ? mask : M_GEN1NT_FULL_MASK; 163 | unsigned int bit; 164 | // Let's load this limb, 165 | genint_limb_ct next, org = atomic_load(&s->data[i]); 166 | do { 167 | // If it is now full, we have been preempted by another. 168 | if (M_UNLIKELY (org == mask)) 169 | goto next_element; 170 | M_ASSERT (org != M_GEN1NT_FULL_MASK); 171 | // At least one bit is free in the limb. Find one. 172 | bit = M_GEN1NT_LIMBSIZE - 1 - m_core_clz64(~org); 173 | M_ASSERT (bit < M_GEN1NT_LIMBSIZE); 174 | M_ASSERT ((org & (M_GEN1NT_ONE<n); 176 | // Set the integer as being used. 177 | next = org | (M_GEN1NT_ONE << bit); 178 | // Try to reserve the integer 179 | } while (!atomic_compare_exchange_weak (&s->data[i], &org, next)); 180 | // We have reserved the integer. 181 | // If the limb is now full, try to update master 182 | if (M_UNLIKELY(next == mask)) { 183 | while (true) { 184 | genint_limb_ct newMaster; 185 | if (next == mask) { 186 | newMaster = M_GEN1NT_MASTER_SET(master, i); 187 | } else { 188 | newMaster = M_GEN1NT_MASTER_RESET(master, i); 189 | } 190 | if (atomic_compare_exchange_weak (&s->master, &master, newMaster)) 191 | break; 192 | // Fail to update. Reload limb to check if it is still full. 193 | next = atomic_load(&s->data[i]); 194 | } 195 | } 196 | // Return the new number 197 | M_GEN1NT_CONTRACT(s); 198 | return i * M_GEN1NT_LIMBSIZE + M_GEN1NT_LIMBSIZE - 1 - bit; 199 | next_element: 200 | // Reload master 201 | master = atomic_load(&s->master); 202 | } 203 | M_GEN1NT_CONTRACT(s); 204 | return GENINT_ERROR; // No more resource available 205 | } 206 | 207 | /* Restore a used integer in the integer generator. 208 | * NOTE: For a typical case, the amortized cost is one CAS per pop */ 209 | static inline void 210 | genint_push(genint_t s, unsigned int n) 211 | { 212 | M_GEN1NT_CONTRACT(s); 213 | M_ASSERT (n < s->n); 214 | const unsigned int i = n / M_GEN1NT_LIMBSIZE; 215 | const unsigned int bit = M_GEN1NT_LIMBSIZE - 1 - (n % M_GEN1NT_LIMBSIZE); 216 | genint_limb_ct master = atomic_load(&s->master); 217 | // Load the limb 218 | genint_limb_ct next, org = atomic_load(&s->data[i]); 219 | do { 220 | M_ASSERT ((org & (M_GEN1NT_ONE << bit)) != 0); 221 | // Reset it 222 | next = org & (~(M_GEN1NT_ONE << bit)); 223 | // Try to unreserve it. 224 | } while (!atomic_compare_exchange_weak (&s->data[i], &org, next)); 225 | // if the limb was marked as full by master 226 | genint_limb_ct mask = s->mask0; 227 | mask = (i == s->max) ? mask : M_GEN1NT_FULL_MASK; 228 | if (M_UNLIKELY (next != mask)) { 229 | // Let's compute the mask of this limb representing the limb as being full 230 | // Let's try to update master to say that this limb is not full 231 | while (true) { 232 | genint_limb_ct newMaster; 233 | if (next == mask) { 234 | newMaster = M_GEN1NT_MASTER_SET(master, i); 235 | } else { 236 | newMaster = M_GEN1NT_MASTER_RESET(master, i); 237 | } 238 | if (atomic_compare_exchange_weak (&s->master, &master, newMaster)) 239 | break; 240 | // Fail to update. Reload limb to check if it is still full. 241 | next = atomic_load(&s->data[i]); 242 | } 243 | } 244 | M_GEN1NT_CONTRACT(s); 245 | } 246 | 247 | M_END_PROTECTED_CODE 248 | 249 | #endif 250 | -------------------------------------------------------------------------------- /fapulator/theseus/applications/gui/font/fonts.cpp: -------------------------------------------------------------------------------- 1 | #include "fonts.h" 2 | 3 | /* 4 | Fontname: -Adobe-Helvetica-Bold-R-Normal--11-80-100-100-P-60-ISO10646-1 5 | Copyright: Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved. 6 | Glyphs: 95/756 7 | BBX Build Mode: 0 8 | */ 9 | const uint8_t u8g2_font_helvB08_tr[1062] = 10 | "_\0\3\3\4\4\2\4\5\13\13\377\376\10\376\10\376\1d\2\317\4\11 \5\0\346\4!\10\202#" 11 | "\305A\22\23\42\10\63wED%\0#\20w\241U$\30\211\231\42!S$\30\211\1$\17\245\236" 12 | "Ul\222B[\212HRf!\0%\22\207\42\216L\42\11I\242\341hD\24\211\314$\0&\20\207" 13 | "\42\316L\42\222\310f\22\223D\62\232\10'\6\61\367\304\0(\13\243\32UD\22\321)$\12)\15" 14 | "\243\32EH\24\222h\211HB\0*\7\63\66E$\7+\12V\246\225Pd\22\212\0,\7B\332" 15 | "\4E\2-\6\24n\5\1.\6\42\342\4\1/\14\204\42]R,\24\13\305b\0\60\13\205\242\315" 16 | "D\302\27\311\4\0\61\7\203\243ME\37\62\12\205\242\315DMI[\1\63\15\205\242\315DM$\224" 17 | "Q$\23\0\64\16\206\242eP\66\212HB\22\233P\2\65\14\205\242\305MF\34Q$\23\0\66\15" 18 | "\205\242\315DB\243HX$\23\0\67\13\205\242EMI&\222)\1\70\16\205\242\315DB\221L$" 19 | ",\222\11\0\71\15\205\242\315D\302\42\241Q$\23\0:\7b\342\4\221\0;\10\202\332\4\221\42\1" 20 | "<\10Tf\225DM\1=\7\65\252E\265\0>\11Tf\205LE\11\0\77\14\205\242\315DM\244" 21 | "&\207\210\0@\27\232\336^M\32\12I\42\242$I,$\11\245\204$\262\70\254\6A\17\207\42\326" 22 | "p&\21ID\22\311AF\23B\14\206\342EED\272\210\230.\0C\16\207\42\26ID\234*F" 23 | "D\42\12\0D\13\206\342\5I\242\304\213\204\4E\11\205\242\305MvS+F\12\205\242\305MF\221" 24 | "i\3G\16\207\42\26ID\234\212j\22\221h\22H\12\206\342\205\210\351\60\342$I\7\202\342\304\203" 25 | "\0J\12\205\242\235\336(\222\11\0K\17\207\342\205H\242D\33\322$\42\25\231\0L\10\205\242\205L" 26 | "\337\12M\20\211\242\206\264f\273XR(\23R\210\24\22N\16\207\42\206\254T\242D(\21R\233\0" 27 | "O\14\207\42\326L\242\306M\242\66\2P\13\206\342EE\304t\21*\2Q\15\227\36\326L\242\306%" 28 | "\242\215\16\10R\13\206\342EE\304t\21\61\11S\14\206\342\15ETd#I(\0T\11\206\342\205" 29 | "I\250O\0U\12\206\342\205\210\237$\24\0V\17\207\42\206\214&Q\222\210$\262\341\64\6W\23\212" 30 | "\342\206HD\22\221T\64I\224\16\61\221P$\2X\16\207\42\206\214&Q\33\316$j\64\1Y\15" 31 | "\210b\206\220(\21i\243\212\325\0Z\12\206\342\205Q\246\66S\64[\10\243\32EE\277\14\134\14\204" 32 | "\42E,\30\13\306\202\261\0]\10\243\32\305D\277\24^\10Dr\215\244$\12_\6\26\232\205\1`" 33 | "\7\42\376D(\0a\13f\242\315($\242\350$\21b\13\205\242\205LF\221p\261\0c\11db" 34 | "Me\244\22\31d\12\205\242\235\212\205\213\204\0e\14e\242\315Dr\220I$\23\0f\12\205!\325" 35 | "DD\222\351\4g\14\205\232\215d\302EB\223L\0h\12\205\242\205LF\221\360\42i\7\202\342\204" 36 | "\350Pj\12\243\331\214P\242\27\12\0k\15\206\242\205PEB\232\221$J\2l\7\202\342\304\203\0" 37 | "m\16hbFD\27\212\204\42\241H(\12n\11e\242ED\205\27\1o\12e\242\315D\302E\62" 38 | "\1p\13\205\232ED\205\213E&\3q\12\205\232\215d\302EBSr\11d\42E\244\42\322\4s" 39 | "\13e\242\315DeH\221L\0t\12\204!\215HB\21i\23u\11e\242\205\204\27\225\0v\13e" 40 | "\242\205\204E\222\64\13\1w\20g\42\206$B\211P\42\232$\42\211\4\0x\14f\342\205HB\223" 41 | "QD$\1y\14\205\232\205\204\213\204$\23\211\0z\11e\242EM\244\255\0{\13\244Z\225D\244" 42 | "\42\23i\23|\7\241\333\304a\0}\14\244Z\205L\244&\21i\21\1~\7%\256\215\26\0\0\0" 43 | "\0\4\377\377\0"; 44 | 45 | /* 46 | Fontname: -FreeType-HaxrCorp 4089-Medium-R-Normal--16-160-72-72-P-51-ISO10646-1 47 | Copyright: Copyright sahwar 2009 48 | Glyphs: 95/165 49 | BBX Build Mode: 0 50 | */ 51 | const uint8_t u8g2_font_haxrcorp4089_tr[946] = 52 | "_\0\3\2\4\4\1\4\5\10\12\0\376\7\376\10\377\1\71\2~\3\225 \5\0Q\2!\7qQ" 53 | "bP\2\42\7\63\231\42\261\4#\13U\323\246\62(\225A\251\0$\16\225\317*[*J\266%J" 54 | "e\213\0%\14w\21'T\242\326R\213\230\0&\14v\361F\213\332$)\321\242%'\5\61Yb" 55 | "(\10\223\217*)u+)\11\223\217\42+u)\1*\12U\325\252RY\232\42\0+\12U\323*" 56 | "\214\6)\214\0,\7\62o&Q\0-\6\25\327b\20.\5\21Q\42/\10w\21;\355\63\0\60" 57 | "\14u\321f\311\244%\231\264d\1\61\7rqf\351\1\62\12u\321f\311\302\254m\20\63\13u\321" 58 | "f\311\302H\325\222\5\64\14u\321.\223\222R\62ha\2\65\13u\321\342\70\244\241\226,\0\66\14" 59 | "u\321f\311\304!\311\264d\1\67\13u\321b\20ka\26F\0\70\14u\321f\311\264d\311\264d" 60 | "\1\71\14u\321f\311\264d\10\265d\1:\6AS\42\12;\10bo&M\24\0<\7S\223*" 61 | "\251\25=\10\65\325bP\7\1>\10S\223\42\253\224\0\77\13u\321f\311\302\254\16E\0@\23\210" 62 | "\61gXbiQ\242D\211\22iHrdX\0A\13u\321f\311\264a\310l\1B\15u\321b" 63 | "H\62mP\62mP\0C\12u\321f\311\304\266d\1D\12u\321bH\62o\203\2E\13u\321" 64 | "\342\30\16I\30\16\2F\12u\321\342\30\16IX\4G\13u\321f\311\304h\323\222\5H\12u\321" 65 | "\42\263\15Cf\13I\6qQ\342\20J\11u\321\262\243\226,\0K\14u\321\42\223\222\222\226D\225" 66 | ",L\10u\321\42\354q\20M\14w\21#\335\226\212\24\251\326\0N\13u\321\42\323&%\221\66-" 67 | "O\12u\321f\311\274%\13\0P\14u\321bH\62mP\302\42\0Q\12\205\317f\311\274%k\0" 68 | "R\14u\321bH\62mPJ\225,S\13u\321f\311\324UK\26\0T\11u\321b\220\302\236\0" 69 | "U\11u\321\42\363-Y\0V\14u\321\42\263%\245$\13#\0W\17w\21#\212\244H\212\244H" 70 | "\212\244\212\5X\13u\321\42\323\222Z\245\246\5Y\12u\321\42\323\222Z\330\4Z\12u\321b\20\263" 71 | "\216\203\0[\10\223\217b\210\372\64\134\16w\21#\7r \7r \7r ]\10\223\217b\352\323" 72 | "\20^\10\65\331*Kj\1_\6\25\321b\20`\6\63\231\42+a\11T\261f\210L\311\0b\13" 73 | "t\261\42\313\226\310\64$\0c\12T\261F\211\264(Q\0d\11t\261\256\62D\246de\11T\261" 74 | "F\211\206Q\1f\11rqFI\226\12\0g\13t\255f\210L\311\226(\0h\11t\261\42\313\226" 75 | "\310)i\7qQ\42\31\4j\11\222m&K\272(\0k\13t\261\42+)\211\224\224\2l\6q" 76 | "Q\342\20m\14W\21cQ\242H\212\244H*n\10T\261b\211\234\2o\11T\261F\211L\211\2" 77 | "p\13t\255b\211LC\222e\0q\11t\255f\210L\311Vr\10S\221b\210\232\0s\10S\221" 78 | "f\313\26\0t\11s\221&J\226\250\5u\10T\261\42rJ\6v\11T\261\42rJ\24\0w\14" 79 | "W\21#\212\244H\212\244\212\5x\12T\261\42\222\22%\222\2y\12t\255\42rJ\266D\1z\12" 80 | "T\261b\310\22%\33\2{\12\223\217*\211*YT\13|\6\221O\342\1}\13\223\217\42\213jI" 81 | "T\211\0~\7&\367F\262\0\0\0\0\4\377\377\0"; 82 | 83 | /* 84 | Fontname: ProFont11 85 | Copyright: ProFont Distribution 2.2 � Generated by Fontographer 4.1.5 86 | Glyphs: 96/256 87 | BBX Build Mode: 2 88 | */ 89 | const uint8_t u8g2_font_profont11_mr[1203] = 90 | "`\2\3\2\3\4\1\2\4\6\13\0\376\7\376\10\377\1{\3\42\4\226 \7\336\370\371\67\0!\12" 91 | "\336\370\11iw\60\247\0\42\14\336\370\201$K\262$\347\23\0#\16\336\370\341$\32\244$\32\244$" 92 | "g\5$\16\336\370\221pJ:\216I\247\61\207\0%\15\336\370\341!\351\242%\231\322SN&\16\336" 93 | "\370a-\252\204\225\246L\311a\0'\11\336\370\221\264\316\67\0(\12\336\370\241\260\332\271\216\0)\12" 94 | "\336\370\201\270\332\261\216\1*\14\336\370\11YR\232\222Z\316\14+\13\336\370\311i\66hi\316\0," 95 | "\11\336\370\371\42\252a\10-\10\336\370\271\355\34\1.\10\336\370\371$\352\4/\15\336\370\261\64L\303" 96 | "\64L\303\64\5\60\16\336\370\341)K\42\245EJ\262h'\61\13\336\370\11\331\332m\320a\0\62\14" 97 | "\336\370\341)K\303\306A\207\1\63\15\336\370\341)K\63\71\311\242\235\0\64\15\336\370)\241\226D\245" 98 | "A\14w\30\65\15\336\370\321AI\207\70M\262h'\66\16\336\370\341)\35\242,\311\222,\332\11\67" 99 | "\13\336\370\321A-V\353\24\0\70\16\336\370\341)K\262h\312\222,\332\11\71\16\336\370\341)K\262" 100 | "$\213\206\64\332\11:\12\336\370YE\35\23u\2;\13\336\370\31\355\230\250\206!\0<\11\336\370I" 101 | "as;\14=\13\336\370\331\6\35\30tF\0>\11\336\370\341\216\355$\0\77\14\336\370\341)K\303" 102 | ":\230S\0@\16\336\370\341)K\222\245\313\22\17\71\14A\20\336\370\11a\222%Q\226\14J\226d" 103 | "\71\14B\20\336\370\321!\312\222,\31\242,\311\222!'C\13\336\370\341)K\322\266h'D\20\336" 104 | "\370\321!\312\222,\311\222,\311\222!'E\16\336\370\321AI\323!J\323A\207\1F\15\336\370\321" 105 | "AI\323!J\353\64\0G\16\336\370\341)K\322H\311\222,\332\11H\21\336\370\321,\311\222,\31" 106 | "\224,\311\222,\207\1I\13\336\370\321AK\273\15:\14J\13\336\370IiK\226d\321NK\17\336" 107 | "\370\321,\211*\231\230dQ-\207\1L\11\336\370\321\276\16:\14M\16\336\370\321,\261\364[\222%" 108 | "Y\16\3N\20\336\370\321,\221\222NJ\226dI\226\303\0O\17\336\370\341)K\262$K\262$\213" 109 | "v\2P\16\336\370\321!\312\222,\31\242\264N\3Q\17\336\370\341)K\262$K\262\244i\316\1R" 110 | "\21\336\370\321!\312\222,\31\242,\311\222,\207\1S\15\336\370\341)K\342\71\311\242\235\0T\12\336" 111 | "\370\321AK{\247\0U\20\336\370\321,\311\222,\311\222,\311\242\235\0V\20\336\370\321,\311\222," 112 | "J\262$Ls\12\0W\17\336\370\321,\311\222,\351\213%\313a\0X\20\336\370\321,\311\242$\254" 113 | "DY\222\345\60\0Y\16\336\370\321,\311\222,J\302\264N\1Z\13\336\370\321A\15\273\16:\14[" 114 | "\12\336\370\21\61\355\253\216\0\134\14\336\370\201\64N\343\64N\343\64]\11\336\370\1\265\37u\10^\13" 115 | "\336\370\11a\22e\71O\0_\7\336\370\371\343\60`\11\336\370\201\70\347'\0a\16\336\370\31\207$" 116 | "K\262$\262\344\60\0b\16\336\370\321\352\20eI\226d\311\220\23c\14\336\370\31\247,I\343!\207" 117 | "\1d\20\336\370Ii\64$Y\222%Y\64\344\60\0e\15\336\370\31\247,\31\224x\310a\0f\13" 118 | "\336\370)Z\70\246\355\24\0g\17\336\370\31\207$K\262$\213\206\64\232\0h\17\336\370\321\352\20e" 119 | "I\226dI\226\303\0i\12\336\370\11\71\246\66\356\4j\12\336\370\11\71\246\366&\2k\14\336\370\321" 120 | "\246J\266E\265\34\6l\11\336\370a\265\307\235\0m\13\336\370\331\206(\351\177\207\1n\16\336\370\331" 121 | "\22IJ\262$K\262\34\6o\14\336\370\31\247,\311\222,\332\11p\17\336\370\331\206(K\262$K" 122 | "\206(M\1q\16\336\370\31\207$K\262$\213\206\264\2r\13\336\370\331\22IJ\322:\15s\13\336" 123 | "\370\31\207$\236\223!'t\13\336\370\11i\70\246e\35\6u\15\336\370\331*Y\222%\221%\207\1" 124 | "v\15\336\370\331*Y\224dI\230S\0w\12\336\370\331\222\376\247$'x\14\336\370\331JIX\211" 125 | "\262\34\6y\16\336\370\331*Y\222%Y\64\244\321\4z\13\336\370\331\6\261q\320a\0{\13\336\370" 126 | "\302\264\61N\233#\0|\11\336\370\221\264\277C\0}\13\336x\342\264\71L\33C\0~\11\336\370\31" 127 | "\225\212\316\21\177\7\336\370\371\67\0\0\0\0\4\377\377\0"; 128 | 129 | /* 130 | Fontname: ProFont22 131 | Copyright: ProFont Distribution 2.2 � Generated by Fontographer 4.1.5 132 | Glyphs: 18/256 133 | BBX Build Mode: 0 134 | */ 135 | const uint8_t u8g2_font_profont22_tn[345] = 136 | "\22\0\4\4\4\5\3\4\5\13\24\0\374\16\374\20\376\0\0\0\0\1< \5\0\210\34*\23\252\310" 137 | "\234\4\12\22\42\204\211!#L$\22(\10\0+\15\252\250\234\4\312\350A!\201\62\2,\17\225L" 138 | "\134\204\220 \62F\310\210i\302\0-\6&\354\34\30.\12D\236free_list = NULL; \ 97 | mem->current_segment = M_MEMORY_ALLOC(M_C(name,_segment_ct)); \ 98 | if (mem->current_segment == NULL) { \ 99 | M_MEMORY_FULL(sizeof (M_C(name,_segment_ct))); \ 100 | return; \ 101 | } \ 102 | mem->current_segment->next = NULL; \ 103 | mem->current_segment->count = 0; \ 104 | M_M3MPOOL_CONTRACT(mem, type); \ 105 | } \ 106 | \ 107 | static inline void \ 108 | M_C(name,_clear)(name_t mem) \ 109 | { \ 110 | M_M3MPOOL_CONTRACT(mem, type); \ 111 | M_C(name,_segment_ct) *segment = mem->current_segment; \ 112 | while (segment != NULL) { \ 113 | M_C(name,_segment_ct) *next = segment->next; \ 114 | M_MEMORY_DEL (segment); \ 115 | segment = next; \ 116 | } \ 117 | /* Clean pointers to be safer */ \ 118 | mem->free_list = NULL; \ 119 | mem->current_segment = NULL; \ 120 | } \ 121 | \ 122 | static inline type * \ 123 | M_C(name,_alloc)(name_t mem) \ 124 | { \ 125 | M_M3MPOOL_CONTRACT(mem, type); \ 126 | /* Test if one object is in the free list */ \ 127 | M_C(name,_union_ct) *ret = mem->free_list; \ 128 | if (ret != NULL) { \ 129 | /* Yes, so return it, and pop it from the free list */ \ 130 | mem->free_list = ret->next; \ 131 | return &ret->t; \ 132 | } \ 133 | /* No cheap free object exist. Test within a segment */ \ 134 | M_C(name,_segment_ct) *segment = mem->current_segment; \ 135 | M_ASSERT(segment != NULL); \ 136 | unsigned int count = segment->count; \ 137 | /* If segment is full, allocate a new one from the system */ \ 138 | if (M_UNLIKELY (count >= M_USE_MEMPOOL_MAX_PER_SEGMENT(type))) { \ 139 | M_C(name,_segment_ct) *new_segment = M_MEMORY_ALLOC (M_C(name,_segment_ct)); \ 140 | if (M_UNLIKELY (new_segment == NULL)) { \ 141 | M_MEMORY_FULL(sizeof (M_C(name,_segment_ct))); \ 142 | return NULL; \ 143 | } \ 144 | new_segment->next = segment; \ 145 | new_segment->count = 0; \ 146 | mem->current_segment = new_segment; \ 147 | segment = new_segment; \ 148 | count = 0; \ 149 | } \ 150 | /* Return the object as the last element of the current segment */ \ 151 | ret = &segment->tab[count]; \ 152 | segment->count = count + 1; \ 153 | M_M3MPOOL_CONTRACT(mem, type); \ 154 | return &ret->t; \ 155 | } \ 156 | \ 157 | static inline void \ 158 | M_C(name,_free)(name_t mem, type *ptr) \ 159 | { \ 160 | M_M3MPOOL_CONTRACT(mem, type); \ 161 | /* NOTE: Unsafe cast: suppose that the given pointer \ 162 | was allocated by the previous alloc function. */ \ 163 | M_C(name,_union_ct) *ret = (M_C(name,_union_ct) *)(uintptr_t)ptr; \ 164 | /* Add the object back in the free list */ \ 165 | ret->next = mem->free_list; \ 166 | mem->free_list = ret; \ 167 | /* NOTE: the objects are NOT given back to the system until the mempool \ 168 | is fully cleared */ \ 169 | M_M3MPOOL_CONTRACT(mem, type); \ 170 | } \ 171 | 172 | /* MEMPOOL contract */ 173 | #define M_M3MPOOL_CONTRACT(mempool, type) do { \ 174 | M_ASSERT((mempool) != NULL); \ 175 | M_ASSERT((mempool)->current_segment != NULL); \ 176 | M_ASSERT((mempool)->current_segment->count <= M_USE_MEMPOOL_MAX_PER_SEGMENT(type)); \ 177 | } while (0) 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /fapulator/theseus/applications/gui/canvas.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "font/fonts.h" 6 | #include "font/u8g2_font_render.h" 7 | 8 | #define U8G2_DRAW_UPPER_RIGHT 0x01 9 | #define U8G2_DRAW_UPPER_LEFT 0x02 10 | #define U8G2_DRAW_LOWER_LEFT 0x04 11 | #define U8G2_DRAW_LOWER_RIGHT 0x08 12 | #define U8G2_DRAW_ALL \ 13 | (U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT | U8G2_DRAW_LOWER_RIGHT | U8G2_DRAW_LOWER_LEFT) 14 | 15 | class CanvasInstance { 16 | private: 17 | Canvas* canvas; 18 | std::bitset display_buffer; 19 | Color color = ColorBlack; 20 | CanvasDirection font_direction = CanvasDirectionLeftToRight; 21 | const uint8_t* font = u8g2_font_haxrcorp4089_tr; 22 | 23 | public: 24 | CanvasInstance(Canvas* _canvas) { 25 | canvas = _canvas; 26 | } 27 | 28 | ~CanvasInstance() { 29 | } 30 | 31 | void fill(bool value) { 32 | if(value) { 33 | display_buffer.set(); 34 | } else { 35 | display_buffer.reset(); 36 | } 37 | } 38 | 39 | void set_pixel(size_t x, size_t y) { 40 | if(x < DISPLAY_WIDTH && y < DISPLAY_HEIGHT) { 41 | display_buffer.set(x + y * DISPLAY_WIDTH, color); 42 | } 43 | } 44 | 45 | void clear_pixel(size_t x, size_t y) { 46 | if(x < DISPLAY_WIDTH && y < DISPLAY_HEIGHT) { 47 | display_buffer.set(x + y * DISPLAY_WIDTH, !color); 48 | } 49 | } 50 | 51 | bool get_pixel(size_t x, size_t y) { 52 | bool pixel = false; 53 | if(x < DISPLAY_WIDTH && y < DISPLAY_HEIGHT) { 54 | pixel = display_buffer.test(x + y * DISPLAY_WIDTH); 55 | } 56 | return pixel; 57 | } 58 | 59 | void set_color(Color _color) { 60 | color = _color; 61 | } 62 | 63 | Color get_color() { 64 | return color; 65 | } 66 | 67 | void set_font(const uint8_t* font) { 68 | this->font = font; 69 | } 70 | 71 | void set_font_direction(CanvasDirection direction) { 72 | font_direction = direction; 73 | } 74 | 75 | CanvasDirection get_font_direction() { 76 | return font_direction; 77 | } 78 | 79 | static void draw_pixel_fg(uint8_t x, uint8_t y, void* context) { 80 | CanvasInstance* canvas = (CanvasInstance*)context; 81 | canvas->set_pixel(x, y); 82 | } 83 | 84 | static void draw_pixel_bg(uint8_t x, uint8_t y, void* context) { 85 | CanvasInstance* canvas = (CanvasInstance*)context; 86 | canvas->clear_pixel(x, y); 87 | } 88 | 89 | void draw_string(uint16_t x, uint16_t y, const char* text) { 90 | U8G2FontRender_t render = U8G2FontRender(this->font, draw_pixel_fg, draw_pixel_bg, this); 91 | U8G2FontRender_Print(&render, x, y, text); 92 | } 93 | 94 | void draw_vertical_line(uint16_t x, uint16_t y, uint16_t length) { 95 | for(uint16_t i = 0; i < length; i++) { 96 | set_pixel(x, y + i); 97 | } 98 | } 99 | 100 | void draw_horizontal_line(uint16_t x, uint16_t y, uint16_t length) { 101 | for(uint16_t i = 0; i < length; i++) { 102 | set_pixel(x + i, y); 103 | } 104 | } 105 | 106 | void draw_circle_section(uint8_t x, uint8_t y, uint8_t x0, uint8_t y0, uint8_t option) { 107 | /* upper right */ 108 | if(option & U8G2_DRAW_UPPER_RIGHT) { 109 | set_pixel(x0 + x, y0 - y); 110 | set_pixel(x0 + y, y0 - x); 111 | } 112 | 113 | /* upper left */ 114 | if(option & U8G2_DRAW_UPPER_LEFT) { 115 | set_pixel(x0 - x, y0 - y); 116 | set_pixel(x0 - y, y0 - x); 117 | } 118 | 119 | /* lower right */ 120 | if(option & U8G2_DRAW_LOWER_RIGHT) { 121 | set_pixel(x0 + x, y0 + y); 122 | set_pixel(x0 + y, y0 + x); 123 | } 124 | 125 | /* lower left */ 126 | if(option & U8G2_DRAW_LOWER_LEFT) { 127 | set_pixel(x0 - x, y0 + y); 128 | set_pixel(x0 - y, y0 + x); 129 | } 130 | } 131 | 132 | void draw_circle(uint8_t x0, uint8_t y0, uint8_t rad, uint8_t option) { 133 | int8_t f; 134 | int8_t ddF_x; 135 | int8_t ddF_y; 136 | uint8_t x; 137 | uint8_t y; 138 | 139 | f = 1; 140 | f -= rad; 141 | ddF_x = 1; 142 | ddF_y = 0; 143 | ddF_y -= rad; 144 | ddF_y *= 2; 145 | x = 0; 146 | y = rad; 147 | 148 | draw_circle_section(x, y, x0, y0, option); 149 | 150 | while(x < y) { 151 | if(f >= 0) { 152 | y--; 153 | ddF_y += 2; 154 | f += ddF_y; 155 | } 156 | x++; 157 | ddF_x += 2; 158 | f += ddF_x; 159 | 160 | draw_circle_section(x, y, x0, y0, option); 161 | } 162 | } 163 | 164 | void draw_disc_section(uint8_t x, uint8_t y, uint8_t x0, uint8_t y0, uint8_t option) { 165 | /* upper right */ 166 | if(option & U8G2_DRAW_UPPER_RIGHT) { 167 | draw_vertical_line(x0 + x, y0 - y, y + 1); 168 | draw_vertical_line(x0 + y, y0 - x, x + 1); 169 | } 170 | 171 | /* upper left */ 172 | if(option & U8G2_DRAW_UPPER_LEFT) { 173 | draw_vertical_line(x0 - x, y0 - y, y + 1); 174 | draw_vertical_line(x0 - y, y0 - x, x + 1); 175 | } 176 | 177 | /* lower right */ 178 | if(option & U8G2_DRAW_LOWER_RIGHT) { 179 | draw_vertical_line(x0 + x, y0, y + 1); 180 | draw_vertical_line(x0 + y, y0, x + 1); 181 | } 182 | 183 | /* lower left */ 184 | if(option & U8G2_DRAW_LOWER_LEFT) { 185 | draw_vertical_line(x0 - x, y0, y + 1); 186 | draw_vertical_line(x0 - y, y0, x + 1); 187 | } 188 | } 189 | 190 | void draw_disc(uint8_t x0, uint8_t y0, uint8_t rad, uint8_t option) { 191 | int8_t f; 192 | int8_t ddF_x; 193 | int8_t ddF_y; 194 | uint8_t x; 195 | uint8_t y; 196 | 197 | f = 1; 198 | f -= rad; 199 | ddF_x = 1; 200 | ddF_y = 0; 201 | ddF_y -= rad; 202 | ddF_y *= 2; 203 | x = 0; 204 | y = rad; 205 | 206 | draw_disc_section(x, y, x0, y0, option); 207 | 208 | while(x < y) { 209 | if(f >= 0) { 210 | y--; 211 | ddF_y += 2; 212 | f += ddF_y; 213 | } 214 | x++; 215 | ddF_x += 2; 216 | f += ddF_x; 217 | 218 | draw_disc_section(x, y, x0, y0, option); 219 | } 220 | } 221 | 222 | void draw_box(uint8_t x, uint8_t y, uint8_t width, uint8_t height) { 223 | for(uint8_t i = 0; i < height; i++) { 224 | draw_horizontal_line(x, y + i, width); 225 | } 226 | } 227 | 228 | void draw_frame(uint8_t x, uint8_t y, uint8_t width, uint8_t height) { 229 | draw_vertical_line(x, y, height); 230 | draw_vertical_line(x + width - 1, y, height); 231 | draw_horizontal_line(x, y, width); 232 | draw_horizontal_line(x, y + height - 1, width); 233 | } 234 | 235 | void draw_rounded_frame(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t radius) { 236 | draw_horizontal_line(x + radius, y, width - 2 * radius); 237 | draw_horizontal_line(x + radius, y + height - 1, width - 2 * radius); 238 | draw_vertical_line(x, y + radius, height - 2 * radius); 239 | draw_vertical_line(x + width - 1, y + radius, height - 2 * radius); 240 | draw_circle(x + radius + 1, y + radius, radius, U8G2_DRAW_UPPER_RIGHT); 241 | draw_circle(x + width - radius - 2, y + radius, radius, U8G2_DRAW_UPPER_LEFT); 242 | draw_circle(x + radius + 1, y + height - radius - 1, radius, U8G2_DRAW_LOWER_RIGHT); 243 | draw_circle(x + width - radius - 2, y + height - radius - 1, radius, U8G2_DRAW_LOWER_LEFT); 244 | } 245 | }; 246 | 247 | Canvas* canvas_init() { 248 | Canvas* canvas = new Canvas; 249 | canvas->orientation = CanvasOrientationHorizontal; 250 | canvas->fb = new CanvasInstance(canvas); 251 | 252 | // Clear buffer and send to device 253 | canvas_clear(canvas); 254 | canvas_commit(canvas); 255 | 256 | return canvas; 257 | } 258 | 259 | void canvas_free(Canvas* canvas) { 260 | delete static_cast(canvas->fb); 261 | delete canvas; 262 | } 263 | 264 | void canvas_clear(Canvas* canvas) { 265 | static_cast(canvas->fb)->fill(false); 266 | } 267 | 268 | void canvas_commit(Canvas* canvas) { 269 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 270 | DisplayBuffer* display_buffer = get_display_buffer(); 271 | 272 | for(size_t x = 0; x < DISPLAY_WIDTH; x++) { 273 | for(size_t y = 0; y < DISPLAY_HEIGHT; y++) { 274 | display_buffer->set_pixel(x, y, canvas_instance->get_pixel(x, y)); 275 | } 276 | } 277 | commit_display_buffer(true); 278 | } 279 | 280 | void canvas_set_font(Canvas* canvas, Font font) { 281 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 282 | 283 | if(font == FontPrimary) { 284 | canvas_instance->set_font(u8g2_font_helvB08_tr); 285 | } else if(font == FontSecondary) { 286 | canvas_instance->set_font(u8g2_font_haxrcorp4089_tr); 287 | } else if(font == FontKeyboard) { 288 | canvas_instance->set_font(u8g2_font_profont11_mr); 289 | } else if(font == FontBigNumbers) { 290 | canvas_instance->set_font(u8g2_font_profont22_tn); 291 | } else { 292 | abort(); 293 | } 294 | } 295 | 296 | void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { 297 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 298 | if(!str) return; 299 | x += canvas->offset_x; 300 | y += canvas->offset_y; 301 | canvas_instance->draw_string(x, y, str); 302 | } 303 | 304 | void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) { 305 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 306 | x += canvas->offset_x; 307 | y += canvas->offset_y; 308 | canvas_instance->draw_circle(x, y, radius, U8G2_DRAW_ALL); 309 | } 310 | 311 | void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) { 312 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 313 | x += canvas->offset_x; 314 | y += canvas->offset_y; 315 | canvas_instance->draw_disc(x, y, radius, U8G2_DRAW_ALL); 316 | } 317 | 318 | void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { 319 | // TODO: Implement 320 | } 321 | 322 | void canvas_frame_set( 323 | Canvas* canvas, 324 | uint8_t offset_x, 325 | uint8_t offset_y, 326 | uint8_t width, 327 | uint8_t height) { 328 | canvas->offset_x = offset_x; 329 | canvas->offset_y = offset_y; 330 | canvas->width = width; 331 | canvas->height = height; 332 | } 333 | 334 | void canvas_reset(Canvas* canvas) { 335 | canvas_clear(canvas); 336 | canvas_set_color(canvas, ColorBlack); 337 | canvas_set_font(canvas, FontSecondary); 338 | canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); 339 | } 340 | 341 | void canvas_set_color(Canvas* canvas, Color color) { 342 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 343 | canvas_instance->set_color(color); 344 | } 345 | 346 | void canvas_set_font_direction(Canvas* canvas, CanvasDirection dir) { 347 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 348 | canvas_instance->set_font_direction(dir); 349 | } 350 | 351 | void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { 352 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 353 | x += canvas->offset_x; 354 | y += canvas->offset_y; 355 | canvas_instance->draw_box(x, y, width, height); 356 | } 357 | 358 | void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { 359 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 360 | x += canvas->offset_x; 361 | y += canvas->offset_y; 362 | canvas_instance->draw_frame(x, y, width, height); 363 | } 364 | 365 | void canvas_draw_rframe( 366 | Canvas* canvas, 367 | uint8_t x, 368 | uint8_t y, 369 | uint8_t width, 370 | uint8_t height, 371 | uint8_t radius) { 372 | CanvasInstance* canvas_instance = static_cast(canvas->fb); 373 | x += canvas->offset_x; 374 | y += canvas->offset_y; 375 | canvas_instance->draw_rounded_frame(x, y, width, height, radius); 376 | } -------------------------------------------------------------------------------- /fapulator/hal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "hal/hal.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct { 11 | InputCallback callback; 12 | void* context; 13 | } InputCallbackRecord; 14 | 15 | static constexpr size_t DISPLAY_SCALE = 4; 16 | static constexpr size_t DISPLAY_WIDTH_SCALED = DISPLAY_WIDTH * DISPLAY_SCALE; 17 | static constexpr size_t DISPLAY_HEIGHT_SCALED = DISPLAY_HEIGHT * DISPLAY_SCALE; 18 | 19 | DisplayBuffer display_buffer_handler; 20 | static std::bitset display_buffer; 21 | static std::mutex display_buffer_mutex; 22 | 23 | static std::mutex input_callback_mutex; 24 | static std::vector input_callbacks; 25 | 26 | static const char* button_names[] = { 27 | [static_cast(InputKeyUp)] = "↑", 28 | [static_cast(InputKeyDown)] = "↓", 29 | [static_cast(InputKeyRight)] = "→", 30 | [static_cast(InputKeyLeft)] = "←", 31 | [static_cast(InputKeyOk)] = "○", 32 | [static_cast(InputKeyBack)] = "⇤", 33 | }; 34 | 35 | bool get_key_from_button_name(const char* name, InputKey* key) { 36 | for(size_t i = 0; i < sizeof(button_names) / sizeof(button_names[0]); i++) { 37 | if(strcmp(button_names[i], name) == 0) { 38 | *key = static_cast(i); 39 | return true; 40 | } 41 | } 42 | return false; 43 | } 44 | 45 | void DisplayBuffer::set_pixel(size_t x, size_t y, bool pixel) { 46 | if(x < DISPLAY_WIDTH && y < DISPLAY_HEIGHT) { 47 | display_buffer[(y * DISPLAY_WIDTH) + (x)] = pixel; 48 | } 49 | } 50 | 51 | void DisplayBuffer::fill(bool value) { 52 | if(value) { 53 | display_buffer.set(); 54 | } else { 55 | display_buffer.reset(); 56 | } 57 | } 58 | 59 | class HALEmulator; 60 | 61 | QApplication* main_app; 62 | HALEmulator* hal_emulator; 63 | 64 | typedef struct Color { 65 | uint8_t r; 66 | uint8_t g; 67 | uint8_t b; 68 | } Color; 69 | 70 | static Color color_set = {0x00, 0x00, 0x00}; 71 | static Color color_reset = {0xFF, 0x82, 0x00}; 72 | 73 | class DisplayWidget : public QWidget { 74 | private: 75 | QImage _image; 76 | static const size_t buffer_colors = 3; 77 | uchar _buffer[DISPLAY_HEIGHT][DISPLAY_WIDTH][buffer_colors]; 78 | 79 | void set_pixel(size_t x, size_t y, bool pixel) { 80 | if(pixel) { 81 | _buffer[y][x][0] = color_set.r; 82 | _buffer[y][x][1] = color_set.g; 83 | _buffer[y][x][2] = color_set.b; 84 | } else { 85 | _buffer[y][x][0] = color_reset.r; 86 | _buffer[y][x][1] = color_reset.g; 87 | _buffer[y][x][2] = color_reset.b; 88 | } 89 | } 90 | 91 | void copy_buffer_to_image() { 92 | const std::lock_guard lock(display_buffer_mutex); 93 | 94 | // copy buffer to image 95 | for(size_t y = 0; y < DISPLAY_HEIGHT; y++) { 96 | for(size_t x = 0; x < DISPLAY_WIDTH; x++) { 97 | bool bit = display_buffer[(y * DISPLAY_WIDTH) + (x)]; 98 | set_pixel(x, y, bit); 99 | } 100 | } 101 | } 102 | 103 | public: 104 | explicit DisplayWidget(QWidget* parent = 0) { 105 | memset(_buffer, 0, DISPLAY_HEIGHT * DISPLAY_WIDTH * buffer_colors); 106 | _image = QImage((uchar*)_buffer, DISPLAY_WIDTH, DISPLAY_HEIGHT, QImage::Format_RGB888); 107 | } 108 | 109 | ~DisplayWidget(){ 110 | 111 | }; 112 | 113 | void force_redraw() { 114 | copy_buffer_to_image(); 115 | update(); 116 | } 117 | 118 | protected: 119 | void paintEvent(QPaintEvent* event) { 120 | QPainter painter(this); 121 | painter.scale(DISPLAY_SCALE, DISPLAY_SCALE); 122 | painter.drawImage(0, 0, this->_image); 123 | } 124 | }; 125 | 126 | #define INPUT_PRESS_TICKS 150 127 | #define INPUT_LONG_PRESS_COUNTS 2 128 | 129 | class ButtonTimer : public QWidget { 130 | private: 131 | InputKey _key; 132 | QTimer* _timer; 133 | uint32_t _counter; 134 | 135 | void send_input_event(InputType type, InputKey key) { 136 | InputEvent event; 137 | event.type = type; 138 | event.key = key; 139 | 140 | const std::lock_guard lock(input_callback_mutex); 141 | for(auto& callback : input_callbacks) { 142 | callback.callback(&event, callback.context); 143 | } 144 | } 145 | 146 | public: 147 | ButtonTimer(InputKey key) { 148 | _key = key; 149 | _timer = new QTimer(); 150 | _timer->setSingleShot(true); 151 | connect(_timer, &QTimer::timeout, this, &ButtonTimer::timeout); 152 | } 153 | 154 | ~ButtonTimer() { 155 | delete _timer; 156 | } 157 | 158 | void start() { 159 | _counter = 0; 160 | _timer->start(std::chrono::milliseconds(INPUT_PRESS_TICKS)); 161 | } 162 | 163 | void continue_timer() { 164 | _timer->start(std::chrono::milliseconds(INPUT_PRESS_TICKS)); 165 | } 166 | 167 | void stop() { 168 | _timer->stop(); 169 | } 170 | 171 | uint32_t get_counter() { 172 | return _counter; 173 | } 174 | 175 | void timeout() { 176 | _counter++; 177 | if(_counter == INPUT_LONG_PRESS_COUNTS) { 178 | send_input_event(InputTypeLong, _key); 179 | } else if(_counter > INPUT_LONG_PRESS_COUNTS) { 180 | send_input_event(InputTypeRepeat, _key); 181 | } 182 | continue_timer(); 183 | } 184 | }; 185 | 186 | class HALEmulator : public QWidget { 187 | private: 188 | static const size_t buttons_count = 6; 189 | DisplayWidget* _display; 190 | QHBoxLayout* mainLayout; 191 | QGroupBox* inputs; 192 | QPushButton* button[buttons_count]; 193 | ButtonTimer* button_timer[buttons_count]; 194 | QPlainTextEdit* log; 195 | 196 | void send_input_raw_event(InputType type, InputKey key) { 197 | InputEvent event; 198 | event.type = type; 199 | event.key = key; 200 | 201 | const std::lock_guard lock(input_callback_mutex); 202 | for(auto& callback : input_callbacks) { 203 | callback.callback(&event, callback.context); 204 | } 205 | } 206 | 207 | void send_input_event(QPushButton* button, InputType type) { 208 | std::string name = button->text().toStdString(); 209 | 210 | InputEvent event; 211 | event.type = type; 212 | 213 | if(get_key_from_button_name(name.c_str(), &event.key)) { 214 | ButtonTimer* timer = button_timer[static_cast(event.key)]; 215 | if(type == InputTypePress) { 216 | timer->start(); 217 | } else if(type == InputTypeRelease) { 218 | timer->stop(); 219 | if(timer->get_counter() < INPUT_LONG_PRESS_COUNTS) { 220 | send_input_raw_event(InputTypeShort, event.key); 221 | } 222 | } 223 | 224 | send_input_raw_event(type, event.key); 225 | } 226 | } 227 | 228 | QFont get_monospace_font() { 229 | QFont font; 230 | font.setStyleHint(QFont::Monospace); 231 | font.setFamily("Monospace"); 232 | font.setFixedPitch(true); 233 | font.setPointSize(10); 234 | return font; 235 | } 236 | 237 | QPushButton* allocate_button(InputKey key) { 238 | QPushButton* btn = new QPushButton(tr(button_names[static_cast(key)])); 239 | button[static_cast(key)] = btn; 240 | button_timer[static_cast(key)] = new ButtonTimer(key); 241 | return btn; 242 | } 243 | private slots: 244 | 245 | void handle_button_pressed() { 246 | QPushButton* button = (QPushButton*)sender(); 247 | send_input_event(button, InputTypePress); 248 | } 249 | 250 | void handle_button_released() { 251 | QPushButton* button = (QPushButton*)sender(); 252 | send_input_event(button, InputTypeRelease); 253 | } 254 | 255 | public: 256 | HALEmulator(QWidget* parent = 0) { 257 | _display = new DisplayWidget(this); 258 | _display->resize(DISPLAY_WIDTH_SCALED, DISPLAY_HEIGHT_SCALED); 259 | _display->setFixedSize(DISPLAY_WIDTH_SCALED, DISPLAY_HEIGHT_SCALED); 260 | 261 | inputs = new QGroupBox(); 262 | QGridLayout* layout = new QGridLayout; 263 | layout->addWidget(allocate_button(InputKeyUp), 0, 1); 264 | layout->addWidget(allocate_button(InputKeyDown), 2, 1); 265 | layout->addWidget(allocate_button(InputKeyLeft), 1, 0); 266 | layout->addWidget(allocate_button(InputKeyRight), 1, 2); 267 | layout->addWidget(allocate_button(InputKeyOk), 1, 1); 268 | layout->addWidget(allocate_button(InputKeyBack), 2, 2); 269 | 270 | for(size_t i = 0; i < buttons_count; i++) { 271 | button[i]->setMaximumHeight(50); 272 | button[i]->setMaximumWidth(50); 273 | connect(button[i], &QPushButton::pressed, this, &HALEmulator::handle_button_pressed); 274 | connect(button[i], &QPushButton::released, this, &HALEmulator::handle_button_released); 275 | } 276 | 277 | inputs->setLayout(layout); 278 | 279 | log = new QPlainTextEdit(); 280 | log->setFont(get_monospace_font()); 281 | log->setReadOnly(true); 282 | 283 | QHBoxLayout* screen_layout = new QHBoxLayout; 284 | screen_layout->addWidget(_display); 285 | screen_layout->addWidget(inputs); 286 | 287 | QVBoxLayout* top_layout = new QVBoxLayout; 288 | top_layout->addLayout(screen_layout); 289 | top_layout->addWidget(log); 290 | 291 | mainLayout = new QHBoxLayout; 292 | mainLayout->addLayout(top_layout); 293 | setLayout(mainLayout); 294 | 295 | setWindowTitle(QApplication::translate("halemulator", "FAPulator")); 296 | log_message("FAPulator started"); 297 | } 298 | 299 | ~HALEmulator() { 300 | } 301 | 302 | void force_display_redraw() { 303 | _display->force_redraw(); 304 | } 305 | 306 | void log_message(const char* message) { 307 | QMetaObject::invokeMethod( 308 | log, "appendHtml", Qt::QueuedConnection, Q_ARG(QString, QString(message))); 309 | } 310 | }; 311 | 312 | DisplayBuffer* get_display_buffer() { 313 | display_buffer_mutex.lock(); 314 | return &display_buffer_handler; 315 | } 316 | 317 | void commit_display_buffer(bool redraw) { 318 | display_buffer_mutex.unlock(); 319 | if(redraw) { 320 | hal_emulator->force_display_redraw(); 321 | } 322 | } 323 | 324 | /***************************** Input *****************************/ 325 | 326 | void hal_input_init(void) { 327 | } 328 | 329 | extern "C" void hal_input_add_callback(InputCallback callback, void* context) { 330 | const std::lock_guard lock(input_callback_mutex); 331 | input_callbacks.push_back({callback, context}); 332 | } 333 | 334 | /***************************** Log *****************************/ 335 | 336 | #include 337 | #include 338 | #include 339 | 340 | static uint32_t start_time; 341 | 342 | void hal_log_init() { 343 | start_time = std::chrono::duration_cast( 344 | std::chrono::system_clock::now().time_since_epoch()) 345 | .count(); 346 | } 347 | 348 | uint32_t log_get_time() { 349 | return std::chrono::duration_cast( 350 | std::chrono::system_clock::now().time_since_epoch()) 351 | .count() - 352 | start_time; 353 | } 354 | 355 | void hal_log_raw(const char* text) { 356 | hal_emulator->log_message(text); 357 | } 358 | 359 | static const char* log_colors[] = { 360 | [static_cast(FuriLogLevelDefault)] = "", 361 | [static_cast(FuriLogLevelNone)] = "", 362 | [static_cast(FuriLogLevelError)] = "red", 363 | [static_cast(FuriLogLevelWarn)] = "brown", 364 | [static_cast(FuriLogLevelInfo)] = "green", 365 | [static_cast(FuriLogLevelDebug)] = "blue", 366 | [static_cast(FuriLogLevelTrace)] = "purple", 367 | }; 368 | 369 | static const char* log_letters[] = { 370 | [static_cast(FuriLogLevelDefault)] = "", 371 | [static_cast(FuriLogLevelNone)] = "", 372 | [static_cast(FuriLogLevelError)] = "E", 373 | [static_cast(FuriLogLevelWarn)] = "W", 374 | [static_cast(FuriLogLevelInfo)] = "I", 375 | [static_cast(FuriLogLevelDebug)] = "D", 376 | [static_cast(FuriLogLevelTrace)] = "T", 377 | }; 378 | 379 | void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) { 380 | const char* color = log_colors[static_cast(level)]; 381 | const char* letter = log_letters[static_cast(level)]; 382 | std::string time = std::to_string(log_get_time()); 383 | std::string message = 384 | time + " [" + letter + "][" + tag + "] "; 385 | 386 | va_list args; 387 | va_start(args, format); 388 | char* buffer = nullptr; 389 | int size = vasprintf(&buffer, format, args); 390 | va_end(args); 391 | if(size > 0) { 392 | message += buffer; 393 | free(buffer); 394 | } 395 | 396 | hal_emulator->log_message(message.c_str()); 397 | } 398 | 399 | /***************************** HAL *****************************/ 400 | 401 | void hal_pre_init(void) { 402 | hal_log_init(); 403 | hal_input_init(); 404 | furi_record_init(); 405 | 406 | int argc = 0; 407 | char** argv = NULL; 408 | main_app = new QApplication(argc, argv); 409 | hal_emulator = new HALEmulator(); 410 | hal_emulator->show(); 411 | } 412 | 413 | void hal_post_init(void) { 414 | main_app->exec(); 415 | } -------------------------------------------------------------------------------- /app/snake_game.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct { 7 | // +-----x 8 | // | 9 | // | 10 | // y 11 | uint8_t x; 12 | uint8_t y; 13 | } Point; 14 | 15 | typedef enum { 16 | GameStateLife, 17 | 18 | // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto 19 | // Armanto: While testing the early versions of the game, I noticed it was hard 20 | // to control the snake upon getting close to and edge but not crashing — especially 21 | // in the highest speed levels. I wanted the highest level to be as fast as I could 22 | // possibly make the device "run," but on the other hand, I wanted to be friendly 23 | // and help the player manage that level. Otherwise it might not be fun to play. So 24 | // I implemented a little delay. A few milliseconds of extra time right before 25 | // the player crashes, during which she can still change the directions. And if 26 | // she does, the game continues. 27 | GameStateLastChance, 28 | 29 | GameStateGameOver, 30 | } GameState; 31 | 32 | // Note: do not change without purpose. Current values are used in smart 33 | // orthogonality calculation in `snake_game_get_turn_snake`. 34 | typedef enum { 35 | DirectionUp, 36 | DirectionRight, 37 | DirectionDown, 38 | DirectionLeft, 39 | } Direction; 40 | 41 | #define MAX_SNAKE_LEN 253 42 | 43 | typedef struct { 44 | Point points[MAX_SNAKE_LEN]; 45 | uint16_t len; 46 | Direction currentMovement; 47 | Direction nextMovement; // if backward of currentMovement, ignore 48 | Point fruit; 49 | GameState state; 50 | } SnakeState; 51 | 52 | typedef enum { 53 | EventTypeTick, 54 | EventTypeKey, 55 | } EventType; 56 | 57 | typedef struct { 58 | EventType type; 59 | InputEvent input; 60 | } SnakeEvent; 61 | 62 | static void snake_game_render_callback(Canvas* const canvas, void* ctx) { 63 | const SnakeState* snake_state = acquire_mutex((ValueMutex*)ctx, 25); 64 | if(snake_state == NULL) { 65 | return; 66 | } 67 | 68 | // Before the function is called, the state is set with the canvas_reset(canvas) 69 | 70 | // Frame 71 | canvas_draw_frame(canvas, 0, 0, 128, 64); 72 | 73 | // Fruit 74 | Point f = snake_state->fruit; 75 | f.x = f.x * 4 + 1; 76 | f.y = f.y * 4 + 1; 77 | canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2); 78 | 79 | // Snake 80 | for(uint16_t i = 0; i < snake_state->len; i++) { 81 | Point p = snake_state->points[i]; 82 | p.x = p.x * 4 + 2; 83 | p.y = p.y * 4 + 2; 84 | canvas_draw_box(canvas, p.x, p.y, 4, 4); 85 | } 86 | 87 | // Game Over banner 88 | if(snake_state->state == GameStateGameOver) { 89 | // Screen is 128x64 px 90 | canvas_set_color(canvas, ColorWhite); 91 | canvas_draw_box(canvas, 34, 20, 62, 24); 92 | 93 | canvas_set_color(canvas, ColorBlack); 94 | canvas_draw_frame(canvas, 34, 20, 62, 24); 95 | 96 | canvas_set_font(canvas, FontPrimary); 97 | canvas_draw_str(canvas, 37, 31, "Game Over"); 98 | 99 | canvas_set_font(canvas, FontSecondary); 100 | char buffer[12]; 101 | snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7); 102 | // canvas_draw_str_aligned(canvas, 64, 41, buffer); 103 | canvas_draw_str(canvas, 49, 41, buffer); 104 | } 105 | 106 | release_mutex((ValueMutex*)ctx, snake_state); 107 | } 108 | 109 | static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { 110 | furi_assert(event_queue); 111 | 112 | SnakeEvent event = {.type = EventTypeKey, .input = *input_event}; 113 | furi_message_queue_put(event_queue, &event, FuriWaitForever); 114 | } 115 | 116 | static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) { 117 | furi_assert(event_queue); 118 | 119 | SnakeEvent event = {.type = EventTypeTick}; 120 | furi_message_queue_put(event_queue, &event, 0); 121 | } 122 | 123 | static void snake_game_init_game(SnakeState* const snake_state) { 124 | Point p[] = {{8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}}; 125 | memcpy(snake_state->points, p, sizeof(p)); 126 | 127 | snake_state->len = 7; 128 | 129 | snake_state->currentMovement = DirectionRight; 130 | 131 | snake_state->nextMovement = DirectionRight; 132 | 133 | Point f = {18, 6}; 134 | snake_state->fruit = f; 135 | 136 | snake_state->state = GameStateLife; 137 | } 138 | 139 | static Point snake_game_get_new_fruit(SnakeState const* const snake_state) { 140 | // 1 bit for each point on the playing field where the snake can turn 141 | // and where the fruit can appear 142 | uint16_t buffer[8]; 143 | memset(buffer, 0, sizeof(buffer)); 144 | uint8_t empty = 8 * 16; 145 | 146 | for(uint16_t i = 0; i < snake_state->len; i++) { 147 | Point p = snake_state->points[i]; 148 | 149 | if(p.x % 2 != 0 || p.y % 2 != 0) { 150 | continue; 151 | } 152 | p.x /= 2; 153 | p.y /= 2; 154 | 155 | buffer[p.y] |= 1 << p.x; 156 | empty--; 157 | } 158 | // Bit set if snake use that playing field 159 | 160 | uint16_t newFruit = rand() % empty; 161 | 162 | // Skip random number of _empty_ fields 163 | for(uint8_t y = 0; y < 8; y++) { 164 | for(uint16_t x = 0, mask = 1; x < 16; x += 1, mask <<= 1) { 165 | if((buffer[y] & mask) == 0) { 166 | if(newFruit == 0) { 167 | Point p = { 168 | .x = x * 2, 169 | .y = y * 2, 170 | }; 171 | return p; 172 | } 173 | newFruit--; 174 | } 175 | } 176 | } 177 | // We will never be here 178 | Point p = {0, 0}; 179 | return p; 180 | } 181 | 182 | static bool snake_game_collision_with_frame(Point const next_step) { 183 | // if x == 0 && currentMovement == left then x - 1 == 255 , 184 | // so check only x > right border 185 | return next_step.x > 30 || next_step.y > 14; 186 | } 187 | 188 | static bool 189 | snake_game_collision_with_tail(SnakeState const* const snake_state, Point const next_step) { 190 | for(uint16_t i = 0; i < snake_state->len; i++) { 191 | Point p = snake_state->points[i]; 192 | if(p.x == next_step.x && p.y == next_step.y) { 193 | return true; 194 | } 195 | } 196 | 197 | return false; 198 | } 199 | 200 | static Direction snake_game_get_turn_snake(SnakeState const* const snake_state) { 201 | // Sum of two `Direction` lies between 0 and 6, odd values indicate orthogonality. 202 | bool is_orthogonal = (snake_state->currentMovement + snake_state->nextMovement) % 2 == 1; 203 | return is_orthogonal ? snake_state->nextMovement : snake_state->currentMovement; 204 | } 205 | 206 | static Point snake_game_get_next_step(SnakeState const* const snake_state) { 207 | Point next_step = snake_state->points[0]; 208 | switch(snake_state->currentMovement) { 209 | // +-----x 210 | // | 211 | // | 212 | // y 213 | case DirectionUp: 214 | next_step.y--; 215 | break; 216 | case DirectionRight: 217 | next_step.x++; 218 | break; 219 | case DirectionDown: 220 | next_step.y++; 221 | break; 222 | case DirectionLeft: 223 | next_step.x--; 224 | break; 225 | } 226 | return next_step; 227 | } 228 | 229 | static void snake_game_move_snake(SnakeState* const snake_state, Point const next_step) { 230 | memmove(snake_state->points + 1, snake_state->points, snake_state->len * sizeof(Point)); 231 | snake_state->points[0] = next_step; 232 | } 233 | 234 | static void snake_game_process_game_step(SnakeState* const snake_state) { 235 | if(snake_state->state == GameStateGameOver) { 236 | return; 237 | } 238 | 239 | bool can_turn = (snake_state->points[0].x % 2 == 0) && (snake_state->points[0].y % 2 == 0); 240 | if(can_turn) { 241 | snake_state->currentMovement = snake_game_get_turn_snake(snake_state); 242 | } 243 | 244 | Point next_step = snake_game_get_next_step(snake_state); 245 | 246 | bool crush = snake_game_collision_with_frame(next_step); 247 | if(crush) { 248 | if(snake_state->state == GameStateLife) { 249 | snake_state->state = GameStateLastChance; 250 | return; 251 | } else if(snake_state->state == GameStateLastChance) { 252 | snake_state->state = GameStateGameOver; 253 | return; 254 | } 255 | } else { 256 | if(snake_state->state == GameStateLastChance) { 257 | snake_state->state = GameStateLife; 258 | } 259 | } 260 | 261 | crush = snake_game_collision_with_tail(snake_state, next_step); 262 | if(crush) { 263 | snake_state->state = GameStateGameOver; 264 | return; 265 | } 266 | 267 | bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y); 268 | if(eatFruit) { 269 | snake_state->len++; 270 | if(snake_state->len >= MAX_SNAKE_LEN) { 271 | snake_state->state = GameStateGameOver; 272 | return; 273 | } 274 | } 275 | 276 | snake_game_move_snake(snake_state, next_step); 277 | 278 | if(eatFruit) { 279 | snake_state->fruit = snake_game_get_new_fruit(snake_state); 280 | } 281 | } 282 | 283 | int32_t snake_game_app(void* p) { 284 | UNUSED(p); 285 | 286 | FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); 287 | 288 | SnakeState* snake_state = malloc(sizeof(SnakeState)); 289 | snake_game_init_game(snake_state); 290 | 291 | ValueMutex state_mutex; 292 | if(!init_mutex(&state_mutex, snake_state, sizeof(SnakeState))) { 293 | FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); 294 | free(snake_state); 295 | return 255; 296 | } 297 | 298 | ViewPort* view_port = view_port_alloc(); 299 | view_port_draw_callback_set(view_port, snake_game_render_callback, &state_mutex); 300 | view_port_input_callback_set(view_port, snake_game_input_callback, event_queue); 301 | 302 | FuriTimer* timer = 303 | furi_timer_alloc(snake_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); 304 | furi_timer_start(timer, 250); 305 | 306 | // Open GUI and register view_port 307 | Gui* gui = furi_record_open(RECORD_GUI); 308 | gui_add_view_port(gui, view_port, GuiLayerFullscreen); 309 | 310 | SnakeEvent event; 311 | for(bool processing = true; processing;) { 312 | FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); 313 | 314 | SnakeState* snake_state = (SnakeState*)acquire_mutex_block(&state_mutex); 315 | 316 | if(event_status == FuriStatusOk) { 317 | // press events 318 | if(event.type == EventTypeKey) { 319 | if(event.input.type == InputTypePress) { 320 | switch(event.input.key) { 321 | case InputKeyUp: 322 | snake_state->nextMovement = DirectionUp; 323 | break; 324 | case InputKeyDown: 325 | snake_state->nextMovement = DirectionDown; 326 | break; 327 | case InputKeyRight: 328 | snake_state->nextMovement = DirectionRight; 329 | break; 330 | case InputKeyLeft: 331 | snake_state->nextMovement = DirectionLeft; 332 | break; 333 | case InputKeyOk: 334 | if(snake_state->state == GameStateGameOver) { 335 | snake_game_init_game(snake_state); 336 | } 337 | break; 338 | case InputKeyBack: 339 | processing = false; 340 | break; 341 | default: 342 | break; 343 | } 344 | } 345 | } else if(event.type == EventTypeTick) { 346 | snake_game_process_game_step(snake_state); 347 | } 348 | } else { 349 | // event timeout 350 | } 351 | 352 | view_port_update(view_port); 353 | release_mutex(&state_mutex, snake_state); 354 | } 355 | 356 | furi_timer_free(timer); 357 | view_port_enabled_set(view_port, false); 358 | gui_remove_view_port(gui, view_port); 359 | furi_record_close(RECORD_GUI); 360 | view_port_free(view_port); 361 | furi_message_queue_free(event_queue); 362 | delete_mutex(&state_mutex); 363 | free(snake_state); 364 | 365 | return 0; 366 | } 367 | 368 | // Screen is 128x64 px 369 | // (4 + 4) * 16 - 4 + 2 + 2border == 128 370 | // (4 + 4) * 8 - 4 + 2 + 2border == 64 371 | // Game field from point{x: 0, y: 0} to point{x: 30, y: 14}. 372 | // The snake turns only in even cells - intersections. 373 | // ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐ 374 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 375 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 376 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 377 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 378 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 379 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 380 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 381 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 382 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 383 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 384 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 385 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 386 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 387 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 388 | // ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ 389 | // └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘ 390 | -------------------------------------------------------------------------------- /fapulator/flipper/lib/mlib/m-atomic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * M*LIB - Thin stdatomic wrapper for C++ compatibility 3 | * 4 | * Copyright (c) 2017-2021, Patrick Pelissier 5 | * All rights reserved. 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * + Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * + Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | #ifndef MSTARLIB_ATOMIC_H 26 | #define MSTARLIB_ATOMIC_H 27 | 28 | /* NOTE: Due to the C++ not having recognized stdatomic.h officialy, 29 | it is hard to use this header directly with a C++ compiler. 30 | See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932 31 | clang++ has no issue with this header but if someone includes 32 | atomic from C++, there is incompatibility between atomic & stdatomic. 33 | Moreover some compilers lack a working stdatomic header. 34 | GCC 4.9 doesn't have a working implementation of 'atomic'. 35 | APPLE Clang defines __GNUC__ to be only 4 despite having full support 36 | for atomic. 37 | */ 38 | #if defined(__cplusplus) && __cplusplus >= 201103L \ 39 | && !(defined(__GNUC__) && __GNUC__ < 5 && !defined(__APPLE__)) 40 | 41 | /* NOTE: This is what the stdatomic.h header shall do in C++ mode. */ 42 | #include 43 | 44 | using std::memory_order; 45 | 46 | using std::atomic_bool; 47 | using std::atomic_char; 48 | using std::atomic_short; 49 | using std::atomic_int; 50 | using std::atomic_long; 51 | using std::atomic_llong; 52 | using std::atomic_uchar; 53 | using std::atomic_schar; 54 | using std::atomic_ushort; 55 | using std::atomic_uint; 56 | using std::atomic_ulong; 57 | using std::atomic_ullong; 58 | using std::atomic_intptr_t; 59 | using std::atomic_uintptr_t; 60 | using std::atomic_size_t; 61 | using std::atomic_ptrdiff_t; 62 | using std::atomic_intmax_t; 63 | using std::atomic_uintmax_t; 64 | using std::atomic_flag; 65 | 66 | using std::kill_dependency; 67 | using std::atomic_thread_fence; 68 | using std::atomic_signal_fence; 69 | using std::atomic_is_lock_free; 70 | using std::atomic_store_explicit; 71 | using std::atomic_store; 72 | using std::atomic_load_explicit; 73 | using std::atomic_load; 74 | using std::atomic_exchange_explicit; 75 | using std::atomic_exchange; 76 | using std::atomic_compare_exchange_strong_explicit; 77 | using std::atomic_compare_exchange_strong; 78 | using std::atomic_compare_exchange_weak_explicit; 79 | using std::atomic_compare_exchange_weak; 80 | using std::atomic_fetch_add; 81 | using std::atomic_fetch_add_explicit; 82 | using std::atomic_fetch_sub; 83 | using std::atomic_fetch_sub_explicit; 84 | using std::atomic_fetch_or; 85 | using std::atomic_fetch_or_explicit; 86 | using std::atomic_fetch_xor; 87 | using std::atomic_fetch_xor_explicit; 88 | using std::atomic_fetch_and; 89 | using std::atomic_fetch_and_explicit; 90 | using std::atomic_flag_test_and_set; 91 | using std::atomic_flag_test_and_set_explicit; 92 | using std::atomic_flag_clear; 93 | using std::atomic_flag_clear_explicit; 94 | 95 | using std::memory_order_relaxed; 96 | using std::memory_order_consume; 97 | using std::memory_order_acquire; 98 | using std::memory_order_release; 99 | using std::memory_order_acq_rel; 100 | using std::memory_order_seq_cst; 101 | 102 | /* CLANG provides a warning on defining _Atomic as it sees it 103 | * as a reserved system macro. It is true. However, the goal of this 104 | * header is to provide stdatomic semantic, so it needs to define 105 | * _Atomic macro. 106 | * 107 | * So, this warning has to be ignored. 108 | * 109 | * It cannot use M_BEGIN_PROTECTED_CODE as this header is normally 110 | * independent of m-core.h 111 | */ 112 | #if defined(__clang__) && __clang_major__ >= 4 113 | _Pragma("clang diagnostic push") 114 | _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"") 115 | #endif 116 | 117 | #define _Atomic(T) std::atomic< T > 118 | 119 | #if defined(__clang__) && __clang_major__ >= 4 120 | _Pragma("clang diagnostic pop") 121 | #endif 122 | 123 | /* C11 with working stdatomic 124 | STDATOMIC doesn't work with C++ except for clang but is incompatible with atomic. 125 | GCC < 4.9 doesn't provide a compliant stdatomic.h 126 | CLANG 3.5 has issues with GCC's stdatomic.h and doesn't provide its own 127 | ICC < 18 doesn't provide a compliant stdatomic.h 128 | */ 129 | #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) ) \ 130 | || (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \ 131 | || (defined(__clang__) && (__clang_major__ * 100 + __clang_minor__) >= 308) \ 132 | || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1800) 133 | 134 | #include 135 | 136 | /* MSYS2 has a conflict between cdefs.h which defines a _Atomic macro (if not C11) 137 | not compatible with the used stdatomic.h (from GCC). 138 | Provide a configurable mechanism to undef it with auto-detection of msys2 / gcc */ 139 | #ifndef M_USE_UNDEF_ATOMIC 140 | # if defined(__MSYS__) && defined(__GNUC__) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L) 141 | # define M_USE_UNDEF_ATOMIC 1 142 | # endif 143 | #endif 144 | #if defined(M_USE_UNDEF_ATOMIC) && M_USE_UNDEF_ATOMIC == 1 145 | # undef _Atomic 146 | #endif 147 | 148 | /* Non working C++ atomic header, nor working stdatomic.h found. 149 | Write a compatible layer using mutex as slin as possible. 150 | Supports only up to 64-bits atomic (sizeof long long to be more precise). 151 | The locks are never properly cleared and remain active until 152 | the end of the program. 153 | We also assume that the call to the atomic_* interface is "macro clean". 154 | */ 155 | #else 156 | 157 | #include "m-mutex.h" 158 | #include "m-core.h" 159 | 160 | M_BEGIN_PROTECTED_CODE 161 | 162 | /* _Atomic qualifier for a type (emulation). 163 | The structure is quite large: 164 | _val : value of the atomic type, 165 | _zero : zero value of the atomic type (constant), 166 | _previous: temporary value used within the mutex lock, 167 | _lock : the mutex lock. 168 | Support up to sizeof (long long) type. 169 | */ 170 | #define _Atomic(T) \ 171 | struct { \ 172 | T volatile _val; \ 173 | T _zero; \ 174 | T _previous; \ 175 | m_mutex_t _lock; \ 176 | } 177 | 178 | /* Define the supported memory order. 179 | Even if memory order is defined, only the strongest constraint is used */ 180 | typedef enum { 181 | memory_order_relaxed, 182 | memory_order_consume, 183 | memory_order_acquire, 184 | memory_order_release, 185 | memory_order_acq_rel, 186 | memory_order_seq_cst 187 | } memory_order; 188 | 189 | typedef _Atomic(bool) atomic_bool; 190 | typedef _Atomic(char) atomic_char; 191 | typedef _Atomic(short) atomic_short; 192 | typedef _Atomic(int) atomic_int; 193 | typedef _Atomic(long) atomic_long; 194 | typedef _Atomic(long long) atomic_llong; 195 | typedef _Atomic(unsigned char) atomic_uchar; 196 | typedef _Atomic(signed char) atomic_schar; 197 | typedef _Atomic(unsigned short) atomic_ushort; 198 | typedef _Atomic(unsigned int) atomic_uint; 199 | typedef _Atomic(unsigned long) atomic_ulong; 200 | typedef _Atomic(unsigned long long) atomic_ullong; 201 | typedef _Atomic(intptr_t) atomic_intptr_t; 202 | typedef _Atomic(uintptr_t) atomic_uintptr_t; 203 | typedef _Atomic(size_t) atomic_size_t; 204 | typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t; 205 | 206 | /* Define the minimum size supported by the architecture 207 | for an atomic read or write. 208 | This can help a lot since it avoids locking for atomic_load and 209 | atomic_store. 210 | */ 211 | #if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) 212 | # define ATOMICI_MIN_RW_SIZE 8 213 | #elif defined(_M_86) || defined (__i386__) 214 | # define ATOMICI_MIN_RW_SIZE 4 215 | #else 216 | # define ATOMICI_MIN_RW_SIZE 0 217 | #endif 218 | 219 | /* Detect if stdint.h was included */ 220 | #if (defined (INTMAX_C) && defined (UINTMAX_C) && !defined(__cplusplus)) || \ 221 | defined (_STDINT_H) || defined (_STDINT_H_) || defined (_STDINT) || \ 222 | defined (_SYS_STDINT_H_) 223 | /* Define additional atomic types */ 224 | typedef _Atomic(intmax_t) atomic_intmax_t; 225 | typedef _Atomic(uintmax_t) atomic_uintmax_t; 226 | #endif 227 | 228 | /* (INTERNAL) Unlock the mutex and return the given value */ 229 | static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val) 230 | { 231 | m_mutex_unlock (*lock); 232 | return val; 233 | } 234 | 235 | /* (INTERNAL) This is the heart of the wrapper: 236 | lock the atomic value, read it and returns the value. 237 | In order to avoid any compiler extension, we need to transform the 238 | atomic type into 'long long' then convert it back to its value. 239 | This is because _previous can't be read after the lock, and we can't 240 | generate temporary variable within a macro. 241 | The trick is computing _val - _zero within the lock, then 242 | returns retvalue + _zero after the release of the lock. 243 | */ 244 | #define atomic_fetch_op(ptr, val, op) \ 245 | (m_mutex_lock((ptr)->_lock), \ 246 | (ptr)->_previous = (ptr)->_val, \ 247 | (ptr)->_val op (val), \ 248 | atomic_fetch_unlock(&(ptr)->_lock, (long long)((ptr)->_previous-(ptr)->_zero))+(ptr)->_zero) 249 | 250 | /* Perform an atomic add (EMULATION) */ 251 | #define atomic_fetch_add(ptr, val) atomic_fetch_op(ptr, val, +=) 252 | /* Perform an atomic sub (EMULATION) */ 253 | #define atomic_fetch_sub(ptr, val) atomic_fetch_op(ptr, val, -=) 254 | /* Perform an atomic or (EMULATION) */ 255 | #define atomic_fetch_or(ptr, val) atomic_fetch_op(ptr, val, |=) 256 | /* Perform an atomic xor (EMULATION) */ 257 | #define atomic_fetch_xor(ptr, val) atomic_fetch_op(ptr, val, ^=) 258 | /* Perform an atomic and (EMULATION) */ 259 | #define atomic_fetch_and(ptr, val) atomic_fetch_op(ptr, val, &=) 260 | /* Perform an atomic exchange (EMULATION) */ 261 | #define atomic_exchange(ptr, val) atomic_fetch_op(ptr, val, =) 262 | 263 | /* Initialize an atomic GLOBAL variable */ 264 | #define ATOMIC_VAR_INIT(val) { val, 0, 0, M_MUTEXI_INIT_VALUE } 265 | 266 | /* Initialize an atomic variable */ 267 | #define atomic_init(ptr, val) \ 268 | (m_mutex_init((ptr)->_lock), (ptr)->_val = val, (ptr)->_zero = 0) 269 | 270 | /* (INTERNAL) Load an atomic variable within a lock 271 | (needed for variable greater than CPU atomic size) */ 272 | #define atomic_load_lock(ptr) \ 273 | (m_mutex_lock((ptr)->_lock), \ 274 | (ptr)->_previous = (ptr)->_val, \ 275 | atomic_fetch_unlock(&(ptr)->_lock, (long long) ((ptr)->_previous-(ptr)->_zero))+(ptr)->_zero) 276 | 277 | /* (INTERNAL) Store an atomic variable within a lock 278 | (needed for variable greater than CPU atomic size) */ 279 | #define atomic_store_lock(ptr, val) \ 280 | (m_mutex_lock((ptr)->_lock), \ 281 | (ptr)->_val = (val), \ 282 | m_mutex_unlock((ptr)->_lock)) 283 | 284 | /* Atomic load of a variable (EMULATION) 285 | If the atomic type size is not greater than the CPU atomic size, 286 | we can perform a direct read of the variable (much faster) */ 287 | #define atomic_load(ptr) \ 288 | ( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE \ 289 | ? (ptr)->_val \ 290 | : atomic_load_lock(ptr)) 291 | 292 | /* Atomic store of a variable (EMULATION) 293 | If the atomic type size is not greater than the CPU atomic size, 294 | we can perform a direct write of the variable (much faster) */ 295 | #define atomic_store(ptr, val) do { \ 296 | if ( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE) { \ 297 | (ptr)->_val = (val); \ 298 | } else { \ 299 | long long _offset = (long long) ((val) - (ptr)->_zero); \ 300 | atomic_store_lock(ptr, (ptr)->_zero + _offset); \ 301 | } \ 302 | } while (0) 303 | 304 | /* Perform a CAS (Compare and swap) operation (EMULATION) */ 305 | #define atomic_compare_exchange_strong(ptr, exp, val) \ 306 | (m_mutex_lock((ptr)->_lock), \ 307 | atomic_fetch_unlock(&(ptr)->_lock, \ 308 | (ptr)->_val == *(exp) \ 309 | ? ((ptr)->_val = (val), true) \ 310 | : (*(exp) = (ptr)->_val, false))) 311 | 312 | 313 | #define atomic_fetch_add_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, +=) 314 | #define atomic_fetch_sub_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, -=) 315 | #define atomic_fetch_or_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, |=) 316 | #define atomic_fetch_xor_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, ^=) 317 | #define atomic_fetch_and_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, &=) 318 | #define atomic_exchange_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, =) 319 | #define atomic_load_explicit(ptr, mem) atomic_load(ptr) 320 | #define atomic_store_explicit(ptr, val, mem) atomic_store(ptr, val) 321 | #define kill_dependency(ptr) atomic_load(ptr) 322 | #define atomic_thread_fence(mem) (void) 0 323 | #define atomic_signal_fence(mem) (void) 0 324 | #define atomic_is_lock_free(ptr) false 325 | #define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val) 326 | #define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val) 327 | #define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val) 328 | 329 | /* TODO: Missing atomic_flag. Problem: it is supposed to be lock free! */ 330 | 331 | M_END_PROTECTED_CODE 332 | 333 | #endif 334 | 335 | #endif 336 | --------------------------------------------------------------------------------