From a7767e42bd238f4e92a35a2613f5c34d41ba2e1c Mon Sep 17 00:00:00 2001 From: Adam PIppin Date: Tue, 13 Jul 2021 00:38:22 -0700 Subject: [PATCH] Mostly a lot of comments and documentation --- watchos2/Event.h | 15 +++ watchos2/EventListener.h | 26 +++++ watchos2/IDrawable.h | 7 ++ watchos2/IRunnable.h | 6 + watchos2/Kernel.h | 98 ++++++++++++++-- watchos2/Module_Input.h | 6 + watchos2/Module_Power.h | 51 +++++++- watchos2/Module_RTC.cpp | 2 +- watchos2/Module_RTC.h | 34 ++++++ watchos2/Module_Storage.h | 91 ++++++++++++++- watchos2/Module_UI.cpp | 20 +++- watchos2/Module_UI.h | 207 +++++++++++++++++++++++++++++++-- watchos2/Queue.h | 42 ++++++- watchos2/Task.h | 15 +++ watchos2/Task_Clock.cpp | 5 +- watchos2/Task_Test.cpp | 4 +- watchos2/__vm/Compile.vmps.xml | 2 +- watchos2/__vm/Upload.vmps.xml | 2 +- watchos2/watchos.h | 71 +++++++++++ watchos2/watchos2.ino | 6 +- watchos2/watchos_config.h | 10 +- watchos2/watchos_types.h | 3 + 22 files changed, 678 insertions(+), 45 deletions(-) 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