Browse Source

Mostly a lot of comments and documentation

master
Adam PIppin 3 years ago
parent
commit
a7767e42bd
  1. 15
      watchos2/Event.h
  2. 26
      watchos2/EventListener.h
  3. 7
      watchos2/IDrawable.h
  4. 6
      watchos2/IRunnable.h
  5. 98
      watchos2/Kernel.h
  6. 6
      watchos2/Module_Input.h
  7. 51
      watchos2/Module_Power.h
  8. 2
      watchos2/Module_RTC.cpp
  9. 34
      watchos2/Module_RTC.h
  10. 91
      watchos2/Module_Storage.h
  11. 20
      watchos2/Module_UI.cpp
  12. 207
      watchos2/Module_UI.h
  13. 42
      watchos2/Queue.h
  14. 15
      watchos2/Task.h
  15. 5
      watchos2/Task_Clock.cpp
  16. 4
      watchos2/Task_Test.cpp
  17. 2
      watchos2/__vm/Compile.vmps.xml
  18. 2
      watchos2/__vm/Upload.vmps.xml
  19. 71
      watchos2/watchos.h
  20. 6
      watchos2/watchos2.ino
  21. 10
      watchos2/watchos_config.h
  22. 3
      watchos2/watchos_types.h

15
watchos2/Event.h

@ -1,11 +1,26 @@
#ifndef _EVENTS_h
#define _EVENTS_h
/// <summary>
/// An event you receive through the eventing system
/// </summary>
struct Event
{
/// <summary>
/// Sender of the event; one of the WATCHOS_MODULE_* consts
/// </summary>
well_known_handle_t source;
/// <summary>
/// Id of the actual event; one of the WATCHOS_EVENT_TYPE_<MODULE>_<EVENT> consts
/// </summary>
uint16_t event;
/// <summary>
/// Event defined data
/// </summary>
byte param1;
/// <summary>
/// Event defined data
/// </summary>
byte param2;
};

26
watchos2/EventListener.h

@ -5,16 +5,42 @@
#include "Queue.h"
#include "Event.h"
/// <summary>
/// Mixin for a object which can receive events from the kernel
/// </summary>
class EventListener
{
private:
Queue* m_event_queue;
protected:
/// <summary>
/// Check whether there are any events available in the buffer
/// </summary>
/// <returns>True if there are events; False if no events</returns>
bool hasEvent();
/// <summary>
/// Fetch the next event from the buffer and return it
/// </summary>
/// <returns>A pointer to the next event in the buffer; shared, do not modify</returns>
Event* getEvent();
/// <summary>
/// Remove the last fetched event from the buffer; releases our reference to its handle
/// </summary>
void popEvent();
public:
/// <summary>
/// Initialize the ring buffer
/// </summary>
EventListener();
/// <summary>
/// Push an event into the queue
/// </summary>
/// <remarks>
/// This is passed a kernel handle so the event system may take a reference to it (increasing the
/// kernel's refcount) and eventually release it when processed (decreasing the refcount) so that
/// the kernel can handle allocating/releasing this for us.
/// </remarks>
/// <param name="handle"></param>
void pushEvent(kernel_handle_t handle);
};

7
watchos2/IDrawable.h

@ -3,9 +3,16 @@
#include "watchos_types.h"
/// <summary>
/// Something that can be drawn on the screen by the UI module
/// </summary>
class IDrawable
{
public:
/// <summary>
/// Draw this object's interface
/// </summary>
/// <param name="handle">the handle of the window we are drawing</param>
virtual void draw(kernel_handle_t handle) = 0;
};

6
watchos2/IRunnable.h

@ -1,9 +1,15 @@
#ifndef _IRUNNABLE_h
#define _IRUNNABLE_h
/// <summary>
/// Something that does on-going processing while the system is active
/// </summary>
class IRunnable
{
public:
/// <summary>
/// Do processing
/// </summary>
virtual void tick() = 0;
};

98
watchos2/Kernel.h

@ -14,57 +14,135 @@ struct KernelHandle;
struct KernelTask;
struct KernelEventSubscription;
/// <summary>
/// WatchOS kernel providing the supervisor that makes it all go
/// </summary>
class Kernel
{
private:
/// <summary>
/// Holds the accounting of all handles the kernel has allocated
/// </summary>
KernelHandle* m_handle[KERNEL_MAX_HANDLES];
/// <summary>
/// Id of the next handle to allocate
/// </summary>
int m_next_handle = 1;
/// <summary>
/// Holds a reference and metadata for every task registered with the kernel
/// </summary>
KernelTask* m_task[KERNEL_MAX_TASKS];
/// <summary>
/// Holds a reference and metadata for every event subscription
/// </summary>
KernelEventSubscription* m_event_subscription[KERNEL_MAX_EVENT_SUBSCRIPTIONS];
/// <summary>
/// Perform the actual actions to free a handle
/// </summary>
/// <remarks>
/// Calls the destructor (if set), and removes it from the kernel's internal tracking
/// </remarks>
/// <param name="handle">handle to free</param>
void freeHandle(kernel_handle_t handle);
public:
Kernel();
/// <summary>
/// Initialize the kernel on first boot and notify tasks to do the same
/// </summary>
void initialize();
/// <summary>
/// Have all tasks perform startup after booting
/// </summary>
void start();
/// <summary>
/// Notify all tasks and the kernel to prepare for deep sleep by saving any state
/// required to persistent storage.
/// </summary>
void suspend();
/// <summary>
/// Create a new kernel handle pointing to an object
/// </summary>
/// <param name="type">Type of the handle, one of WATCHOS_HANDLE_TYPE_ consts</param>
/// <param name="object">Pointer to the object this handle references</param>
/// <param name="destructor">Function to call when this handle has released all references</param>
/// <param name="well_known">Specify a 'well_known' value this pointer can be fetched by</param>
/// <returns>A handle which can be used to fetch or release this object</returns>
/// <param name="type">type of the handle, one of WATCHOS_HANDLE_TYPE_ consts</param>
/// <param name="object">pointer to the object this handle references</param>
/// <param name="destructor">function to call when this handle has released all references</param>
/// <param name="well_known">specify a 'well_known' value this pointer can be fetched by</param>
/// <returns>a handle which can be used to fetch or release this object</returns>
kernel_handle_t allocHandle(byte type, void* object, kernel_handle_destructor_t destructor = nullptr, uint32_t well_known = 0);
/// <summary>
/// Fetch the object referenced by this handle, increasing the reference count
/// Fetch the object referenced by this handle
/// </summary>
/// <param name="handle">Handle to fetch</param>
/// <returns>A pointer to the object the handle references</returns>
/// <param name="handle">handle to fetch</param>
/// <returns>a pointer to the object the handle references</returns>
void* getHandle(kernel_handle_t handle);
/// <summary>
/// Increase the reference count of a handle
/// </summary>
/// <param name="handle">handle to increase reference count on</param>
void useHandle(kernel_handle_t handle);
/// <summary>
/// Release a fetched reference to this object
/// Decrease the reference count of a handle
/// </summary>
/// <param name="handle">Handle to release</param>
/// <param name="handle">handle to decrease reference count on</param>
void releaseHandle(kernel_handle_t handle);
/// <summary>
/// Fetch the handle associated with a "well known" handle
/// </summary>
/// <param name="handle">a well known handle we're trying to locate</param>
/// <returns>the kernel handle of the well known object</returns>
kernel_handle_t getHandleForWellKnown(well_known_handle_t handle);
/// <summary>
/// Register a new task with the kernel
/// </summary>
/// <param name="task">task implementation to register</param>
/// <param name="wk_handle">optionally, register this as a well known object</param>
/// <returns>the handle of the registered task</returns>
kernel_handle_t registerTask(Task* task, well_known_handle_t wk_handle = WATCHOS_HANDLE_NULL);
/// <summary>
/// Unregisters a task with the kernel
/// </summary>
/// <param name="task">task implementation to unregister</param>
void unregisterTask(Task* task);
/// <summary>
/// Push an event to all relevant subscribers
/// </summary>
/// <remarks>
/// This will look through all subscriptions and see which subscriptions' masks are
/// set to receive events of this source/type, and push the event through to the
/// applicable listeners.
/// </remarks>
/// <param name="source">handle of the sender</param>
/// <param name="event">id of the event; what the event means</param>
/// <param name="param1">event-defined data</param>
/// <param name="param2">event-defined data</param>
void pushEvent(well_known_handle_t source, uint16_t event, byte param1, byte param2);
/// <summary>
/// Subscribe a listener to an event
/// </summary>
/// <param name="listener">listener that should receive events</param>
/// <param name="source_mask">a bitmask of the event sources that we want to receive</param>
/// <param name="event_mask">a bitmask of the event types that we want to receive</param>
/// <returns>a handle to the subscription</returns>
kernel_handle_t subscribeEvent(EventListener* listener, well_known_handle_t source_mask, uint16_t event_mask);
/// <summary>
/// Remove an event subscription
/// </summary>
/// <param name="subscription">the subscription to remove</param>
void unsubscribeEvent(KernelEventSubscription* subscription);
/// <summary>
/// Have the kernel and any runnable tasks do processing
/// </summary>
void tick();
};

6
watchos2/Module_Input.h

@ -5,8 +5,14 @@
struct InputButtonState;
/// <summary>
/// Module responsible for monitoring button presses and converting them into events
/// </summary>
class Module_Input : public Task, public IRunnable
{
/// <summary>
/// Track the state of the buttons so we can do debouncing/etc
/// </summary>
InputButtonState* m_button[MODULE_INPUT_BUTTONS];
public:

51
watchos2/Module_Power.h

@ -13,23 +13,68 @@
#define MODULE_POWER_NONINTERACTIVE_LIGHT_SLEEP_DELAY_MS 200
#define MODULE_POWER_NONINTERACTIVE_DEEP_SLEEP_DELAY_MS 500
/// <summary>
/// Module responsible for monitoring activity, wake causes, timeouts, etc and deciding when
/// to put the system into light/deep sleep.
/// </summary>
class Module_Power : public Task, public IRunnable, public EventListener
{
int m_light_sleep_delay = 0, m_deep_sleep_delay = 0;
int m_light_sleep_accumulator = 0, m_deep_sleep_accumulator = 0;
/// <summary>
/// How many milliseconds to wait before entering light sleep
/// </summary>
int m_light_sleep_delay = 0;
/// <summary>
/// How many milliseconds to wait before entering deep sleep
/// </summary>
int m_deep_sleep_delay = 0;
/// <summary>
/// How many milliseconds of inactivity have elapsed while light sleep is enabled
/// </summary>
int m_light_sleep_accumulator = 0;
/// <summary>
/// How many milliseconds of inactivity have elapsed while deep sleep is enabled
/// </summary>
int m_deep_sleep_accumulator = 0;
/// <summary>
/// Timestamp of the last time the module updated the accumulators
/// </summary>
unsigned long m_last_tick;
/// <summary>
/// Track whether we have entered light sleep; if we have we need to disable the internal RTC
/// before deep sleep.
/// </summary>
bool m_did_light_sleep;
bool m_light_sleep_enabled = true, m_deep_sleep_enabled = true;
/// <summary>
/// Whether the system is eligible to enter light sleep
/// </summary>
bool m_light_sleep_enabled = true;
/// <summary>
/// Whether the system is eligible to enter deep sleep
/// </summary>
bool m_deep_sleep_enabled = true;
public:
void start();
void tick();
/// <summary>
/// Disable light sleep
/// </summary>
void disableLightSleep();
/// <summary>
/// Enable light sleep
/// </summary>
void enableLightSleep();
/// <summary>
/// Disable deep sleep
/// </summary>
void disableDeepSleep();
/// <summary>
/// Enable light sleep
/// </summary>
void enableDeepSleep();
};

2
watchos2/Module_RTC.cpp

@ -65,7 +65,7 @@ int Module_RTC::getSecond()
int Module_RTC::getYear()
{
return rtc_time.Year;
return rtc_time.Year + 1970;
}
int Module_RTC::getMonth()

34
watchos2/Module_RTC.h

@ -7,8 +7,14 @@
#include "Task.h"
#include "IRunnable.h"
/// <summary>
/// Module responsible for managing RTC hardware and alarms and generating events
/// </summary>
class Module_RTC : public Task, public IRunnable
{
/// <summary>
/// Refresh our in-memory time from the RTC
/// </summary>
void refresh();
public:
@ -18,12 +24,40 @@ public:
void suspend();
void tick();
/// <summary>
/// Get the hours component of the current time
/// </summary>
/// <returns>current hour; 0-23</returns>
int getHour();
/// <summary>
/// Get the minutes component of the current time
/// </summary>
/// <returns>current minute; 0-59</returns>
int getMinute();
/// <summary>
/// Get the seconds component of the current time
/// </summary>
/// <returns>current seconds; 0-59</returns>
int getSecond();
/// <summary>
/// Get the year component of the current date
/// </summary>
/// <returns>current year; e.g., 2021</returns>
int getYear();
/// <summary>
/// Get the month component of the current date
/// </summary>
/// <returns>current month; 1-12</returns>
int getMonth();
/// <summary>
/// Get the day component of the current date
/// </summary>
/// <returns>current day; 1-31</returns>
int getDay();
/// <summary>
/// Get the day of week component of the current date
/// </summary>
/// <returns>current day of week; Sunday-Saturday</returns>
char* getDayOfWeek();
};

91
watchos2/Module_Storage.h

@ -6,21 +6,54 @@
#include "Task.h"
// 4kB RTC
// 4kB EEPROM
// We pre-allocate a table for tracking storage allocations. This defines how
// many entries that supports.
#define MODULE_STORAGE_MAX_ENTRIES 16
struct StorageEntry;
/// <summary>
/// Module providing a coherent interface for persisting data
/// </summary>
class Module_Storage : public Task
{
/// <summary>
/// To validate that the storage we're reading is initialized, we store two bytes of magic
/// at the beginning.
/// </summary>
uint16_t m_magic;
/// <summary>
/// Storage tracks a version number to allow non-backward-compatible updates to be made
/// to the format in the future.
/// </summary>
byte m_version;
/// <summary>
/// One byte of storage is reserved to track top-level flags/options
/// </summary>
byte m_options;
/// <summary>
/// In-memory list of all storage allocations
/// </summary>
StorageEntry* m_entry[MODULE_STORAGE_MAX_ENTRIES];
/// <summary>
/// Convert a kernel well-known handle into a byte in the range 0-63
/// </summary>
/// <remarks>
/// Well known handles are used as a bitmask, so each distinct handle
/// only sets a single bit. In order to save on storage, internally we map
/// those to a single byte in the range 0-63.
/// </remarks>
/// <param name="handle">well known handle; one of the WATCHOS_MODULE_* consts</param>
/// <returns>a byte with a number in the range 0-63</returns>
byte wellKnownHandleToByte(well_known_handle_t handle);
/// <summary>
/// Get the offset of a specific module's data within storage
/// </summary>
/// <param name="module">id of the module to get offset for</param>
/// <returns>offset, in bytes, of the module's data</returns>
int getOffset(byte module);
public:
@ -28,18 +61,70 @@ public:
void start();
void suspend();
/// <summary>
/// Check whether the underlying storage contains valid and initialized storage
/// </summary>
/// <returns>true if valid; false if invalid</returns>
bool isValid();
/// <summary>
/// Load the storage allocations from persistent storage
/// </summary>
void load();
/// <summary>
/// Commit the storage allocations to persistent storage
/// </summary>
void commit();
/// <summary>
/// Reset all storage allocations and write an empty allocation
/// table to persistent storage.
/// </summary>
void reset();
/// <summary>
/// Allocate storage for a module
/// </summary>
/// <param name="module">module's handle</param>
/// <param name="size">number of bytes of storage to allocate</param>
/// <param name="options"></param>
/// <returns>true if an allocation was performed; false if an allocation already existed for the given module + size</returns>
bool allocate(well_known_handle_t module, uint16_t size, byte options = 0);
/// <summary>
/// Write data to a module's storage
/// </summary>
/// <param name="module">module to write for</param>
/// <param name="offset">offset, in bytes, within module's storage</param>
/// <param name="value">value to write at offset</param>
void write(well_known_handle_t module, uint16_t offset, byte value);
/// <summary>
/// Fetch data from a module's storage
/// </summary>
/// <param name="module">module to read for</param>
/// <param name="offset">offset, in bytes, within module's storage</param>
/// <returns>the value previously stored at that address</returns>
byte read(well_known_handle_t module, uint16_t offset);
/// <summary>
/// Allocate storage for a module
/// </summary>
/// <param name="module">module's handle</param>
/// <param name="size">number of bytes of storage to allocate</param>
/// <param name="options"></param>
/// <returns>true if an allocation was performed; false if an allocation already existed for the given module + size</returns>
bool allocate(byte module, uint16_t size, byte options = 0);
/// <summary>
/// Write data to a module's storage
/// </summary>
/// <param name="module">module to write for</param>
/// <param name="offset">offset, in bytes, within module's storage</param>
/// <param name="value">value to write at offset</param>
void write(byte module, uint16_t offset, byte value);
/// <summary>
/// Fetch data from a module's storage
/// </summary>
/// <param name="module">module to read for</param>
/// <param name="offset">offset, in bytes, within module's storage</param>
/// <returns>the value previously stored at that address</returns>
byte read(byte module, uint16_t offset);
};

20
watchos2/Module_UI.cpp

@ -5,6 +5,8 @@
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);
#define MODULE_UI_MULTICORE_STACK_SIZE 10240
struct UiWindow
{
kernel_handle_t handle;
@ -79,7 +81,7 @@ kernel_handle_t Module_UI::createWindow(IDrawable* drawable, kernel_handle_t par
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]->layout_mode = ui_layout_mode::NONE;
m_window[i]->parent = nullptr;
m_window[i]->dirty = true;
m_window[i]->needs_draw = false;
@ -118,6 +120,12 @@ kernel_handle_t Module_UI::getRoot()
return m_root;
}
void Module_UI::setRoot(kernel_handle_t handle)
{
m_root = handle;
m_redraw = true;
}
void Module_UI::draw()
{
if (m_dirty)
@ -261,7 +269,7 @@ void Module_UI::draw_window(UiWindow* window, int x, int y, int width, int heigh
int child_width, child_height;
switch (window->layout_mode)
{
case MODULE_UI_LAYOUT_MODE_NONE:
case ui_layout_mode::NONE:
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++)
{
if (m_window[i] != nullptr && m_window[i]->parent == window)
@ -270,7 +278,7 @@ void Module_UI::draw_window(UiWindow* window, int x, int y, int width, int heigh
}
}
break;
case MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL:
case ui_layout_mode::SPLIT_HORIZONTAL:
child_height = height / child_count;
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++)
{
@ -280,7 +288,7 @@ void Module_UI::draw_window(UiWindow* window, int x, int y, int width, int heigh
}
}
break;
case MODULE_UI_LAYOUT_MODE_SPLIT_VERTICAL:
case ui_layout_mode::SPLIT_VERTICAL:
child_width = width / child_count;
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++)
{
@ -290,7 +298,7 @@ void Module_UI::draw_window(UiWindow* window, int x, int y, int width, int heigh
}
}
break;
case MODULE_UI_LAYOUT_MODE_ABSOLUTE:
case ui_layout_mode::ABSOLUTE:
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++)
{
if (m_window[i] != nullptr && m_window[i]->parent == window)
@ -302,7 +310,7 @@ void Module_UI::draw_window(UiWindow* window, int x, int y, int width, int heigh
}
}
void Module_UI::setLayoutMode(kernel_handle_t window_handle, byte layout_mode)
void Module_UI::setLayoutMode(kernel_handle_t window_handle, ui_layout_mode layout_mode)
{
for (int i = 0; i < MODULE_UI_MAX_WINDOWS; i++)
{

207
watchos2/Module_UI.h

@ -11,59 +11,246 @@
#include "IRunnable.h"
#include "IDrawable.h"
// The maximum number of windows that can be created within the UI system.
#define MODULE_UI_MAX_WINDOWS 32
#define MODULE_UI_MULTICORE_STACK_SIZE 10240
// The maximum number of fonts that can be registered with the UI system.
#define MODULE_UI_MAX_FONTS 32
#define MODULE_UI_LAYOUT_MODE_NONE 0
#define MODULE_UI_LAYOUT_MODE_SPLIT_VERTICAL 1
#define MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL 2
#define MODULE_UI_LAYOUT_MODE_ABSOLUTE 3
/// <summary>
/// How to lay out the children of a given window
/// </summary>
enum ui_layout_mode : byte {
/// <summary>
/// Perform no layout, all children will overlap
/// </summary>
NONE = 0,
/// <summary>
/// Split vertically, windows laid out left to right
/// </summary>
SPLIT_VERTICAL = 1,
/// <summary>
/// Split horizontally, windows laid out top to bottom
/// </summary>
SPLIT_HORIZONTAL = 2,
/// <summary>
/// Manually lay out windows, each child must have its position and size set
/// </summary>
ABSOLUTE = 3
};
struct UiWindow;
struct UiFont;
/// <summary>
/// Module to handle layout out and putting things on the screen
/// </summary>
class Module_UI : public Task, public IRunnable
{
/// <summary>
/// Reference to the power module so we can disable sleep while draws are in progress; the display chip
/// doesn't like it when you kill power mid-draw
/// </summary>
Module_Power* module_power;
/// <summary>
/// All registered/created windows
/// </summary>
UiWindow* m_window[MODULE_UI_MAX_WINDOWS];
/// <summary>
/// All fonts registered with the UI system
/// </summary>
UiFont* m_font[MODULE_UI_MAX_FONTS];
/// <summary>
/// Whether we've initialized the graphics hardware
/// </summary>
/// <remarks>
/// This has to be done after every power-on (reset or deep sleep), but we do it lazily since it's often
/// not necessary and is time consuming.
/// </remarks>
bool m_initialized = false;
bool m_dirty = false, m_redraw = false;
// temp, until we implement scenes
/// <summary>
/// Whether any windows have been marked as "dirty" (requiring a redraw) since the last draw
/// </summary>
bool m_dirty = false;
/// <summary>
/// Whether we desire a full-screen redraw at the next available opportunity
/// </summary>
bool m_redraw = false;
/// <summary>
/// Handle to the root window
/// </summary>
/// <remarks>
/// We only dirty / draw windows that exist somewhere underneath the root window
/// </remarks>
kernel_handle_t m_root;
int m_draw_x, m_draw_y, m_draw_w, m_draw_h;
// We track these values so methods like print/fill/etc can be aware of context when called by
// tasks and automatically draw within their allocated space.
/// <summary>
/// The X offset of the window we're currently drawing
/// </summary>
int m_draw_x;
/// <summary>
/// The Y offset of the window we're currently drawing
/// </summary>
int m_draw_y;
/// <summary>
/// The width of the window we're currently drawing
/// </summary>
int m_draw_w;
/// <summary>
/// The height of the window we're currently drawing
/// </summary>
int m_draw_h;
/// <summary>
/// Draw a window to screen, including all of its children
/// </summary>
/// <param name="window">window to draw</param>
/// <param name="x">x offset on screen to start at</param>
/// <param name="y">y offset on screen to start at</param>
/// <param name="width">width of the space to draw within</param>
/// <param name="height">height of the space to draw within</param>
void draw_window(UiWindow* window, int x, int y, int width, int height);
void local_to_screen(int* x, int* y);
void get_screen_coordinates(UiWindow* window, int* x, int* y);
public:
Module_UI();
/// <summary>
/// Create a new window
/// </summary>
/// <param name="drawable">drawable object</param>
/// <param name="parent">handle of a window to create this window under, WATCHOS_HANDLE_NULL for a root window</param>
/// <returns>handle of the newly created window</returns>
kernel_handle_t createWindow(IDrawable* drawable, kernel_handle_t parent);
/// <summary>
/// Create a new placeholder window -- one that can have children, but is not drawn itself
/// </summary>
/// <param name="parent">handle of the window to create this window under, WATCHOS_HANDLE_NULL for a root window</param>
/// <returns>handle of the newly created window</returns>
kernel_handle_t createWindow(kernel_handle_t parent);
/// <summary>
/// Get the handle of the current root window
/// </summary>
/// <returns>handle of the current root window</returns>
kernel_handle_t getRoot();
/// <summary>
/// Set the current root window
/// </summary>
/// <remarks>
/// Triggers a full screen redraw
/// </remarks>
/// <param name="handle">handle of the window that should be the root window</param>
void setRoot(kernel_handle_t handle);
void initialize();
void start();
void tick();
/// <summary>
/// Trigger a draw
/// </summary>
/// <remarks>
/// Will either perform the draw synchronously (if multicore is disabled) or spawns a task
/// on the other core to run the draw asynchronously.
/// </remarks>
void draw();
/// <summary>
/// Internal draw call, either called by draw() or indirectly if spawned on another core
/// </summary>
void do_draw();
void setLayoutMode(kernel_handle_t window_handle, byte layout_mode);
/// <summary>
/// Set the layout mode of a window
/// </summary>
/// <param name="window_handle">handle of the window to set the layout mode of</param>
/// <param name="layout_mode">one of the layout mode enum</param>
void setLayoutMode(kernel_handle_t window_handle, ui_layout_mode layout_mode);
/// <summary>
/// Set the absolute position of an absolutely positioned window relative to parent
/// </summary>
/// <remarks>
/// This only has any effect if the parent window is set to absolute layout
/// </remarks>
/// <param name="window_handle">handle to set the location of</param>
/// <param name="x">x offset from parent</param>
/// <param name="y">y offset from parent</param>
/// <param name="w">width of window</param>
/// <param name="h">height of window</param>
void setAbsolutePosition(kernel_handle_t window_handle, byte x, byte y, byte w, byte h);
/// <summary>
/// Mark the window as dirty, requiring a draw
/// </summary>
/// <param name="window_handle">handle of window to mark dirty</param>
void setDirty(kernel_handle_t window_handle);
/// <summary>
/// Get the width of the current window
/// </summary>
/// <returns>width in pixels</returns>
int getWidth();
/// <summary>
/// Get the height of the current window
/// </summary>
/// <returns>height in pixels</returns>
int getHeight();
/// <summary>
/// Fill the current window with the given colour
/// </summary>
/// <param name="colour">colour to fill with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
void fill(uint16_t colour = COLOUR_SECONDARY);
/// <summary>
/// Fill a portion of the current window with the given colour
/// </summary>
/// <param name="x">x offset within window</param>
/// <param name="y">y offset within window</param>
/// <param name="width">width of area to fill</param>
/// <param name="height">height of area to fill</param>
/// <param name="colour">colour to fill with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
void fill(int x, int y, int width, int height, uint16_t colour = COLOUR_SECONDARY);
/// <summary>
/// Print text to the screen
/// </summary>
/// <param name="x">x offset within window to place bottom left corner of text</param>
/// <param name="y">y offset within window to place bottom left corner of text</param>
/// <param name="str">text to write</param>
/// <param name="colour">colour to render text with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
/// <param name="font">pointer to GFXfont to use for text</param>
void print(int x, int y, char* str, uint16_t colour, void* font);
/// <summary>
/// Print text to the screen
/// </summary>
/// <param name="x">x offset within window to place bottom left corner of text</param>
/// <param name="y">y offset within window to place bottom left corner of text</param>
/// <param name="str">text to write</param>
/// <param name="colour">colour to render text with; either COLOUR_PRIMARY or COLOUR_SECONDARY</param>
/// <param name="font_id">id of a registered font</param>
void print(int x, int y, char* str, uint16_t colour = COLOUR_PRIMARY, byte font_id = 0);
/// <summary>
/// Register a font with an id
/// </summary>
/// <remarks>
/// This can be passed to print(), avoids having to pull in the entire GFX library in order to
/// print text.
/// </remarks>
/// <param name="id">id to register the font with</param>
/// <param name="font">a pointer to a GFXfont</param>
void registerFont(byte id, const void* font);
};

42
watchos2/Queue.h

@ -5,18 +5,58 @@
#define QUEUE_RING_SIZE 8
/// <summary>
/// Simple FIFO ring buffer implementation for kernel handles
/// </summary>
class Queue
{
private:
/// <summary>
/// Buffered data
/// </summary>
kernel_handle_t m_item[QUEUE_RING_SIZE];
int m_read = 0, m_write = 0;
/// <summary>
/// Buffer offset to read the next item from
/// </summary>
int m_read = 0;
/// <summary>
/// Buffer offset to write the next item to
/// </summary>
int m_write = 0;
/// <summary>
/// Set if the buffer is full
/// </summary>
/// <remarks>
/// When m_read == m_write, the buffer is either full or empty. This allows us to differentiate.
/// </remarks>
bool m_full = false;
/// <summary>
/// Get the next buffer index, implementing wrapping
/// </summary>
/// <param name="n">current buffer index</param>
/// <returns>the buffer index after the passed buffer index</returns>
int next(int n);
public:
Queue();
/// <summary>
/// Push a handle into the buffer
/// </summary>
/// <param name="item">handle to add to the buffer</param>
void push(kernel_handle_t item);
/// <summary>
/// Remove and return the next item in the buffer
/// </summary>
/// <returns>next handle in the buffer</returns>
kernel_handle_t pop();
/// <summary>
/// Return the next item in the buffer without removing it
/// </summary>
/// <returns>next handle in the buffer</returns>
kernel_handle_t peek();
/// <summary>
/// Check whether there are any items in the buffer
/// </summary>
/// <returns>true if no items in buffer, false if any items in buffer</returns>
bool isEmpty();
};

15
watchos2/Task.h

@ -1,11 +1,26 @@
#ifndef _TASK_h
#define _TASK_h
/// <summary>
/// General implementation of something that could be considered a "process"
/// </summary>
class Task
{
public:
/// <summary>
/// Initialize the task after a reset/first boot
/// </summary>
virtual void initialize();
/// <summary>
/// Start the task after a deep sleep
/// </summary>
/// <remarks>
/// This is run even on a first boot, but _after_ initialize.
/// </remarks>
virtual void start();
/// <summary>
/// Prepare the task for the system to shutdown/deep sleep
/// </summary>
virtual void suspend();
};

5
watchos2/Task_Clock.cpp

@ -15,10 +15,9 @@ public:
watchos::subscribe(this, WATCHOS_MODULE_RTC, WATCHOS_EVENT_TYPE_RTC_MINUTE);
ui = static_cast<Module_UI*>(watchos::module(WATCHOS_MODULE_UI));
ui->setLayoutMode(ui->getRoot(), MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL);
window_handle = ui->createWindow(this, ui->getRoot());
ui->createWindow(ui->getRoot());
ui->setAbsolutePosition(window_handle, 0, 0, 200, 96);
}
void tick()
{
@ -55,7 +54,7 @@ public:
sprintf(time, "%02d:%02d", hour, minute);
ui->fill(COLOUR_SECONDARY);
ui->print(0, ui->getHeight(), time, COLOUR_PRIMARY, 1);
ui->print(0, ui->getHeight() - 8, time, COLOUR_PRIMARY, 1);
}
};

4
watchos2/Task_Test.cpp

@ -27,10 +27,8 @@ public:
}
ui = static_cast<Module_UI*>(watchos::module(WATCHOS_MODULE_UI));
ui->setLayoutMode(ui->getRoot(), MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL);
window_handle = ui->createWindow(this, ui->getRoot());
ui->createWindow(ui->getRoot());
ui->setAbsolutePosition(window_handle, 0, 96, 200, 104);
}
void tick()
{

2
watchos2/__vm/Compile.vmps.xml

File diff suppressed because one or more lines are too long

2
watchos2/__vm/Upload.vmps.xml

File diff suppressed because one or more lines are too long

71
watchos2/watchos.h

@ -14,28 +14,99 @@
#include "IRunnable.h"
#include "IDrawable.h"
/// <summary>
/// General watchos interface for use in code
/// </summary>
class watchos
{
private:
/// <summary>
/// kernel() only ever instantiates one instance of the kernel, this is that instance.
/// </summary>
static Kernel* s_kernel;
public:
/// <summary>
/// Call from your .ino's setup() method after registering tasks and modules
/// </summary>
/// <remarks>
/// This will check the wake-up reason and determine whether we need to initialize tasks,
/// and finally call start() on all registered tasks.
/// </remarks>
static void setup();
/// <summary>
/// Call from your .ino's tick() method
/// </summary>
/// <remarks>
/// Give's the kernel a chance to run any tasks that need it
/// </remarks>
static void tick();
/// <summary>
/// Initialize the system
/// </summary>
/// <remarks>
/// Currently initializes the serial interface and Wire library
/// </remarks>
static void initialize();
/// <summary>
/// Fetch an instance of the kernel
/// </summary>
/// <returns>The same instance of the kernel with every call</returns>
static Kernel* kernel();
/// <summary>
/// Trigger a kernel panic
/// </summary>
/// <remarks>
/// Prints the passed formatted string then either:
/// if WATCHOS_DEBUG is set: goes into an infinite loop to wait for reset
/// if WATCHOS_DEBUG is not set: resets the system after a few seconds
/// </remarks>
static void panic(char* fmt...);
/// <summary>
/// Print debugging output
/// </summary>
/// <remarks>
/// Only when WATCHOS_DEBUG is set, will print the passed formatted string to
/// the serial console.
/// </remarks>
static void debug(char* fmt...);
/// <summary>
/// Fetch an instance of an event by handle
/// </summary>
static Event* event(kernel_handle_t event);
/// <summary>
/// Fetch an instance of a task by handle
/// </summary>
static Task* task(kernel_handle_t task);
/// <summary>
/// Fetch an instance of a task by a well-known handle
/// </summary>
static Task* module(well_known_handle_t handle);
/// <summary>
/// Increase the reference count for the passed handle
/// </summary>
static void reference(kernel_handle_t handle);
/// <summary>
/// Decrease the reference count for the passed handle
/// </summary>
static void release(kernel_handle_t handle);
/// <summary>
/// Register a new task with the kernel
/// </summary>
/// <param name="task">an instance of a task</param>
/// <returns>handle for the newly registered task</returns>
static kernel_handle_t run(Task* task);
/// <summary>
/// Subscribe a task to a set of events
/// </summary>
/// <param name="task">task to receive events; must implement EventListener</param>
/// <param name="source_mask">bitmask of which well known handles / modules we should receive events from</param>
/// <param name="event_mask">bitmask of which events we should receive</param>
/// <returns>handle for the event subscription; release to unsubscribe</returns>
static kernel_handle_t subscribe(Task* task, well_known_handle_t source_mask, uint16_t event_mask);
};

6
watchos2/watchos2.ino

@ -20,7 +20,7 @@
// the setup function runs once when you press reset or power the board
void setup()
{
delay(100);
ulong start = millis();
watchos::initialize();
Kernel* kernel = watchos::kernel();
Module_Storage* storage;
@ -43,6 +43,10 @@ void setup()
ui->registerFont(1, &DSEG7_Classic_Bold_53);
watchos::setup();
ui->setLayoutMode(ui->getRoot(), ui_layout_mode::ABSOLUTE);
watchos::debug("Startup took %d ms", (int)(millis() - start));
}

10
watchos2/watchos_config.h

@ -1,13 +1,19 @@
#ifndef _WATCHOS_CONFIG_h
#define _WATCHOS_CONFIG_h
// If defined, enable debugging output.
// If defined, set debug mode.
// Set: Enables output from watchos::debug; When the kernel panics goes into a tight loop
// Unset: Disables output from watchos::debug; When the kernel panics automatically restart
#define WATCHOS_DEBUG
// If defined, use black background + white fg
// If defined, use dark mode.
// Set: White foreground, Black background
// Unset: Black foreground, White background
#define WATCHOS_DARK_MODE
// If defined, use the second core for drawing
// Set: Spawn a task on the other core to do drawing
// Unset: Do drawing, blocking on the app core
#define WATCHOS_UI_MULTICORE
#endif

3
watchos2/watchos_types.h

@ -5,6 +5,9 @@
/// A handle to any kernel object
/// </summary>
typedef uint16_t kernel_handle_t;
/// <summary>
/// A static handle for locating modules
/// </summary>
typedef uint64_t well_known_handle_t;
#endif

Loading…
Cancel
Save