diff --git a/watchos2/Event.h b/watchos2/Event.h
index 182f277..ad9fd19 100644
--- a/watchos2/Event.h
+++ b/watchos2/Event.h
@@ -1,11 +1,26 @@
#ifndef _EVENTS_h
#define _EVENTS_h
+///
+/// An event you receive through the eventing system
+///
struct Event
{
+ ///
+ /// Sender of the event; one of the WATCHOS_MODULE_* consts
+ ///
well_known_handle_t source;
+ ///
+ /// Id of the actual event; one of the WATCHOS_EVENT_TYPE__ consts
+ ///
uint16_t event;
+ ///
+ /// Event defined data
+ ///
byte param1;
+ ///
+ /// Event defined data
+ ///
byte param2;
};
diff --git a/watchos2/EventListener.h b/watchos2/EventListener.h
index bd64433..fcee047 100644
--- a/watchos2/EventListener.h
+++ b/watchos2/EventListener.h
@@ -5,16 +5,42 @@
#include "Queue.h"
#include "Event.h"
+///
+/// Mixin for a object which can receive events from the kernel
+///
class EventListener
{
private:
Queue* m_event_queue;
protected:
+ ///
+ /// Check whether there are any events available in the buffer
+ ///
+ /// True if there are events; False if no events
bool hasEvent();
+ ///
+ /// Fetch the next event from the buffer and return it
+ ///
+ /// A pointer to the next event in the buffer; shared, do not modify
Event* getEvent();
+ ///
+ /// Remove the last fetched event from the buffer; releases our reference to its handle
+ ///
void popEvent();
public:
+ ///
+ /// Initialize the ring buffer
+ ///
EventListener();
+ ///
+ /// Push an event into the queue
+ ///
+ ///
+ /// 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.
+ ///
+ ///
void pushEvent(kernel_handle_t handle);
};
diff --git a/watchos2/IDrawable.h b/watchos2/IDrawable.h
index b065add..8eab683 100644
--- a/watchos2/IDrawable.h
+++ b/watchos2/IDrawable.h
@@ -3,9 +3,16 @@
#include "watchos_types.h"
+///
+/// Something that can be drawn on the screen by the UI module
+///
class IDrawable
{
public:
+ ///
+ /// Draw this object's interface
+ ///
+ /// the handle of the window we are drawing
virtual void draw(kernel_handle_t handle) = 0;
};
diff --git a/watchos2/IRunnable.h b/watchos2/IRunnable.h
index e2d9c91..d45902c 100644
--- a/watchos2/IRunnable.h
+++ b/watchos2/IRunnable.h
@@ -1,9 +1,15 @@
#ifndef _IRUNNABLE_h
#define _IRUNNABLE_h
+///
+/// Something that does on-going processing while the system is active
+///
class IRunnable
{
public:
+ ///
+ /// Do processing
+ ///
virtual void tick() = 0;
};
diff --git a/watchos2/Kernel.h b/watchos2/Kernel.h
index b6a89f2..c51aa5a 100644
--- a/watchos2/Kernel.h
+++ b/watchos2/Kernel.h
@@ -14,57 +14,135 @@ struct KernelHandle;
struct KernelTask;
struct KernelEventSubscription;
+///
+/// WatchOS kernel providing the supervisor that makes it all go
+///
class Kernel
{
private:
+ ///
+ /// Holds the accounting of all handles the kernel has allocated
+ ///
KernelHandle* m_handle[KERNEL_MAX_HANDLES];
+ ///
+ /// Id of the next handle to allocate
+ ///
int m_next_handle = 1;
+ ///
+ /// Holds a reference and metadata for every task registered with the kernel
+ ///
KernelTask* m_task[KERNEL_MAX_TASKS];
+ ///
+ /// Holds a reference and metadata for every event subscription
+ ///
KernelEventSubscription* m_event_subscription[KERNEL_MAX_EVENT_SUBSCRIPTIONS];
+ ///
+ /// Perform the actual actions to free a handle
+ ///
+ ///
+ /// Calls the destructor (if set), and removes it from the kernel's internal tracking
+ ///
+ /// handle to free
void freeHandle(kernel_handle_t handle);
public:
Kernel();
+ ///
+ /// Initialize the kernel on first boot and notify tasks to do the same
+ ///
void initialize();
+ ///
+ /// Have all tasks perform startup after booting
+ ///
void start();
+ ///
+ /// Notify all tasks and the kernel to prepare for deep sleep by saving any state
+ /// required to persistent storage.
+ ///
void suspend();
///
/// Create a new kernel handle pointing to an object
///
- /// Type of the handle, one of WATCHOS_HANDLE_TYPE_ consts
- /// Pointer to the object this handle references
- /// Function to call when this handle has released all references
- /// Specify a 'well_known' value this pointer can be fetched by
- /// A handle which can be used to fetch or release this object
+ /// type of the handle, one of WATCHOS_HANDLE_TYPE_ consts
+ /// pointer to the object this handle references
+ /// function to call when this handle has released all references
+ /// specify a 'well_known' value this pointer can be fetched by
+ /// a handle which can be used to fetch or release this object
kernel_handle_t allocHandle(byte type, void* object, kernel_handle_destructor_t destructor = nullptr, uint32_t well_known = 0);
///
- /// Fetch the object referenced by this handle, increasing the reference count
+ /// Fetch the object referenced by this handle
///
- /// Handle to fetch
- /// A pointer to the object the handle references
+ /// handle to fetch
+ /// a pointer to the object the handle references
void* getHandle(kernel_handle_t handle);
+ ///
+ /// Increase the reference count of a handle
+ ///
+ /// handle to increase reference count on
void useHandle(kernel_handle_t handle);
///
- /// Release a fetched reference to this object
+ /// Decrease the reference count of a handle
///
- /// Handle to release
+ /// handle to decrease reference count on
void releaseHandle(kernel_handle_t handle);
+ ///
+ /// Fetch the handle associated with a "well known" handle
+ ///
+ /// a well known handle we're trying to locate
+ /// the kernel handle of the well known object
kernel_handle_t getHandleForWellKnown(well_known_handle_t handle);
+ ///
+ /// Register a new task with the kernel
+ ///
+ /// task implementation to register
+ /// optionally, register this as a well known object
+ /// the handle of the registered task
kernel_handle_t registerTask(Task* task, well_known_handle_t wk_handle = WATCHOS_HANDLE_NULL);
+ ///
+ /// Unregisters a task with the kernel
+ ///
+ /// task implementation to unregister
void unregisterTask(Task* task);
+ ///
+ /// Push an event to all relevant subscribers
+ ///
+ ///
+ /// 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.
+ ///
+ /// handle of the sender
+ /// id of the event; what the event means
+ /// event-defined data
+ /// event-defined data
void pushEvent(well_known_handle_t source, uint16_t event, byte param1, byte param2);
+
+ ///
+ /// Subscribe a listener to an event
+ ///
+ /// listener that should receive events
+ /// a bitmask of the event sources that we want to receive
+ /// a bitmask of the event types that we want to receive
+ /// a handle to the subscription
kernel_handle_t subscribeEvent(EventListener* listener, well_known_handle_t source_mask, uint16_t event_mask);
+ ///
+ /// Remove an event subscription
+ ///
+ /// the subscription to remove
void unsubscribeEvent(KernelEventSubscription* subscription);
+ ///
+ /// Have the kernel and any runnable tasks do processing
+ ///
void tick();
};
diff --git a/watchos2/Module_Input.h b/watchos2/Module_Input.h
index 446a256..3c89c5d 100644
--- a/watchos2/Module_Input.h
+++ b/watchos2/Module_Input.h
@@ -5,8 +5,14 @@
struct InputButtonState;
+///
+/// Module responsible for monitoring button presses and converting them into events
+///
class Module_Input : public Task, public IRunnable
{
+ ///
+ /// Track the state of the buttons so we can do debouncing/etc
+ ///
InputButtonState* m_button[MODULE_INPUT_BUTTONS];
public:
diff --git a/watchos2/Module_Power.h b/watchos2/Module_Power.h
index 6b20004..5e0e3ab 100644
--- a/watchos2/Module_Power.h
+++ b/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
+///
+/// Module responsible for monitoring activity, wake causes, timeouts, etc and deciding when
+/// to put the system into light/deep sleep.
+///
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;
+ ///
+ /// How many milliseconds to wait before entering light sleep
+ ///
+ int m_light_sleep_delay = 0;
+ ///
+ /// How many milliseconds to wait before entering deep sleep
+ ///
+ int m_deep_sleep_delay = 0;
+ ///
+ /// How many milliseconds of inactivity have elapsed while light sleep is enabled
+ ///
+ int m_light_sleep_accumulator = 0;
+ ///
+ /// How many milliseconds of inactivity have elapsed while deep sleep is enabled
+ ///
+ int m_deep_sleep_accumulator = 0;
+ ///
+ /// Timestamp of the last time the module updated the accumulators
+ ///
unsigned long m_last_tick;
+
+ ///
+ /// Track whether we have entered light sleep; if we have we need to disable the internal RTC
+ /// before deep sleep.
+ ///
bool m_did_light_sleep;
- bool m_light_sleep_enabled = true, m_deep_sleep_enabled = true;
+ ///
+ /// Whether the system is eligible to enter light sleep
+ ///
+ bool m_light_sleep_enabled = true;
+ ///
+ /// Whether the system is eligible to enter deep sleep
+ ///
+ bool m_deep_sleep_enabled = true;
public:
void start();
void tick();
+ ///
+ /// Disable light sleep
+ ///
void disableLightSleep();
+ ///
+ /// Enable light sleep
+ ///
void enableLightSleep();
+ ///
+ /// Disable deep sleep
+ ///
void disableDeepSleep();
+ ///
+ /// Enable light sleep
+ ///
void enableDeepSleep();
};
diff --git a/watchos2/Module_RTC.cpp b/watchos2/Module_RTC.cpp
index eb284a9..f7be728 100644
--- a/watchos2/Module_RTC.cpp
+++ b/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()
diff --git a/watchos2/Module_RTC.h b/watchos2/Module_RTC.h
index f73399b..bf55c7b 100644
--- a/watchos2/Module_RTC.h
+++ b/watchos2/Module_RTC.h
@@ -7,8 +7,14 @@
#include "Task.h"
#include "IRunnable.h"
+///
+/// Module responsible for managing RTC hardware and alarms and generating events
+///
class Module_RTC : public Task, public IRunnable
{
+ ///
+ /// Refresh our in-memory time from the RTC
+ ///
void refresh();
public:
@@ -18,12 +24,40 @@ public:
void suspend();
void tick();
+ ///
+ /// Get the hours component of the current time
+ ///
+ /// current hour; 0-23
int getHour();
+ ///
+ /// Get the minutes component of the current time
+ ///
+ /// current minute; 0-59
int getMinute();
+ ///
+ /// Get the seconds component of the current time
+ ///
+ /// current seconds; 0-59
int getSecond();
+ ///
+ /// Get the year component of the current date
+ ///
+ /// current year; e.g., 2021
int getYear();
+ ///
+ /// Get the month component of the current date
+ ///
+ /// current month; 1-12
int getMonth();
+ ///
+ /// Get the day component of the current date
+ ///
+ /// current day; 1-31
int getDay();
+ ///
+ /// Get the day of week component of the current date
+ ///
+ /// current day of week; Sunday-Saturday
char* getDayOfWeek();
};
diff --git a/watchos2/Module_Storage.h b/watchos2/Module_Storage.h
index 6f5d934..7bdee94 100644
--- a/watchos2/Module_Storage.h
+++ b/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;
+///
+/// Module providing a coherent interface for persisting data
+///
class Module_Storage : public Task
{
+ ///
+ /// To validate that the storage we're reading is initialized, we store two bytes of magic
+ /// at the beginning.
+ ///
uint16_t m_magic;
+ ///
+ /// Storage tracks a version number to allow non-backward-compatible updates to be made
+ /// to the format in the future.
+ ///
byte m_version;
+ ///
+ /// One byte of storage is reserved to track top-level flags/options
+ ///
byte m_options;
+
+ ///
+ /// In-memory list of all storage allocations
+ ///
StorageEntry* m_entry[MODULE_STORAGE_MAX_ENTRIES];
+ ///
+ /// Convert a kernel well-known handle into a byte in the range 0-63
+ ///
+ ///
+ /// 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.
+ ///
+ /// well known handle; one of the WATCHOS_MODULE_* consts
+ /// a byte with a number in the range 0-63
byte wellKnownHandleToByte(well_known_handle_t handle);
+
+ ///
+ /// Get the offset of a specific module's data within storage
+ ///
+ /// id of the module to get offset for
+ /// offset, in bytes, of the module's data
int getOffset(byte module);
public:
@@ -28,18 +61,70 @@ public:
void start();
void suspend();
+ ///
+ /// Check whether the underlying storage contains valid and initialized storage
+ ///
+ /// true if valid; false if invalid
bool isValid();
+ ///
+ /// Load the storage allocations from persistent storage
+ ///
void load();
+ ///
+ /// Commit the storage allocations to persistent storage
+ ///
void commit();
+ ///
+ /// Reset all storage allocations and write an empty allocation
+ /// table to persistent storage.
+ ///
void reset();
+ ///
+ /// Allocate storage for a module
+ ///
+ /// module's handle
+ /// number of bytes of storage to allocate
+ ///
+ /// true if an allocation was performed; false if an allocation already existed for the given module + size
bool allocate(well_known_handle_t module, uint16_t size, byte options = 0);
+ ///
+ /// Write data to a module's storage
+ ///
+ /// module to write for
+ /// offset, in bytes, within module's storage
+ /// value to write at offset
void write(well_known_handle_t module, uint16_t offset, byte value);
+ ///
+ /// Fetch data from a module's storage
+ ///
+ /// module to read for
+ /// offset, in bytes, within module's storage
+ /// the value previously stored at that address
byte read(well_known_handle_t module, uint16_t offset);
+ ///
+ /// Allocate storage for a module
+ ///
+ /// module's handle
+ /// number of bytes of storage to allocate
+ ///
+ /// true if an allocation was performed; false if an allocation already existed for the given module + size
bool allocate(byte module, uint16_t size, byte options = 0);
+ ///
+ /// Write data to a module's storage
+ ///
+ /// module to write for
+ /// offset, in bytes, within module's storage
+ /// value to write at offset
void write(byte module, uint16_t offset, byte value);
+ ///
+ /// Fetch data from a module's storage
+ ///
+ /// module to read for
+ /// offset, in bytes, within module's storage
+ /// the value previously stored at that address
byte read(byte module, uint16_t offset);
};
diff --git a/watchos2/Module_UI.cpp b/watchos2/Module_UI.cpp
index d87b3ff..3a2de68 100644
--- a/watchos2/Module_UI.cpp
+++ b/watchos2/Module_UI.cpp
@@ -5,6 +5,8 @@
GxEPD2_BW 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++)
{
diff --git a/watchos2/Module_UI.h b/watchos2/Module_UI.h
index 4e4c5c4..bece678 100644
--- a/watchos2/Module_UI.h
+++ b/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
+///
+/// How to lay out the children of a given window
+///
+enum ui_layout_mode : byte {
+ ///
+ /// Perform no layout, all children will overlap
+ ///
+ NONE = 0,
+ ///
+ /// Split vertically, windows laid out left to right
+ ///
+ SPLIT_VERTICAL = 1,
+ ///
+ /// Split horizontally, windows laid out top to bottom
+ ///
+ SPLIT_HORIZONTAL = 2,
+ ///
+ /// Manually lay out windows, each child must have its position and size set
+ ///
+ ABSOLUTE = 3
+};
struct UiWindow;
struct UiFont;
+///
+/// Module to handle layout out and putting things on the screen
+///
class Module_UI : public Task, public IRunnable
{
+ ///
+ /// 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
+ ///
Module_Power* module_power;
+
+ ///
+ /// All registered/created windows
+ ///
UiWindow* m_window[MODULE_UI_MAX_WINDOWS];
+ ///
+ /// All fonts registered with the UI system
+ ///
UiFont* m_font[MODULE_UI_MAX_FONTS];
+ ///
+ /// Whether we've initialized the graphics hardware
+ ///
+ ///
+ /// 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.
+ ///
bool m_initialized = false;
- bool m_dirty = false, m_redraw = false;
- // temp, until we implement scenes
+
+ ///
+ /// Whether any windows have been marked as "dirty" (requiring a redraw) since the last draw
+ ///
+ bool m_dirty = false;
+ ///
+ /// Whether we desire a full-screen redraw at the next available opportunity
+ ///
+ bool m_redraw = false;
+
+ ///
+ /// Handle to the root window
+ ///
+ ///
+ /// We only dirty / draw windows that exist somewhere underneath the root window
+ ///
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.
+
+ ///
+ /// The X offset of the window we're currently drawing
+ ///
+ int m_draw_x;
+ ///
+ /// The Y offset of the window we're currently drawing
+ ///
+ int m_draw_y;
+ ///
+ /// The width of the window we're currently drawing
+ ///
+ int m_draw_w;
+ ///
+ /// The height of the window we're currently drawing
+ ///
+ int m_draw_h;
+
+ ///
+ /// Draw a window to screen, including all of its children
+ ///
+ /// window to draw
+ /// x offset on screen to start at
+ /// y offset on screen to start at
+ /// width of the space to draw within
+ /// height of the space to draw within
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();
+
+ ///
+ /// Create a new window
+ ///
+ /// drawable object
+ /// handle of a window to create this window under, WATCHOS_HANDLE_NULL for a root window
+ /// handle of the newly created window
kernel_handle_t createWindow(IDrawable* drawable, kernel_handle_t parent);
+ ///
+ /// Create a new placeholder window -- one that can have children, but is not drawn itself
+ ///
+ /// handle of the window to create this window under, WATCHOS_HANDLE_NULL for a root window
+ /// handle of the newly created window
kernel_handle_t createWindow(kernel_handle_t parent);
+
+ ///
+ /// Get the handle of the current root window
+ ///
+ /// handle of the current root window
kernel_handle_t getRoot();
+ ///
+ /// Set the current root window
+ ///
+ ///
+ /// Triggers a full screen redraw
+ ///
+ /// handle of the window that should be the root window
+ void setRoot(kernel_handle_t handle);
+
void initialize();
void start();
void tick();
+ ///
+ /// Trigger a draw
+ ///
+ ///
+ /// Will either perform the draw synchronously (if multicore is disabled) or spawns a task
+ /// on the other core to run the draw asynchronously.
+ ///
void draw();
+
+ ///
+ /// Internal draw call, either called by draw() or indirectly if spawned on another core
+ ///
void do_draw();
- void setLayoutMode(kernel_handle_t window_handle, byte layout_mode);
+ ///
+ /// Set the layout mode of a window
+ ///
+ /// handle of the window to set the layout mode of
+ /// one of the layout mode enum
+ void setLayoutMode(kernel_handle_t window_handle, ui_layout_mode layout_mode);
+
+ ///
+ /// Set the absolute position of an absolutely positioned window relative to parent
+ ///
+ ///
+ /// This only has any effect if the parent window is set to absolute layout
+ ///
+ /// handle to set the location of
+ /// x offset from parent
+ /// y offset from parent
+ /// width of window
+ /// height of window
void setAbsolutePosition(kernel_handle_t window_handle, byte x, byte y, byte w, byte h);
+
+ ///
+ /// Mark the window as dirty, requiring a draw
+ ///
+ /// handle of window to mark dirty
void setDirty(kernel_handle_t window_handle);
+ ///
+ /// Get the width of the current window
+ ///
+ /// width in pixels
int getWidth();
+ ///
+ /// Get the height of the current window
+ ///
+ /// height in pixels
int getHeight();
+
+ ///
+ /// Fill the current window with the given colour
+ ///
+ /// colour to fill with; either COLOUR_PRIMARY or COLOUR_SECONDARY
void fill(uint16_t colour = COLOUR_SECONDARY);
+
+ ///
+ /// Fill a portion of the current window with the given colour
+ ///
+ /// x offset within window
+ /// y offset within window
+ /// width of area to fill
+ /// height of area to fill
+ /// colour to fill with; either COLOUR_PRIMARY or COLOUR_SECONDARY
void fill(int x, int y, int width, int height, uint16_t colour = COLOUR_SECONDARY);
+
+ ///
+ /// Print text to the screen
+ ///
+ /// x offset within window to place bottom left corner of text
+ /// y offset within window to place bottom left corner of text
+ /// text to write
+ /// colour to render text with; either COLOUR_PRIMARY or COLOUR_SECONDARY
+ /// pointer to GFXfont to use for text
void print(int x, int y, char* str, uint16_t colour, void* font);
+ ///
+ /// Print text to the screen
+ ///
+ /// x offset within window to place bottom left corner of text
+ /// y offset within window to place bottom left corner of text
+ /// text to write
+ /// colour to render text with; either COLOUR_PRIMARY or COLOUR_SECONDARY
+ /// id of a registered font
void print(int x, int y, char* str, uint16_t colour = COLOUR_PRIMARY, byte font_id = 0);
+ ///
+ /// Register a font with an id
+ ///
+ ///
+ /// This can be passed to print(), avoids having to pull in the entire GFX library in order to
+ /// print text.
+ ///
+ /// id to register the font with
+ /// a pointer to a GFXfont
void registerFont(byte id, const void* font);
};
diff --git a/watchos2/Queue.h b/watchos2/Queue.h
index cb8e8cf..83be1df 100644
--- a/watchos2/Queue.h
+++ b/watchos2/Queue.h
@@ -5,18 +5,58 @@
#define QUEUE_RING_SIZE 8
+///
+/// Simple FIFO ring buffer implementation for kernel handles
+///
class Queue
{
private:
+ ///
+ /// Buffered data
+ ///
kernel_handle_t m_item[QUEUE_RING_SIZE];
- int m_read = 0, m_write = 0;
+ ///
+ /// Buffer offset to read the next item from
+ ///
+ int m_read = 0;
+ ///
+ /// Buffer offset to write the next item to
+ ///
+ int m_write = 0;
+ ///
+ /// Set if the buffer is full
+ ///
+ ///
+ /// When m_read == m_write, the buffer is either full or empty. This allows us to differentiate.
+ ///
bool m_full = false;
+ ///
+ /// Get the next buffer index, implementing wrapping
+ ///
+ /// current buffer index
+ /// the buffer index after the passed buffer index
int next(int n);
public:
Queue();
+ ///
+ /// Push a handle into the buffer
+ ///
+ /// handle to add to the buffer
void push(kernel_handle_t item);
+ ///
+ /// Remove and return the next item in the buffer
+ ///
+ /// next handle in the buffer
kernel_handle_t pop();
+ ///
+ /// Return the next item in the buffer without removing it
+ ///
+ /// next handle in the buffer
kernel_handle_t peek();
+ ///
+ /// Check whether there are any items in the buffer
+ ///
+ /// true if no items in buffer, false if any items in buffer
bool isEmpty();
};
diff --git a/watchos2/Task.h b/watchos2/Task.h
index ec072fb..a913a7d 100644
--- a/watchos2/Task.h
+++ b/watchos2/Task.h
@@ -1,11 +1,26 @@
#ifndef _TASK_h
#define _TASK_h
+///
+/// General implementation of something that could be considered a "process"
+///
class Task
{
public:
+ ///
+ /// Initialize the task after a reset/first boot
+ ///
virtual void initialize();
+ ///
+ /// Start the task after a deep sleep
+ ///
+ ///
+ /// This is run even on a first boot, but _after_ initialize.
+ ///
virtual void start();
+ ///
+ /// Prepare the task for the system to shutdown/deep sleep
+ ///
virtual void suspend();
};
diff --git a/watchos2/Task_Clock.cpp b/watchos2/Task_Clock.cpp
index fb231bd..f3086a8 100644
--- a/watchos2/Task_Clock.cpp
+++ b/watchos2/Task_Clock.cpp
@@ -15,10 +15,9 @@ public:
watchos::subscribe(this, WATCHOS_MODULE_RTC, WATCHOS_EVENT_TYPE_RTC_MINUTE);
ui = static_cast(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);
}
};
\ No newline at end of file
diff --git a/watchos2/Task_Test.cpp b/watchos2/Task_Test.cpp
index 8229d48..32b1477 100644
--- a/watchos2/Task_Test.cpp
+++ b/watchos2/Task_Test.cpp
@@ -27,10 +27,8 @@ public:
}
ui = static_cast(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()
{
diff --git a/watchos2/__vm/Compile.vmps.xml b/watchos2/__vm/Compile.vmps.xml
index 020ff79..7b14af7 100644
--- a/watchos2/__vm/Compile.vmps.xml
+++ b/watchos2/__vm/Compile.vmps.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/watchos2/__vm/Upload.vmps.xml b/watchos2/__vm/Upload.vmps.xml
index 020ff79..7b14af7 100644
--- a/watchos2/__vm/Upload.vmps.xml
+++ b/watchos2/__vm/Upload.vmps.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/watchos2/watchos.h b/watchos2/watchos.h
index 547d7f9..3fb13df 100644
--- a/watchos2/watchos.h
+++ b/watchos2/watchos.h
@@ -14,28 +14,99 @@
#include "IRunnable.h"
#include "IDrawable.h"
+///
+/// General watchos interface for use in code
+///
class watchos
{
private:
+ ///
+ /// kernel() only ever instantiates one instance of the kernel, this is that instance.
+ ///
static Kernel* s_kernel;
public:
+ ///
+ /// Call from your .ino's setup() method after registering tasks and modules
+ ///
+ ///
+ /// This will check the wake-up reason and determine whether we need to initialize tasks,
+ /// and finally call start() on all registered tasks.
+ ///
static void setup();
+ ///
+ /// Call from your .ino's tick() method
+ ///
+ ///
+ /// Give's the kernel a chance to run any tasks that need it
+ ///
static void tick();
+ ///
+ /// Initialize the system
+ ///
+ ///
+ /// Currently initializes the serial interface and Wire library
+ ///
static void initialize();
+ ///
+ /// Fetch an instance of the kernel
+ ///
+ /// The same instance of the kernel with every call
static Kernel* kernel();
+ ///
+ /// Trigger a kernel panic
+ ///
+ ///
+ /// 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
+ ///
static void panic(char* fmt...);
+ ///
+ /// Print debugging output
+ ///
+ ///
+ /// Only when WATCHOS_DEBUG is set, will print the passed formatted string to
+ /// the serial console.
+ ///
static void debug(char* fmt...);
+ ///
+ /// Fetch an instance of an event by handle
+ ///
static Event* event(kernel_handle_t event);
+ ///
+ /// Fetch an instance of a task by handle
+ ///
static Task* task(kernel_handle_t task);
+ ///
+ /// Fetch an instance of a task by a well-known handle
+ ///
static Task* module(well_known_handle_t handle);
+ ///
+ /// Increase the reference count for the passed handle
+ ///
static void reference(kernel_handle_t handle);
+ ///
+ /// Decrease the reference count for the passed handle
+ ///
static void release(kernel_handle_t handle);
+ ///
+ /// Register a new task with the kernel
+ ///
+ /// an instance of a task
+ /// handle for the newly registered task
static kernel_handle_t run(Task* task);
+ ///
+ /// Subscribe a task to a set of events
+ ///
+ /// task to receive events; must implement EventListener
+ /// bitmask of which well known handles / modules we should receive events from
+ /// bitmask of which events we should receive
+ /// handle for the event subscription; release to unsubscribe
static kernel_handle_t subscribe(Task* task, well_known_handle_t source_mask, uint16_t event_mask);
};
diff --git a/watchos2/watchos2.ino b/watchos2/watchos2.ino
index bd810de..2affaf3 100644
--- a/watchos2/watchos2.ino
+++ b/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));
}
diff --git a/watchos2/watchos_config.h b/watchos2/watchos_config.h
index 54590af..f0f88d3 100644
--- a/watchos2/watchos_config.h
+++ b/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
\ No newline at end of file
diff --git a/watchos2/watchos_types.h b/watchos2/watchos_types.h
index 36d669e..037cdda 100644
--- a/watchos2/watchos_types.h
+++ b/watchos2/watchos_types.h
@@ -5,6 +5,9 @@
/// A handle to any kernel object
///
typedef uint16_t kernel_handle_t;
+///
+/// A static handle for locating modules
+///
typedef uint64_t well_known_handle_t;
#endif