diff options
author | ConfuSomu | 2024-02-04 14:38:58 -0500 |
---|---|---|
committer | ConfuSomu | 2024-02-04 14:38:58 -0500 |
commit | 9a74ae13182061a239f308011a3fd3ba8121c015 (patch) | |
tree | 836f7b6848bd0867338966955c242e5a17d33407 /hal | |
parent | 6b306c8dcd65016b6e022c648e2477931f9c4b95 (diff) | |
download | pico-watch-9a74ae13182061a239f308011a3fd3ba8121c015.tar pico-watch-9a74ae13182061a239f308011a3fd3ba8121c015.tar.gz pico-watch-9a74ae13182061a239f308011a3fd3ba8121c015.zip |
Implement basic SDL2 port
This allows using pico watch on a desktop computer/host which allows a
faster development cycle and easy screenshots.
Diffstat (limited to 'hal')
-rw-r--r-- | hal/api.cpp | 25 | ||||
-rw-r--r-- | hal/api.hpp | 9 | ||||
-rw-r--r-- | hal/sdl2/display.cpp | 137 | ||||
-rw-r--r-- | hal/sdl2/display.hpp | 30 |
4 files changed, 196 insertions, 5 deletions
diff --git a/hal/api.cpp b/hal/api.cpp index 92b3cd1..f15c4ec 100644 --- a/hal/api.cpp +++ b/hal/api.cpp @@ -1,10 +1,15 @@ #include <stdio.h> +#ifndef SDL2_BUILD #include "pico/stdlib.h" extern "C" { #include "hardware/rtc.h" } +#else +#include "sdl2/display.hpp" +#endif #include "api.hpp" +#include "../buttons.hpp" #include "../init.hpp" #include "../globals.hpp" @@ -35,6 +40,7 @@ void Api::display_set_contrast(unsigned char contrast) { int Api::display_write_string(int iScrollX, int x, int y, const char *szMsg, int iSize, int bInvert, int bRender) { oledWriteString(&m_oled, iScrollX, x, y, szMsg, iSize, bInvert, 0); m_writebb_needed = true; + return 0; } void Api::display_fill(unsigned char ucData, int bRender) { @@ -60,10 +66,15 @@ void Api::display_write_buffer(uint8_t *pBuffer) { oledDumpBuffer(&m_oled, pBuffer); } -void Api::display_write_backbuffer() { +bool Api::display_write_backbuffer() { +#ifndef SDL2_BUILD if (m_writebb_needed) oledDumpBuffer(&m_oled, m_ucBuffer); m_writebb_needed = false; + return false; +#else + return m_sdl_display.update_display(m_ucBuffer); +#endif } int Api::display_write_pixel(int x, int y, unsigned char ucColor, int bRender) { @@ -284,7 +295,7 @@ bool Api::gui_footer_text(std::string text, int offset_x, int offset_row, bool i oledRectangle(&m_oled, 0,56-offset_row*8, 127,64-offset_row*8, invert, 1); m_writebb_needed = true; } - oledWriteString(&m_oled, 0,offset_x,7-offset_row, text.c_str(), font, invert, 0); + return oledWriteString(&m_oled, 0,offset_x,7-offset_row, text.c_str(), font, invert, 0); } bool Api::gui_header_text(std::string text, int offset_x, int offset_row, bool invert, bool no_bg) { @@ -305,7 +316,7 @@ bool Api::gui_header_text(std::string text, int offset_x, int offset_row, bool i oledRectangle(&m_oled, 0,0+offset_row*8, 127,8+offset_row*8, invert, 1); m_writebb_needed = true; } - oledWriteString(&m_oled, 0,offset_x,0+offset_row, text.c_str(), font, invert, 0); + return oledWriteString(&m_oled, 0,offset_x,0+offset_row, text.c_str(), font, invert, 0); } bool Api::performance_set(int perf) { @@ -313,11 +324,19 @@ bool Api::performance_set(int perf) { } bool Api::datetime_get(datetime_t *t) { +#ifndef SDL2_BUILD return rtc_get_datetime(t); +#else + return false; +#endif } bool Api::datetime_set(datetime_t *t) { +#ifndef SDL2_BUILD return rtc_set_datetime(t); +#else + return false; +#endif } uint Api::button_last_get() { diff --git a/hal/api.hpp b/hal/api.hpp index 5d0c33e..eaba324 100644 --- a/hal/api.hpp +++ b/hal/api.hpp @@ -3,7 +3,7 @@ #include <iostream> #include "pico/util/datetime.h" #include "../oled/ss_oled.h" - +#include "sdl2/display.hpp" #include "../buttons.hpp" class Api { @@ -31,6 +31,10 @@ class Api { ENTER_SHALLOW_SLEEP, EXIT_SHALLOW_SLEEP // Restore perf setting }; +#ifdef SDL2_BUILD + SDLDisplay m_sdl_display; +#endif + void init(); // Control the display's power (on or off) void display_power(bool mode); @@ -44,7 +48,8 @@ class Api { void display_draw_ellipse(int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled); void display_write_buffer(uint8_t *pBuffer); // Write the internal backbuffer to the display. Should be called when after all drawing calls. One call is done to avoid flickering of the display. - void display_write_backbuffer(); + // Retunrs true if must quit app + bool display_write_backbuffer(); int display_write_pixel(int x, int y, unsigned char ucColor, int bRender); // Display a popup over the current view and wait for select button to be pressed. // This is a blocking function and should be used only in the app's render method. diff --git a/hal/sdl2/display.cpp b/hal/sdl2/display.cpp new file mode 100644 index 0000000..ecb6870 --- /dev/null +++ b/hal/sdl2/display.cpp @@ -0,0 +1,137 @@ +#ifdef SDL2_BUILD +#include <SDL.h> +#include "display.hpp" +#include "../../buttons.hpp" +#include <iostream> + +#define SCREEN_HEIGHT 64 +#define SCREEN_WIDTH 128 + +SDLDisplay::SDLDisplay() { + SDL_Init(SDL_INIT_VIDEO); + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); + /* scale here to make the window larger than the surface itself. + Int*eger scalings only as set by function below. */ + m_window = SDL_CreateWindow("SDL2 pico-watch build", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, 0); + if (m_window == nullptr) { + SDL_Log("Unable to create renderer: %s", SDL_GetError()); + m_should_quit = true; + return; + } + + m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_PRESENTVSYNC); + if (m_renderer == nullptr) { + SDL_Log("Unable to create renderer: %s", SDL_GetError()); + m_should_quit = true; + return; + } + + const int width = SCREEN_WIDTH; + const int height = SCREEN_HEIGHT; + + /* Since we are going to display a low resolution buffer, + i t i*s best to limit the window size so that it cannot be + smaller than our internal buffer size. */ + SDL_SetWindowMinimumSize(m_window, width, height); + SDL_RenderSetLogicalSize(m_renderer, width, height); + SDL_RenderSetIntegerScale(m_renderer, SDL_bool(1)); + + /* A one-bit-per-pixel Surface, indexed to these colors */ + m_surface = SDL_CreateRGBSurfaceWithFormat(SDL_SWSURFACE, + width, height, 1, SDL_PIXELFORMAT_INDEX1MSB); + SDL_Color colors[2] = {{0, 0, 0, 255}, {255, 255, 255, 255}}; + SDL_SetPaletteColors(m_surface->format->palette, colors, 0, 2); +} + +bool SDLDisplay::update_display(const uint8_t* oled_screen) { + if (m_should_quit) return true; + + SDL_Event e; + while (SDL_PollEvent(&e) != 0) { + switch (e.type) { + case SDL_QUIT: + m_should_quit = true; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + uint button = get_button(e.key.keysym.sym); + if (button) + gpio_interrupt_cb(button, e.type == SDL_KEYDOWN); + break; + } + } + + // NOTE: hardcoded display size (might be fine, see todo.md and oledWriteDataBlock in ss_oled.c) + // memcpy(m_surface->pixels, oled_screen, (size_t)(128*8)); + convert_display(oled_screen); + + // render on screen + SDL_RenderClear(m_renderer); + SDL_Texture* screen_texture = SDL_CreateTextureFromSurface(m_renderer, m_surface); + SDL_RenderCopy(m_renderer, screen_texture, NULL, NULL); + SDL_RenderPresent(m_renderer); + SDL_DestroyTexture(screen_texture); + return false; +} + +SDLDisplay::~SDLDisplay() { + SDL_DestroyTexture(m_texture); + SDL_DestroyRenderer(m_renderer); + SDL_DestroyWindow(m_window); + SDL_Quit(); +} + +unsigned char rorb(unsigned char x, unsigned char n) { + return __rorb(x,n); +} + +unsigned char rolb(unsigned char x, unsigned char n) { + return __rolb(x,n); +} + +void SDLDisplay::convert_display(const uint8_t* in) { + unsigned char* out = (unsigned char*)m_surface->pixels; + // clear screen + for (int i = 0; i < 8*128; ++i) + out[i] = 0x00; + + // draw to screen + for (int i = 0; i < 8*128; ++i) { + int row = i / 128; // "super-row" consisting of 8 subrows + int col = i % 128; + + // create a column of a subrow + // int subrow represents which bit of byte i + for (int subrow = 0; subrow < 8; ++subrow) + // OR instead of XOR seems to do exactly the same thing? + out[col/8 + (row*8 + subrow)*16] ^= rorb( + (bool)(in[i] & rolb(1, subrow)), (col % 8) + 1); + } +} + +uint SDLDisplay::get_button(SDL_Keycode key) { + switch (key) { + case SDLK_w: + case SDLK_UP: + return BUTTON_UP; + case SDLK_s: + case SDLK_DOWN: + return BUTTON_DOWN; + case SDLK_RETURN: + case SDLK_o: + return BUTTON_SELECT; + case SDLK_TAB: + case SDLK_m: + return BUTTON_MODE; + case SDLK_BACKSPACE: + case SDLK_h: + return BUTTON_HOME; + case SDLK_x: + m_testbool = !m_testbool; + default: + return 0; + } +} + +#endif diff --git a/hal/sdl2/display.hpp b/hal/sdl2/display.hpp new file mode 100644 index 0000000..55d82b7 --- /dev/null +++ b/hal/sdl2/display.hpp @@ -0,0 +1,30 @@ +#pragma once +#ifdef SDL2_BUILD +#include <SDL.h> + +class SDLDisplay { +public: + SDLDisplay(); + ~SDLDisplay(); + // bool init_display(); + // When returning false, please quit the program (return from main) + bool update_display(const uint8_t* oled_screen); +private: + SDL_Window* m_window = nullptr; + SDL_Renderer* m_renderer = nullptr; + SDL_Surface* m_surface = nullptr; + SDL_Texture* m_texture = nullptr; + + bool m_should_quit = false; + bool m_testbool = false; + + // Convert the surface's pixels from the internal SSOLED display representation + // to a valid representation that can be correctly displayed + void convert_display(const uint8_t* oled_screen); + + // Get the gpio button that corresponds to the key that has been pressed + uint get_button(SDL_Keycode key); +}; + +#endif + |