aboutsummaryrefslogtreecommitdiffstats
path: root/hal
diff options
context:
space:
mode:
authorConfuSomu2024-02-04 14:38:58 -0500
committerConfuSomu2024-02-04 14:38:58 -0500
commit9a74ae13182061a239f308011a3fd3ba8121c015 (patch)
tree836f7b6848bd0867338966955c242e5a17d33407 /hal
parent6b306c8dcd65016b6e022c648e2477931f9c4b95 (diff)
downloadpico-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.cpp25
-rw-r--r--hal/api.hpp9
-rw-r--r--hal/sdl2/display.cpp137
-rw-r--r--hal/sdl2/display.hpp30
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
+