Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/micropython-unix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: MicroPython SDL

on:
push:
pull_request:
release:
types: [created]

env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
MICROPYTHON_VERSION: v1.17
BUILD_TYPE: Release
MICROPY_VARIANT: standard

jobs:
build:
name: ${{matrix.name}}
strategy:
matrix:
include:
- os: ubuntu-20.04
name: MicroPython Build (Unix, SDL) (Linux)
cache-key: linux
apt-packages: libsdl2-dev libreadline-dev

runs-on: ${{matrix.os}}

steps:
# Check out MicroPython
- name: Checkout MicroPython
uses: actions/checkout@v2
with:
repository: pimoroni/micropython
ref: experimental/picosystem-sdl-unix #${{env.MICROPYTHON_VERSION}}
submodules: false # MicroPython submodules are hideously broken
path: micropython

- uses: actions/checkout@v2
with:
submodules: true
path: picosystem-${{ github.sha }}

# Linux deps
- name: Install deps
if: runner.os == 'Linux'
run: |
sudo apt update && sudo apt install ${{matrix.apt-packages}}

- name: Fetch base MicroPython submodules
shell: bash
working-directory: micropython
run: git submodule update --init

- name: Build mpy-cross
shell: bash
working-directory: micropython/mpy-cross
run: make

- name: Make MicroPython Build Dir
shell: bash
working-directory: micropython/ports/unix
run: mkdir build

- name: Build MicroPython
shell: bash
working-directory: micropython/ports/unix/build
run: |
cmake .. -DUSER_C_MODULES=../../../picosystem-${GITHUB_SHA}/micropython/modules/micropython-unix.cmake -DSDL2_DIR=/usr/lib/x86_64-linux-gnu/cmake/SDL2/
make -j2

- name: Rename .uf2 for artifact & release
shell: bash
working-directory: micropython/ports/unix/build
run: |
cp micropython ${{github.event.repository.name}}-${{github.sha}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}}
cp micropython ${{github.event.repository.name}}-${{github.event.release.tag_name}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}}

- name: Store .uf2 as artifact
uses: actions/upload-artifact@v2
with:
name: ${{github.event.repository.name}}-${{github.sha}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}}
path: micropython/ports/unix/build/${{github.event.repository.name}}-${{github.sha}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}}

- name: Upload .uf2
if: github.event_name == 'release'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
with:
asset_path: micropython/ports/unix/build/micropython
upload_url: ${{github.event.release.upload_url}}
asset_name: ${{github.event.repository.name}}-${{github.event.release.tag_name}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}}
asset_content_type: application/octet-stream
303 changes: 303 additions & 0 deletions libraries/hardware-unix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
#include <math.h>
#include <string.h>
#include <iostream>
#include <chrono>
#include <thread>
#include <map>
#include "SDL.h"

#include "picosystem.hpp"

SDL_Window* window = nullptr;
SDL_Thread *t_event_pump = nullptr;
SDL_Renderer *renderer = nullptr;
SDL_Texture *texture = nullptr;
SDL_mutex *m_flip = nullptr;
static bool running = true;
static bool _done_init = false;
std::chrono::time_point<std::chrono::high_resolution_clock> t_start;
std::chrono::time_point<std::chrono::high_resolution_clock> t_last_flip;

const uint32_t FPS = 50;

namespace picosystem {
uint32_t _sdl_input = 0;

std::map<int, int> keys = {
// arrow keys
{SDLK_DOWN, button::DOWN},
{SDLK_UP, button::UP},
{SDLK_LEFT, button::LEFT},
{SDLK_RIGHT, button::RIGHT},

// wasd
{SDLK_w, button::UP},
{SDLK_a, button::LEFT},
{SDLK_s, button::DOWN},
{SDLK_d, button::RIGHT},

// action buttons
{SDLK_z, button::A},
{SDLK_x, button::B},
{SDLK_c, button::X},
{SDLK_v, button::Y},

{SDLK_u, button::A},
{SDLK_i, button::B},
{SDLK_o, button::X},
{SDLK_p, button::Y},
};

std::map<int, int> buttons = {
// dpad
{SDL_CONTROLLER_BUTTON_DPAD_DOWN, button::DOWN},
{SDL_CONTROLLER_BUTTON_DPAD_UP, button::UP},
{SDL_CONTROLLER_BUTTON_DPAD_LEFT, button::LEFT},
{SDL_CONTROLLER_BUTTON_DPAD_RIGHT, button::RIGHT},

// action buttons
{SDL_CONTROLLER_BUTTON_A, button::A},
{SDL_CONTROLLER_BUTTON_B, button::B},
{SDL_CONTROLLER_BUTTON_X, button::X},
{SDL_CONTROLLER_BUTTON_Y, button::Y},
};

enum pin {
RED = 14, GREEN = 13, BLUE = 15, // user rgb led
CS = 5, SCK = 6, MOSI = 7, // spi
VSYNC = 8, DC = 9, LCD_RESET = 4, BACKLIGHT = 12, // screen
AUDIO = 11, // audio
CHARGE_LED = 2, CHARGING = 24, BATTERY_LEVEL = 26 // battery / charging
};

int _sdl_find_key(int key) {
auto iter = keys.find(key);
if (iter == keys.end()) return 0;
else return iter->second;
}

int _sdl_find_button(int button) {
auto iter = buttons.find(button);
if (iter == buttons.end()) return 0;
else return iter->second;
}

void _sdl_resize() {
SDL_LockMutex(m_flip);
SDL_DestroyTexture(texture);
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB4444, SDL_TEXTUREACCESS_STREAMING, SCREEN->w, SCREEN->h);
SDL_UnlockMutex(m_flip);
}

void _sdl_handle_keyboard(int key, bool state) {
if (int button = _sdl_find_key(key)) {
_sdl_input &= ~(1U << button);
_sdl_input |= state << button;
}
}

void _sdl_handle_controller_button(int btn, bool state) {
if (int button = _sdl_find_button(btn)) {
_sdl_input &= ~(1U << button);
_sdl_input |= state << button;
}
}

void _sdl_handle_event(SDL_Event &event) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;

case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
_sdl_resize();
}
break;

case SDL_KEYDOWN: // fall-though
case SDL_KEYUP:
_sdl_handle_keyboard(event.key.keysym.sym, event.type == SDL_KEYDOWN);
break;

case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
_sdl_handle_controller_button(event.cbutton.button, event.type == SDL_CONTROLLERBUTTONDOWN);
break;

case SDL_CONTROLLERDEVICEADDED:
SDL_GameControllerOpen(event.cdevice.which);
break;

case SDL_CONTROLLERDEVICEREMOVED:
SDL_GameControllerClose(SDL_GameControllerFromInstanceID(event.cdevice.which));
break;

case SDL_RENDER_TARGETS_RESET:
std::cout << "Targets reset" << std::endl;

case SDL_RENDER_DEVICE_RESET:
std::cout << "Device reset" << std::endl;
break;

default:
break;
}
}

static int _sdl_event_pump(void *ptr) {
SDL_Event event;

while (running && SDL_WaitEvent(&event)) {
_sdl_handle_event(event);
}

exit(0);
}

bool pressed(uint32_t b) {
return !(_io & (1U << b)) && (_lio & (1U << b));
}

bool button(uint32_t b) {
return !(_io & (1U << b));
}

void _reset_to_dfu() {
}

float _battery_voltage() {
return 3.3f;
}

uint32_t time() {
auto elapsed = std::chrono::high_resolution_clock::now() - t_start;
return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
}

uint32_t time_us() {
auto elapsed = std::chrono::high_resolution_clock::now() - t_start;
return std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
}

void sleep(uint32_t d) {
std::this_thread::sleep_for(std::chrono::milliseconds(d));
}

void sleep_us(uint32_t d) {
std::this_thread::sleep_for(std::chrono::microseconds(d));
}

uint32_t battery() {
// convert to 0..1 range for battery between 2.8v and 4.1v
float c = (_battery_voltage() - 2.8f) / 1.3f;
return std::max(0.0f, std::min(1.0f, c)) * 100;
}

void _wait_vsync() {}

bool _in_flip = false;
bool _is_flipping() {
return _in_flip;
}

void _flip() {
SDL_LockMutex(m_flip);
SDL_Rect dest;
dest.x = 0;
dest.y = 0;
dest.w = SCREEN->w;
dest.h = SCREEN->h;

color_t data[SCREEN->w * SCREEN->h];
for (auto i = 0; i < SCREEN->w * SCREEN->h; i++) {
data[i] = (SCREEN->data[i] >> 8) | ((SCREEN->data[i] & 0xff) << 8);
}

SDL_UpdateTexture(texture, nullptr, data, SCREEN->w * 2);

SDL_SetRenderTarget(renderer, nullptr);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, (const SDL_Rect *)&dest);

SDL_RenderPresent(renderer);
SDL_UnlockMutex(m_flip);

t_last_flip = std::chrono::high_resolution_clock::now();
}

uint16_t _gamma_correct(uint8_t v) {
float gamma = 2.8;
return (uint16_t)(pow((float)(v) / 100.0f, gamma) * 65535.0f + 0.5f);
}

void backlight(uint8_t b) {
(void)b;
}

void _play_note(uint32_t f, uint32_t v) {
(void)f;
(void)v;
}

void led(uint8_t r, uint8_t g, uint8_t b) {
(void)r;
(void)g;
(void)b;
}

uint32_t _gpio_get() {
return ~_sdl_input;
}

void _sdl_set_mode() {
SDL_RenderSetLogicalSize(renderer, SCREEN->w, SCREEN->h);
SDL_RenderSetIntegerScale(renderer, SDL_TRUE);
}

void _init_hardware() {
if(_done_init) return;
_done_init = true;

t_start = std::chrono::high_resolution_clock::now();
m_flip = SDL_CreateMutex();

std::cout << "Powered by PicoSystem SDL2 debugger runtime" << std::endl << std::endl;
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_GAMECONTROLLER|SDL_INIT_AUDIO) < 0) {
std::cerr << "could not initialize SDL2: " << SDL_GetError() << std::endl;
return;
}

std::cout << "Screen " << SCREEN->w << "x" << SCREEN->h << std::endl << std::endl;

window = SDL_CreateWindow(
"SDL Debug",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SCREEN->w*2, SCREEN->h*2,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
);

if (window == nullptr) {
std::cerr << "could not create window: " << SDL_GetError() << std::endl;
return;
}
SDL_SetWindowMinimumSize(window, SCREEN->w, SCREEN->h);

renderer = SDL_CreateRenderer(window, -1, 0);
if (renderer == nullptr) {
std::cerr << "could not create renderer: " << SDL_GetError() << std::endl;
}

_sdl_set_mode();

SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);

_sdl_resize();

t_event_pump = SDL_CreateThread(_sdl_event_pump, "Events", nullptr);
}

}
Loading