diff --git a/firmware/application/external/breakout/Arial12x12.h b/firmware/application/external/breakout/Arial12x12.h new file mode 100644 index 000000000..4dc451eeb --- /dev/null +++ b/firmware/application/external/breakout/Arial12x12.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Mark Thompson + * + * This file is part of PortaPack. + * + * 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, 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +// dummy include file to avoid changing original source + +#ifndef __UI_Arial12x12_H__ +#define __UI_Arial12x12_H__ + +#define Arial12x12 (0) + +#endif /*__UI_Arial12x12_H__*/ diff --git a/firmware/application/external/breakout/SPI_TFT_ILI9341.h b/firmware/application/external/breakout/SPI_TFT_ILI9341.h new file mode 100644 index 000000000..245a201b2 --- /dev/null +++ b/firmware/application/external/breakout/SPI_TFT_ILI9341.h @@ -0,0 +1,68 @@ +/* + * ------------------------------------------------------------ + * | Made by RocketGod | + * | Find me at https://betaskynet.com | + * | Argh matey! | + * ------------------------------------------------------------ + */ + +#ifndef __UI_SPI_TFT_ILI9341_H__ +#define __UI_SPI_TFT_ILI9341_H__ + +ui::Painter painter; + +static int bg_color; + +enum { + White, + Blue, + Yellow, + Purple, + Green, + Red, + Maroon, + Orange, + Black, +}; + +static const Color pp_colors[] = { + Color::white(), + Color::blue(), + Color::yellow(), + Color::purple(), + Color::green(), + Color::red(), + Color::magenta(), + Color::orange(), + Color::black(), +}; + +static void claim(__FILE* x) { + (void)x; +}; + +static void cls() { + painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black()); +}; + +static void background(int color) { + bg_color = color; +}; + +static void set_orientation(int x) { + (void)x; +}; + +static void set_font(unsigned char* x) { + (void)x; +}; + +static void fillrect(int x1, int y1, int x2, int y2, int color) { + painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]); +}; + +static void rect(int x1, int y1, int x2, int y2, int color) { + painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]); +}; + +#endif /*__UI_SPI_TFT_ILI9341_H__*/ diff --git a/firmware/application/external/breakout/breakout.cpp b/firmware/application/external/breakout/breakout.cpp new file mode 100644 index 000000000..ee198f4c7 --- /dev/null +++ b/firmware/application/external/breakout/breakout.cpp @@ -0,0 +1,429 @@ +/* + * ------------------------------------------------------------ + * | Made by RocketGod | + * | Find me at https://betaskynet.com | + * | Argh matey! | + * ------------------------------------------------------------ + */ + +#include "mbed.h" +#include "SPI_TFT_ILI9341.h" +#include "Arial12x12.h" +#include "ui.hpp" + +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 +#define PADDLE_WIDTH 40 +#define PADDLE_HEIGHT 10 +#define BALL_SIZE 8 +#define BRICK_WIDTH 20 +#define BRICK_HEIGHT 10 +#define BRICK_ROWS 5 +#define BRICK_COLS 10 +#define BRICK_GAP 2 +#define GAME_AREA_TOP 50 +#define GAME_AREA_BOTTOM 310 +#define PADDLE_Y (GAME_AREA_BOTTOM - PADDLE_HEIGHT) +#define BALL_SPEED_INCREASE 0.1f + +#define STATE_MENU 0 +#define STATE_PLAYING 1 +#define STATE_GAME_OVER 3 + +#define COLOR_BACKGROUND Black +#define COLOR_PADDLE Blue +#define COLOR_BALL White +#define COLOR_BORDER White +#define COLOR_BRICK_COLORS \ + { Red, Orange, Yellow, Green, Purple } + +Ticker game_timer; + +int paddle_x = (SCREEN_WIDTH - PADDLE_WIDTH) / 2; +float ball_x = SCREEN_WIDTH / 2; +float ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1; +float ball_dx = 1.5f; +float ball_dy = -2.0f; +int score = 0; +int lives = 3; +int level = 1; +int game_state = STATE_MENU; +bool initialized = false; +bool ball_attached = true; +unsigned int brick_count = 0; + +bool bricks[BRICK_ROWS][BRICK_COLS]; +int brick_colors[BRICK_ROWS]; + +extern ui::Painter painter; + +void init_game(); +void init_level(); +void draw_screen(); +void draw_bricks(); +void draw_paddle(); +void draw_ball(); +void draw_score(); +void draw_lives(); +void draw_level(); +void draw_borders(); +void move_paddle_left(); +void move_paddle_right(); +void launch_ball(); +void update_game(); +void check_collisions(); +bool check_brick_collision(int row, int col); +void handle_game_over(); +void show_menu(); +void show_game_over(); +bool check_level_complete(); +void next_level(); +void reset_game(); + +void game_timer_check() { + if (game_state == STATE_PLAYING) { + update_game(); + } +} + +void init_game() { + claim(stdout); + set_orientation(2); + set_font((unsigned char*)Arial12x12); + + paddle_x = (SCREEN_WIDTH - PADDLE_WIDTH) / 2; + score = 0; + lives = 3; + level = 1; + + brick_colors[0] = Red; + brick_colors[1] = Orange; + brick_colors[2] = Yellow; + brick_colors[3] = Green; + brick_colors[4] = Purple; + + init_level(); + + game_state = STATE_MENU; + show_menu(); +} + +void init_level() { + ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2); + ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1; + + float speed_multiplier = (level == 1) ? 1.0f : 1.0f + ((level - 1) * BALL_SPEED_INCREASE); + ball_dx = (ball_dx > 0 ? 1.5f : -1.5f) * speed_multiplier; + ball_dy = -2.0f * speed_multiplier; + + ball_attached = true; + + brick_count = 0; + for (int row = 0; row < BRICK_ROWS; row++) { + for (int col = 0; col < BRICK_COLS; col++) { + bricks[row][col] = true; + brick_count++; + } + } +} + +void draw_screen() { + cls(); + background(COLOR_BACKGROUND); + + draw_borders(); + draw_bricks(); + draw_paddle(); + draw_ball(); + draw_score(); + draw_lives(); + draw_level(); +} + +void draw_borders() { + rect(0, GAME_AREA_TOP - 1, SCREEN_WIDTH, GAME_AREA_TOP, COLOR_BORDER); +} + +void draw_bricks() { + for (int row = 0; row < BRICK_ROWS; row++) { + for (int col = 0; col < BRICK_COLS; col++) { + if (bricks[row][col]) { + int x = col * (BRICK_WIDTH + BRICK_GAP); + int y = GAME_AREA_TOP + row * (BRICK_HEIGHT + BRICK_GAP) + 5; + fillrect(x, y, x + BRICK_WIDTH, y + BRICK_HEIGHT, brick_colors[row]); + rect(x, y, x + BRICK_WIDTH, y + BRICK_HEIGHT, Black); + } + } + } +} + +void draw_paddle() { + fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_PADDLE); +} + +void draw_ball() { + fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BALL); +} + +void draw_score() { + auto style = *ui::Theme::getInstance()->fg_green; + painter.draw_string({5, 10}, style, "Score: " + std::to_string(score)); +} + +void draw_lives() { + auto style = *ui::Theme::getInstance()->fg_red; + painter.draw_string({5, 30}, style, "Lives: " + std::to_string(lives)); +} + +void draw_level() { + auto style = *ui::Theme::getInstance()->fg_yellow; + painter.draw_string({80, 30}, style, "Level: " + std::to_string(level)); +} + +void move_paddle_left() { + if (paddle_x > 0) { + fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_BACKGROUND); + if (ball_attached) { + fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND); + } + + paddle_x -= 10; + if (paddle_x < 0) paddle_x = 0; + + if (ball_attached) { + ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2); + } + + draw_paddle(); + if (ball_attached) { + draw_ball(); + } + } +} + +void move_paddle_right() { + if (paddle_x < SCREEN_WIDTH - PADDLE_WIDTH) { + fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_BACKGROUND); + if (ball_attached) { + fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND); + } + + paddle_x += 10; + if (paddle_x > SCREEN_WIDTH - PADDLE_WIDTH) paddle_x = SCREEN_WIDTH - PADDLE_WIDTH; + + if (ball_attached) { + ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2); + } + + draw_paddle(); + if (ball_attached) { + draw_ball(); + } + } +} + +void launch_ball() { + if (ball_attached) { + ball_attached = false; + ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2); + ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1; + float speed_multiplier = (level == 1) ? 1.0f : 1.0f + ((level - 1) * BALL_SPEED_INCREASE); + ball_dx = 1.5f * speed_multiplier; + ball_dy = -2.0f * speed_multiplier; + } +} + +void update_game() { + if (ball_attached) { + return; + } + + fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND); + + float next_ball_y = ball_y + ball_dy; + if (next_ball_y > GAME_AREA_BOTTOM) { + lives--; + draw_lives(); + if (lives <= 0) { + handle_game_over(); + } else { + ball_attached = true; + ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2); + ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1; + draw_ball(); + } + return; + } + + ball_x += ball_dx; + ball_y = next_ball_y; + + if (ball_x < 0) { + ball_x = 0; + ball_dx = -ball_dx; + } else if (ball_x > SCREEN_WIDTH - BALL_SIZE) { + ball_x = SCREEN_WIDTH - BALL_SIZE; + ball_dx = -ball_dx; + } + + if (ball_y < GAME_AREA_TOP) { + ball_y = GAME_AREA_TOP; + ball_dy = -ball_dy; + } + + if (ball_y + BALL_SIZE >= PADDLE_Y && ball_y <= PADDLE_Y + PADDLE_HEIGHT) { + if (ball_x + BALL_SIZE >= paddle_x && ball_x <= paddle_x + PADDLE_WIDTH) { + ball_y = PADDLE_Y - BALL_SIZE; + float hit_position = (ball_x + (BALL_SIZE / 2)) - paddle_x; + float angle = (hit_position / PADDLE_WIDTH) - 0.5f; + ball_dx = angle * 4.0f; + if (ball_dx > -0.5f && ball_dx < 0.5f) { + ball_dx = (ball_dx > 0) ? 0.5f : -0.5f; + } + ball_dy = -ball_dy; + } + } + + check_collisions(); + + draw_ball(); + + if (check_level_complete()) { + next_level(); + } +} + +void check_collisions() { + int grid_x = ball_x / (BRICK_WIDTH + BRICK_GAP); + int grid_y = (ball_y - GAME_AREA_TOP - 5) / (BRICK_HEIGHT + BRICK_GAP); + + for (int row = grid_y - 1; row <= grid_y + 1; row++) { + for (int col = grid_x - 1; col <= grid_x + 1; col++) { + if (row >= 0 && row < BRICK_ROWS && col >= 0 && col < BRICK_COLS) { + if (bricks[row][col] && check_brick_collision(row, col)) { + return; + } + } + } + } +} + +bool check_brick_collision(int row, int col) { + int brick_x = col * (BRICK_WIDTH + BRICK_GAP); + int brick_y = GAME_AREA_TOP + row * (BRICK_HEIGHT + BRICK_GAP) + 5; + + if (ball_x + BALL_SIZE >= brick_x && ball_x <= brick_x + BRICK_WIDTH && + ball_y + BALL_SIZE >= brick_y && ball_y <= brick_y + BRICK_HEIGHT) { + fillrect(brick_x, brick_y, brick_x + BRICK_WIDTH, brick_y + BRICK_HEIGHT, COLOR_BACKGROUND); + + bricks[row][col] = false; + brick_count--; + + score += (5 - row) * 10; + draw_score(); + + float center_x = brick_x + BRICK_WIDTH / 2; + float center_y = brick_y + BRICK_HEIGHT / 2; + float ball_center_x = ball_x + BALL_SIZE / 2; + float ball_center_y = ball_y + BALL_SIZE / 2; + float dx = std::abs(ball_center_x - center_x); + float dy = std::abs(ball_center_y - center_y); + + if (dx * BRICK_HEIGHT > dy * BRICK_WIDTH) { + ball_dx = -ball_dx; + } else { + ball_dy = -ball_dy; + } + + return true; + } + + return false; +} + +bool check_level_complete() { + return brick_count == 0; +} + +void next_level() { + level++; + init_level(); + draw_screen(); +} + +void handle_game_over() { + game_state = STATE_GAME_OVER; + show_game_over(); +} + +void show_menu() { + cls(); + background(COLOR_BACKGROUND); + + auto style_yellow = *ui::Theme::getInstance()->fg_yellow; + auto style_white = *ui::Theme::getInstance()->fg_light; + auto style_green = *ui::Theme::getInstance()->fg_green; + auto style_red = *ui::Theme::getInstance()->fg_red; + + painter.draw_string({0, 40}, style_yellow, "* * * BREAKOUT * * *"); + painter.draw_string({0, 70}, style_white, "========================"); + painter.draw_string({0, 120}, style_green, "| ROTARY: MOVE PADDLE |"); + painter.draw_string({0, 150}, style_green, "| SELECT: START/LAUNCH |"); + painter.draw_string({0, 190}, style_white, "========================"); + painter.draw_string({24, 230}, style_red, "* PRESS SELECT *"); +} + +void show_game_over() { + cls(); + background(COLOR_BACKGROUND); + + auto style_red = *ui::Theme::getInstance()->fg_red; + auto style_yellow = *ui::Theme::getInstance()->fg_yellow; + auto style_green = *ui::Theme::getInstance()->fg_green; + + painter.draw_string({72, 120}, style_red, "GAME OVER"); + painter.draw_string({12, 160}, style_yellow, "FINAL SCORE: " + std::to_string(score)); + painter.draw_string({0, 200}, style_green, "PRESS SELECT TO RESTART"); + + wait(1); +} + +void reset_game() { + level = 1; + score = 0; + lives = 3; + game_state = STATE_PLAYING; + init_level(); + draw_screen(); +} + +int main() { + if (!initialized) { + initialized = true; + game_timer.attach(&game_timer_check, 1.0 / 60.0); + init_game(); + } + + while (1) { + if (but_SELECT && game_state == STATE_MENU) { + game_state = STATE_PLAYING; + reset_game(); + } + + if (but_SELECT && game_state == STATE_GAME_OVER) { + reset_game(); + } + + if (but_SELECT && game_state == STATE_PLAYING && ball_attached) { + launch_ball(); + } + + if (but_LEFT && game_state == STATE_PLAYING) { + move_paddle_left(); + } + + if (but_RIGHT && game_state == STATE_PLAYING) { + move_paddle_right(); + } + } +} \ No newline at end of file diff --git a/firmware/application/external/breakout/main.cpp b/firmware/application/external/breakout/main.cpp new file mode 100644 index 000000000..4522e5f2f --- /dev/null +++ b/firmware/application/external/breakout/main.cpp @@ -0,0 +1,70 @@ +/* + * ------------------------------------------------------------ + * | Made by RocketGod | + * | Find me at https://betaskynet.com | + * | Argh matey! | + * ------------------------------------------------------------ + */ + +#include "ui.hpp" +#include "ui_breakout.hpp" +#include "ui_navigation.hpp" +#include "external_app.hpp" + +namespace ui::external_app::breakout { +void initialize_app(ui::NavigationView& nav) { + nav.push(); +} +} // namespace ui::external_app::breakout + +extern "C" { + +__attribute__((section(".external_app.app_breakout.application_information"), used)) application_information_t _application_information_breakout = { + /*.memory_location = */ (uint8_t*)0x00000000, // will be filled at compile time + /*.externalAppEntry = */ ui::external_app::breakout::initialize_app, + /*.header_version = */ CURRENT_HEADER_VERSION, + /*.app_version = */ VERSION_MD5, + + /*.app_name = */ "Breakout", + /*.bitmap_data = */ { + 0xFF, + 0xFF, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0xFF, + 0xFF, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0xFF, + 0xFF, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0xFF, + 0xFF, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + }, + /*.icon_color = */ ui::Color::green().v, + /*.menu_location = */ app_location_t::UTILITIES, + /*.desired_menu_position = */ -1, + + /*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0}, + /*.m4_app_offset = */ 0x00000000, // will be filled at compile time +}; +} \ No newline at end of file diff --git a/firmware/application/external/breakout/mbed.h b/firmware/application/external/breakout/mbed.h new file mode 100644 index 000000000..d06d09a18 --- /dev/null +++ b/firmware/application/external/breakout/mbed.h @@ -0,0 +1,105 @@ +/* + * ------------------------------------------------------------ + * | Made by RocketGod | + * | Find me at https://betaskynet.com | + * | Argh matey! | + * ------------------------------------------------------------ + */ + +#ifndef __UI_mbed_H__ +#define __UI_mbed_H__ + +using Callback = void (*)(void); + +#define wait_us(x) (void)0 +#define wait(x) chThdSleepMilliseconds(x * 1000) +#define PullUp 1 + +#include "ui_navigation.hpp" + +enum { + dp0, + dp1, + dp2, + dp3, + dp4, + dp5, + dp6, + dp7, + dp8, + dp9, + dp10, + dp11, + dp12, + dp13, + dp14, + dp15, + dp16, + dp17, + dp18, + dp19, + dp20, + dp21, + dp22, + dp23, + dp24, + dp25, +}; + +static bool but_RIGHT; +static bool but_LEFT; +static bool but_SELECT; + +class Timer { + public: + Timer() { (void)0; }; + void reset() { (void)0; }; + void start() { (void)0; } + uint32_t read_ms() { return 1000; }; + + private: +}; + +static Callback game_update_callback; +static uint32_t game_update_timeout; +static uint32_t game_update_counter; + +static void check_game_timer() { + if (game_update_callback) { + if (++game_update_counter >= game_update_timeout) { + game_update_counter = 0; + game_update_callback(); + } + } +} + +class Ticker { + public: + Ticker() { (void)0; }; + + void attach(Callback func, double delay_sec) { + game_update_callback = func; + game_update_timeout = delay_sec * 60; + } + + void detach() { + game_update_callback = nullptr; + } + + private: +}; + +static Callback button_callback; + +class InterruptIn { + public: + InterruptIn(int reg) { + (void)reg; + }; + void fall(Callback func) { button_callback = func; }; + void mode(int v) { (void)v; }; + + private: +}; + +#endif /*__UI_mbed_H__*/ \ No newline at end of file diff --git a/firmware/application/external/breakout/ui_breakout.cpp b/firmware/application/external/breakout/ui_breakout.cpp new file mode 100644 index 000000000..e3fe768d7 --- /dev/null +++ b/firmware/application/external/breakout/ui_breakout.cpp @@ -0,0 +1,79 @@ +/* + * ------------------------------------------------------------ + * | Made by RocketGod | + * | Find me at https://betaskynet.com | + * | Argh matey! | + * ------------------------------------------------------------ + */ + +#include "ui_breakout.hpp" + +namespace ui::external_app::breakout { + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#include "breakout.cpp" +#pragma GCC diagnostic pop + +BreakoutView::BreakoutView(NavigationView& nav) + : nav_{nav} { + add_children({&dummy}); + game_timer.attach(&game_timer_check, 1.0 / 60.0); +} + +void BreakoutView::on_show() { +} + +void BreakoutView::paint(Painter& painter) { + (void)painter; + + if (!initialized) { + initialized = true; + std::srand(LPC_RTC->CTIME0); + init_game(); + } +} + +void BreakoutView::frame_sync() { + check_game_timer(); + set_dirty(); +} + +bool BreakoutView::on_encoder(const EncoderEvent delta) { + if (game_state == STATE_PLAYING) { + if (delta > 0) { + move_paddle_right(); + set_dirty(); + } else if (delta < 0) { + move_paddle_left(); + set_dirty(); + } + } + return true; +} + +bool BreakoutView::on_key(const KeyEvent key) { + if (key == KeyEvent::Select) { + if (game_state == STATE_MENU) { + game_state = STATE_PLAYING; + reset_game(); + } else if (game_state == STATE_PLAYING && ball_attached) { + launch_ball(); + } else if (game_state == STATE_GAME_OVER) { + reset_game(); + } + } else if (key == KeyEvent::Left) { + if (game_state == STATE_PLAYING) { + move_paddle_left(); + } + } else if (key == KeyEvent::Right) { + if (game_state == STATE_PLAYING) { + move_paddle_right(); + } + } + + set_dirty(); + return true; +} + +} // namespace ui::external_app::breakout \ No newline at end of file diff --git a/firmware/application/external/breakout/ui_breakout.hpp b/firmware/application/external/breakout/ui_breakout.hpp new file mode 100644 index 000000000..14724ec64 --- /dev/null +++ b/firmware/application/external/breakout/ui_breakout.hpp @@ -0,0 +1,53 @@ +/* + * ------------------------------------------------------------ + * | Made by RocketGod | + * | Find me at https://betaskynet.com | + * | Argh matey! | + * ------------------------------------------------------------ + */ + +#ifndef __UI_BREAKOUT_H__ +#define __UI_BREAKOUT_H__ + +#include "ui_navigation.hpp" +#include "event_m0.hpp" +#include "message.hpp" +#include "irq_controls.hpp" +#include "random.hpp" +#include "lpc43xx_cpp.hpp" +#include "limits.h" +#include "ui_widget.hpp" + +namespace ui::external_app::breakout { + +class BreakoutView : public View { + public: + BreakoutView(NavigationView& nav); + void on_show() override; + + std::string title() const override { return "Breakout"; }; + + void focus() override { dummy.focus(); }; + void paint(Painter& painter) override; + void frame_sync(); + bool on_encoder(const EncoderEvent event) override; + bool on_key(KeyEvent key) override; + + private: + bool initialized = false; + NavigationView& nav_; + + Button dummy{ + {240, 0, 0, 0}, + ""}; + + MessageHandlerRegistration message_handler_frame_sync{ + Message::ID::DisplayFrameSync, + [this](const Message* const) { + this->frame_sync(); + }}; +}; + +} // namespace ui::external_app::breakout + +#endif /*__UI_BREAKOUT_H__*/ \ No newline at end of file diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index f5c1425b7..9218e2cf5 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -4,6 +4,10 @@ set(EXTCPPSRC external/tetris/main.cpp external/tetris/ui_tetris.cpp + #tetris + external/breakout/main.cpp + external/breakout/ui_breakout.cpp + #afsk_rx external/afsk_rx/main.cpp external/afsk_rx/ui_afsk_rx.cpp @@ -187,6 +191,7 @@ set(EXTAPPLIST spainter keyfob tetris + breakout extsensors foxhunt_rx audio_test diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index 74d487a57..aac524cb9 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -38,6 +38,7 @@ MEMORY ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k ram_external_app_extsensors(rwx) : org = 0xADBF0000, len = 32k + ram_external_app_breakout(rwx) : org = 0xADE00000, len = 32k ram_external_app_foxhunt_rx(rwx) : org = 0xADC00000, len = 32k ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k @@ -152,6 +153,12 @@ SECTIONS *(*ui*external_app*tetris*); } > ram_external_app_tetris + .external_app_breakout : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.external_app.app_breakout.application_information)); + *(*ui*external_app*breakout*); + } > ram_external_app_breakout + .external_app_extsensors : ALIGN(4) SUBALIGN(4) { KEEP(*(.external_app.app_extsensors.application_information));