blob: 909fe324cfb5827228b5cb4cad83c10ec76b6dd8 [file] [log] [blame]
// Copyright 2023 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// 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 <cstdint>
#define PW_LOG_MODULE_NAME "SnakeGame"
#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
#include "app_common/common.h"
#include "graphics/surface.hpp"
#include "libkudzu/framecounter.h"
#include "pw_assert/check.h"
#include "pw_board_led/led.h"
#include "pw_display/display.h"
#include "pw_framebuffer/framebuffer.h"
#include "pw_log/log.h"
#include "pw_spin_delay/delay.h"
#include "pw_system/target_hooks.h"
#include "pw_system/work_queue.h"
#include "pw_thread/detached_thread.h"
#include "pw_thread/thread_core.h"
#include "pw_touchscreen/buttons.h"
#include "pw_touchscreen/touchscreen.h"
#include "snake/game.h"
namespace {
constexpr uint32_t kFramesPerSecond = 16;
constexpr uint32_t kWaitMillis = 1000 / kFramesPerSecond;
class PollingTouchButtonsThread : public pw::thread::ThreadCore {
public:
PollingTouchButtonsThread(
pw::touchscreen::Touchscreen& touchscreen,
pw::touchscreen::DirectionButtonListener& button_listener,
int32_t display_width,
int32_t display_height)
: touchscreen_(touchscreen),
buttons_(button_listener, display_width, display_height) {}
void Run() override {
while (true) {
pw::touchscreen::TouchEvent touch_event = touchscreen_.GetTouchPoint();
buttons_.OnTouchEvent(touch_event);
pw::spin_delay::WaitMillis(kWaitMillis);
}
}
private:
pw::touchscreen::Touchscreen& touchscreen_;
pw::touchscreen::DirectionTouchButtons buttons_;
};
void MainTask(void*) {
pw::board_led::Init();
PW_CHECK_OK(Common::Init());
pw::display::Display& display = Common::GetDisplay();
pw::framebuffer::Framebuffer framebuffer = display.GetFramebuffer();
PW_CHECK(framebuffer.is_valid());
blit::Surface screen = blit::Surface(
static_cast<uint8_t*>(framebuffer.data()),
blit::PixelFormat::RGB565,
blit::Size(framebuffer.size().width, framebuffer.size().height));
screen.pen = blit::Pen(0, 0, 0, 255);
screen.clear();
const auto display_width = framebuffer.size().width;
const auto display_height = framebuffer.size().height;
display.ReleaseFramebuffer(std::move(framebuffer));
snake::Game game(display_width, display_height);
PollingTouchButtonsThread touch_buttons_thread{
Common::GetTouchscreen(), game, display_width, display_height};
pw::thread::DetachedThread(Common::TouchscreenThreadOptions(),
touch_buttons_thread);
game.Start();
// Display and app loop.
kudzu::FrameCounter frame_counter = kudzu::FrameCounter();
uint32_t last_report_time = pw::spin_delay::Millis();
while (true) {
frame_counter.StartFrame();
// Get frame buffer.
framebuffer = display.GetFramebuffer();
PW_CHECK(framebuffer.is_valid());
screen.data = static_cast<uint8_t*>(framebuffer.data());
// Clear the screen
screen.pen = blit::Pen(0, 0, 0);
screen.clear();
// Let game draw its frame.
game.OnFrame(framebuffer);
// Update timers
frame_counter.EndDraw();
display.ReleaseFramebuffer(std::move(framebuffer));
frame_counter.EndFlush();
frame_counter.LogTiming();
pw::spin_delay::WaitMillis(kWaitMillis);
// Periodically make a log message.
if (pw::spin_delay::Millis() > last_report_time + 10000) {
Common::EndOfFrameCallback();
last_report_time = pw::spin_delay::Millis();
}
}
}
} // namespace
namespace pw::system {
void UserAppInit() {
PW_LOG_INFO("UserAppInit");
pw::thread::DetachedThread(Common::DisplayDrawThreadOptions(), MainTask);
}
} // namespace pw::system