| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fcntl.h> |
| #include <fuchsia/hardware/dotmatrixdisplay/c/fidl.h> |
| #include <fuchsia/hardware/ftdi/c/fidl.h> |
| #include <lib/fdio/fdio.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <zircon/syscalls.h> |
| |
| #include <filesystem> |
| #include <iostream> |
| #include <vector> |
| |
| constexpr int kWidth = 128; |
| constexpr int kHeight = 64; |
| |
| std::vector<uint8_t> frame_buffer_(kWidth * 8); |
| |
| void ClearScreen() { |
| for (size_t i = 0; i < 8; i++) { |
| for (size_t j = 0; j < kWidth; j++) { |
| frame_buffer_[i * kWidth + j] = 0; |
| } |
| } |
| } |
| void SetPixel(uint32_t x, uint32_t y) { |
| int row = y / 8; |
| int offset = y % 8; |
| |
| uint8_t data = frame_buffer_[row * kWidth + x]; |
| uint8_t mask = static_cast<uint8_t>(1 << offset); |
| frame_buffer_[row * kWidth + x] = static_cast<uint8_t>(data | mask); |
| } |
| |
| class Invader { |
| public: |
| static constexpr int kXSize = 11; |
| static constexpr int kYSize = 7; |
| |
| Invader(int x, int y) { |
| x_ = x; |
| y_ = y; |
| } |
| |
| void Update(int rel_x, int rel_y) { |
| x_ += rel_x; |
| y_ += rel_y; |
| } |
| |
| void Draw() { |
| int x_vals[] = {3, 9, 4, 8, 3, 4, 5, 6, 7, 8, 9, 2, 3, 5, 6, 7, |
| 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 3, 4, |
| 5, 6, 7, 8, 9, 11, 1, 3, 9, 11, 4, 5, 7, 8 |
| |
| }; |
| int y_vals[] = {0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, |
| 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, |
| 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7}; |
| |
| for (size_t i = 0; i < sizeof(x_vals) / sizeof(*x_vals); i++) { |
| SetPixel(x_ + x_vals[i], y_ + y_vals[i]); |
| } |
| } |
| |
| private: |
| int x_ = 0; |
| int y_ = 0; |
| }; |
| |
| class InvaderBlock { |
| public: |
| static constexpr int kBlockWidth = kWidth - 30; |
| static constexpr int kHeightJump = 3; |
| static constexpr int kNumRows = 3; |
| static constexpr int kBlockHeight = (Invader::kYSize + 3) * kNumRows; |
| |
| InvaderBlock() { |
| for (int rows = 0; rows < 3; rows++) { |
| for (int i = 0; i < kBlockWidth / Invader::kXSize; i++) { |
| Invader inv(i * (Invader::kXSize + 1), rows * (Invader::kYSize + 3)); |
| invaders_.push_back(std::move(inv)); |
| } |
| } |
| } |
| |
| void UpdateAndDraw() { |
| int rel_x = 0; |
| int rel_y = 0; |
| if (!IsTurnAround()) { |
| rel_x = x_jump_; |
| } else { |
| rel_y += kHeightJump; |
| x_jump_ *= -1; |
| if (y_ + kBlockHeight + 5 >= kHeight) { |
| rel_x = -1 * x_; |
| rel_y = -1 * y_; |
| } |
| } |
| x_ += rel_x; |
| y_ += rel_y; |
| for (Invader& i : invaders_) { |
| i.Update(rel_x, rel_y); |
| i.Draw(); |
| } |
| } |
| |
| private: |
| bool IsTurnAround() const { |
| if (x_jump_ > 0) { |
| return (x_ + kBlockWidth) >= kWidth; |
| } else { |
| return (x_ <= 0); |
| } |
| } |
| |
| int x_ = 0; |
| int y_ = 0; |
| int x_jump_ = 1; |
| std::vector<Invader> invaders_; |
| }; |
| |
| class Player { |
| public: |
| static constexpr int kBlockWidth = 8; |
| void Draw() { |
| int x_vals[] = { |
| 4, 3, 5, 2, 6, 1, 7, 0, 8, |
| |
| }; |
| int y_vals[] = { |
| 0, 1, 1, 2, 2, 3, 3, 4, 4, |
| }; |
| |
| for (size_t i = 0; i < sizeof(x_vals) / sizeof(*x_vals); i++) { |
| SetPixel(x_ + x_vals[i], y_ + y_vals[i]); |
| } |
| } |
| |
| void UpdateAndDraw() { |
| x_ += x_jump_; |
| int rand = std::rand() % 20; |
| if (rand == 0) { |
| x_jump_ *= -1; |
| } |
| if (IsTurnAround()) { |
| x_jump_ *= -1; |
| } |
| Draw(); |
| } |
| |
| private: |
| bool IsTurnAround() { |
| if (x_jump_ > 0) { |
| return (x_ + kBlockWidth) >= kWidth; |
| } else { |
| return (x_ <= 0); |
| } |
| } |
| int x_jump_ = 1; |
| int x_ = 0; |
| int y_ = kHeight - 5; |
| }; |
| |
| int RunInvaders() { |
| const char* path = "/dev/class/dotmatrix-display/"; |
| int fd_display = -1; |
| for (const auto& entry : std::filesystem::directory_iterator(path)) { |
| fd_display = open(entry.path().c_str(), O_RDWR); |
| if (fd_display > 0) { |
| break; |
| } |
| } |
| if (fd_display <= 0) { |
| printf("Open dotmatrix-display failed with %d\n", fd_display); |
| return 1; |
| } |
| |
| zx_handle_t handle_display; |
| zx_status_t status = fdio_get_service_handle(fd_display, &handle_display); |
| if (status != ZX_OK) { |
| printf("Ftdio get handle failed with %d\n", status); |
| return 1; |
| } |
| |
| fuchsia_hardware_dotmatrixdisplay_DotmatrixDisplayConfig display_config; |
| fuchsia_hardware_dotmatrixdisplay_DotmatrixDisplayGetConfig(handle_display, |
| &display_config); |
| if (display_config.width != kWidth || display_config.height != kHeight || |
| display_config.format != |
| fuchsia_hardware_dotmatrixdisplay_PixelFormat_MONOCHROME || |
| display_config.layout != |
| fuchsia_hardware_dotmatrixdisplay_ScreenLayout_COLUMN_TB_ROW_LR) { |
| printf("Error: Display configs do not match supported config\n"); |
| printf("Width: Support: %d Recieved: %d \n", kWidth, display_config.width); |
| printf("Height: Support: %d Recieved: %d \n", kHeight, |
| display_config.height); |
| printf("Format: Support: %d Recieved: %d \n", |
| fuchsia_hardware_dotmatrixdisplay_PixelFormat_MONOCHROME, |
| display_config.format); |
| printf("Layout: Support: %d Recieved: %d \n", |
| fuchsia_hardware_dotmatrixdisplay_ScreenLayout_COLUMN_TB_ROW_LR, |
| display_config.layout); |
| return 1; |
| } |
| |
| InvaderBlock invBlock; |
| Player player; |
| Invader inv = Invader(0, 0); |
| |
| while (true) { |
| ClearScreen(); |
| invBlock.UpdateAndDraw(); |
| player.UpdateAndDraw(); |
| |
| fuchsia_hardware_dotmatrixdisplay_DotmatrixDisplaySetScreen( |
| handle_display, frame_buffer_.data(), frame_buffer_.size(), &status); |
| if (status != ZX_OK) { |
| printf("Display SetScreen failed with %d\n", status); |
| return 1; |
| } |
| } |
| return 0; |
| } |