Adam PIppin
3 years ago
15 changed files with 491 additions and 38 deletions
@ -1,10 +1,12 @@ |
|||
#ifndef _IDRAWABLE_h |
|||
#define _IDRAWABLE_h |
|||
|
|||
#include "watchos_types.h" |
|||
|
|||
class IDrawable |
|||
{ |
|||
public: |
|||
virtual void draw(int hwnd, int x, int y, int width, int height) = 0; |
|||
virtual void draw(kernel_handle_t handle) = 0; |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,276 @@ |
|||
#include "watchos.h" |
|||
#include "Module_UI.h" |
|||
#include <GxEPD2_BW.h> |
|||
#include <Fonts/FreeMono12pt7b.h> |
|||
|
|||
GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> gfx = GxEPD2_154_D67(HW_DISPLAY_CS, HW_DISPLAY_DC, HW_DISPLAY_RESET, HW_DISPLAY_BUSY); |
|||
|
|||
struct UiWindow |
|||
{ |
|||
kernel_handle_t handle; |
|||
IDrawable* drawable; |
|||
byte layout_mode; |
|||
UiWindow* parent; |
|||
bool dirty; |
|||
}; |
|||
|
|||
void destruct_window(kernel_handle_t handle, void* object) |
|||
{ |
|||
} |
|||
|
|||
#ifdef WATCHOS_UI_MULTICORE |
|||
TaskHandle_t draw_task; |
|||
void draw_wrapper(void* parameter) |
|||
{ |
|||
Module_UI* ui = static_cast<Module_UI*>(parameter); |
|||
ui->do_draw(); |
|||
draw_task = nullptr; |
|||
vTaskDelete(NULL); |
|||
} |
|||
#endif |
|||
|
|||
Module_UI::Module_UI() |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
m_window[i] = nullptr; |
|||
} |
|||
|
|||
m_root = createWindow(WATCHOS_HANDLE_NULL); |
|||
} |
|||
|
|||
void Module_UI::tick() |
|||
{ |
|||
draw(); |
|||
} |
|||
|
|||
kernel_handle_t Module_UI::createWindow(IDrawable* drawable, kernel_handle_t parent) |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] == nullptr) |
|||
{ |
|||
m_window[i] = new UiWindow(); |
|||
m_window[i]->drawable = drawable; |
|||
m_window[i]->handle = watchos::kernel()->allocHandle(WATCHOS_HANDLE_TYPE_UI_WINDOW, m_window[i], (kernel_handle_destructor_t)destruct_window); |
|||
m_window[i]->layout_mode = MODULE_UI_LAYOUT_MODE_NONE; |
|||
m_window[i]->parent = nullptr; |
|||
m_window[i]->dirty = false; |
|||
|
|||
if (parent != WATCHOS_HANDLE_NULL) |
|||
{ |
|||
for (int j = 0; j < MODULE_UI_MAX_WINDOWS; j++) |
|||
{ |
|||
if (m_window[j] != nullptr && m_window[j]->handle == parent) |
|||
{ |
|||
m_window[i]->parent = m_window[j]; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (m_window[i]->parent == nullptr) |
|||
{ |
|||
watchos::panic("Cannot find parent window handle %#010x", parent); |
|||
} |
|||
} |
|||
|
|||
return m_window[i]->handle; |
|||
} |
|||
} |
|||
|
|||
watchos::panic("Exceeded MODULE_UI_MAX_WINDOWS"); |
|||
} |
|||
|
|||
kernel_handle_t Module_UI::createWindow(kernel_handle_t parent) |
|||
{ |
|||
return createWindow(nullptr, parent); |
|||
} |
|||
|
|||
kernel_handle_t Module_UI::getRoot() |
|||
{ |
|||
return m_root; |
|||
} |
|||
|
|||
void Module_UI::draw() |
|||
{ |
|||
#ifdef WATCHOS_UI_MULTICORE |
|||
if (draw_task == nullptr) |
|||
{ |
|||
xTaskCreatePinnedToCore(draw_wrapper, "Draw", MODULE_UI_MULTICORE_STACK_SIZE, this, 0, &draw_task, 0); |
|||
} |
|||
#else |
|||
do_draw(); |
|||
#endif |
|||
} |
|||
|
|||
void Module_UI::do_draw() |
|||
{ |
|||
// TODO: Disable light/deep sleep
|
|||
|
|||
// Initialize gfx if necessary
|
|||
if (!m_initialized) |
|||
{ |
|||
m_initialized = true; |
|||
gfx.init(0, false); |
|||
} |
|||
|
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->handle == m_root) |
|||
{ |
|||
draw_window(m_window[i], 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// TODO: Redraw only parts of the screen if necessary, etc
|
|||
gfx.display(true); |
|||
|
|||
// TODO: Reenable sleep
|
|||
} |
|||
|
|||
void Module_UI::draw_window(UiWindow* window, int x, int y, int width, int height) |
|||
{ |
|||
m_draw_x = x; |
|||
m_draw_y = y; |
|||
m_draw_w = width; |
|||
m_draw_h = height; |
|||
|
|||
// Draw window
|
|||
if (window->drawable != nullptr) |
|||
{ |
|||
window->drawable->draw(window->handle); |
|||
} |
|||
|
|||
// Count children
|
|||
int child_count = 0; |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->parent == window) |
|||
{ |
|||
child_count++; |
|||
} |
|||
} |
|||
|
|||
if (child_count == 0) |
|||
{ |
|||
// No children to draw, we're done!
|
|||
return; |
|||
} |
|||
|
|||
int child_offset = 0; |
|||
int child_width, child_height; |
|||
switch (window->layout_mode) |
|||
{ |
|||
case MODULE_UI_LAYOUT_MODE_NONE: |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->parent == window) |
|||
{ |
|||
draw_window(m_window[i], x, y, width, height); |
|||
} |
|||
} |
|||
break; |
|||
case MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL: |
|||
child_height = height / child_count; |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->parent == window) |
|||
{ |
|||
draw_window(m_window[i], x, y + (child_offset++ * child_height), width, child_height); |
|||
} |
|||
} |
|||
break; |
|||
case MODULE_UI_LAYOUT_MODE_SPLIT_VERTICAL: |
|||
child_width = width / child_count; |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->parent == window) |
|||
{ |
|||
draw_window(m_window[i], x + (child_offset++ * child_width), y, child_width, height); |
|||
} |
|||
} |
|||
break; |
|||
case MODULE_UI_LAYOUT_MODE_ABSOLUTE: |
|||
// TODO:
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
void Module_UI::setLayoutMode(kernel_handle_t window_handle, byte layout_mode) |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->handle == window_handle) |
|||
{ |
|||
m_window[i]->layout_mode = layout_mode; |
|||
return; |
|||
} |
|||
} |
|||
watchos::panic("Trying to set layout mode on non-existent handle: %#010x", window_handle); |
|||
} |
|||
|
|||
void Module_UI::setDirty(kernel_handle_t window_handle) |
|||
{ |
|||
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++) |
|||
{ |
|||
if (m_window[i] != nullptr && m_window[i]->handle == window_handle) |
|||
{ |
|||
m_window[i]->dirty = true; |
|||
m_dirty = true; |
|||
return; |
|||
} |
|||
} |
|||
watchos::panic("Trying to set dirty non-existent window handle: %#010x", window_handle); |
|||
} |
|||
|
|||
int Module_UI::getWidth() |
|||
{ |
|||
return m_draw_w; |
|||
} |
|||
|
|||
int Module_UI::getHeight() |
|||
{ |
|||
return m_draw_h; |
|||
} |
|||
|
|||
void Module_UI::fill(uint16_t colour) |
|||
{ |
|||
fill(m_draw_x, m_draw_y, m_draw_w, m_draw_h, colour); |
|||
} |
|||
|
|||
void Module_UI::fill(int x, int y, int width, int height, uint16_t colour) |
|||
{ |
|||
local_to_screen(&x, &y); |
|||
gfx.fillRect(x, y, width, height, colour); |
|||
} |
|||
|
|||
void Module_UI::print(int x, int y, char* str, uint16_t colour, void* font) |
|||
{ |
|||
local_to_screen(&x, &y); |
|||
gfx.setCursor(x, y); |
|||
gfx.setTextColor(colour); |
|||
if (font == nullptr) |
|||
{ |
|||
gfx.setFont(&FreeMono12pt7b); |
|||
} |
|||
else |
|||
{ |
|||
gfx.setFont(static_cast<GFXfont*>(font)); |
|||
} |
|||
gfx.print(str); |
|||
} |
|||
|
|||
void Module_UI::local_to_screen(int* x, int* y) |
|||
{ |
|||
// TODO: Check bounds?
|
|||
if (x < 0) |
|||
*x = (m_draw_w - *x) + m_draw_x; |
|||
else |
|||
*x = m_draw_x + *x; |
|||
|
|||
if (y < 0) |
|||
*y = (m_draw_h - *y) + m_draw_y; |
|||
else |
|||
*y = m_draw_y + *y; |
|||
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,13 @@ |
|||
#ifndef _WATCHOS_CONFIG_h |
|||
#define _WATCHOS_CONFIG_h |
|||
|
|||
// If defined, enable debugging output.
|
|||
#define WATCHOS_DEBUG |
|||
|
|||
// If defined, use black background + white fg
|
|||
#define WATCHOS_DARK_MODE |
|||
|
|||
// If defined, use the second core for drawing
|
|||
#define WATCHOS_UI_MULTICORE |
|||
|
|||
#endif |
@ -1,14 +1,23 @@ |
|||
#ifndef _WATCHOS_CONSTS_h |
|||
#define _WATCHOS_CONSTS_h |
|||
|
|||
// If defined, enable debugging output.
|
|||
#define WATCHOS_DEBUG |
|||
#include "watchos_config.h" |
|||
|
|||
#define WATCHOS_WK_TEST 0x00000001 |
|||
#ifdef WATCHOS_DARK_MODE |
|||
#define COLOUR_PRIMARY 0xFFFF // white
|
|||
#define COLOUR_SECONDARY 0x0000 // black
|
|||
#else |
|||
#define COLOUR_PRIMARY 0x0000 // black
|
|||
#define COLOUR_SECONDARY 0xFFFF // white
|
|||
#endif |
|||
|
|||
#define WATCHOS_MODULE_UI 0x00000001 |
|||
|
|||
#define WATCHOS_HANDLE_NULL 0x00000000 |
|||
|
|||
#define WATCHOS_HANDLE_TYPE_TASK 0x01 |
|||
#define WATCHOS_HANDLE_TYPE_EVENT 0x02 |
|||
#define WATCHOS_HANDLE_TYPE_EVENT_SUBSCRIPTION 0x03 |
|||
#define WATCHOS_HANDLE_TYPE_UI_WINDOW 0x04 |
|||
|
|||
#endif |
Loading…
Reference in new issue