Skip to content

Instantly share code, notes, and snippets.

@Neal
Created August 16, 2015 13:30
Show Gist options
  • Save Neal/b0bcabacfe809bc341d1 to your computer and use it in GitHub Desktop.
Save Neal/b0bcabacfe809bc341d1 to your computer and use it in GitHub Desktop.
Pebble SDK 3.2 header file
#pragma once
#include "pebble_fonts.h"
#include "src/resource_ids.auto.h"
#define PBL_APP_INFO(...) _Pragma("message \"\n\n \
*** PBL_APP_INFO has been replaced with appinfo.json\n \
Try updating your project with `pebble convert-project`\n \
Visit our developer guides to learn more about appinfo.json:\n \
http://developer.getpebble.com/guides/pebble-apps/\n \""); \
_Pragma("GCC error \"PBL_APP_INFO has been replaced with appinfo.json\"");
#define PBL_APP_INFO_SIMPLE PBL_APP_INFO
#include <locale.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include "pebble_warn_unsupported_functions.h"
#ifndef __FILE_NAME__
#define __FILE_NAME__ __FILE__
#endif
//! Calculate the length of an array, based on the size of the element type.
//! @param array The array to be evaluated.
//! @return The length of the array.
#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
struct tm;
typedef struct tm tm;
//! Determine whether a variable is signed or not.
//! @param var The variable to evaluate.
//! @return true if the variable is signed.
#define IS_SIGNED(var) (__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned char), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned short), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned int), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned long), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned long long), false, true))))) \
)
//! @addtogroup UI
//! @{
//! @addtogroup Clicks
//! \brief Handling button click interactions
//!
//! Each Pebble window handles Pebble's buttons while it is displayed. Raw button down and button
//! up events are transformed into click events that can be transferred to your app:
//!
//! * Single-click. Detects a single click, that is, a button down event followed by a button up event.
//! It also offers hold-to-repeat functionality (repeated click).
//! * Multi-click. Detects double-clicking, triple-clicking and other arbitrary click counts.
//! It can fire its event handler on all of the matched clicks, or just the last.
//! * Long-click. Detects long clicks, that is, press-and-hold.
//! * Raw. Simply forwards the raw button events. It is provided as a way to use both the higher level
//! "clicks" processing and the raw button events at the same time.
//!
//! To receive click events when a window is displayed, you must register a \ref ClickConfigProvider for
//! this window with \ref window_set_click_config_provider(). Your \ref ClickConfigProvider will be called every time
//! the window becomes visible with one context argument. By default this context is a pointer to the window but you can
//! change this with \ref window_set_click_config_provider_with_context().
//!
//! In your \ref ClickConfigProvider you call the \ref window_single_click_subscribe(), \ref window_single_repeating_click_subscribe(),
//! \ref window_multi_click_subscribe(), \ref window_long_click_subscribe() and \ref window_raw_click_subscribe() functions to register
//! a handler for each event you wish to receive.
//!
//! For convenience, click handlers are provided with a \ref ClickRecognizerRef and a user-specified context.
//!
//! The \ref ClickRecognizerRef can be used in combination with \ref click_number_of_clicks_counted(), \ref
//! click_recognizer_get_button_id() and \ref click_recognizer_is_repeating() to get more information about the click. This is
//! useful if you want different buttons or event types to share the same handler.
//!
//! The user-specified context is the context of your \ref ClickConfigProvider (see above). By default it points to the window.
//! You can override it for all handlers with \ref window_set_click_config_provider_with_context() or for a specific button with \ref
//! window_set_click_context().
//!
//! <h3>User interaction in watchfaces</h3>
//! Watchfaces cannot use the buttons to interact with the user. Instead, you can use the \ref AccelerometerService.
//!
//! <h3>About the Back button</h3>
//! By default, the Back button will always pop to the previous window on the \ref WindowStack (and leave the app if the current
//! window is the only window). You can override the default back button behavior with \ref window_single_click_subscribe() and
//! \ref window_multi_click_subscribe() but you cannot set a repeating, long or raw click handler on the back button because a long press
//! will always terminate the app and return to the main menu.
//!
//! <h3>Usage example</h3>
//! First associate a click config provider callback with your window:
//! \code{.c}
//! void app_init(void) {
//! ...
//! window_set_click_config_provider(&window, (ClickConfigProvider) config_provider);
//! ...
//! }
//! \endcode
//! Then in the callback, you set your desired configuration for each button:
//! \code{.c}
//! void config_provider(Window *window) {
//! // single click / repeat-on-hold config:
//! window_single_click_subscribe(BUTTON_ID_DOWN, down_single_click_handler);
//! window_single_repeating_click_subscribe(BUTTON_ID_SELECT, 1000, select_single_click_handler);
//!
//! // multi click config:
//! window_multi_click_subscribe(BUTTON_ID_SELECT, 2, 10, 0, true, select_multi_click_handler);
//!
//! // long click config:
//! window_long_click_subscribe(BUTTON_ID_SELECT, 700, select_long_click_handler, select_long_click_release_handler);
//! }
//! \endcode
//! Now you implement the handlers for each click you've subscribed to and set up:
//! \code{.c}
//! void down_single_click_handler(ClickRecognizerRef recognizer, void *context) {
//! ... called on single click ...
//! Window *window = (Window *)context;
//! }
//! void select_single_click_handler(ClickRecognizerRef recognizer, void *context) {
//! ... called on single click, and every 1000ms of being held ...
//! Window *window = (Window *)context;
//! }
//!
//! void select_multi_click_handler(ClickRecognizerRef recognizer, void *context) {
//! ... called for multi-clicks ...
//! Window *window = (Window *)context;
//! const uint16_t count = click_number_of_clicks_counted(recognizer);
//! }
//!
//! void select_long_click_handler(ClickRecognizerRef recognizer, void *context) {
//! ... called on long click start ...
//! Window *window = (Window *)context;
//! }
//!
//! void select_long_click_release_handler(ClickRecognizerRef recognizer, void *context) {
//! ... called when long click is released ...
//! Window *window = (Window *)context;
//! }
//! \endcode
//!
//! <h3>See also</h3>
//! Refer to the \htmlinclude UiFramework.html (chapter "Clicks") for a conceptual
//! overview of clicks and relevant code examples.
//!
//! @{
//! Button ID values
//! @see \ref click_recognizer_get_button_id()
typedef enum {
//! Back button
BUTTON_ID_BACK = 0,
//! Up button
BUTTON_ID_UP,
//! Select (middle) button
BUTTON_ID_SELECT,
//! Down button
BUTTON_ID_DOWN,
//! Total number of buttons
NUM_BUTTONS
} ButtonId;
//! @} // group Clicks
//! @} // group UI
//! @addtogroup Foundation
//! @{
//! @addtogroup Internationalization
//! \brief Internationalization & Localization APIs
//!
//! @{
//! Get the ISO locale name for the language currently set on the watch
//! @return A string containing the ISO locale name (e.g. "fr", "en_US", ...)
//! @note It is possible for the locale to change while your app is running.
//! And thus, two calls to i18n_get_system_locale may return different values.
const char *i18n_get_system_locale(void);
//! @} // group Internationalization
//! @addtogroup WatchInfo
//! \brief Provides information about the watch itself.
//!
//! This API provides access to information such as the watch model, watch color
//! and watch firmware version.
//! @{
//! The different watch models.
typedef enum {
WATCH_INFO_MODEL_UNKNOWN, //!< Unknown model
WATCH_INFO_MODEL_PEBBLE_ORIGINAL, //!< Original Pebble
WATCH_INFO_MODEL_PEBBLE_STEEL, //!< Pebble Steel
WATCH_INFO_MODEL_PEBBLE_TIME, //!< Pebble Time
WATCH_INFO_MODEL_PEBBLE_TIME_STEEL //!< Pebble Time Steel
} WatchInfoModel;
//! The different watch colors.
typedef enum {
WATCH_INFO_COLOR_UNKNOWN = 0, //!< Unknown color
WATCH_INFO_COLOR_BLACK = 1, //!< Black
WATCH_INFO_COLOR_WHITE = 2, //!< White
WATCH_INFO_COLOR_RED = 3, //!< Red
WATCH_INFO_COLOR_ORANGE = 4, //!< Orange
WATCH_INFO_COLOR_GRAY = 5, //!< Gray
WATCH_INFO_COLOR_STAINLESS_STEEL = 6, //!< Stainless Steel
WATCH_INFO_COLOR_MATTE_BLACK = 7, //!< Matte Black
WATCH_INFO_COLOR_BLUE = 8, //!< Blue
WATCH_INFO_COLOR_GREEN = 9, //!< Green
WATCH_INFO_COLOR_PINK = 10, //!< Pink
WATCH_INFO_COLOR_TIME_WHITE = 11, //!< Time White
WATCH_INFO_COLOR_TIME_BLACK = 12, //!< Time Black
WATCH_INFO_COLOR_TIME_RED = 13, //!< Time Red
WATCH_INFO_COLOR_TIME_STEEL_SILVER = 14, //!< Time Steel Silver
WATCH_INFO_COLOR_TIME_STEEL_BLACK = 15, //!< Time Steel Black
WATCH_INFO_COLOR_TIME_STEEL_GOLD = 16 //!< Time Steel Gold
} WatchInfoColor;
//! Data structure containing the version of the firmware running on the watch.
//! The version of the firmware has the form X.[X.[X]]. If a version number is not present it will be 0.
//! For example: the version numbers of 2.4.1 are 2, 4, and 1. The version numbers of 2.4 are 2, 4, and 0.
typedef struct {
uint8_t major; //!< Major version number
uint8_t minor; //!< Minor version number
uint8_t patch; //!< Patch version number
} WatchInfoVersion;
//! Provides the model of the watch.
//! @return {@link WatchInfoModel} representing the model of the watch.
WatchInfoModel watch_info_get_model(void);
//! Provides the version of the firmware running on the watch.
//! @return {@link WatchInfoVersion} representing the version of the firmware running on the watch.
WatchInfoVersion watch_info_get_firmware_version(void);
//! Provides the color of the watch.
//! @return {@link WatchInfoColor} representing the color of the watch.
WatchInfoColor watch_info_get_color(void);
//! @} // group WatchInfo
//! @addtogroup Math
//! @{
//! The largest value that can result from a call to \ref sin_lookup or \ref cos_lookup.
//! For a code example, see the detailed description at the top of this chapter: \ref Math
#define TRIG_MAX_RATIO 0xffff
//! Angle value that corresponds to 360 degrees or 2 PI radians
//! @see \ref sin_lookup
//! @see \ref cos_lookup
#define TRIG_MAX_ANGLE 0x10000
//! Look-up the sine of the given angle from a pre-computed table.
//! @param angle The angle for which to compute the cosine.
//! The angle value is scaled linearly, such that a value of 0x10000 corresponds to 360 degrees or 2 PI radians.
int32_t sin_lookup(int32_t angle);
//! Look-up the cosine of the given angle from a pre-computed table.
//! This is equivalent to calling `sin_lookup(angle + TRIG_MAX_ANGLE / 4)`.
//! @param angle The angle for which to compute the cosine.
//! The angle value is scaled linearly, such that a value of 0x10000 corresponds to 360 degrees or 2 PI radians.
int32_t cos_lookup(int32_t angle);
//! Look-up the arctangent of a given x, y pair
//! The angle value is scaled linearly, such that a value of 0x10000 corresponds to 360 degrees or 2 PI radians.
int32_t atan2_lookup(int16_t y, int16_t x);
//! @} // group Math
//! @addtogroup WallTime Wall Time
//! \brief Functions, data structures and other things related to wall clock time.
//!
//! This module contains utilities to get the current time and create strings with formatted
//! dates and times.
//! @{
//! Weekday values
typedef enum {
TODAY = 0, //!< Today
SUNDAY, //!< Sunday
MONDAY, //!< Monday
TUESDAY, //!< Tuesday
WEDNESDAY, //!< Wednesday
THURSDAY, //!< Thursday
FRIDAY, //!< Friday
SATURDAY, //!< Saturday
} WeekDay;
//! Copies a time string into the buffer, formatted according to the user's time display preferences (such as 12h/24h
//! time).
//! Example results: "7:30" or "15:00".
//! @note AM/PM are also outputted with the time if the user's preference is 12h time.
//! @param[out] buffer A pointer to the buffer to copy the time string into
//! @param size The maximum size of buffer
void clock_copy_time_string(char *buffer, uint8_t size);
//! Gets the user's 12/24h clock style preference.
//! @return `true` if the user prefers 24h-style time display or `false` if the
//! user prefers 12h-style time display.
bool clock_is_24h_style(void);
//! Converts a (day, hour, minute) specification to a UTC timestamp occurring in the future
//! Always returns a timestamp for the next occurring instance,
//! example: specifying TODAY@14:30 when it is 14:40 will return a timestamp for 7 days from
//! now at 14:30
//! @note This function does not support Daylight Saving Time (DST) changes, events scheduled
//! during a DST change will be off by an hour.
//! @param day WeekDay day of week including support for specifying TODAY
//! @param hour hour specified in 24-hour format [0-23]
//! @param minute minute [0-59]
time_t clock_to_timestamp(WeekDay day, int hour, int minute);
//! Checks if timezone is currently set, otherwise gmtime == localtime.
//! @return `true` if timezone has been set, false otherwise
bool clock_is_timezone_set(void);
//! The maximum length for a timezone full name (e.g. America/Chicago)
#define TIMEZONE_NAME_LENGTH 32
//! If timezone is set, copies the current timezone long name (e.g. America/Chicago)
//! to user-provided buffer.
//! @param timezone A pointer to the buffer to copy the timezone long name into
//! @param buffer_size Size of the allocated buffer to copy the timezone long name into
//! @note timezone buffer should be at least TIMEZONE_NAME_LENGTH bytes
void clock_get_timezone(char *timezone, const size_t buffer_size);
//! @} // group WallTime
//! @addtogroup EventService
//! @{
//! @addtogroup BluetoothConnectionService
//! \brief Determine when Pebble is connected to the phone
//!
//! The BluetoothConnectionService allows your app to know whether Pebble is connected to the phone.
//! You can ask the system for this information at a given time or you can register
//! to receive events every time Pebble connects or disconnects to the phone.
//! @{
//! Callback type for bluetooth connection events
//! @param connected true on bluetooth connection, false on disconnection
typedef void (*BluetoothConnectionHandler)(bool connected);
//! Query the bluetooth connection service for the current connection status
//! @return true if connected, false otherwise
bool bluetooth_connection_service_peek(void);
//! Subscribe to the bluetooth event service. Once subscribed, the handler gets called
//! on every bluetooth connection event.
//! @param handler A callback to be executed on connection event
void bluetooth_connection_service_subscribe(BluetoothConnectionHandler handler);
//! Unsubscribe from the bluetooth event service. Once unsubscribed, the previously registered
//! handler will no longer be called.
void bluetooth_connection_service_unsubscribe(void);
//! @} // group BluetoothConnectionService
//! @addtogroup AppFocusService
//!
//!
//! \brief Handling app focus
//!
//! The AppFocusService is used for apps that require a high degree of user interactivity, like a game
//! when you need to know when to pause your app when a notification covers your app window.
//!
//! @{
//! Callback type for focus events
//! @param in_focus True if the app is in focus, false otherwise.
typedef void (*AppFocusHandler)(bool in_focus);
//! Subscribe to the focus event service. Once subscribed, the handler
//! gets called every time the app focus changes.
//! @note In focus events are triggered when the app is no longer covered by a
//! modal window.
//! @note Out focus events are triggered when the app becomes covered by a modal
//! window.
//! @param handler A callback to be executed on in-focus events.
void app_focus_service_subscribe(AppFocusHandler handler);
//! Unscribe from the focus event service. Once unsubscribed, the previously
//! registered handler will no longer be called.
void app_focus_service_unsubscribe(void);
//! @} // group AppFocusService
//! @addtogroup BatteryStateService
//!
//! \brief Determines when the battery state changes
//!
//! The BatteryStateService API lets you know when the battery state changes, that is,
//! its current charge level, whether it is plugged and charging. It uses the
//! BatteryChargeState structure to describe the current power state of Pebble.
//!
//! Refer to /Examples/watchfaces/classio-battery-connection,
//! which demonstrates using the battery state service in a watchface.
//! @{
//! Structure for retrieval of the battery charge state
typedef struct {
//! A percentage (0-100) of how full the battery is
uint8_t charge_percent;
//! True if the battery is currently being charged. False if not.
bool is_charging;
//! True if the charger cable is connected. False if not.
bool is_plugged;
} BatteryChargeState;
//! Callback type for battery state change events
//! @param charge the state of the battery \ref BatteryChargeState
typedef void (*BatteryStateHandler)(BatteryChargeState charge);
//! Subscribe to the battery state event service. Once subscribed, the handler gets called
//! on every battery state change
//! @param handler A callback to be executed on battery state change event
void battery_state_service_subscribe(BatteryStateHandler handler);
//! Unsubscribe from the battery state event service. Once unsubscribed, the previously registered
//! handler will no longer be called.
void battery_state_service_unsubscribe(void);
//! Peek at the last known battery state.
//! @return a \ref BatteryChargeState containing the last known data
BatteryChargeState battery_state_service_peek(void);
//! @} // group BatteryStateService
//! @addtogroup AccelerometerService
//!
//! \brief Using the Pebble accelerometer
//!
//! The AccelerometerService enables the Pebble accelerometer to detect taps,
//! perform measures at a given frequency, and transmit samples in batches to save CPU time
//! and processing.
//!
//! For available code samples, see
//! Examples/watchapps/feature_accel_discs
//! @{
//! A single accelerometer sample for all three axes including timestamp and
//! vibration rumble status.
typedef struct __attribute__((__packed__)) AccelData {
//! acceleration along the x axis
int16_t x;
//! acceleration along the y axis
int16_t y;
//! acceleration along the z axis
int16_t z;
//! true if the watch vibrated when this sample was collected
bool did_vibrate;
//! timestamp, in milliseconds
uint64_t timestamp;
} AccelData;
//! A single accelerometer sample for all three axes
typedef struct __attribute__((__packed__)) {
//! acceleration along the x axis
int16_t x;
//! acceleration along the y axis
int16_t y;
//! acceleration along the z axis
int16_t z;
} AccelRawData;
//! Enumerated values defining the three accelerometer axes.
typedef enum {
//! Accelerometer's X axis. The positive direction along the X axis goes
//! toward the right of the watch.
ACCEL_AXIS_X = 0,
//! Accelerometer's Y axis. The positive direction along the Y axis goes
//! toward the top of the watch.
ACCEL_AXIS_Y = 1,
//! Accelerometer's Z axis. The positive direction along the Z axis goes
//! vertically out of the watchface.
ACCEL_AXIS_Z = 2,
} AccelAxisType;
//! Callback type for accelerometer data events
//! @param data Pointer to the collected accelerometer samples.
//! @param num_samples the number of samples stored in data.
typedef void (*AccelDataHandler)(AccelData *data, uint32_t num_samples);
//! Callback type for accelerometer raw data events
//! @param data Pointer to the collected accelerometer samples.
//! @param num_samples the number of samples stored in data.
//! @param timestamp the timestamp, in ms, of the first sample.
typedef void (*AccelRawDataHandler)(AccelRawData *data, uint32_t num_samples, uint64_t timestamp);
//! Callback type for accelerometer tap events
//! @param axis the axis on which a tap was registered (x, y, or z)
//! @param direction the direction (-1 or +1) of the tap
typedef void (*AccelTapHandler)(AccelAxisType axis, int32_t direction);
//! Valid accelerometer sampling rates, in Hz
typedef enum {
//! 10 HZ sampling rate
ACCEL_SAMPLING_10HZ = 10,
//! 25 HZ sampling rate [Default]
ACCEL_SAMPLING_25HZ = 25,
//! 50 HZ sampling rate
ACCEL_SAMPLING_50HZ = 50,
//! 100 HZ sampling rate
ACCEL_SAMPLING_100HZ = 100,
} AccelSamplingRate;
//! Peek at the last recorded reading.
//! @param[out] data a pointer to a pre-allocated AccelData item
//! @note Cannot be used when subscribed to accelerometer data events.
//! @return -1 if the accel is not running
//! @return -2 if subscribed to accelerometer events.
int accel_service_peek(AccelData *data);
//! Change the accelerometer sampling rate.
//! @param rate The sampling rate in Hz (10Hz, 25Hz, 50Hz, and 100Hz possible)
int accel_service_set_sampling_rate(AccelSamplingRate rate);
//! Change the number of samples buffered between each accelerometer data event
//! @param num_samples the number of samples to buffer, between 0 and 25.
int accel_service_set_samples_per_update(uint32_t num_samples);
//! Subscribe to the accelerometer data event service. Once subscribed, the handler
//! gets called every time there are new accelerometer samples available.
//! @note Cannot use \ref accel_service_peek() when subscribed to accelerometer data events.
//! @param handler A callback to be executed on accelerometer data events
//! @param samples_per_update the number of samples to buffer, between 0 and 25.
void accel_data_service_subscribe(uint32_t samples_per_update, AccelDataHandler handler);
//! Unsubscribe from the accelerometer data event service. Once unsubscribed,
//! the previously registered handler will no longer be called.
void accel_data_service_unsubscribe(void);
//! Subscribe to the accelerometer tap event service. Once subscribed, the handler
//! gets called on every tap event emitted by the accelerometer.
//! @param handler A callback to be executed on tap event
void accel_tap_service_subscribe(AccelTapHandler handler);
//! Unsubscribe from the accelerometer tap event service. Once unsubscribed,
//! the previously registered handler will no longer be called.
void accel_tap_service_unsubscribe(void);
//! Subscribe to the accelerometer raw data event service. Once subscribed, the handler
//! gets called every time there are new accelerometer samples available.
//! @note Cannot use \ref accel_service_peek() when subscribed to accelerometer data events.
//! @param handler A callback to be executed on accelerometer data events
//! @param samples_per_update the number of samples to buffer, between 0 and 25.
void accel_raw_data_service_subscribe(uint32_t samples_per_update, AccelRawDataHandler handler);
//! @} // group AccelerometerService
//! @addtogroup CompassService
//!
//! \brief The Compass Service combines information from Pebble's accelerometer and
//! magnetometer to automatically calibrate
//! the compass and transform the raw magnetic field information into a \ref CompassHeading,
//! that is an angle to north. It also
//! provides magnetic north and information about its status and accuracy through the \ref
//! CompassHeadingData structure. The API is designed to also provide true north in a future
//! release.
//!
//! To learn more about the Compass Service and how to use it, read the
//! <a href="https://developer.getpebble.com/guides/pebble-apps/sensors/magnetometer/">
//! Determining Direction</a> guide.
//!
//! For available code samples, see
//! <a href="https://github.com/pebble/pebble-sdk-examples/tree/master/watchfaces/feature_compass">
//! Examples/watchfaces/feature_compass</a>.
//!
//! @{
//! Converts from a fixed point value representation of trig_angle to the equivalent value in degrees
#define TRIGANGLE_TO_DEG(trig_angle) (((trig_angle) * 360) / TRIG_MAX_ANGLE)
typedef struct __attribute__((__packed__)) {
//! magnetic field along the x axis
int16_t x;
//! magnetic field along the y axis
int16_t y;
//! magnetic field along the z axis
int16_t z;
} MagData;
//! Enum describing the current state of the Compass Service calibration
typedef enum {
//! Compass is calibrating: data is invalid and should not be used
CompassStatusDataInvalid = 0,
//! Compass is calibrating: the data is valid but the calibration is still being refined
CompassStatusCalibrating,
//! Compass data is valid and the calibration has completed
CompassStatusCalibrated
} CompassStatus;
//! Represents an angle relative to a reference direction, e.g. (magnetic) north.
//! The angle value is scaled linearly, such that a value of TRIG_MAX_ANGLE corresponds to 360 degrees or 2 PI radians.
//! Thus, if heading towards north, north is 0, east is TRIG_MAX_ANGLE/4, south is TRIG_MAX_ANGLE/2, and so on.
typedef int32_t CompassHeading;
//! Structure containing a single heading towards magnetic and true north.
typedef struct {
//! measured angle relative to magnetic north
CompassHeading magnetic_heading;
//! measured angle relative to true north (or to magnetic north if declination
//! is invalid)
CompassHeading true_heading;
//! indicates the current state of the Compass Service calibration
CompassStatus compass_status;
//! true, if the current declination is known and applied to `true_heading`, false otherwise
bool is_declination_valid;
} CompassHeadingData;
//! Callback type for compass heading events
//! @param heading copy of last recorded heading
typedef void (*CompassHeadingHandler)(CompassHeadingData heading);
//! Set the minimum angular change required to generate new compass heading events.
//! The angular distance is measured relative to the last delivered heading event.
//! Use 0 to be notified of all movements.
//! Negative values and values > TRIG_MAX_ANGLE / 2 are not valid.
//! The default value of this property is TRIG_MAX_ANGLE / 360.
//! @return 0, success.
//! @return Non-Zero, if filter is invalid.
//! @see compass_service_subscribe
int compass_service_set_heading_filter(CompassHeading filter);
//! Subscribe to the compass heading event service. Once subscribed, the handler
//! gets called every time the angular distance relative to the previous value
//! exceeds the configured filter.
//! @param handler A callback to be executed on heading events
//! @see compass_service_set_heading_filter
//! @see compass_service_unsubscribe
void compass_service_subscribe(CompassHeadingHandler handler);
//! Unsubscribe from the compass heading event service. Once unsubscribed,
//! the previously registered handler will no longer be called.
//! @see compass_service_subscribe
void compass_service_unsubscribe(void);
//! Peek at the last recorded reading.
//! @param[out] data a pointer to a pre-allocated CompassHeadingData
//! @return Always returns 0 to indicate success.
int compass_service_peek(CompassHeadingData *data);
//! @} // group CompassService
//! @addtogroup TickTimerService
//! \brief Handling time components
//!
//! The TickTimerService allows your app to be called every time one Time component has changed.
//! This is extremely important for watchfaces. Your app can choose on which time component
//! change a tick should occur. Time components are defined by a \ref TimeUnits enum bitmask.
//! @{
//! Time unit flags that can be used to create a bitmask for use in \ref tick_timer_service_subscribe().
//! This will also be passed to \ref TickHandler.
typedef enum {
//! Flag to represent the "seconds" time unit
SECOND_UNIT = 1 << 0,
//! Flag to represent the "minutes" time unit
MINUTE_UNIT = 1 << 1,
//! Flag to represent the "hours" time unit
HOUR_UNIT = 1 << 2,
//! Flag to represent the "days" time unit
DAY_UNIT = 1 << 3,
//! Flag to represent the "months" time unit
MONTH_UNIT = 1 << 4,
//! Flag to represent the "years" time unit
YEAR_UNIT = 1 << 5
} TimeUnits;
//! Callback type for tick timer events
//! @param tick_time the time at which the tick event was triggered
//! @param units_changed which unit change triggered this tick event
typedef void (*TickHandler)(struct tm *tick_time, TimeUnits units_changed);
//! Subscribe to the tick timer event service. Once subscribed, the handler gets called
//! on every requested unit change.
//! Calling this function multiple times will override the units and handler (i.e., only 
//! the last tick_units and handler passed will be used).
//! @param handler The callback to be executed on tick events
//! @param tick_units a bitmask of all the units that have changed
void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler);
//! Unsubscribe from the tick timer event service. Once unsubscribed, the previously registered
//! handler will no longer be called.
void tick_timer_service_unsubscribe(void);
//! @} // group TickTimerService
//! @} // group EventService
//! @addtogroup DataLogging
//! \brief Enables logging data asynchronously to a mobile app
//!
//! In Pebble OS, data logging is a data storage and transfer subsystem that allows watchapps to
//! save data on non-volatile storage devices when the phone is not available to process it. The
//! API provides your watchapp with a mechanism for short-term data buffering for asynchronous data
//! transmission to a mobile app.
//!
//! Using this API, your Pebble watchapp can create an arbitrary number of logs, but you’re
//! limited in the amount of storage space you can use. Note that approximately 640K is available
//! for data logging, which is shared among all watchapps that use it. This value is subject to
//! change in the future. When the data spool is full, an app will start overwriting its own data.
//! An app cannot overwrite another apps's data. However, the other app might have 0 bytes for data
//! logging.
//!
//! Your app can log data to a session, either creating, adding or deleting data to that session.
//! The data is then sent to the associated phone application at the earliest convenience.
//! If a phone is available, the data is sent directly to the phone. Otherwise, it is saved to the
//! watch storage until the watch is connected to a phone.
//!
//!
//! For example:
//!
//! To create a data logging session for 4-byte unsigned integers with a tag of 0x1234, you would
//! do this: \code{.c}
//!
//! DataLoggingSessionRef logging_session = data_logging_create(0x1234, DATA_LOGGING_UINT, 4,
//! false);
//!
//! // Fake creating some data and logging it to the session.
//! uint32_t data[] = { 1, 2, 3};
//! data_logging_log(logging_session, &data, 3);
//!
//! // Fake creating more data and logging that as well.
//! uint32_t data2[] = { 1, 2 };
//! data_logging_log(logging_session, &data, 2);
//!
//! // When we don't need to log anything else, we can close off the session.
//! data_logging_finish(logging_session);
//! \endcode
//!
//! For code samples, refer to Examples/data-logging-demo.
//! @{
typedef void *DataLoggingSessionRef;
//! The different types of session data that Pebble supports. This type describes the type of a
//! singular item in the data session. Every item in a given session is the same type and size.
typedef enum {
//! Array of bytes. Remember that this is the type of a single item in the logging session, so
//! using this type means you'll be logging multiple byte arrays (each a fixed length described
//! by item_length) for the duration of the session.
DATA_LOGGING_BYTE_ARRAY = 0,
//! Unsigned integer. This may be a 1, 2, or 4 byte integer depending on the item_length parameter
DATA_LOGGING_UINT = 2,
//! Signed integer. This may be a 1, 2, or 4 byte integer depending on the item_length parameter
DATA_LOGGING_INT = 3,
} DataLoggingItemType;
//! Enumerated values describing the possible outcomes of data logging operations
typedef enum {
DATA_LOGGING_SUCCESS = 0, //!< Successful operation
DATA_LOGGING_BUSY, //!< Someone else is writing to this logging session
DATA_LOGGING_FULL, //!< No more space to save data
DATA_LOGGING_NOT_FOUND, //!< The logging session does not exist
DATA_LOGGING_CLOSED, //!< The logging session was made inactive
DATA_LOGGING_INVALID_PARAMS, //!< An invalid parameter was passed to one of the functions
DATA_LOGGING_INTERNAL_ERR //!< An internal error occurred
} DataLoggingResult;
//! Create a new data logging session.
//!
//! @param tag A tag associated with the logging session.
//! @param item_type The type of data stored in this logging session
//! @param item_length The size of a single data item in bytes
//! @param resume True if we want to look for a logging session of the same tag and
//! resume logging to it. If this is false and a session with the specified tag exists, that
//! session will be closed and a new session will be opened.
//! @return An opaque reference to the data logging session
DataLoggingSessionRef data_logging_create(uint32_t tag, DataLoggingItemType item_type,
uint16_t item_length, bool resume);
//! Finish up a data logging_session. Logging data is kept until it has successfully been
//! transferred over to the phone, but no data may be added to the session after this function is
//! called.
//!
//! @param logging_session a reference to the data logging session previously allocated using
//! data_logging_create
void data_logging_finish(DataLoggingSessionRef logging_session);
//! Add data to the data logging session. If a phone is available, the data is sent directly
//! to the phone. Otherwise, it is saved to the watch storage until the watch is connected to a
//! phone.
//!
//! @param logging_session a reference to the data logging session you want to add the data to
//! @param data a pointer to the data buffer that contains multiple items
//! @param num_items the number of items to log. This means data must be at least
//! (num_items * item_length) long in bytes
//! @return
//! DATA_LOGGING_SUCCESS on success
//!
//! @return
//! DATA_LOGGING_NOT_FOUND if the logging session is invalid
//!
//! @return
//! DATA_LOGGING_CLOSED if the sesion is not active
//!
//! @return
//! DATA_LOGGING_BUSY if the sesion is not available for writing
//!
//! @return
//! DATA_LOGGING_INVALID_PARAMS if num_items is 0 or data is NULL
DataLoggingResult data_logging_log(DataLoggingSessionRef logging_session, const void *data,
uint32_t num_items);
//! @} // group DataLogging
//! @addtogroup DataStructures
//! @{
//! @addtogroup UUID
//! @{
typedef struct __attribute__((__packed__)) {
uint8_t byte0;
uint8_t byte1;
uint8_t byte2;
uint8_t byte3;
uint8_t byte4;
uint8_t byte5;
uint8_t byte6;
uint8_t byte7;
uint8_t byte8;
uint8_t byte9;
uint8_t byte10;
uint8_t byte11;
uint8_t byte12;
uint8_t byte13;
uint8_t byte14;
uint8_t byte15;
} Uuid;
#define UUID_SIZE 16
//! Make a Uuid object from sixteen bytes.
//! @return A Uuid structure representing the bytes p0 to p15.
#define UuidMake(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15) ((Uuid) {p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15})
//! Creates a Uuid from an array of bytes with 16 bytes in Big Endian order.
//! @return The created Uuid
#define UuidMakeFromBEBytes(b) ((Uuid) { b[0], b[1], b[2], b[3], \
b[4], b[5], b[6], b[7], \
b[8], b[9], b[10], b[11], \
b[12], b[13], b[14], b[15] })
//! Creates a Uuid from an array of bytes with 16 bytes in Little Endian order.
//! @return The created Uuid
#define UuidMakeFromLEBytes(b) ((Uuid) { b[15], b[14], b[13], b[12], \
b[11], b[10], b[9], b[8], \
b[7], b[6], b[5], b[4], \
b[3], b[2], b[1], b[0] })
//! Compares two UUIDs.
//! @return True if the two UUIDs are equal, false if they are not.
bool uuid_equal(const Uuid *uu1, const Uuid *uu2);
//! Writes UUID in a string form into buffer that looks like the following...
//! {12345678-1234-5678-1234-567812345678}
//! @param uuid The Uuid to write into the buffer as human-readable string
//! @param buffer Memory to write the string to. Must be at least \ref UUID_STRING_BUFFER_LENGTH bytes long.
void uuid_to_string(const Uuid *uuid, char *buffer);
//! The minimum required length of a string used to hold a uuid (including null).
#define UUID_STRING_BUFFER_LENGTH (32 + 4 + 2 + 1)
//! @} // group UUID
//! @} // group DataStructures
//! @addtogroup Logging Logging
//! \brief Functions related to logging from apps.
//!
//! This module contains the functions necessary to log messages through
//! Bluetooth.
//! @note It is no longer necessary to enable app logging output from the "settings->about" menu on the Pebble for
//! them to be transmitted! Instead use the "pebble logs" command included with the SDK to activate logs. The logs
//! will appear right in your console. Logging
//! over Bluetooth is a fairly power hungry operation that non-developers will
//! not need when your apps are distributed.
//! @{
//! Log an app message.
//! @param log_level
//! @param src_filename The source file where the log originates from
//! @param src_line_number The line number in the source file where the log originates from
//! @param fmt A C formatting string
//! @param ... The arguments for the formatting string
//! @param log_level
//! \sa snprintf for details about the C formatting string.
void app_log(uint8_t log_level, const char* src_filename, int src_line_number, const char* fmt, ...)
__attribute__((format(printf, 4, 5)));
//! A helper macro that simplifies the use of the app_log function
//! @param level The log level to log output as
//! @param fmt A C formatting string
//! @param args The arguments for the formatting string
#define APP_LOG(level, fmt, args...) \
app_log(level, __FILE_NAME__, __LINE__, fmt, ## args)
//! Suggested log level values
typedef enum {
//! Error level log message
APP_LOG_LEVEL_ERROR = 1,
//! Warning level log message
APP_LOG_LEVEL_WARNING = 50,
//! Info level log message
APP_LOG_LEVEL_INFO = 100,
//! Debug level log message
APP_LOG_LEVEL_DEBUG = 200,
//! Verbose Debug level log message
APP_LOG_LEVEL_DEBUG_VERBOSE = 255,
} AppLogLevel;
//! @} // group Logging
//! @addtogroup Dictionary
//! \brief Data serialization utilities
//!
//!
//! Data residing in different parts of Pebble memory (RAM) may need to be gathered and assembled into
//! a single continuous block for transport over the network via Bluetooth. The process of gathering
//! and assembling this continuous block of data is called serialization.
//!
//! You use data serialization utilities, like Dictionary, Tuple and Tuplet data structures and accompanying
//! functions, to accomplish this task. No transformations are performed on the actual data, however.
//! These Pebble utilities simply help assemble the data into one continuous buffer according to a
//! specific format.
//!
//! \ref AppMessage uses these utilities--in particular, Dictionary--to send information between mobile
//! and Pebble watchapps.
//!
//! <h3>Writing key/value pairs</h3>
//! To write two key/value pairs, without using Tuplets, you would do this:
//! \code{.c}
//! // Byte array + key:
//! static const uint32_t SOME_DATA_KEY = 0xb00bf00b;
//! static const uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
//!
//! // CString + key:
//! static const uint32_t SOME_STRING_KEY = 0xabbababe;
//! static const char *string = "Hello World";
//!
//! // Calculate the buffer size that is needed for the final Dictionary:
//! const uint8_t key_count = 2;
//! const uint32_t size = dict_calc_buffer_size(key_count, sizeof(data),
//! strlen(string) + 1);
//!
//! // Stack-allocated buffer in which to create the Dictionary:
//! uint8_t buffer[size];
//!
//! // Iterator variable, keeps the state of the creation serialization process:
//! DictionaryIterator iter;
//!
//! // Begin:
//! dict_write_begin(&iter, buffer, sizeof(buffer));
//! // Write the Data:
//! dict_write_data(&iter, SOME_DATA_KEY, data, sizeof(data));
//! // Write the CString:
//! dict_write_cstring(&iter, SOME_STRING_KEY, string);
//! // End:
//! const uint32_t final_size = dict_write_end(&iter);
//!
//! // buffer now contains the serialized information
//!
//! \endcode
//!
//! <h3>Reading key/value pairs</h3>
//! To iterate over the key/value pairs in the dictionary that
//! was created in the previous example code, you would do this:
//!
//! \code{.c}
//! Tuple *tuple = dict_read_begin_from_buffer(&iter, buffer, final_size);
//! while (tuple) {
//! switch (tuple->key) {
//! case SOME_DATA_KEY:
//! foo(tuple->value->data, tuple->length);
//! break;
//! case SOME_STRING_KEY:
//! bar(tuple->value->cstring);
//! break;
//! }
//! tuple = dict_read_next(&iter);
//! }
//! \endcode
//!
//! <h3>Tuple and Tuplet data structures</h3>
//! To understand the difference between Tuple and Tuplet data structures:
//! Tuple is the header for a serialized key/value pair, while Tuplet is a helper
//! data structure that references the value you want to serialize. This data
//! structure exists to make the creation of a Dictionary easier to write.
//! Use this mnemonic to remember the difference: TupleT(emplate), the Tuplet being
//! a template to create a Dictionary with Tuple structures.
//!
//! For example:
//! \code{.c}
//! Tuplet pairs[] = {
//! TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1),
//! TupletCString(WEATHER_TEMPERATURE_KEY, "1234 Fahrenheit"),
//! };
//! uint8_t buffer[256];
//! uint32_t size = sizeof(buffer);
//! dict_serialize_tuplets_to_buffer(pairs, ARRAY_LENGTH(pairs), buffer, &size);
//!
//! // buffer now contains the serialized information
//! \endcode
//! @{
//! Return values for dictionary write/conversion functions.
typedef enum {
//! The operation returned successfully
DICT_OK = 0,
//! There was not enough backing storage to complete the operation
DICT_NOT_ENOUGH_STORAGE = 1 << 1,
//! One or more arguments were invalid or uninitialized
DICT_INVALID_ARGS = 1 << 2,
//! The lengths and/or count of the dictionary its tuples are inconsistent
DICT_INTERNAL_INCONSISTENCY = 1 << 3,
//! A requested operation required additional memory to be allocated, but
//! the allocation failed, likely due to insufficient remaining heap memory.
DICT_MALLOC_FAILED = 1 << 4,
} DictionaryResult;
//! Values representing the type of data that the `value` field of a Tuple contains
typedef enum {
//! The value is an array of bytes
TUPLE_BYTE_ARRAY = 0,
//! The value is a zero-terminated, UTF-8 C-string
TUPLE_CSTRING = 1,
//! The value is an unsigned integer. The tuple's `.length` field is used to
//! determine the size of the integer (1, 2, or 4 bytes).
TUPLE_UINT = 2,
//! The value is a signed integer. The tuple's `.length` field is used to
//! determine the size of the integer (1, 2, or 4 bytes).
TUPLE_INT = 3,
} TupleType;
//! Data structure for one serialized key/value tuple
//! @note The structure is variable length! The length depends on the value data that the tuple
//! contains.
typedef struct __attribute__((__packed__)) {
//! The key
uint32_t key;
//! The type of data that the `.value` fields contains.
TupleType type:8;
//! The length of `.value` in bytes
uint16_t length;
//! @brief The value itself.
//!
//! The different union fields are provided for convenience, avoiding the need for manual casts.
//! @note The array length is of incomplete length on purpose, to facilitate
//! variable length data and because a data length of zero is valid.
//! @note __Important: The integers are little endian!__
union {
//! The byte array value. Valid when `.type` is \ref TUPLE_BYTE_ARRAY.
uint8_t data[0];
//! The C-string value. Valid when `.type` is \ref TUPLE_CSTRING.
char cstring[0];
//! The 8-bit unsigned integer value. Valid when `.type` is \ref TUPLE_UINT
//! and `.length` is 1 byte.
uint8_t uint8;
//! The 16-bit unsigned integer value. Valid when `.type` is \ref TUPLE_UINT
//! and `.length` is 2 byte.
uint16_t uint16;
//! The 32-bit unsigned integer value. Valid when `.type` is \ref TUPLE_UINT
//! and `.length` is 4 byte.
uint32_t uint32;
//! The 8-bit signed integer value. Valid when `.type` is \ref TUPLE_INT
//! and `.length` is 1 byte.
int8_t int8;
//! The 16-bit signed integer value. Valid when `.type` is \ref TUPLE_INT
//! and `.length` is 2 byte.
int16_t int16;
//! The 32-bit signed integer value. Valid when `.type` is \ref TUPLE_INT
//! and `.length` is 4 byte.
int32_t int32;
} value[];
} Tuple;
struct Dictionary;
typedef struct Dictionary Dictionary;
//! An iterator can be used to iterate over the key/value
//! tuples in an existing dictionary, using \ref dict_read_begin_from_buffer(),
//! \ref dict_read_first() and \ref dict_read_next().
//! An iterator can also be used to append key/value tuples to a dictionary,
//! for example using \ref dict_write_data() or \ref dict_write_cstring().
typedef struct {
Dictionary *dictionary; //!< The dictionary being iterated
const void *end; //!< Points to the first memory address after the last byte of the dictionary
//! Points to the next Tuple in the dictionary. Given the end of the
//! Dictionary has not yet been reached: when writing, the next key/value
//! pair will be written at the cursor. When reading, the next call
//! to \ref dict_read_next() will return the cursor.
Tuple *cursor;
} DictionaryIterator;
//! Calculates the number of bytes that a dictionary will occupy, given
//! one or more value lengths that need to be stored in the dictionary.
//! @note The formula to calculate the size of a Dictionary in bytes is:
//! <pre>1 + (n * 7) + D1 + ... + Dn</pre>
//! Where `n` is the number of Tuples in the Dictionary and `Dx` are the sizes
//! of the values in the Tuples. The size of the Dictionary header is 1 byte.
//! The size of the header for each Tuple is 7 bytes.
//! @param tuple_count The total number of key/value pairs in the dictionary.
//! @param ... The sizes of each of the values that need to be
//! stored in the dictionary.
//! @return The total number of bytes of storage needed.
uint32_t dict_calc_buffer_size(const uint8_t tuple_count, ...);
//! Calculates the size of data that has been written to the dictionary.
//! AKA, the "dictionary size". Note that this is most likely different
//! than the size of the backing storage/backing buffer.
//! @param iter The dictionary iterator
//! @return The total number of bytes which have been written to the dictionary.
uint32_t dict_size(DictionaryIterator* iter);
//! Initializes the dictionary iterator with a given buffer and size,
//! resets and empties it, in preparation of writing key/value tuples.
//! @param iter The dictionary iterator
//! @param buffer The storage of the dictionary
//! @param size The storage size of the dictionary
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
//! @see dict_calc_buffer_size
//! @see dict_write_end
DictionaryResult dict_write_begin(DictionaryIterator *iter, uint8_t * const buffer, const uint16_t size);
//! Adds a key with a byte array value pair to the dictionary.
//! @param iter The dictionary iterator
//! @param key The key
//! @param data Pointer to the byte array
//! @param size Length of the byte array
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
//! @note The data will be copied into the backing storage of the dictionary.
//! @note There is _no_ checking for duplicate keys.
DictionaryResult dict_write_data(DictionaryIterator *iter, const uint32_t key, const uint8_t * const data, const uint16_t size);
//! Adds a key with a C string value pair to the dictionary.
//! @param iter The dictionary iterator
//! @param key The key
//! @param cstring Pointer to the zero-terminated C string
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
//! @note The string will be copied into the backing storage of the dictionary.
//! @note There is _no_ checking for duplicate keys.
DictionaryResult dict_write_cstring(DictionaryIterator *iter, const uint32_t key, const char * const cstring);
//! Adds a key with an integer value pair to the dictionary.
//! @param iter The dictionary iterator
//! @param key The key
//! @param integer Pointer to the integer value
//! @param width_bytes The width of the integer value
//! @param is_signed Whether the integer's type is signed or not
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
//! @note There is _no_ checking for duplicate keys. dict_write_int() is only for serializing a single
//! integer. width_bytes can only be 1, 2, or 4.
DictionaryResult dict_write_int(DictionaryIterator *iter, const uint32_t key, const void *integer, const uint8_t width_bytes, const bool is_signed);
//! Adds a key with an unsigned, 8-bit integer value pair to the dictionary.
//! @param iter The dictionary iterator
//! @param key The key
//! @param value The unsigned, 8-bit integer value
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
//! @note There is _no_ checking for duplicate keys.
//! @note There are counterpart functions for different signedness and widths,
//! `dict_write_uint16()`, `dict_write_uint32()`, `dict_write_int8()`,
//! `dict_write_int16()` and `dict_write_int32()`. The documentation is not
//! repeated for brevity's sake.
DictionaryResult dict_write_uint8(DictionaryIterator *iter, const uint32_t key, const uint8_t value);
DictionaryResult dict_write_uint16(DictionaryIterator *iter, const uint32_t key, const uint16_t value);
DictionaryResult dict_write_uint32(DictionaryIterator *iter, const uint32_t key, const uint32_t value);
DictionaryResult dict_write_int8(DictionaryIterator *iter, const uint32_t key, const int8_t value);
DictionaryResult dict_write_int16(DictionaryIterator *iter, const uint32_t key, const int16_t value);
DictionaryResult dict_write_int32(DictionaryIterator *iter, const uint32_t key, const int32_t value);
//! End a series of writing operations to a dictionary.
//! This must be called before reading back from the dictionary.
//! @param iter The dictionary iterator
//! @return The size in bytes of the finalized dictionary, or 0 if the parameters were invalid.
uint32_t dict_write_end(DictionaryIterator *iter);
//! Initializes the dictionary iterator with a given buffer and size,
//! in preparation of reading key/value tuples.
//! @param iter The dictionary iterator
//! @param buffer The storage of the dictionary
//! @param size The storage size of the dictionary
//! @return The first tuple in the dictionary, or NULL in case the dictionary was empty or if there was a parsing error.
Tuple * dict_read_begin_from_buffer(DictionaryIterator *iter, const uint8_t * const buffer, const uint16_t size);
//! Progresses the iterator to the next key/value pair.
//! @param iter The dictionary iterator
//! @return The next tuple in the dictionary, or NULL in case the end has been reached or if there was a parsing error.
Tuple * dict_read_next(DictionaryIterator *iter);
//! Resets the iterator back to the same state as a call to \ref dict_read_begin_from_buffer() would do.
//! @param iter The dictionary iterator
//! @return The first tuple in the dictionary, or NULL in case the dictionary was empty or if there was a parsing error.
Tuple * dict_read_first(DictionaryIterator *iter);
//! Non-serialized, template data structure for a key/value pair.
//! For strings and byte arrays, it only has a pointer to the actual data.
//! For integers, it provides storage for integers up to 32-bits wide.
//! The Tuplet data structure is useful when creating dictionaries from values
//! that are already stored in arbitrary buffers.
//! See also \ref Tuple, with is the header of a serialized key/value pair.
typedef struct Tuplet {
//! The type of the Tuplet. This determines which of the struct fields in the
//! anonymomous union are valid.
TupleType type;
//! The key.
uint32_t key;
//! Anonymous union containing the reference to the Tuplet's value, being
//! either a byte array, c-string or integer. See documentation of `.bytes`,
//! `.cstring` and `.integer` fields.
union {
//! Valid when `.type.` is \ref TUPLE_BYTE_ARRAY
struct {
//! Pointer to the data
const uint8_t *data;
//! Length of the data
const uint16_t length;
} bytes;
//! Valid when `.type.` is \ref TUPLE_CSTRING
struct {
//! Pointer to the c-string data
const char *data;
//! Length of the c-string, including terminating zero.
const uint16_t length;
} cstring;
//! Valid when `.type.` is \ref TUPLE_INT or \ref TUPLE_UINT
struct {
//! Actual storage of the integer.
//! The signedness can be derived from the `.type` value.
uint32_t storage;
//! Width of the integer.
const uint16_t width;
} integer;
}; //!< See documentation of `.bytes`, `.cstring` and `.integer` fields.
} Tuplet;
//! Macro to create a Tuplet with a byte array value
//! @param _key The key
//! @param _data Pointer to the bytes
//! @param _length Length of the buffer
#define TupletBytes(_key, _data, _length) \
((const Tuplet) { .type = TUPLE_BYTE_ARRAY, .key = _key, .bytes = { .data = _data, .length = _length }})
//! Macro to create a Tuplet with a c-string value
//! @param _key The key
//! @param _cstring The c-string value
#define TupletCString(_key, _cstring) \
((const Tuplet) { .type = TUPLE_CSTRING, .key = _key, .cstring = { .data = _cstring, .length = _cstring ? strlen(_cstring) + 1 : 0 }})
//! Macro to create a Tuplet with an integer value
//! @param _key The key
//! @param _integer The integer value
#define TupletInteger(_key, _integer) \
((const Tuplet) { .type = IS_SIGNED(_integer) ? TUPLE_INT : TUPLE_UINT, .key = _key, .integer = { .storage = _integer, .width = sizeof(_integer) }})
//! Callback for \ref dict_serialize_tuplets() utility.
//! @param data The data of the serialized dictionary
//! @param size The size of data
//! @param context The context pointer as passed in to \ref dict_serialize_tuplets()
//! @see dict_serialize_tuplets
typedef void (*DictionarySerializeCallback)(const uint8_t * const data, const uint16_t size, void *context);
//! Utility function that takes a list of Tuplets from which a dictionary
//! will be serialized, ready to transmit or store.
//! @note The callback will be called before the function returns, so the data that
//! that `context` points to, can be stack allocated.
//! @param callback The callback that will be called with the serialized data of the generated dictionary.
//! @param context Pointer to any application specific data that gets passed into the callback.
//! @param tuplets An array of Tuplets that need to be serialized into the dictionary.
//! @param tuplets_count The number of tuplets that follow.
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
DictionaryResult dict_serialize_tuplets(DictionarySerializeCallback callback, void *context, const Tuplet * const tuplets, const uint8_t tuplets_count);
//! Utility function that takes an array of Tuplets and serializes them into
//! a dictionary with a given buffer and size.
//! @param tuplets The array of tuplets
//! @param tuplets_count The number of tuplets in the array
//! @param buffer The buffer in which to write the serialized dictionary
//! @param [in] size_in_out The available buffer size in bytes
//! @param [out] size_in_out The number of bytes written
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
DictionaryResult dict_serialize_tuplets_to_buffer(const Tuplet * const tuplets, const uint8_t tuplets_count, uint8_t *buffer, uint32_t *size_in_out);
//! Serializes an array of Tuplets into a dictionary with a given buffer and size.
//! @param iter The dictionary iterator
//! @param tuplets The array of tuplets
//! @param tuplets_count The number of tuplets in the array
//! @param buffer The buffer in which to write the serialized dictionary
//! @param [in] size_in_out The available buffer size in bytes
//! @param [out] size_in_out The number of bytes written
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
DictionaryResult dict_serialize_tuplets_to_buffer_with_iter(DictionaryIterator *iter, const Tuplet * const tuplets, const uint8_t tuplets_count, uint8_t *buffer, uint32_t *size_in_out);
//! Serializes a Tuplet and writes the resulting Tuple into a dictionary.
//! @param iter The dictionary iterator
//! @param tuplet The Tuplet describing the key/value pair to write
//! @return \ref DICT_OK, \ref DICT_NOT_ENOUGH_STORAGE or \ref DICT_INVALID_ARGS
DictionaryResult dict_write_tuplet(DictionaryIterator *iter, const Tuplet * const tuplet);
//! Calculates the number of bytes that a dictionary will occupy, given
//! one or more Tuplets that need to be stored in the dictionary.
//! @note See \ref dict_calc_buffer_size() for the formula for the calculation.
//! @param tuplets An array of Tuplets that need to be stored in the dictionary.
//! @param tuplets_count The total number of Tuplets that follow.
//! @return The total number of bytes of storage needed.
//! @see Tuplet
uint32_t dict_calc_buffer_size_from_tuplets(const Tuplet * const tuplets, const uint8_t tuplets_count);
//! Type of the callback used in \ref dict_merge()
//! @param key The key that is being updated.
//! @param new_tuple The new tuple. The tuple points to the actual, updated destination dictionary or NULL_TUPLE
//! in case there was an error (e.g. backing buffer was too small).
//! Therefore the Tuple can be used after the callback returns, until the destination dictionary
//! storage is free'd (by the application itself).
//! @param old_tuple The values that will be replaced with `new_tuple`. The key, value and type will be
//! equal to the previous tuple in the old destination dictionary, however the `old_tuple points
//! to a stack-allocated copy of the old data.
//! @param context Pointer to application specific data
//! The storage backing `old_tuple` can only be used during the callback and
//! will no longer be valid after the callback returns.
//! @see dict_merge
typedef void (*DictionaryKeyUpdatedCallback)(const uint32_t key, const Tuple *new_tuple, const Tuple *old_tuple, void *context);
//! Merges entries from another "source" dictionary into a "destination" dictionary.
//! All Tuples from the source are written into the destination dictionary, while
//! updating the exsting Tuples with matching keys.
//! @param dest The destination dictionary to update
//! @param [in,out] dest_max_size_in_out In: the maximum size of buffer backing `dest`. Out: the final size of the updated dictionary.
//! @param source The source dictionary of which its Tuples will be used to update dest.
//! @param update_existing_keys_only Specify True if only the existing keys in `dest` should be updated.
//! @param key_callback The callback that will be called for each Tuple in the merged destination dictionary.
//! @param context Pointer to app specific data that will get passed in when `update_key_callback` is called.
//! @return \ref DICT_OK, \ref DICT_INVALID_ARGS, \ref DICT_NOT_ENOUGH_STORAGE
DictionaryResult dict_merge(DictionaryIterator *dest, uint32_t *dest_max_size_in_out,
DictionaryIterator *source,
const bool update_existing_keys_only,
const DictionaryKeyUpdatedCallback key_callback, void *context);
//! Tries to find a Tuple with specified key in a dictionary
//! @param iter Iterator to the dictionary to search in.
//! @param key The key for which to find a Tuple
//! @return Pointer to a found Tuple, or NULL if there was no Tuple with the specified key.
Tuple *dict_find(const DictionaryIterator *iter, const uint32_t key);
//! @} // group Dictionary
//! @addtogroup AppMessage
//!
//!
//!
//! \brief Bi-directional communication between phone apps and Pebble watchapps
//!
//! AppMessage is a bi-directional messaging subsystem that enables communication between phone apps
//! and Pebble watchapps. This is accomplished by allowing phone and watchapps to exchange arbitrary
//! sets of key/value pairs. The key/value pairs are stored in the form of a Dictionary, the layout
//! of which is left for the application developer to define.
//!
//! AppMessage implements a push-oriented messaging protocol, enabling your app to call functions and
//! methods to push messages from Pebble to phone and vice versa. The protocol is symmetric: both Pebble
//! and the phone can send messages. All messages are acknowledged. In this context, there is no
//! client-server model, as such.
//!
//! During the sending phase, one side initiates the communication by transferring a dictionary over the air.
//! The other side then receives this message and is given an opportunity to perform actions on that data.
//! As soon as possible, the other side is expected to reply to the message with a simple acknowledgment
//! that the message was received successfully.
//!
//! PebbleKit JavaScript provides you with a set of standard JavaScript APIs that let your app receive messages
//! from the watch, make HTTP requests, and send new messages to the watch. AppMessage APIs are used to send and
//! receive data. A Pebble watchapp can use the resources of the connected phone to fetch information from web services,
//! send information to web APIs, or store login credentials. On the JavaScript side, you communicate
//! with Pebble via a Pebble object exposed in the namespace.
//!
//! Messages always need to get either ACKnowledged or "NACK'ed," that is, not acknowledged.
//! If not, messages will result in a time-out failure. The AppMessage subsystem takes care of this implicitly.
//! In the phone libraries, this step is a bit more explicit.
//!
//! The Pebble watch interfaces make a distinction between the Inbox and the Outbox calls. The Inbox
//! receives messages from the phone on the watch; the Outbox sends messages from the watch to the phone.
//! These two buffers can be managed separately.
//!
//! <h4>Warning</h4>
//! A critical constraint of AppMessage is that messages are limited in size. An ingoing (outgoing) message
//! larger than the inbox (outbox) will not be transmitted and will generate an error. You can choose your
//! inbox and outbox size when you call app_message_open().
//!
//! Pebble SDK provides a static minimum guaranteed size (APP_MESSAGE_INBOX_SIZE_MINIMUM and APP_MESSAGE_OUTBOX_SIZE_MINIMUM).
//! Requesting a buffer of the minimum guaranteed size (or smaller) is always guaranteed to succeed on all
//! Pebbles in this SDK version or higher, and with every phone.
//!
//! In some context, Pebble might be able to provide your application with larger inbox/outbox.
//! You can call app_message_inbox_size_maximum() and app_message_outbox_size_maximum() in your code to get
//! the largest possible value you can use.
//!
//! To always get the largest buffer available, follow this best practice:
//!
//! app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum())
//!
//! AppMessage uses your application heap space. That means that the sizes you pick for the AppMessage
//! inbox and outbox buffers are important in optimizing your app’s performance. The more you use for
//! AppMessage, the less space you’ll have for the rest of your app.
//!
//! To register callbacks, you should call app_message_register_inbox_received(), app_message_register_inbox_dropped(),
//! app_message_register_outbox_sent(), app_message_register_outbox_failed().
//!
//! Pebble recommends that you call them before app_message_open() to ensure you do not miss a message
//! arriving between starting AppMessage and registering the callback. You can set a context that will be passed
//! to all the callbacks with app_message_set_context().
//!
//! In circumstances that may not be ideal, when using AppMessage several types of errors may occur.
//! For example:
//!
//! * The send can’t start because the system state won't allow for a success. Several reasons
//! you're unable to perform a send: A send() is already occurring (only one is possible at a time) or Bluetooth
//! is not enabled or connected.
//! * The send and receive occur, but the receiver can’t accept the message. For instance, there is no app
//! that receives such a message.
//! * The send occurs, but the receiver either does not actually receive the message or can’t handle it
//! in a timely fashion.
//! * In the case of a dropped message, the phone sends a message to the watchapp, while there is still
//! an unprocessed message in the Inbox.
//!
//! Other errors are possible and described by AppMessageResult. A client of the AppMessage interface
//! should use the result codes to be more robust in the face of communication problems either in the field or while debugging.
//!
//! Refer to the \htmlinclude app-phone-communication.html for a conceptual overview and code usage.
//!
//! For code examples, refer to the SDK Examples that directly use App Message. These include:
//! * <a href="https://github.com/pebble/pebble-sdk-examples/tree/master/pebblekit-js/weather">
//! Examples/pebblekit-js/weather</a>
//! * <a href="https://github.com/pebble/pebble-sdk-examples/tree/master/pebblekit-js/quotes">
//! Examples/pebblekit-js/quotes</a>
//! @{
//! AppMessage result codes.
typedef enum {
//! All good, operation was successful.
APP_MSG_OK = 0,
//! The other end did not confirm receiving the sent data with an (n)ack in time.
APP_MSG_SEND_TIMEOUT = 1 << 1,
//! The other end rejected the sent data, with a "nack" reply.
APP_MSG_SEND_REJECTED = 1 << 2,
//! The other end was not connected.
APP_MSG_NOT_CONNECTED = 1 << 3,
//! The local application was not running.
APP_MSG_APP_NOT_RUNNING = 1 << 4,
//! The function was called with invalid arguments.
APP_MSG_INVALID_ARGS = 1 << 5,
//! There are pending (in or outbound) messages that need to be processed first before
//! new ones can be received or sent.
APP_MSG_BUSY = 1 << 6,
//! The buffer was too small to contain the incoming message.
APP_MSG_BUFFER_OVERFLOW = 1 << 7,
//! The resource had already been released.
APP_MSG_ALREADY_RELEASED = 1 << 9,
//! The callback node was already registered, or its ListNode has not been initialized.
APP_MSG_CALLBACK_ALREADY_REGISTERED = 1 << 10,
//! The callback could not be deregistered, because it had not been registered before.
APP_MSG_CALLBACK_NOT_REGISTERED = 1 << 11,
//! The support library did not have sufficient application memory to perform the requested operation.
APP_MSG_OUT_OF_MEMORY = 1 << 12,
//! App message was closed
APP_MSG_CLOSED = 1 << 13,
//! An internal OS error prevented APP_MSG from completing an operation
APP_MSG_INTERNAL_ERROR = 1 << 14,
} AppMessageResult;
//! Open AppMessage to transfers.
//!
//! Use \ref dict_calc_buffer_size_from_tuplets() or \ref dict_calc_buffer_size() to estimate the size you need.
//!
//! \param[in] size_inbound The required size for the Inbox buffer
//! \param[in] size_outbound The required size for the Outbox buffer
//!
//! \return A result code such as \ref APP_MSG_OK or \ref APP_MSG_OUT_OF_MEMORY.
//!
//! \note It is recommended that if the Inbox will be used, that at least the Inbox callbacks should be registered
//! before this call. Otherwise it is possible for an Inbox message to be NACK'ed without being seen by the
//! application.
//!
AppMessageResult app_message_open(const uint32_t size_inbound, const uint32_t size_outbound);
//! Deregisters all callbacks and their context.
//!
void app_message_deregister_callbacks(void);
//! Called after an incoming message is received.
//!
//! \param[in] iterator
//! The dictionary iterator to the received message. Never NULL. Note that the iterator cannot be modified or
//! saved off. The library may need to re-use the buffered space where this message is supplied. Returning from
//! the callback indicates to the library that the received message contents are no longer needed or have already
//! been externalized outside its buffering space and iterator.
//!
//! \param[in] context
//! Pointer to application data as specified when registering the callback.
//!
typedef void (*AppMessageInboxReceived)(DictionaryIterator *iterator, void *context);
//! Called after an incoming message is dropped.
//!
//! \param[in] result
//! The reason why the message was dropped. Some possibilities include \ref APP_MSG_BUSY and
//! \ref APP_MSG_BUFFER_OVERFLOW.
//!
//! \param[in] context
//! Pointer to application data as specified when registering the callback.
//!
//! Note that you can call app_message_outbox_begin() from this handler to prepare a new message.
//! This will invalidate the previous dictionary iterator; do not use it after calling app_message_outbox_begin().
//!
typedef void (*AppMessageInboxDropped)(AppMessageResult reason, void *context);
//! Called after an outbound message has been sent and the reply has been received.
//!
//! \param[in] iterator
//! The dictionary iterator to the sent message. The iterator will be in the final state that was sent. Note that
//! the iterator cannot be modified or saved off as the library will re-open the dictionary with dict_begin() after
//! this callback returns.
//!
//! \param[in] context
//! Pointer to application data as specified when registering the callback.
//!
typedef void (*AppMessageOutboxSent)(DictionaryIterator *iterator, void *context);
//! Called after an outbound message has not been sent successfully.
//!
//! \param[in] iterator
//! The dictionary iterator to the sent message. The iterator will be in the final state that was sent. Note that
//! the iterator cannot be modified or saved off as the library will re-open the dictionary with dict_begin() after
//! this callback returns.
//!
//! \param[in] result
//! The result of the operation. Some possibilities for the value include \ref APP_MSG_SEND_TIMEOUT,
//! \ref APP_MSG_SEND_REJECTED, \ref APP_MSG_NOT_CONNECTED, \ref APP_MSG_APP_NOT_RUNNING, and the combination
//! `(APP_MSG_NOT_CONNECTED | APP_MSG_APP_NOT_RUNNING)`.
//!
//! \param context
//! Pointer to application data as specified when registering the callback.
//!
//! Note that you can call app_message_outbox_begin() from this handler to prepare a new message.
//! This will invalidate the previous dictionary iterator; do not use it after calling app_message_outbox_begin().
//!
typedef void (*AppMessageOutboxFailed)(DictionaryIterator *iterator, AppMessageResult reason, void *context);
//! Gets the context that will be passed to all AppMessage callbacks.
//!
//! \return The current context on record.
//!
void *app_message_get_context(void);
//! Sets the context that will be passed to all AppMessage callbacks.
//!
//! \param[in] context The context that will be passed to all AppMessage callbacks.
//!
//! \return The previous context that was on record.
//!
void *app_message_set_context(void *context);
//! Registers a function that will be called after any Inbox message is received successfully.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] received_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageInboxReceived app_message_register_inbox_received(AppMessageInboxReceived received_callback);
//! Registers a function that will be called after any Inbox message is received but dropped by the system.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] dropped_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageInboxDropped app_message_register_inbox_dropped(AppMessageInboxDropped dropped_callback);
//! Registers a function that will be called after any Outbox message is sent and an ACK reply occurs in a timely
//! fashion.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] sent_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageOutboxSent app_message_register_outbox_sent(AppMessageOutboxSent sent_callback);
//! Registers a function that will be called after any Outbox message is not sent with a timely ACK reply.
//! The call to \ref app_message_outbox_send() must have succeeded.
//!
//! Only one callback may be registered at a time. Each subsequent call to this function will replace the previous
//! callback. The callback is optional; setting it to NULL will deregister the current callback and no function will
//! be called anymore.
//!
//! \param[in] failed_callback The callback that will be called going forward; NULL to not have a callback.
//!
//! \return The previous callback (or NULL) that was on record.
//!
AppMessageOutboxFailed app_message_register_outbox_failed(AppMessageOutboxFailed failed_callback);
//! Programatically determine the inbox size maximum in the current configuration.
//!
//! \return The inbox size maximum on this firmware.
//!
//! \sa APP_MESSAGE_INBOX_SIZE_MINIMUM
//! \sa app_message_outbox_size_maximum()
//!
uint32_t app_message_inbox_size_maximum(void);
//! Programatically determine the outbox size maximum in the current configuration.
//!
//! \return The outbox size maximum on this firmware.
//!
//! \sa APP_MESSAGE_OUTBOX_SIZE_MINIMUM
//! \sa app_message_inbox_size_maximum()
//!
uint32_t app_message_outbox_size_maximum(void);
//! Begin writing to the Outbox's Dictionary buffer.
//!
//! \param[out] iterator Location to write the DictionaryIterator pointer. This will be NULL on failure.
//!
//! \return A result code, including but not limited to \ref APP_MSG_OK, \ref APP_MSG_INVALID_ARGS or
//! \ref APP_MSG_BUSY.
//!
//! \note After a successful call, one can add values to the dictionary using functions like \ref dict_write_data()
//! and friends.
//!
//! \sa Dictionary
//!
AppMessageResult app_message_outbox_begin(DictionaryIterator **iterator);
//! Sends the outbound dictionary.
//!
//! \return A result code, including but not limited to \ref APP_MSG_OK or \ref APP_MSG_BUSY. The APP_MSG_OK code does
//! not mean that the message was sent successfully, but only that the start of processing was successful.
//! Since this call is asynchronous, callbacks provide the final result instead.
//!
//! \sa AppMessageOutboxSent
//! \sa AppMessageOutboxFailed
//!
AppMessageResult app_message_outbox_send(void);
//! As long as the firmware maintains its current major version, inboxes of this size or smaller will be allowed.
//!
//! \sa app_message_inbox_size_maximum()
//! \sa APP_MESSAGE_OUTBOX_SIZE_MINIMUM
//!
#define APP_MESSAGE_INBOX_SIZE_MINIMUM 124
//! As long as the firmware maintains its current major version, outboxes of this size or smaller will be allowed.
//!
//! \sa app_message_outbox_size_maximum()
//! \sa APP_MESSAGE_INBOX_SIZE_MINIMUM
//!
#define APP_MESSAGE_OUTBOX_SIZE_MINIMUM 636
//! @} // group AppMessage
//! @addtogroup AppSync
//! \brief UI synchronization layer for AppMessage
//!
//! AppSync is a convenience layer that resides on top of \ref AppMessage, and serves
//! as a UI synchronization layer for AppMessage. In so doing, AppSync makes it easier
//! to drive the information displayed in the watchapp UI with messages sent by a phone app.
//!
//! AppSync maintains and updates a Dictionary, and provides your app with a callback
//! (AppSyncTupleChangedCallback) routine that is called whenever the Dictionary changes
//! and the app's UI is updated. Note that the app UI is not updated automatically.
//! To update the UI, you need to implement the callback.
//!
//! Pebble OS provides support for data serialization utilities, like Dictionary,
//! Tuple and Tuplet data structures and their accompanying functions. You use Tuplets to create
//! a Dictionary with Tuple structures.
//!
//! AppSync manages the storage and bookkeeping chores of the current Tuple values. AppSync copies
//! incoming AppMessage Tuples into this "current" Dictionary, so that the key/values remain available
//! for the UI to use. For example, it is safe to use a C-string value provided by AppSync and use it
//! directly in a text_layer_set_text() call.
//!
//! Your app needs to supply the buffer that AppSync uses for the "current" Dictionary when initializing AppSync.
//!
//! Refer to the
//! <a href="https://developer.getpebble.com/guides/pebble-apps/communications/appsync/">
//! Synchronizing App UI</a>
//! guide for a conceptual overview and code usage.
//! @{
//! Called whenever a Tuple changes. This does not necessarily mean the value in
//! the Tuple has changed. When the internal "current" dictionary gets updated,
//! existing Tuples might get shuffled around in the backing buffer, even though
//! the values stay the same. In this callback, the client code gets the chance
//! to remove the old reference and start using the new one.
//! In this callback, your application MUST clean up any references to the
//! `old_tuple` of a PREVIOUS call to this callback (and replace it with the
//! `new_tuple` that is passed in with the current call).
//! @param key The key for which the Tuple was changed.
//! @param new_tuple The new tuple. The tuple points to the actual, updated
//! "current" dictionary, as backed by the buffer internal to the AppSync
//! struct. Therefore the Tuple can be used after the callback returns, until
//! the AppSync is deinited. In case there was an error (e.g. storage shortage),
//! this `new_tuple` can be `NULL_TUPLE`.
//! @param old_tuple The values that will be replaced with `new_tuple`. The key,
//! value and type will be equal to the previous tuple in the old destination
//! dictionary; however, the `old_tuple` points to a stack-allocated copy of the
//! old data. This value will be `NULL_TUPLE` when the initial values are
//! being set.
//! @param context Pointer to application specific data, as set using
//! \ref app_sync_init()
//! @see \ref app_sync_init()
typedef void (*AppSyncTupleChangedCallback)(const uint32_t key, const Tuple *new_tuple, const Tuple *old_tuple, void *context);
//! Called whenever there was an error.
//! @param dict_error The dictionary result error code, if the error was
//! dictionary related.
//! @param app_message_error The app_message result error code, if the error
//! was app_message related.
//! @param context Pointer to application specific data, as set using
//! \ref app_sync_init()
//! @see \ref app_sync_init()
typedef void (*AppSyncErrorCallback)(DictionaryResult dict_error, AppMessageResult app_message_error, void *context);
typedef struct AppSync {
DictionaryIterator current_iter;
union {
Dictionary *current;
uint8_t *buffer;
};
uint16_t buffer_size;
struct {
AppSyncTupleChangedCallback value_changed;
AppSyncErrorCallback error;
void *context;
} callback;
} AppSync;
//! Initialized an AppSync system with specific buffer size and initial keys and
//! values. The `callback.value_changed` callback will be called
//! __asynchronously__ with the initial keys and values, as to avoid duplicating
//! code to update your app's UI.
//! @param s The AppSync context to initialize
//! @param buffer The buffer that AppSync should use
//! @param buffer_size The size of the backing storage of the "current"
//! dictionary. Use \ref dict_calc_buffer_size_from_tuplets() to estimate the
//! size you need.
//! @param keys_and_initial_values An array of Tuplets with the initial keys and
//! values.
//! @param count The number of Tuplets in the `keys_and_initial_values` array.
//! @param tuple_changed_callback The callback that will handle changed
//! key/value pairs
//! @param error_callback The callback that will handle errors
//! @param context Pointer to app specific data that will get passed into calls
//! to the callbacks
//! @note Only updates for the keys specified in this initial array will be
//! accepted by AppSync, updates for other keys that might come in will just be
//! ignored.
void app_sync_init(struct AppSync *s, uint8_t *buffer, const uint16_t buffer_size, const Tuplet * const keys_and_initial_values, const uint8_t count,
AppSyncTupleChangedCallback tuple_changed_callback, AppSyncErrorCallback error_callback, void *context);
//! Cleans up an AppSync system.
//! It frees the buffer allocated by an \ref app_sync_init() call and
//! deregisters itself from the \ref AppMessage subsystem.
//! @param s The AppSync context to deinit.
void app_sync_deinit(struct AppSync *s);
//! Updates key/value pairs using an array of Tuplets.
//! @note The call will attempt to send the updated keys and values to the
//! application on the other end.
//! Only after the other end has acknowledged the update, the `.value_changed`
//! callback will be called to confirm the update has completed and your
//! application code can update its user interface.
//! @param s The AppSync context
//! @param keys_and_values_to_update An array of Tuplets with the keys and
//! values to update. The data in the Tuplets are copied during the call, so the
//! array can be stack-allocated.
//! @param count The number of Tuplets in the `keys_and_values_to_update` array.
//! @return The result code from the \ref AppMessage subsystem.
//! Can be \ref APP_MSG_OK, \ref APP_MSG_BUSY or \ref APP_MSG_INVALID_ARGS
AppMessageResult app_sync_set(struct AppSync *s, const Tuplet * const keys_and_values_to_update, const uint8_t count);
//! Finds and gets a tuple in the "current" dictionary.
//! @param s The AppSync context
//! @param key The key for which to find a Tuple
//! @return Pointer to a found Tuple, or NULL if there was no Tuple with the
//! specified key.
const Tuple * app_sync_get(const struct AppSync *s, const uint32_t key);
//! @} // group AppSync
//! @addtogroup Resources
//! \brief Managing application resources
//!
//! Resources are data files that are bundled with your application binary and can be
//! loaded at runtime. You use resources to embed images or custom fonts in your app,
//! but also to embed any data file. Resources are always read-only.
//!
//! Resources are stored on Pebble’s flash memory and only loaded in RAM when you load
//! them. This means that you can have a large number of resources embedded inside your app,
//! even though Pebble’s RAM memory is very limited.
//!
//! See \htmlinclude UsingResources.html for information on how to embed
//! resources into your app's bundle.
//!
//! @{
//! @addtogroup FileFormats File Formats
//! @{
//! @addtogroup PNGFileFormat PNG8 File Format
//!
//! Pebble supports both a PBIs (uncompressed bitmap images) as well as PNG8 images.
//! PNG images are compressed allowing for storage savings up to 90%.
//! PNG8 is a PNG that uses palette-based or grayscale images with 1, 2, 4 or 8 bits per pixel.
//! For palette-based images the pixel data represents the index into the palette, such
//! that each pixel only needs to be large enough to represent the palette size, so
//! \li \c 1-bit supports up to 2 colors,
//! \li \c 2-bit supports up to 4 colors,
//! \li \c 4-bit supports up to 16 colors,
//! \li \c 8-bit supports up to 256 colors.
//!
//! There are 2 parts to the palette: the RGB24 color-mapping palette ("PLTE"), and the optional
//! 8-bit transparency palette ("tRNs"). A pixel's color index maps to both tables, combining to
//! allow the pixel to have both color as well as transparency.
//!
//! For grayscale images, the pixel data represents the luminosity (or shade of gray).
//! \li \c 1-bit supports black and white
//! \li \c 2-bit supports black, dark_gray, light_gray and white
//! \li \c 4-bit supports black, white and 14 shades of gray
//! \li \c 8-bit supports black, white and 254 shades of gray
//!
//! Optionally, grayscale images allow for 1 fully transparent color, which is removed from
//! the fully-opaque colors above (e.g. a 2 bit grayscale image can have black, white, dark_gray
//! and a transparent color).
//!
//! The Basalt Platform provides for 2-bits per color channel, so images are optimized by the
//! SDK tooling when loaded as a resource-type "png" to the Pebble's 64-colors with 4 levels
//! of transparency. This optimization also handles mapping unsupported colors to the nearest
//! supported color, and reducing the pixel depth to the number of bits required to support
//! the optimized number of colors. PNG8 images from other sources are supported, with the colors
//! truncated to match supported colors at runtime.
//!
//! @see \ref gbitmap_create_from_png_data
//! @see \ref gbitmap_create_with_resource
//!
//! @{
//! @} // group PNGFileFormat
//! @addtogroup PBIFileFormat PBI File Format
//!
//! PBIs are uncompressed bitmap images with support for color-mapping palettes.
//! PBIs store images either as raw image pixels (1-bit black and white, or 8-bit ARGB) or as
//! palette-based images with 1, 2, or 4 bits per pixel.
//! For palette-based images the pixel data represents the index into the palette, such
//! that each pixel only needs to be large enough to represent the palette size, so
//! \li \c 1-bit supports up to 2 colors,
//! \li \c 2-bit supports up to 4 colors,
//! \li \c 4-bit supports up to 16 colors.
//!
//! The metadata describes how long each row of pixels is in the buffer (the stride).
//! The following restrictions on stride are in place for different formats:
//!
//! - \ref GBitmapFormat1Bit:
//! Each row must be a multiple of 32 pixels (4 bytes). Using the `bounds` field,
//! the area that is actually relevant can be specified.
//! For example, when the image is 29 by 5 pixels
//! (width by height) and the first bit of image data is the pixel at (0, 0),
//! then the bounds.size would be `GSize(29, 5)` and bounds.origin would be `GPoint(0, 0)`.
//! ![](gbitmap.png)
//! In the illustration each pixel is a representated as a square. The white
//! squares are the bits that are used, the gray squares are the padding bits, because
//! each row of image data has to be a multiple of 4 bytes (32 bits).
//! The numbers in the column in the left are the offsets (in bytes) from the `*addr`
//! field of the GBitmap.
//! Each pixel in a bitmap is represented by 1 bit. If a bit is set (`1` or `true`),
//! it will result in a white pixel, and vice versa, if a bit is cleared (`0` or `false`),
//! it will result in a black pixel.
//! ![](pixel_bit_values.png)
//!
//! - \ref GBitmapFormat8Bit:
//! Each pixel in the bitmap is represented by 1 byte. The color value of that byte correspends to
//! a GColor.argb value.
//! There is no restriction on row_size_bytes / stride.
//!
//! - \ref GBitmapFormat1BitPalette, \ref GBitmapFormat2BitPalette, \ref GBitmapFormat4BitPalette:
//! Each pixel in the bitmap is represented by the number of bits the format specifies. Pixels
//! must be packed.
//! For example, in GBitmapFormat2BitPalette, each pixel uses 2 bits. This means 4 pixels / byte.
//! Rows need to be byte-aligned, meaning that there can be up to 3 unused pixels at the end of
//! each line. If the image is 5 pixels wide and 4 pixels tall, row_size_bytes = 2,
//! and each row in the bitmap must take 2 bytes, so the bitmap data is 8 bytes in total.
//!
//! Palettized bitmaps also need to have a palette. The palette must be of the correct size, which
//! is specified by the format. For example, \ref GBitmapFormat4BitPalette uses 4 bits per pixel,
//! meaning that there must be 2^4 = 16 colors in the palette.
//!
//! The Basalt Platform provides for 2-bits per color channel, so images are optimized by the
//! SDK tooling when loaded as a resource-type "pbi" to the Pebble's 64-colors with 4 levels
//! of transparency. This optimization also handles mapping unsupported colors to the nearest
//! supported color, and reducing the pixel depth to the number of bits required to support
//! the optimized number of colors.
//!
//! @see \ref gbitmap_create_with_data
//! @see \ref gbitmap_create_with_resource
//!
//! @{
//! @} // group PBIFileFormat
//! @} // group FileFormats
//! Opaque reference to a resource.
//! @see \ref resource_get_handle()
typedef void * ResHandle;
#define RESOURCE_ID_FONT_FALLBACK RESOURCE_ID_GOTHIC_14
//! Gets the resource handle for a file identifier.
//! @param resource_id The resource ID
//!
//! The resource IDs are auto-generated by the Pebble build process, based
//! on the `appinfo.json`. The "name" field of each resource is prefixed
//! by `RESOURCE_ID_` and made visible to the application (through the
//! `build/src/resource_ids.auto.h` header which is automatically included).
//!
//! For example, given the following fragment of `appinfo.json`:
//! \code{.json}
//! ...
//! "resources" : {
//! "media": [
//! {
//! "name": "MY_ICON",
//! "file": "img/icon.png",
//! "type": "png",
//! },
//! ...
//! \endcode
//! The generated file identifier for this resource is `RESOURCE_ID_MY_ICON`.
//! To get a resource handle for that resource write:
//! \code{.c}
//! ResHandle rh = resource_get_handle(RESOURCE_ID_MY_ICON);
//! \endcode
ResHandle resource_get_handle(uint32_t resource_id);
//! Gets the size of the resource given a resource handle.
//! @param h The handle to the resource
//! @return The size of the resource in bytes
size_t resource_size(ResHandle h);
//! Copies the bytes for the resource with a given handle from flash storage into a given buffer.
//! @param h The handle to the resource
//! @param buffer The buffer to load the resource data into
//! @param max_length The maximum number of bytes to copy
//! @return The number of bytes actually copied
size_t resource_load(ResHandle h, uint8_t *buffer, size_t max_length);
//! Copies a range of bytes from a resource with a given handle into a given buffer.
//! @param h The handle to the resource
//! @param start_offset The offset in bytes at which to start reading from the resource
//! @param buffer The buffer to load the resource data into
//! @param num_bytes The maximum number of bytes to copy
//! @return The number of bytes actually copied
size_t resource_load_byte_range(
ResHandle h, uint32_t start_offset, uint8_t *buffer, size_t num_bytes);
//! @} // group Resources
//! @addtogroup App
//! @{
//! The event loop for apps, to be used in app's main(). Will block until the app is ready to exit.
void app_event_loop(void);
//! @} // group App
//! @addtogroup AppWorker
//! \brief Runs in the background, and can communicate with the foreground app.
//! @{
//! Possible error codes from app_worker_launch, app_worker_kill
typedef enum {
//! Success
APP_WORKER_RESULT_SUCCESS= 0,
//! No worker found for the current app
APP_WORKER_RESULT_NO_WORKER = 1,
//! A worker for a different app is already running
APP_WORKER_RESULT_DIFFERENT_APP = 2,
//! The worker is not running
APP_WORKER_RESULT_NOT_RUNNING = 3,
//! The worker is already running
APP_WORKER_RESULT_ALREADY_RUNNING = 4,
//! The user will be asked for confirmation
APP_WORKER_RESULT_ASKING_CONFIRMATION = 5,
} AppWorkerResult;
//! Generic structure of a worker message that can be sent between an app and its worker
typedef struct {
uint16_t data0;
uint16_t data1;
uint16_t data2;
} AppWorkerMessage;
//! Determine if the worker for the current app is running
//! @return true if running
bool app_worker_is_running(void);
//! Launch the worker for the current app. Note that this is an asynchronous operation, a result code
//! of APP_WORKER_RESULT_SUCCESS merely means that the request was successfully queued up.
//! @return result code
AppWorkerResult app_worker_launch(void);
//! Kill the worker for the current app. Note that this is an asynchronous operation, a result code
//! of APP_WORKER_RESULT_SUCCESS merely means that the request was successfully queued up.
//! @return result code
AppWorkerResult app_worker_kill(void);
//! Callback type for worker messages. Messages can be sent from worker to app or vice versa.
//! @param type An application defined message type
//! @param data pointer to message data. The receiver must know the structure of the data provided by the sender.
typedef void (*AppWorkerMessageHandler)(uint16_t type, AppWorkerMessage *data);
//! Subscribe to worker messages. Once subscribed, the handler gets called on every message emitted by the other task
//! (either worker or app).
//! @param handler A callback to be executed when the event is received
//! @return true on success
bool app_worker_message_subscribe(AppWorkerMessageHandler handler);
//! Unsubscribe from worker messages. Once unsubscribed, the previously registered handler will no longer be called.
//! @return true on success
bool app_worker_message_unsubscribe(void);
//! Send a message to the other task (either worker or app).
//! @param type An application defined message type
//! @param data the message data structure
void app_worker_send_message(uint8_t type, AppWorkerMessage *data);
//! @} // group AppWorker
//! @addtogroup AppComm App Communication
//! \brief API for interacting with the Pebble communication subsystem.
//!
//! @note To send messages to a remote device, see the \ref AppMessage or
//! \ref AppSync modules.
//! @{
//! Intervals during which the Bluetooth module may enter a low power mode.
//! The sniff interval defines the period during which the Bluetooth module may
//! not exchange (ACL) packets. The longer the sniff interval, the more time the
//! Bluetooth module may spend in a low power mode.
//! It may be necessary to reduce the sniff interval if an app requires reduced
//! latency when sending messages.
//! @note These settings have a dramatic effect on the Pebble's energy
//! consumption. Use the normal sniff interval whenever possible.
//! Note, however, that switching between modes increases power consumption
//! during the process. Frequent switching between modes is thus
//! discouraged. Ensure you do not drop to normal frequently. The Bluetooth module
//! is a major consumer of the Pebble's energy.
typedef enum {
//! Set the sniff interval to normal (power-saving) mode
SNIFF_INTERVAL_NORMAL = 0,
//! Reduce the sniff interval to increase the responsiveness of the radio at
//! the expense of increasing Bluetooth energy consumption by a multiple of 2-5
//! (very significant)
SNIFF_INTERVAL_REDUCED = 1,
} SniffInterval;
//! Set the Bluetooth module's sniff interval.
//! The sniff interval will be restored to normal by the OS after the app's
//! de-init handler is called. Set the sniff interval to normal whenever
//! possible.
void app_comm_set_sniff_interval(const SniffInterval interval);
//! Get the Bluetooth module's sniff interval
//! @return The SniffInterval value corresponding to the current interval
SniffInterval app_comm_get_sniff_interval(void);
//! @} // group AppComm
//! @addtogroup Timer
//! \brief Can be used to execute some code at some point in the future.
//! @{
//! Waits for a certain amount of milliseconds
//! @param millis The number of milliseconds to wait for
void psleep(int millis);
struct AppTimer;
typedef struct AppTimer AppTimer;
//! The type of function which can be called when a timer fires. The argument will be the @p callback_data passed to
//! @ref app_timer_register().
typedef void (*AppTimerCallback)(void* data);
//! Registers a timer that ends up in callback being called some specified time in the future.
//! @param timeout_ms The expiry time in milliseconds from the current time
//! @param callback The callback that gets called at expiry time
//! @param callback_data The data that will be passed to callback
//! @return A pointer to an `AppTimer` that can be used to later reschedule or cancel this timer
AppTimer* app_timer_register(uint32_t timeout_ms, AppTimerCallback callback, void* callback_data);
//! Reschedules an already running timer for some point in the future.
//! @param timer_handle The timer to reschedule
//! @param new_timeout_ms The new expiry time in milliseconds from the current time
//! @return true if the timer was rescheduled, false if the timer has already elapsed
bool app_timer_reschedule(AppTimer *timer_handle, uint32_t new_timeout_ms);
//! Cancels an already registered timer.
//! Once cancelled the handle may no longer be used for any purpose.
void app_timer_cancel(AppTimer *timer_handle);
//! @} // group Timer
//! @addtogroup MemoryManagement Memory Management
//! \brief Utility functions for determining an application's memory usage.
//!
//! @{
//! Calculates the number of bytes of heap memory \a not currently being used by the application.
//! @return The number of bytes on the heap not currently being used.
size_t heap_bytes_free(void);
//! Calculates the number of bytes of heap memory currently being used by the application.
//! @return The number of bytes on the heap currently being used.
size_t heap_bytes_used(void);
//! @} // group MemoryManagement
//! @addtogroup Storage
//! \brief A mechanism to store persistent application data and state
//!
//! The Persistent Storage API provides you with a mechanism for performing a variety of tasks,
//! like saving user settings, caching data from the phone app, or counting high scores for
//! Pebble watchapp games.
//!
//! In Pebble OS, storage is defined by a collection of fields that you can create, modify or delete.
//! In the API, a field is specified as a key with a corresponding value.
//!
//! Using the Storage API, every app is able to get its own persistent storage space. Each value
//! in that space is associated with a uint32_t key.
//!
//! Storage supports saving integers, strings and byte arrays. The maximum size of byte arrays and
//! strings is defined by PERSIST_DATA_MAX_LENGTH (currently set to 256 bytes). You call the function
//! persist_exists(key), which returns a boolean indicating if the key exists or not.
//! The Storage API enables your app to save its state, and when compared to using \ref AppMessage to
//! retrieve values from the phone, it provides you with a much faster way to restore state.
//! In addition, it draws less power from the battery.
//!
//! Note that the size of all persisted values cannot exceed 4K.
//! @{
//! The maximum size of a persist value in bytes
#define PERSIST_DATA_MAX_LENGTH 256
//! The maximum size of a persist string in bytes including the NULL terminator
#define PERSIST_STRING_MAX_LENGTH PERSIST_DATA_MAX_LENGTH
//! Status codes. See \ref status_t
typedef enum StatusCode {
//! Operation completed successfully.
S_SUCCESS = 0,
//! An error occurred (no description).
E_ERROR = -1,
//! No idea what went wrong.
E_UNKNOWN = -2,
//! There was a generic internal logic error.
E_INTERNAL = -3,
//! The function was not called correctly.
E_INVALID_ARGUMENT = -4,
//! Insufficient allocatable memory available.
E_OUT_OF_MEMORY = -5,
//! Insufficient long-term storage available.
E_OUT_OF_STORAGE = -6,
//! Insufficient resources available.
E_OUT_OF_RESOURCES = -7,
//! Argument out of range (may be dynamic).
E_RANGE = -8,
//! Target of operation does not exist.
E_DOES_NOT_EXIST = -9,
//! Operation not allowed (may depend on state).
E_INVALID_OPERATION = -10,
//! Another operation prevented this one.
E_BUSY = -11,
//! Operation not completed; try again.
E_AGAIN = -12,
//! Equivalent of boolean true.
S_TRUE = 1,
//! Equivalent of boolean false.
S_FALSE = 0,
//! For list-style requests. At end of list.
S_NO_MORE_ITEMS = 2,
//! No action was taken as none was required.
S_NO_ACTION_REQUIRED = 3,
} StatusCode;
//! Return value for system operations. See \ref StatusCode for possible values.
typedef int32_t status_t;
//! Checks whether a value has been set for a given key in persistent storage.
//! @param key The key of the field to check.
//! @return true if a value exists, otherwise false.
bool persist_exists(const uint32_t key);
//! Gets the size of a value for a given key in persistent storage.
//! @param key The key of the field to lookup the data size.
//! @return The size of the value in bytes or \ref E_DOES_NOT_EXIST
//! if there is no field matching the given key.
int persist_get_size(const uint32_t key);
//! Reads a bool value for a given key from persistent storage.
//! If the value has not yet been set, this will return false.
//! @param key The key of the field to read from.
//! @return The bool value of the key to read from.
bool persist_read_bool(const uint32_t key);
//! Reads an int value for a given key from persistent storage.
//! @note The int is a signed 32-bit integer.
//! If the value has not yet been set, this will return 0.
//! @param key The key of the field to read from.
//! @return The int value of the key to read from.
int32_t persist_read_int(const uint32_t key);
//! Reads a blob of data for a given key from persistent storage into a given buffer.
//! If the value has not yet been set, the given buffer is left unchanged.
//! @param key The key of the field to read from.
//! @param buffer The pointer to a buffer to be written to.
//! @param buffer_size The maximum size of the given buffer.
//! @return The number of bytes written into the buffer or \ref E_DOES_NOT_EXIST
//! if there is no field matching the given key.
int persist_read_data(const uint32_t key, void *buffer, const size_t buffer_size);
//! Reads a string for a given key from persistent storage into a given buffer.
//! The string will be null terminated.
//! If the value has not yet been set, the given buffer is left unchanged.
//! @param key The key of the field to read from.
//! @param buffer The pointer to a buffer to be written to.
//! @param buffer_size The maximum size of the given buffer. This includes the null character.
//! @return The number of bytes written into the buffer or \ref E_DOES_NOT_EXIST
//! if there is no field matching the given key.
int persist_read_string(const uint32_t key, char *buffer, const size_t buffer_size);
//! Writes a bool value flag for a given key into persistent storage.
//! @param key The key of the field to write to.
//! @param value The boolean value to write.
status_t persist_write_bool(const uint32_t key, const bool value);
//! Writes an int value for a given key into persistent storage.
//! @note The int is a signed 32-bit integer.
//! @param key The key of the field to write to.
//! @param value The int value to write.
status_t persist_write_int(const uint32_t key, const int32_t value);
//! Writes a blob of data of a specified size in bytes for a given key into persistent storage.
//! The maximum size is \ref PERSIST_DATA_MAX_LENGTH
//! @param key The key of the field to write to.
//! @param data The pointer to the blob of data.
//! @param size The size in bytes.
//! @return The number of bytes written.
int persist_write_data(const uint32_t key, const void *data, const size_t size);
//! Writes a string a given key into persistent storage.
//! The maximum size is \ref PERSIST_STRING_MAX_LENGTH including the null terminator.
//! @param key The key of the field to write to.
//! @param cstring The pointer to null terminated string.
//! @return The number of bytes written.
int persist_write_string(const uint32_t key, const char *cstring);
//! Deletes the value of a key from persistent storage.
//! @param key The key of the field to delete from.
status_t persist_delete(const uint32_t key);
//! @} // group Storage
//! @addtogroup Wakeup
//! \brief Allows applications to schedule to be launched even if they are not running.
//! @{
//! WakeupId is an identifier for a wakeup event
typedef int32_t WakeupId;
//! The type of function which can be called when a wakeup event occurs.
//! The arguments will be the id of the wakeup event that occurred,
//! as well as the scheduled cookie provided to \ref wakeup_schedule.
typedef void (*WakeupHandler)(WakeupId wakeup_id, int32_t cookie);
//! Registers a WakeupHandler to be called when wakeup events occur.
//! @param handler The callback that gets called when the wakeup event occurs
void wakeup_service_subscribe(WakeupHandler handler);
//! Registers a wakeup event that triggers a callback at the specified time.
//! Applications may only schedule up to 8 wakeup events.
//! Wakeup events are given a 1 minute duration window, in that no application may schedule a
//! wakeup event with 1 minute of a currently scheduled wakeup event.
//! @param timestamp The requested time (UTC) for the wakeup event to occur
//! @param cookie The application specific reason for the wakeup event
//! @param notify_if_missed On powering on Pebble, will alert user when
//! notifications were missed due to Pebble being off.
//! @return negative values indicate errors (StatusCode)
//! E_RANGE if the event cannot be scheduled due to another event in that period.
//! E_INVALID_ARGUMENT if the time requested is in the past.
//! E_OUT_OF_RESOURCES if the application has already scheduled all 8 wakeup events.
//! E_INTERNAL if a system error occurred during scheduling.
WakeupId wakeup_schedule(time_t timestamp, int32_t cookie, bool notify_if_missed);
//! Cancels a wakeup event.
//! @param wakeup_id Wakeup event to cancel
void wakeup_cancel(WakeupId wakeup_id);
//! Cancels all wakeup event for the app.
void wakeup_cancel_all(void);
//! Retrieves the wakeup event info for an app that was launched
//! by a wakeup_event (ie. \ref launch_reason() === APP_LAUNCH_WAKEUP)
//! so that an app may display information regarding the wakeup event
//! @param wakeup_id WakeupId for the wakeup event that caused the app to wakeup
//! @param cookie App provided reason for the wakeup event
//! @return True if app was launched due to a wakeup event, false otherwise
bool wakeup_get_launch_event(WakeupId *wakeup_id, int32_t *cookie);
//! Checks if the current WakeupId is still scheduled and therefore valid
//! @param wakeup_id Wakeup event to query for validity and scheduled time
//! @param timestamp Optionally points to an address of a time_t variable to
//! store the time that the wakeup event is scheduled to occur.
//! (The time is in UTC, but local time when \ref clock_is_timezone_set
//! returns false).
//! You may pass NULL instead if you do not need it.
//! @return True if WakeupId is still scheduled, false if it doesn't exist or has
//! already occurred
bool wakeup_query(WakeupId wakeup_id, time_t *timestamp);
//! @} // group Wakeup
//! @addtogroup LaunchReason Launch Reason
//! \brief API for checking what caused the application to launch.
//!
//! This includes the system, launch by user interaction (User selects the
//! application from the launcher menu),
//! launch by the mobile or a mobile companion application,
//! or launch by a scheduled wakeup event for the specified application.
//!
//! @{
//! AppLaunchReason is used to inform the application about how it was launched
//! @note New launch reasons may be added in the future. As a best practice, it
//! is recommended to only handle the cases that the app needs to know about,
//! rather than trying to handle all possible launch reasons.
typedef enum {
APP_LAUNCH_SYSTEM = 0, //!< App launched by the system
APP_LAUNCH_USER, //!< App launched by user selection in launcher menu
APP_LAUNCH_PHONE, //!< App launched by mobile or companion app
APP_LAUNCH_WAKEUP, //!< App launched by wakeup event
APP_LAUNCH_WORKER, //!< App launched by worker calling worker_launch_app()
APP_LAUNCH_QUICK_LAUNCH, //!< App launched by user using quick launch
APP_LAUNCH_TIMELINE_ACTION, //!< App launched by user opening it from a pin
} AppLaunchReason;
//! Provides the method used to launch the current application.
//! @return The method or reason the current application was launched
AppLaunchReason launch_reason(void);
//! Get the argument passed to the app when it was launched.
//! @note Currently the only way to pass arguments to apps is by using an openWatchApp action
//! on a pin.
//! @return The argument passed to the app, or 0 if the app wasn't launched from a Launch App action
uint32_t launch_get_args(void);
//! @} // group LaunchReason
//! @} // group Foundation
//! @addtogroup Graphics
//! @{
//! @addtogroup GraphicsTypes Graphics Types
//! \brief Basic graphics types (point, rect, size, color, bitmaps, etc.) and utility functions.
//!
//! @{
typedef union GColor8 {
uint8_t argb;
struct {
uint8_t b:2; //!< Blue
uint8_t g:2; //!< Green
uint8_t r:2; //!< Red
uint8_t a:2; //!< Alpha. 3 = 100% opaque, 2 = 66% opaque, 1 = 33% opaque, 0 = transparent.
};
} GColor8;
typedef GColor8 GColor;
#include "gcolor_definitions.h"
//! Comparison function for GColors.
bool gcolor_equal(GColor8 x, GColor8 y);
//! This method assists in improving the legibility of text on various background colors.
//! It takes the background color for the region in question and computes a color for
//! maximum legibility.
//! @param background_color Background color for the region in question
//! @return A legible color for the given background color
GColor8 gcolor_legible_over(GColor8 background_color);
//! Convenience macro allowing use of a fallback color for black and white platforms.
//! On color platforms this defaults to color
#define COLOR_FALLBACK(color, bw) (color)
//! Represents a point in a 2-dimensional coordinate system.
//! @note Conventionally, the origin of Pebble's 2D coordinate system is in the upper,
//! lefthand corner
//! its x-axis extends to the right and its y-axis extends to the bottom of the screen.
typedef struct GPoint {
//! The x-coordinate.
int16_t x;
//! The y-coordinate.
int16_t y;
} GPoint;
//! Convenience macro to make a GPoint.
#define GPoint(x, y) ((GPoint){(x), (y)})
//! Convenience macro to make a GPoint at (0, 0).
#define GPointZero GPoint(0, 0)
//! Tests whether 2 points are equal.
//! @param point_a Pointer to the first point
//! @param point_b Pointer to the second point
//! @return `true` if both points are equal, `false` if not.
bool gpoint_equal(const GPoint * const point_a, const GPoint * const point_b);
//! Represents a 2-dimensional size.
typedef struct GSize {
//! The width
int16_t w;
//! The height
int16_t h;
} GSize;
//! Convenience macro to make a GSize.
#define GSize(w, h) ((GSize){(w), (h)})
//! Convenience macro to make a GSize of (0, 0).
#define GSizeZero GSize(0, 0)
//! Tests whether 2 sizes are equal.
//! @param size_a Pointer to the first size
//! @param size_b Pointer to the second size
//! @return `true` if both sizes are equal, `false` if not.
bool gsize_equal(const GSize *size_a, const GSize *size_b);
//! Represents a rectangle and defining it using the origin of
//! the upper-lefthand corner and its size.
typedef struct GRect {
//! The coordinate of the upper-lefthand corner point of the rectangle.
GPoint origin;
//! The size of the rectangle.
GSize size;
} GRect;
//! Convenience macro to make a GRect
#define GRect(x, y, w, h) ((GRect){{(x), (y)}, {(w), (h)}})
//! Convenience macro to make a GRect of ((0, 0), (0, 0)).
#define GRectZero GRect(0, 0, 0, 0)
//! Tests whether 2 rectangles are equal.
//! @param rect_a Pointer to the first rectangle
//! @param rect_b Pointer to the second rectangle
//! @return `true` if both rectangles are equal, `false` if not.
bool grect_equal(const GRect* const rect_a, const GRect* const rect_b);
//! Tests whether the size of the rectangle is (0, 0).
//! @param rect Pointer to the rectangle
//! @return `true` if the rectangle its size is (0, 0), or `false` if not.
//! @note If the width and/or height of a rectangle is negative, this
//! function will return `true`!
bool grect_is_empty(const GRect* const rect);
//! Converts a rectangle's values so that the components of its size
//! (width and/or height) are both positive. In the width and/or height are negative,
//! the origin will offset, so that the final rectangle overlaps with the original.
//! For example, a GRect with size (-10, -5) and origin (20, 20), will be standardized
//! to size (10, 5) and origin (10, 15).
//! @param[in] rect The rectangle to convert.
//! @param[out] rect The standardized rectangle.
void grect_standardize(GRect *rect);
//! Trim one rectangle using the edges of a second rectangle.
//! @param[in] rect_to_clip The rectangle that needs to be clipped (in place).
//! @param[out] rect_to_clip The clipped rectangle.
//! @param rect_clipper The rectangle of which the edges will serve as "scissors"
//! in order to trim `rect_to_clip`.
void grect_clip(GRect * const rect_to_clip, const GRect * const rect_clipper);
//! Tests whether a rectangle contains a point.
//! @param rect The rectangle
//! @param point The point
//! @return `true` if the rectangle contains the point, or `false` if it does not.
bool grect_contains_point(const GRect *rect, const GPoint *point);
//! Convenience function to compute the center-point of a given rectangle.
//! This is equal to `(rect->x + rect->width / 2, rect->y + rect->height / 2)`.
//! @param rect The rectangle for which to calculate the center point.
//! @return The point at the center of `rect`
GPoint grect_center_point(const GRect *rect);
//! Reduce the width and height of a rectangle by insetting each of the edges with
//! a fixed inset. The returned rectangle will be centered relative to the input rectangle.
//! @note The function will trip an assertion if the crop yields a rectangle with negative width or height.
//! @param rect The rectangle that will be inset
//! @param crop_size_px The inset by which each of the rectangle will be inset.
//! A positive inset value results in a smaller rectangle, while negative inset value results
//! in a larger rectangle.
//! @return The cropped rectangle.
GRect grect_crop(GRect rect, const int32_t crop_size_px);
//! Repeat Sequence or animation indefinitely.
#define PLAY_COUNT_INFINITE UINT32_MAX
//! Duration of Sequence or animation is infinite.
#define PLAY_DURATION_INFINITE UINT32_MAX
//! The format of a GBitmap can either be 1-bit or 8-bit.
typedef enum GBitmapFormat {
GBitmapFormat1Bit = 0, //<! 1-bit black and white. 0 = black, 1 = white.
GBitmapFormat8Bit, //<! 6-bit color + 2 bit alpha channel. See \ref GColor8 for pixel format.
GBitmapFormat1BitPalette,
GBitmapFormat2BitPalette,
GBitmapFormat4BitPalette,
} GBitmapFormat;
struct GBitmap;
typedef struct GBitmap GBitmap;
struct GBitmapSequence;
typedef struct GBitmapSequence GBitmapSequence;
//! Get the number of bytes per row in the bitmap data for the given \ref GBitmap.
//! This can be used as a safe way of iterating over the rows in the bitmap, since bytes per row
//! should be set according to format.
//! @param bitmap A pointer to the GBitmap to get the bytes per row
//! @return The number of bytes per row of the GBitmap
//! @see \ref gbitmap_get_data
uint16_t gbitmap_get_bytes_per_row(const GBitmap *bitmap);
//! Get the \ref GBitmapFormat for the \ref GBitmap.
//! @param bitmap A pointer to the GBitmap to get the format
//! @return The format of the given \ref GBitmap.
GBitmapFormat gbitmap_get_format(const GBitmap *bitmap);
//! Get a pointer to the raw image data section of the given \ref GBitmap as specified by the format
//! of the bitmap.
//! @param bitmap A pointer to the GBitmap to get the data
//! @return pointer to the raw image data for the GBitmap
//! @see \ref gbitmap_get_bytes_per_row
//! @see \ref GBitmap
uint8_t* gbitmap_get_data(const GBitmap *bitmap);
//! Set the bitmap data for the given \ref GBitmap.
//! @param bitmap A pointer to the GBitmap to set data to
//! @param data A pointer to the bitmap data
//! @param format the format of the bitmap data. If this is a palettized format, make sure that
//! there is an accompanying call to \ref gbitmap_set_palette.
//! @param row_size_bytes How many bytes a single row takes. For example, bitmap data of format
//! \ref GBitmapFormat1Bit must have a row size as a multiple of 4 bytes.
//! @param free_on_destroy Set whether the data should be freed when the GBitmap is destroyed.
//! @see \ref gbitmap_destroy
void gbitmap_set_data(GBitmap *bitmap, uint8_t *data, GBitmapFormat format,
uint16_t row_size_bytes, bool free_on_destroy);
//! Gets the bounds of the content for the \ref GBitmap. This is set when loading the image or
//! if changed by \ref gbitmap_set_bounds.
//! @param bitmap A pointer to the GBitmap to get the bounding box from.
//! @return The bounding box for the GBitmap.
//! @see \ref gbitmap_set_bounds
GRect gbitmap_get_bounds(const GBitmap *bitmap);
//! Set the bounds of the given \ref GBitmap.
//! @param bitmap A pointer to the GBitmap to set the bounding box.
//! @param bounds The bounding box to set.
//! @see \ref gbitmap_get_bounds
void gbitmap_set_bounds(GBitmap *bitmap, GRect bounds);
//! Get the palette for the given \ref GBitmap.
//! @param bitmap A pointer to the GBitmap to get the palette from.
//! @return Pointer to a \ref GColor array containing the palette colors.
//! @see \ref gbitmap_set_palette
GColor* gbitmap_get_palette(const GBitmap *bitmap);
//! Set the palette for the given \ref GBitmap.
//! @param bitmap A pointer to the GBitmap to set the palette to
//! @param palette The palette to be used. Make sure that the palette is large enough for the
//! bitmap's format.
//! @param free_on_destroy Set whether the palette data should be freed when the GBitmap is
//! destroyed or when another palette is set.
//! @see \ref gbitmap_get_format
//! @see \ref gbitmap_destroy
//! @see \ref gbitmap_set_palette
void gbitmap_set_palette(GBitmap *bitmap, GColor *palette, bool free_on_destroy);
//! Creates a new \ref GBitmap on the heap using a Pebble image file stored as a resource.
//! The resulting GBitmap must be destroyed using \ref gbitmap_destroy().
//! @param resource_id The ID of the bitmap resource to load
//! @return A pointer to the \ref GBitmap. `NULL` if the GBitmap could not
//! be created
GBitmap* gbitmap_create_with_resource(uint32_t resource_id);
//! Creates a new GBitmap on the heap initialized with the provided Pebble image data.
//!
//! The resulting \ref GBitmap must be destroyed using \ref gbitmap_destroy() but the image
//! data will not be freed automatically. The developer is responsible for keeping the image
//! data in memory as long as the bitmap is used and releasing it after the bitmap is destroyed.
//! @note One way to generate Pebble image data is to use bitmapgen.py in the Pebble
//! SDK to generate a .pbi file.
//! @param data The Pebble image data. Must not be NULL. The function
//! assumes the data to be correct; there are no sanity checks performed on the
//! data. The data will not be copied and the pointer must remain valid for the
//! lifetime of this GBitmap.
//! @return A pointer to the \ref GBitmap. `NULL` if the \ref GBitmap could not
//! be created
GBitmap* gbitmap_create_with_data(const uint8_t *data);
//! Create a new \ref GBitmap on the heap as a sub-bitmap of a 'base' \ref
//! GBitmap, using a GRect to indicate what portion of the base to use. The
//! sub-bitmap will just reference the image data and palette of the base bitmap.
//! No deep-copying occurs as a result of calling this function, thus the caller
//! is responsible for making sure the base bitmap and palette will remain available when
//! using the sub-bitmap. Note that you should not destroy the parent bitmap until
//! the sub_bitmap has been destroyed.
//! The resulting \ref GBitmap must be destroyed using \ref gbitmap_destroy().
//! @param[in] base_bitmap The bitmap that the sub-bitmap of which the image data
//! will be used by the sub-bitmap
//! @param sub_rect The rectangle within the image data of the base bitmap. The
//! bounds of the base bitmap will be used to clip `sub_rect`.
//! @return A pointer to the \ref GBitmap. `NULL` if the GBitmap could not
//! be created
GBitmap* gbitmap_create_as_sub_bitmap(const GBitmap *base_bitmap, GRect sub_rect);
//! Create a \ref GBitmap based on raw PNG data.
//! The resulting \ref GBitmap must be destroyed using \ref gbitmap_destroy().
//! The developer is responsible for freeing png_data following this call.
//! @note PNG decoding currently supports 1,2,4 and 8 bit palettized and grayscale images.
//! @param png_data PNG image data.
//! @param png_data_size PNG image size in bytes.
//! @return A pointer to the \ref GBitmap. `NULL` if the \ref GBitmap could not
//! be created
GBitmap* gbitmap_create_from_png_data(const uint8_t *png_data, size_t png_data_size);
//! Creates a new blank GBitmap on the heap initialized to zeroes.
//! In the case that the format indicates a palettized bitmap, a palette of appropriate size will
//! also be allocated on the heap.
//! The resulting \ref GBitmap must be destroyed using \ref gbitmap_destroy().
//! @param size The Pebble image dimensions as a \ref GSize.
//! @param format The \ref GBitmapFormat the created image should be in.
//! @return A pointer to the \ref GBitmap. `NULL` if the \ref GBitmap could not
//! be created
GBitmap* gbitmap_create_blank(GSize size, GBitmapFormat format);
//! Creates a new blank GBitmap on the heap, initialized to zeroes, and assigns it the given
//! palette.
//! No deep-copying of the palette occurs, so the caller is responsible for making sure the palette
//! remains available when using the resulting bitmap. Management of that memory can be handed off
//! to the system with the free_on_destroy argument.
//! @param size The Pebble image dimensions as a \ref GSize.
//! @param format the \ref GBitmapFormat the created image and palette should be in.
//! @param palette a pointer to a palette that is to be used for this GBitmap. The palette should
//! be large enough to hold enough colors for the specified format. For example,
//! \ref GBitmapFormat2BitPalette should have 4 colors, since 2^2 = 4.
//! @param free_on_destroy Set whether the palette data should be freed along with the bitmap data
//! when the GBitmap is destroyed.
//! @return A Pointer to the \ref GBitmap. `NULL` if the \ref GBitmap could not be created.
GBitmap* gbitmap_create_blank_with_palette(GSize size, GBitmapFormat format,
GColor *palette, bool free_on_destroy);
//! Given a 1-bit GBitmap, create a new bitmap of format GBitmapFormat1BitPalette.
//! The new data buffer is allocated on the heap, and a 2-color palette is allocated as well.
//! @param src_bitmap A GBitmap of format GBitmapFormat1Bit which is to be copied into a newly
//! created GBitmap of format GBitmapFormat1BitPalettized.
//! @returns The newly created 1-bit palettized GBitmap, or NULL if there is not sufficient space.
//! @note The new bitmap does not depend on any data from src_bitmap, so src_bitmap can be freed
//! without worry.
GBitmap* gbitmap_create_palettized_from_1bit(const GBitmap *src_bitmap);
//! Destroy a \ref GBitmap.
//! This must be called for every bitmap that's been created with gbitmap_create_*
//!
//! This function will also free the memory of the bitmap data (bitmap->addr) if the bitmap was created with \ref gbitmap_create_blank()
//! or \ref gbitmap_create_with_resource().
//!
//! If the GBitmap was created with \ref gbitmap_create_with_data(), you must release the memory
//! after calling gbitmap_destroy().
void gbitmap_destroy(GBitmap* bitmap);
//! Creates a GBitmapSequence from the specified resource (APNG/PNG files)
//! @param resource_id Resource to load and create GBitmapSequence from.
//! @return GBitmapSequence pointer if the resource was loaded, NULL otherwise
GBitmapSequence *gbitmap_sequence_create_with_resource(uint32_t resource_id);
//! Updates the contents of the bitmap sequence to the next frame
//! and optionally returns the delay in milliseconds until the next frame.
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @param bitmap Pointer to the initialized GBitmap in which to render the bitmap sequence
//! @param[out] delay_ms If not NULL, returns the delay in milliseconds until the next frame.
//! @return True if frame was rendered. False if all frames (and loops) have been rendered
//! for the sequence. Will also return false if frame could not be rendered
//! (includes out of memory errors).
//! @note GBitmap must be large enough to accommodate the bitmap_sequence image
//! \ref gbitmap_sequence_get_bitmap_size
bool gbitmap_sequence_update_bitmap_next_frame(GBitmapSequence *bitmap_sequence,
GBitmap *bitmap, uint32_t *delay_ms);
//! Updates the contents of the bitmap sequence to the frame at elapsed in the sequence.
//! For looping animations this accounts for the loop, for example an animation of 1 second that
//! is configured to loop 2 times updated to 1500 ms elapsed time will display the sequence
//! frame at 500 ms. Elapsed time is the time from the start of the animation, and will
//! be ignored if it is for a time earlier than the last rendered frame.
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @param bitmap Pointer to the initialized GBitmap in which to render the bitmap sequence
//! @param elapsed_ms Elapsed time in milliseconds in the sequence relative to start
//! @return True if a frame was rendered. False if all frames (and loops) have already
//! been rendered for the sequence. Will also return false if frame could not be rendered
//! (includes out of memory errors).
//! @note GBitmap must be large enough to accommodate the bitmap_sequence image
//! \ref gbitmap_sequence_get_bitmap_size
//! @note This function is disabled for play_count 0
bool gbitmap_sequence_update_bitmap_by_elapsed(GBitmapSequence *bitmap_sequence,
GBitmap *bitmap, uint32_t elapsed_ms);
//! Deletes the GBitmapSequence structure and frees any allocated memory/decoder_data
//! @param bitmap_sequence Pointer to the bitmap sequence to free (delete)
void gbitmap_sequence_destroy(GBitmapSequence *bitmap_sequence);
//! Restarts the GBitmapSequence to the first frame \ref gbitmap_sequence_update_bitmap_next_frame
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @return True if sequence was restarted, false otherwise
bool gbitmap_sequence_restart(GBitmapSequence *bitmap_sequence);
//! This function gets the current frame number for the bitmap sequence
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @return index of current frame in the current loop of the bitmap sequence
int32_t gbitmap_sequence_get_current_frame_idx(GBitmapSequence *bitmap_sequence);
//! This function sets the total number of frames for the bitmap sequence
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @return number of frames contained in a single loop of the bitmap sequence
uint32_t gbitmap_sequence_get_total_num_frames(GBitmapSequence *bitmap_sequence);
//! This function gets the play count (number of times to repeat) the bitmap sequence
//! @note This value is initialized by the bitmap sequence data, and is modified by
//! \ref gbitmap_sequence_set_play_count
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @return Play count of bitmap sequence, PLAY_COUNT_INFINITE for infinite looping
uint32_t gbitmap_sequence_get_play_count(GBitmapSequence *bitmap_sequence);
//! This function sets the play count (number of times to repeat) the bitmap sequence
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @param play_count Number of times to repeat the bitmap sequence
//! with 0 disabling update_by_elapsed and update_next_frame, and
//! PLAY_COUNT_INFINITE for infinite looping of the animation
void gbitmap_sequence_set_play_count(GBitmapSequence *bitmap_sequence, uint32_t play_count);
//! This function gets the minimum required size (dimensions) necessary
//! to render the bitmap sequence to a GBitmap
//! using the /ref gbitmap_sequence_update_bitmap_next_frame
//! @param bitmap_sequence Pointer to loaded bitmap sequence
//! @return Dimensions required to render the bitmap sequence to a GBitmap
GSize gbitmap_sequence_get_bitmap_size(GBitmapSequence *bitmap_sequence);
//! Values to specify how two things should be aligned relative to each other.
//! ![](galign.png)
//! @see \ref bitmap_layer_set_alignment()
typedef enum GAlign {
//! Align by centering
GAlignCenter,
//! Align by making the top edges overlap and left edges overlap
GAlignTopLeft,
//! Align by making the top edges overlap and left edges overlap
GAlignTopRight,
//! Align by making the top edges overlap and centered horizontally
GAlignTop,
//! Align by making the left edges overlap and centered vertically
GAlignLeft,
//! Align by making the bottom edges overlap and centered horizontally
GAlignBottom,
//! Align by making the right edges overlap and centered vertically
GAlignRight,
//! Align by making the bottom edges overlap and right edges overlap
GAlignBottomRight,
//! Align by making the bottom edges overlap and left edges overlap
GAlignBottomLeft
} GAlign;
//! Aligns one rectangle within another rectangle, using an alignment parameter.
//! The relative coordinate systems of both rectangles are assumed to be the same.
//! When clip is true, `rect` is also clipped by the constraint.
//! @param[in] rect The rectangle to align (in place)
//! @param[out] rect The aligned and optionally clipped rectangle
//! @param inside_rect The rectangle in which to align `rect`
//! @param alignment Determines the alignment of `rect` within `inside_rect` by
//! specifying what edges of should overlap.
//! @param clip Determines whether `rect` should be trimmed using the edges of `inside_rect`
//! in case `rect` extends outside of the area that `inside_rect` covers after the alignment.
void grect_align(GRect *rect, const GRect *inside_rect, const GAlign alignment, const bool clip);
//! Values to specify how the source image should be composited onto the destination image.
//!
//! For Aplite, there is no notion of "transparency" in the graphics system. However, the effect of
//! transparency can be created by masking and using compositing modes.
//! ![](compops.png)
//! Contrived example of how the different compositing modes affect drawing.
//! Often, the "destination image" is the render buffer and thus contains the image of
//! what has been drawn before or "underneath".
//!
//! For Basalt, at the moment, only two compositing modes are supported, \ref GCompOpAssign and
//! \ref GCompOpSet. The behavior of other compositing modes are undefined and may change in the
//! future. Transparency can be achieved using \ref GCompOpSet and requires pixel values with alpha
//! value .a < 3.
//! @see \ref bitmap_layer_set_compositing_mode()
//! @see \ref graphics_context_set_compositing_mode()
//! @see \ref graphics_draw_bitmap_in_rect()
//! @see \ref graphics_draw_rotated_bitmap()
typedef enum {
//! Assign the pixel values of the source image to the destination pixels,
//! effectively replacing the previous values for those pixels. For Basalt, when drawing a color
//! palettized or 8-bit \ref GBitmap image, the opacity value is ignored.
GCompOpAssign,
//! Assign the **inverted** pixel values of the source image to the destination pixels,
//! effectively replacing the previous values for those pixels.
//! @note For Basalt, this mode is not supported and the resulting behavior is undefined.
GCompOpAssignInverted,
//! Use the boolean operator `OR` to composite the source and destination pixels.
//! The visual result of this compositing mode is the source's white pixels
//! are painted onto the destination and the source's black pixels are treated
//! as clear.
//! @note For Basalt, this mode is not supported and the resulting behavior is undefined.
GCompOpOr,
//! Use the boolean operator `AND` to composite the source and destination pixels.
//! The visual result of this compositing mode is the source's black pixels
//! are painted onto the destination and the source's white pixels are treated
//! as clear.
//! @note For Basalt, this mode is not supported and the resulting behavior is undefined.
GCompOpAnd,
//! Clears the bits in the destination image, using the source image as mask.
//! The visual result of this compositing mode is that for the parts where the source image is
//! white, the destination image will be painted black. Other parts will be left untouched.
//! @note For Basalt, this mode is not supported and the resulting behavior is undefined.
GCompOpClear,
//! Sets the bits in the destination image, using the source image as mask. For Aplite,
//! the visual result of this compositing mode is that for the parts where the source image is
//! black, the destination image will be painted white. Other parts will be left untouched.
//! For Basalt, when drawing a color palettized or 8-bit \ref GBitmap image, this mode will be
//! required to apply any transparency.
GCompOpSet,
} GCompOp;
struct GContext;
typedef struct GContext GContext;
//! @} // group GraphicsTypes
//! @addtogroup GraphicsContext Graphics Context
//! \brief The "canvas" into which an application draws
//!
//! The Pebble OS graphics engine, inspired by several notable graphics systems, including
//! Apple’s Quartz 2D and its predecessor QuickDraw, provides your app with a canvas into
//! which to draw, namely, the graphics context. A graphics context is the target into which
//! graphics functions can paint, using Pebble drawing routines (see \ref Drawing,
//! \ref PathDrawing and \ref TextDrawing).
//!
//! A graphics context holds a reference to the bitmap into which to paint. It also holds the
//! current drawing state, like the current fill color, stroke color, clipping box, drawing box,
//! compositing mode, and so on. The GContext struct is the type representing the graphics context.
//!
//! For drawing in your Pebble watchface or watchapp, you won't need to create a GContext
//! yourself. In most cases, it is provided by Pebble OS as an argument passed into a render
//! callback (the .update_proc of a Layer).
//!
//! Your app can’t call drawing functions at any given point in time: Pebble OS will request your
//! app to render. Typically, your app will be calling out to graphics functions in
//! the .update_proc callback of a Layer.
//! @see \ref Layer
//! @see \ref Drawing
//! @see \ref PathDrawing
//! @see \ref TextDrawing
//! @{
//! Sets the current stroke color of the graphics context.
//! @param ctx The graphics context onto which to set the stroke color
//! @param color The new stroke color
void graphics_context_set_stroke_color(GContext* ctx, GColor color);
//! Sets the current fill color of the graphics context.
//! @param ctx The graphics context onto which to set the fill color
//! @param color The new fill color
void graphics_context_set_fill_color(GContext* ctx, GColor color);
//! Sets the current text color of the graphics context.
//! @param ctx The graphics context onto which to set the text color
//! @param color The new text color
void graphics_context_set_text_color(GContext* ctx, GColor color);
//! Sets the current bitmap compositing mode of the graphics context.
//! @param ctx The graphics context onto which to set the compositing mode
//! @param mode The new compositing mode
//! @see \ref GCompOp
//! @see \ref bitmap_layer_set_compositing_mode()
//! @note At the moment, this only affects the bitmaps drawing operations
//! -- \ref graphics_draw_bitmap_in_rect(), \ref graphics_draw_rotated_bitmap, and
//! anything that uses those APIs --, but it currently does not affect the filling or stroking
//! operations.
void graphics_context_set_compositing_mode(GContext* ctx, GCompOp mode);
//! Sets whether antialiasing is applied to stroke drawing
//! @param ctx The graphics context onto which to set the antialiasing
//! @param enable True = antialiasing enabled, False = antialiasing disabled
//! @note Default value is true.
void graphics_context_set_antialiased(GContext* ctx, bool enable);
//! Sets the width of the stroke for drawing routines
//! @param ctx The graphics context onto which to set the stroke width
//! @param stroke_width Width in pixels of the stroke.
//! @note If stroke width of zero is passed, it will be ignored and will not change the value
//! stored in GContext. Currently, only odd stroke_width values are supported. If an even value
//! is passed in, the value will be stored as is, but the drawing routines will round down to the
//! previous integral value when drawing. Default value is 1.
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width);
//! @} // group GraphicsContext
//! @addtogroup Drawing Drawing Primitives
//! \brief Functions to draw into a graphics context
//!
//! Use these drawing functions inside a Layer's `.update_proc` drawing
//! callback. A `GContext` is passed into this callback as an argument.
//! This `GContext` can then be used with all of the drawing functions which
//! are documented below.
//! See \ref GraphicsContext for more information about the graphics context.
//!
//! Refer to \htmlinclude UiFramework.html (chapter "Layers" and "Graphics") for a
//! conceptual overview of the drawing system, Layers and relevant code examples.
//!
//! Other drawing functions and related documentation:
//! * \ref TextDrawing
//! * \ref PathDrawing
//! * \ref GraphicsTypes
//! @{
//! Bit mask values to specify the corners of a rectangle.
//! The values can be combines using binary OR (`|`),
//! For example: the mask to indicate top left and bottom right corners can:
//! be created as follows: `(GCornerTopLeft | GCornerBottomRight)`
typedef enum {
//! No corners
GCornerNone = 0,
//! Top-Left corner
GCornerTopLeft = 1 << 0,
//! Top-Right corner
GCornerTopRight = 1 << 1,
//! Bottom-Left corner
GCornerBottomLeft = 1 << 2,
//! Bottom-Right corner
GCornerBottomRight = 1 << 3,
//! All corners
GCornersAll = GCornerTopLeft | GCornerTopRight | GCornerBottomLeft | GCornerBottomRight,
//! Top corners
GCornersTop = GCornerTopLeft | GCornerTopRight,
//! Bottom corners
GCornersBottom = GCornerBottomLeft | GCornerBottomRight,
//! Left corners
GCornersLeft = GCornerTopLeft | GCornerBottomLeft,
//! Right corners
GCornersRight = GCornerTopRight | GCornerBottomRight,
} GCornerMask;
//! Draws a pixel at given point in the current stroke color
//! @param ctx The destination graphics context in which to draw
//! @param point The point at which to draw the pixel
void graphics_draw_pixel(GContext* ctx, GPoint point);
//! Draws line in the current stroke color, current stroke width and AA flag
//! @param ctx The destination graphics context in which to draw
//! @param p0 The starting point of the line
//! @param p1 The ending point of the line
void graphics_draw_line(GContext* ctx, GPoint p0, GPoint p1);
//! Draws a 1-pixel wide rectangle outline in the current stroke color
//! @param ctx The destination graphics context in which to draw
//! @param rect The rectangle for which to draw the outline
void graphics_draw_rect(GContext* ctx, GRect rect);
//! Fills a retangle with the current fill color, optionally rounding all or a selection of its corners
//! @param ctx The destination graphics context in which to draw
//! @param rect The rectangle to fill
//! @param corner_radius The rounding radius of the corners in pixels (maximum is 8 pixels)
//! @param corner_mask Bitmask of the corners that need to be rounded.
//! @see \ref GCornerMask
void graphics_fill_rect(GContext* ctx, GRect rect, uint16_t corner_radius, GCornerMask corner_mask);
//! Draws the outline of a circle in the current stroke color
//! @param ctx The destination graphics context in which to draw
//! @param p The center point of the circle
//! @param radius The radius in pixels
void graphics_draw_circle(GContext* ctx, GPoint p, uint16_t radius);
//! Fills a circle in the current fill color
//! @param ctx The destination graphics context in which to draw
//! @param p The center point of the circle
//! @param radius The radius in pixels
void graphics_fill_circle(GContext* ctx, GPoint p, uint16_t radius);
//! Draws the outline of a rounded rectangle in the current stroke color
//! @param ctx The destination graphics context in which to draw
//! @param rect The rectangle defining the dimensions of the rounded rectangle to draw
//! @param radius The corner radius in pixels
void graphics_draw_round_rect(GContext* ctx, GRect rect, uint16_t radius);
//! Draws a bitmap into the graphics context, inside the specified rectangle
//! @param ctx The destination graphics context in which to draw the bitmap
//! @param bitmap The bitmap to draw
//! @param rect The rectangle in which to draw the bitmap
//! @note If the size of `rect` is smaller than the size of the bitmap,
//! the bitmap will be clipped on right and bottom edges.
//! If the size of `rect` is larger than the size of the bitmap,
//! the bitmap will be tiled automatically in both horizontal and vertical
//! directions, effectively drawing a repeating pattern.
//! @see GBitmap
//! @see GContext
void graphics_draw_bitmap_in_rect(GContext *ctx, const GBitmap *bitmap, GRect rect);
//! A shortcut to capture the framebuffer in the native format of the watch.
//! @see graphics_capture_frame_buffer_format
GBitmap* graphics_capture_frame_buffer(GContext* ctx);
//! Captures the frame buffer for direct access, using the given format.
//! Graphics functions will not affect the frame buffer while it is captured.
//! The frame buffer is released when {@link graphics_release_frame_buffer} is called.
//! The frame buffer must be released before the end of a layer's `.update_proc`
//! for the layer to be drawn properly.
//!
//! While the frame buffer is captured calling {@link graphics_capture_frame_buffer}
//! will fail and return `NULL`.
//! @note When writing to the frame buffer, you should respect the visible boundaries of a
//! window on the screen. Use layer_get_frame(window_get_root_layer(window)).origin to obtain its
//! position relative to the frame buffer. For example, drawing to (5, 5) in the frame buffer
//! while the window is transitioning to the left with its origin at (-20, 0) would
//! effectively draw that point at (25, 5) relative to the window. For this reason you should
//! consider the window's root layer frame when calculating drawing coordinates.
//! @see GBitmap
//! @see GBitmapFormat
//! @see layer_get_frame
//! @see window_get_root_layer
//! @param ctx The graphics context providing the frame buffer
//! @param format The format in which the framebuffer should be captured. Supported formats
//! are \ref GBitmapFormat1Bit and \ref GBitmapFormat8Bit.
//! @return A pointer to the frame buffer. `NULL` if failed.
GBitmap *graphics_capture_frame_buffer_format(GContext *ctx, GBitmapFormat format);
//! Releases the frame buffer.
//! Must be called before the end of a layer's `.update_proc` for the layer to be drawn properly.
//!
//! If `buffer` does not point to the address previously returned by
//! {@link graphics_capture_frame_buffer} the frame buffer will not be released.
//! @param ctx The graphics context providing the frame buffer
//! @param buffer The pointer to frame buffer
//! @return True if the frame buffer was released successfully
bool graphics_release_frame_buffer(GContext* ctx, GBitmap* buffer);
//! Whether or not the frame buffer has been captured by {@link graphics_capture_frame_buffer}.
//! Graphics functions will not affect the frame buffer until it has been released by
//! {@link graphics_release_frame_buffer}.
//! @param ctx The graphics context providing the frame buffer
//! @return True if the frame buffer has been captured
bool graphics_frame_buffer_is_captured(GContext* ctx);
//! Draws a rotated bitmap with a memory-sensitive 2x anti-aliasing technique
//! (using ray-finding instead of super-sampling), which is thresholded into a b/w bitmap for 1-bit
//! and color blended for 8-bit.
//! @note This API has performance limitations that can degrade user experience. Use sparingly.
//! @param ctx The destination graphics context in which to draw
//! @param src The source bitmap to draw
//! @param src_ic Instance center (single point unaffected by rotation) relative to source bitmap
//! @param rotation Angle of rotation. Rotation is an integer between 0 (no rotation)
//! and TRIG_MAX_ANGLE (360 degree rotation).
//! @param dest_ic Where to draw the instance center of the rotated bitmap in the context.
void graphics_draw_rotated_bitmap(GContext* ctx, GBitmap *src, GPoint src_ic, int rotation, GPoint dest_ic);
//! @} // group Drawing
//! @addtogroup DrawCommand Draw Commands
//! \brief Pebble Draw Commands are a way to encode arbitrary path draw and fill calls in binary
//! format, so that vector-like graphics can be represented on the watch.
//!
//! These draw commands can
//! be loaded from resources, manipulated in place and drawn to the current graphics context. Each
//! \ref GDrawCommand can be an arbitrary path or a circle with optional fill or stroke. The stroke
//! width and color of the stroke and fill are also encoded within the \ref GDrawCommand. Paths can
//! can be drawn open or closed.
//!
//! All aspects of a draw command can be modified, except for the number of points in a path (a
//! circle only has one point, the center).
//!
//! Draw commands are grouped into a \ref GDrawCommandList, which can be drawn all at once.
//! Each individual \ref GDrawCommand can be accessed from a \ref GDrawCommandList for modification.
//!
//! A \ref GDrawCommandList forms the basis for \ref GDrawCommandImage and \ref GDrawCommandFrame
//! objects. A \ref GDrawCommandImage represents a static image and can be represented by the PDC
//! file format and can be loaded as a resource.
//!
//! Once you have a \ref GDrawCommandImage loaded in memory you can draw it on the screen in a
//! \ref LayerUpdateProc with the \ref gdraw_command_image_draw().
//!
//! A \ref GDrawCommandFrame represents a single frame of an animated sequence, with multiple frames
//! making up a single \ref GDrawCommandSequence, which can also be stored as a PDC and loaded as a
//! resource.
//!
//! To draw a \ref GDrawCommandSequence, use the \ref gdraw_command_sequence_get_frame_by_elapsed()
//! to obtain the current \ref GDrawCommandFrame and \ref gdraw_command_frame_draw() to draw it.
//!
//! Draw commands also allow access to drawing with sub-pixel precision. The points are treated as
//! Fixed point types in the format 13.3, so that 1/8th of a pixel precision is possible. Only the
//! points in draw commands of the type GDrawCommandTypePrecisePath will be treated as higher
//! precision.
//!
//! @{
//! Draw commands are the basic building block of the draw command system, encoding the type of
//! command to draw, the stroke width and color, fill color, and points that define the path (or
//! center of a circle
typedef struct GDrawCommand GDrawCommand;
//! Draw command frames contain a list of commands to draw for that frame and a duration,
//! indicating the length of time for which the frame should be drawn in an animation sequence.
//! Frames form the building blocks of a \ref GDrawCommandSequence, which consists of multiple
//! frames.
typedef struct GDrawCommandFrame GDrawCommandFrame;
//! Draw command images contain a list of commands that can be drawn. An image can be loaded from
//! PDC file data.
typedef struct GDrawCommandImage GDrawCommandImage;
//! Draw command lists contain a list of commands that can be iterated over and drawn all at once
typedef struct GDrawCommandList GDrawCommandList;
//! Callback for iterating over draw command list
//! @param command current \ref GDrawCommand in iteration
//! @param index index of the current command in the list
//! @param context context pointer for the iteration operation
//! @return true if the iteration should continue after this command is processed
typedef bool (*GDrawCommandListIteratorCb)(GDrawCommand *command, uint32_t index, void *context);
//! Draw command sequences allow the animation of frames over time. Each sequence has a list of
//! frames that can be accessed by the elapsed duration of the animation (not maintained internally)
//! or by index. Sequences can be loaded from PDC file data.
typedef struct GDrawCommandSequence GDrawCommandSequence;
typedef enum {
GDrawCommandTypeInvalid = 0, //!< Invalid draw command type
GDrawCommandTypePath, //!< Arbitrary path draw command type
GDrawCommandTypeCircle, //!< Circle draw command type
GDrawCommandTypePrecisePath, //!< Arbitrary path drawn with sub-pixel precision (1/8th precision)
} GDrawCommandType;
//! Draw a command
//! @param ctx The destination graphics context in which to draw
//! @param command \ref GDrawCommand to draw
void gdraw_command_draw(GContext *ctx, GDrawCommand *command);
//! Get the command type
//! @param command \ref GDrawCommand from which to get the type
//! @return The type of the given \ref GDrawCommand
GDrawCommandType gdraw_command_get_type(GDrawCommand *command);
//! Set the fill color of a command
//! @param command ref DrawCommand for which to set the fill color
//! @param fill_color \ref GColor to set for the fill
void gdraw_command_set_fill_color(GDrawCommand *command, GColor fill_color);
//! Get the fill color of a command
//! @param command \ref GDrawCommand from which to get the fill color
//! @return fill color of the given \ref GDrawCommand
GColor gdraw_command_get_fill_color(GDrawCommand *command);
//! Set the stroke color of a command
//! @param command \ref GDrawCommand for which to set the stroke color
//! @param stroke_color \ref GColor to set for the stroke
void gdraw_command_set_stroke_color(GDrawCommand *command, GColor stroke_color);
//! Get the stroke color of a command
//! @param command \ref GDrawCommand from which to get the stroke color
//! @return The stroke color of the given \ref GDrawCommand
GColor gdraw_command_get_stroke_color(GDrawCommand *command);
//! Set the stroke width of a command
//! @param command \ref GDrawCommand for which to set the stroke width
//! @param stroke_width stroke width to set for the command
void gdraw_command_set_stroke_width(GDrawCommand *command, uint8_t stroke_width);
//! Get the stroke width of a command
//! @param command \ref GDrawCommand from which to get the stroke width
//! @return The stroke width of the given \ref GDrawCommand
uint8_t gdraw_command_get_stroke_width(GDrawCommand *command);
//! Get the number of points in a command
uint16_t gdraw_command_get_num_points(GDrawCommand *command);
//! Set the value of the point in a command at the specified index
//! @param command \ref GDrawCommand for which to set the value of a point
//! @param point_idx Index of the point to set the value for
//! @param point new point value to set
void gdraw_command_set_point(GDrawCommand *command, uint16_t point_idx, GPoint point);
//! Get the value of a point in a command from the specified index
//! @param command \ref GDrawCommand from which to get a point
//! @param point_idx The index to get the point for
//! @return The point in the \ref GDrawCommand specified by point_idx
//! @note The index \b must be less than the number of points
GPoint gdraw_command_get_point(GDrawCommand *command, uint16_t point_idx);
//! Set the radius of a circle command
//! @note This only works for commands of type \ref GDrawCommandCircle
//! @param command \ref GDrawCommand from which to set the circle radius
//! @param radius The radius to set for the circle.
void gdraw_command_set_radius(GDrawCommand *command, uint16_t radius);
//! Get the radius of a circle command.
//! @note this only works for commands of type\ref GDrawCommandCircle.
//! @param command \ref GDrawCommand from which to get the circle radius
//! @return The radius in pixels if command is of type \ref GDrawCommandCircle
uint16_t gdraw_command_get_radius(GDrawCommand *command);
//! Set the path of a stroke command to be open
//! @note This only works for commands of type \ref GDrawCommandPath and
//! \ref GDrawCommandPrecisePath
//! @param command \ref GDrawCommand for which to set the path open status
//! @param path_open true if path should be hidden
void gdraw_command_set_path_open(GDrawCommand *command, bool path_open);
//! Return whether a stroke command path is open
//! @note This only works for commands of type \ref GDrawCommandPath and
//! \ref GDrawCommandPrecisePath
//! @param command \ref GDrawCommand from which to get the path open status
//! @return true if the path is open
bool gdraw_command_get_path_open(GDrawCommand *command);
//! Set a command as hidden. This command will not be drawn when \ref gdraw_command_draw is called
//! with this command
//! @param command \ref GDrawCommand for which to set the hidden status
//! @param hidden true if command should be hidden
void gdraw_command_set_hidden(GDrawCommand *command, bool hidden);
//! Return whether a command is hidden
//! @param command \ref GDrawCommand from which to get the hidden status
//! @return true if command is hidden
bool gdraw_command_get_hidden(GDrawCommand *command);
//! Draw a frame
//! @param ctx The destination graphics context in which to draw
//! @param sequence The sequence from which the frame comes from (this is required)
//! @param frame Frame to draw
//! @param offset Offset from draw context origin to draw the frame
void gdraw_command_frame_draw(GContext *ctx, GDrawCommandSequence *sequence,
GDrawCommandFrame *frame, GPoint offset);
//! Set the duration of the frame
//! @param frame \ref GDrawCommandFrame for which to set the duration
//! @param duration duration of the frame in milliseconds
void gdraw_command_frame_set_duration(GDrawCommandFrame *frame, uint32_t duration);
//! Get the duration of the frame
//! @param frame \ref GDrawCommandFrame from which to get the duration
//! @return duration of the frame in milliseconds
uint32_t gdraw_command_frame_get_duration(GDrawCommandFrame *frame);
//! Creates a GDrawCommandImage from the specified resource (PDC file)
//! @param resource_id Resource containing data to load and create GDrawCommandImage from.
//! @return GDrawCommandImage pointer if the resource was loaded, NULL otherwise
GDrawCommandImage *gdraw_command_image_create_with_resource(uint32_t resource_id);
//! Creates a GDrawCommandImage as a copy from a given image
//! @param image Image to copy.
//! @return cloned image or NULL if the operation failed
GDrawCommandImage *gdraw_command_image_clone(GDrawCommandImage *image);
//! Deletes the GDrawCommandImage structure and frees associated data
//! @param image Pointer to the image to free (delete)
void gdraw_command_image_destroy(GDrawCommandImage *image);
//! Draw an image
//! @param ctx The destination graphics context in which to draw
//! @param image Image to draw
//! @param offset Offset from draw context origin to draw the image
void gdraw_command_image_draw(GContext *ctx, GDrawCommandImage *image, GPoint offset);
//! Get size of the bounding box surrounding all draw commands in the image. This bounding
//! box can be used to set the graphics context or layer bounds when drawing the image.
//! @param image \ref GDrawCommandImage from which to get the bounding box size
//! @return bounding box size
GSize gdraw_command_image_get_bounds_size(GDrawCommandImage *image);
//! Set size of the bounding box surrounding all draw commands in the image. This bounding
//! box can be used to set the graphics context or layer bounds when drawing the image.
//! @param image \ref GDrawCommandImage for which to set the bounding box size
//! @param size bounding box size
void gdraw_command_image_set_bounds_size(GDrawCommandImage *image, GSize size);
//! Get the command list of the image
//! @param image \ref GDrawCommandImage from which to get the command list
//! @return command list
GDrawCommandList *gdraw_command_image_get_command_list(GDrawCommandImage *image);
//! Iterate over all commands in a command list
//! @param command_list \ref GDrawCommandList over which to iterate
//! @param handle_command iterator callback
//! @param callback_context context pointer to be passed into the iterator callback
void gdraw_command_list_iterate(GDrawCommandList *command_list,
GDrawCommandListIteratorCb handle_command, void *callback_context);
//! Draw all commands in a command list
//! @param ctx The destination graphics context in which to draw
//! @param command_list list of commands to draw
void gdraw_command_list_draw(GContext *ctx, GDrawCommandList *command_list);
//! Get the command at the specified index
//! @note the specified index must be less than the number of commands in the list
//! @param command_list \ref GDrawCommandList from which to get a command
//! @param command_idx index of the command to get
//! @return pointer to \ref GDrawCommand at the specified index
GDrawCommand *gdraw_command_list_get_command(GDrawCommandList *command_list, uint16_t command_idx);
//! Get the number of commands in the list
//! @param command_list \ref GDrawCommandList from which to get the number of commands
//! @return number of commands in command list
uint32_t gdraw_command_list_get_num_commands(GDrawCommandList *command_list);
//! Creates a \ref GDrawCommandSequence from the specified resource (PDC file)
//! @param resource_id Resource containing data to load and create GDrawCommandSequence from.
//! @return GDrawCommandSequence pointer if the resource was loaded, NULL otherwise
GDrawCommandSequence *gdraw_command_sequence_create_with_resource(uint32_t resource_id);
//! Creates a \ref GDrawCommandSequence as a copy from a given sequence
//! @param sequence Sequence to copy
//! @return cloned sequence or NULL if the operation failed
GDrawCommandSequence *gdraw_command_sequence_clone(GDrawCommandSequence *sequence);
//! Deletes the \ref GDrawCommandSequence structure and frees associated data
//! @param image Pointer to the sequence to destroy
void gdraw_command_sequence_destroy(GDrawCommandSequence *sequence);
//! Get the frame that should be shown after the specified amount of elapsed time
//! The last frame will be returned if the elapsed time exceeds the total time
//! @param sequence \ref GDrawCommandSequence from which to get the frame
//! @param elapsed_ms elapsed time in milliseconds
//! @return pointer to \ref GDrawCommandFrame that should be displayed at the elapsed time
GDrawCommandFrame *gdraw_command_sequence_get_frame_by_elapsed(GDrawCommandSequence *sequence,
uint32_t elapsed_ms);
//! Get the frame at the specified index
//! @param sequence \ref GDrawCommandSequence from which to get the frame
//! @param index Index of frame to get
//! @return pointer to \ref GDrawCommandFrame at the specified index
GDrawCommandFrame *gdraw_command_sequence_get_frame_by_index(GDrawCommandSequence *sequence,
uint32_t index);
//! Get the size of the bounding box surrounding all draw commands in the sequence. This bounding
//! box can be used to set the graphics context or layer bounds when drawing the frames in the
//! sequence.
//! @param sequence \ref GDrawCommandSequence from which to get the bounds
//! @return bounding box size
GSize gdraw_command_sequence_get_bounds_size(GDrawCommandSequence *sequence);
//! Set size of the bounding box surrounding all draw commands in the sequence. This bounding
//! box can be used to set the graphics context or layer bounds when drawing the frames in the
//! sequence.
//! @param sequence \ref GDrawCommandSequence for which to set the bounds
//! @param size bounding box size
void gdraw_command_sequence_set_bounds_size(GDrawCommandSequence *sequence, GSize size);
//! Get the play count of the sequence
//! @param sequence \ref GDrawCommandSequence from which to get the play count
//! @return play count of sequence
uint32_t gdraw_command_sequence_get_play_count(GDrawCommandSequence *sequence);
//! Set the play count of the sequence
//! @param sequence \ref GDrawCommandSequence for which to set the play count
//! @param play_count play count
void gdraw_command_sequence_set_play_count(GDrawCommandSequence *sequence, uint32_t play_count);
//! Get the total duration of the sequence.
//! @param sequence \ref GDrawCommandSequence from which to get the total duration
//! @return total duration of the sequence in milliseconds
uint32_t gdraw_command_sequence_get_total_duration(GDrawCommandSequence *sequence);
//! Get the number of frames in the sequence
//! @param sequence \ref GDrawCommandSequence from which to get the number of frames
//! @return number of frames in the sequence
uint32_t gdraw_command_sequence_get_num_frames(GDrawCommandSequence *sequence);
//! @} // group DrawCommand
//! @addtogroup PathDrawing Drawing Paths
//! \brief Functions to draw polygons into a graphics context
//!
//! Code example:
//! \code{.c}
//! static GPath *s_my_path_ptr = NULL;
//!
//! static const GPathInfo BOLT_PATH_INFO = {
//! .num_points = 6,
//! .points = (GPoint []) {{21, 0}, {14, 26}, {28, 26}, {7, 60}, {14, 34}, {0, 34}}
//! };
//!
//! // .update_proc of my_layer:
//! void my_layer_update_proc(Layer *my_layer, GContext* ctx) {
//! // Fill the path:
//! graphics_context_set_fill_color(ctx, GColorWhite);
//! gpath_draw_filled(ctx, s_my_path_ptr);
//! // Stroke the path:
//! graphics_context_set_stroke_color(ctx, GColorBlack);
//! gpath_draw_outline(ctx, s_my_path_ptr);
//! }
//!
//! void setup_my_path(void) {
//! s_my_path_ptr = gpath_create(&BOLT_PATH_INFO);
//! // Rotate 15 degrees:
//! gpath_rotate_to(s_my_path_ptr, TRIG_MAX_ANGLE / 360 * 15);
//! // Translate by (5, 5):
//! gpath_move_to(s_my_path_ptr, GPoint(5, 5));
//! }
//!
//! // For brevity, the setup of my_layer is not written out...
//! \endcode
//! @{
//! Data structure describing a naked path
//! @note Note that this data structure only refers to an array of points;
//! the points are not stored inside this data structure itself.
//! In most cases, one cannot use a stack-allocated array of GPoints. Instead
//! one often needs to provide longer-lived (static or "global") storage for the points.
typedef struct GPathInfo {
//! The number of points in the `points` array
uint32_t num_points;
//! Pointer to an array of points.
GPoint *points;
} GPathInfo;
//! Data structure describing a path, plus its rotation and translation.
//! @note See the remark with \ref GPathInfo
typedef struct GPath {
//! The number of points in the `points` array
uint32_t num_points;
//! Pointer to an array of points.
GPoint *points;
//! The rotation that will be used when drawing the path with
//! \ref gpath_draw_filled() or \ref gpath_draw_outline()
int32_t rotation;
//! The translation that will to be used when drawing the path with
//! \ref gpath_draw_filled() or \ref gpath_draw_outline()
GPoint offset;
} GPath;
//! Creates a new GPath on the heap based on a series of points described by a GPathInfo.
//!
//! Values after initialization:
//! * `num_points` and `points` pointer: copied from the GPathInfo.
//! * `rotation`: 0
//! * `offset`: (0, 0)
//! @return A pointer to the GPath. `NULL` if the GPath could not
//! be created
GPath* gpath_create(const GPathInfo *init);
//! Free a dynamically allocated gpath created with \ref gpath_create()
void gpath_destroy(GPath* gpath);
//! Draws the fill of a path into a graphics context, using the current fill color,
//! relative to the drawing area as set up by the layering system.
//! @param ctx The graphics context to draw into
//! @param path The path to fill
//! @see \ref graphics_context_set_fill_color()
void gpath_draw_filled(GContext* ctx, GPath *path);
//! Draws the outline of a path into a graphics context, using the current stroke color and
//! width, relative to the drawing area as set up by the layering system. The first and last points
//! in the path do have a line between them.
//! @param ctx The graphics context to draw into
//! @param path The path to draw
//! @see \ref graphics_context_set_stroke_color()
//! @see \ref gpath_draw_outline_open()
void gpath_draw_outline(GContext* ctx, GPath *path);
//! Sets the absolute rotation of the path.
//! The current rotation will be replaced by the specified angle.
//! @param path The path onto which to set the rotation
//! @param angle The absolute angle of the rotation. The angle is represented in the same way
//! that is used with \ref sin_lookup(). See \ref TRIG_MAX_ANGLE for more information.
//! @note Setting a rotation does not affect the points in the path directly.
//! The rotation is applied on-the-fly during drawing, either using \ref gpath_draw_filled() or
//! \ref gpath_draw_outline().
void gpath_rotate_to(GPath *path, int32_t angle);
//! Sets the absolute offset of the path.
//! The current translation will be replaced by the specified offset.
//! @param path The path onto which to set the translation
//! @param point The point which is used as the vector for the translation.
//! @note Setting a translation does not affect the points in the path directly.
//! The translation is applied on-the-fly during drawing, either using \ref gpath_draw_filled() or
//! \ref gpath_draw_outline().
void gpath_move_to(GPath *path, GPoint point);
//! Draws an open outline of a path into a graphics context, using the current stroke color and
//! width, relative to the drawing area as set up by the layering system. The first and last points
//! in the path do not have a line between them.
//! @param ctx The graphics context to draw into
//! @param path The path to draw
//! @see \ref graphics_context_set_stroke_color()
//! @see \ref gpath_draw_outline()
void gpath_draw_outline_open(GContext* ctx, GPath* path);
//! @} // group PathDrawing
//! @addtogroup Fonts
//! @see \ref TextLayer
//! @see \ref TextDrawing
//! @see \ref text_layer_set_font
//! @see \ref graphics_draw_text
//! @{
//! Pointer to opaque font data structure.
//! @see \ref fonts_load_custom_font()
//! @see \ref text_layer_set_font()
//! @see \ref graphics_draw_text()
typedef void* GFont;
//! Loads a system font corresponding to the specified font key.
//! @param font_key The string key of the font to load. See `pebble_fonts.h` for a list of system fonts.
//! @return An opaque pointer to the loaded font, or, a pointer to the default
//! (fallback) font if the specified font cannot be loaded.
//! @note This may load a font from the flash peripheral into RAM.
GFont fonts_get_system_font(const char *font_key);
//! Loads a custom font.
//! @param handle The resource handle of the font to load. See resource_ids.auto.h
//! for a list of resource IDs, and use \ref resource_get_handle() to obtain the resource handle.
//! @return An opaque pointer to the loaded font, or a pointer to the default
//! (fallback) font if the specified font cannot be loaded.
//! @see Read the <a href="http://developer.getpebble.com/guides/pebble-apps/resources/">App
//! Resources</a> guide on how to embed a font into your app.
//! @note this may load a font from the flash peripheral into RAM.
GFont fonts_load_custom_font(ResHandle handle);
//! Unloads the specified custom font and frees the memory that is occupied by
//! it.
//! @note When an application exits, the system automatically unloads all fonts
//! that have been loaded.
//! @param font The font to unload.
void fonts_unload_custom_font(GFont font);
//! @} // group Fonts
//! @addtogroup TextDrawing Drawing Text
//! \brief Functions to draw text into a graphics context
//!
//! See \ref GraphicsContext for more information about the graphics context.
//!
//! Other drawing functions and related documentation:
//! * \ref Drawing
//! * \ref PathDrawing
//! * \ref GraphicsTypes
//! @{
//! Text overflow mode controls the way text overflows when the string that is drawn does not fit
//! inside the area constraint.
//! @see graphics_draw_text
//! @see text_layer_set_overflow_mode
typedef enum {
//! On overflow, wrap words to a new line below the current one. Once vertical space is consumed, the last line may be clipped.
GTextOverflowModeWordWrap,
//! On overflow, wrap words to a new line below the current one. Once vertical space is consumed, truncate as needed to fit a trailing ellipsis (...). Clipping may occur if the vertical space cannot accomodate the first line of text.
GTextOverflowModeTrailingEllipsis,
//! Acts like \ref GTextOverflowModeTrailingEllipsis, plus trims leading and trailing newlines, while treating all other newlines as spaces.
GTextOverflowModeFill
} GTextOverflowMode;
//! Text aligment controls the way the text is aligned inside the box the text is drawn into.
//! @see graphics_draw_text
//! @see text_layer_set_text_alignment
typedef enum {
//! Aligns the text to the left of the drawing box
GTextAlignmentLeft,
//! Aligns the text centered inside the drawing box
GTextAlignmentCenter,
//! Aligns the text to the right of the drawing box
GTextAlignmentRight,
} GTextAlignment;
struct TextLayout;
typedef struct TextLayout TextLayout;
//! Pointer to opaque text layout cache data structure
typedef TextLayout* GTextLayoutCacheRef;
//! Draw text into the current graphics context, using the context's current text color.
//! The text will be drawn inside a box with the specified dimensions and
//! configuration, with clipping occuring automatically.
//! @param ctx The destination graphics context in which to draw
//! @param text The zero terminated UTF-8 string to draw
//! @param font The font in which the text should be set
//! @param box The bounding box in which to draw the text. The first line of text will be drawn against the top of the box.
//! @param overflow_mode The overflow behavior, in case the text is larger than what fits inside the box.
//! @param alignment The horizontal alignment of the text
//! @param layout Optional layout cache data. Supply `NULL` to ignore the layout caching mechanism.
void graphics_draw_text(GContext* ctx, const char* text, GFont const font, const GRect box, const GTextOverflowMode overflow_mode, const GTextAlignment alignment, const GTextLayoutCacheRef layout);
//! Obtain the maximum size that a text with given font, overflow mode and alignment occupies within a given rectangular constraint.
//! @param text The zero terminated UTF-8 string for which to calculate the size
//! @param font The font in which the text should be set while calculating the size
//! @param box The bounding box in which the text should be constrained
//! @param overflow_mode The overflow behavior, in case the text is larger than what fits inside the box.
//! @param alignment The horizontal alignment of the text
//! @return The maximum size occupied by the text
GSize graphics_text_layout_get_content_size(const char* text, GFont const font, const GRect box, const GTextOverflowMode overflow_mode, const GTextAlignment alignment);
//! @} // group TextDrawing
//! @} // group Graphics
//! @addtogroup UI
//! @{
//! @addtogroup Clicks
//! @{
//! Reference to opaque click recognizer
//! When a \ref ClickHandler callback is called, the recognizer that fired the handler is passed in.
//! @see \ref ClickHandler
//! @see \ref click_number_of_clicks_counted()
//! @see \ref click_recognizer_get_button_id()
//! @see \ref click_recognizer_is_repeating()
typedef void *ClickRecognizerRef;
//! Function signature of the callback that handles a recognized click pattern
//! @param recognizer The click recognizer that detected a "click" pattern
//! @param context Pointer to application specified data (see \ref window_set_click_config_provider_with_context() and
//! \ref window_set_click_context()). This defaults to the window.
//! @see \ref ClickConfigProvider
typedef void (*ClickHandler)(ClickRecognizerRef recognizer, void *context);
//! This callback is called every time the window becomes visible (and when you call \ref window_set_click_config_provider() if
//! the window is already visible).
//!
//! Subscribe to click events using
//! \ref window_single_click_subscribe()
//! \ref window_single_repeating_click_subscribe()
//! \ref window_multi_click_subscribe()
//! \ref window_long_click_subscribe()
//! \ref window_raw_click_subscribe()
//! These subscriptions will get used by the click recognizers of each of the 4 buttons.
//! @param context Pointer to application specific data (see \ref window_set_click_config_provider_with_context()).
typedef void (*ClickConfigProvider)(void *context);
//! Gets the click count.
//! You can use this inside a click handler implementation to get the click count for multi_click
//! and (repeated) click events.
//! @param recognizer The click recognizer for which to get the click count
//! @return The number of consecutive clicks, and for auto-repeating the number of repetitions.
uint8_t click_number_of_clicks_counted(ClickRecognizerRef recognizer);
//! Gets the button identifier.
//! You can use this inside a click handler implementation to get the button id for the click event.
//! @param recognizer The click recognizer for which to get the button id that caused the click event
//! @return the ButtonId of the click recognizer
ButtonId click_recognizer_get_button_id(ClickRecognizerRef recognizer);
//! Is this a repeating click.
//! You can use this inside a click handler implementation to find out whether this is a repeating click or not.
//! @param recognizer The click recognizer for which to find out whether this is a repeating click.
//! @return true if this is a repeating click.
bool click_recognizer_is_repeating(ClickRecognizerRef recognizer);
//! @} // group Clicks
//! @addtogroup Layer Layers
//! \brief User interface layers for displaying graphic components
//!
//! Layers are objects that can be displayed on a Pebble watchapp window, enabling users to see
//! visual objects, like text or images. Each layer stores the information about its state
//! necessary to draw or redraw the object that it represents and uses graphics routines along with
//! this state to draw itself when asked. Layers can be used to display various graphics.
//!
//! Layers are the basic building blocks for your application UI. Layers can be nested inside each other.
//! Every window has a root layer which is always the topmost layer.
//! You provide a function that is called to draw the content of the layer when needed; or
//! you can use standard layers that are provided by the system, such as text layer, image layer,
//! menu layer, action bar layer, and so on.
//!
//! The Pebble layer hierarchy is the list of things that need to be drawn to the screen.
//! Multiple layers can be arranged into a hierarchy. This enables ordering (front to back),
//! layout and hierarchy. Through relative positioning, visual objects that are grouped together by
//! adding them into the same layer can be moved all at once. This means that the child layers
//! will move accordingly. If a parent layer has clipping enabled, all the children will be clipped
//! to the frame of the parent.
//!
//! Pebble OS provides convenience layers with built-in logic for displaying different graphic
//! components, like text and bitmap layers.
//!
//! Refer to the \htmlinclude UiFramework.html (chapter "Layers") for a conceptual overview
//! of Layers and relevant code examples.
//!
//! The Modules listed here contain what can be thought of conceptually as subclasses of Layer. The
//! listed types can be safely type-casted to `Layer` (or `Layer *` in case of a pointer).
//! The `layer_...` functions can then be used with the data structures of these subclasses.
//! <br/>For example, the following is legal:
//! \code{.c}
//! TextLayer *text_layer;
//! ...
//! layer_set_hidden((Layer *)text_layer, true);
//! \endcode
//! @{
struct Layer;
typedef struct Layer Layer;
//! Function signature for a Layer's render callback (the name of the type
//! is derived from the words 'update procedure').
//! The system will call the `.update_proc` callback whenever the Layer needs
//! to be rendered.
//! @param layer The layer that needs to be rendered
//! @param ctx The destination graphics context to draw into
//! @see \ref Graphics
//! @see \ref layer_set_update_proc()
typedef void (*LayerUpdateProc)(struct Layer *layer, GContext* ctx);
//! Creates a layer on the heap and sets its frame and bounds.
//! Default values:
//! * `bounds` : origin (0, 0) and a size equal to the frame that is passed in.
//! * `clips` : `true`
//! * `hidden` : `false`
//! * `update_proc` : `NULL` (draws nothing)
//! @param frame The frame at which the layer should be initialized.
//! @see \ref layer_set_frame()
//! @see \ref layer_set_bounds()
//! @return A pointer to the layer. `NULL` if the layer could not
//! be created
Layer* layer_create(GRect frame);
//! Creates a layer on the heap with extra space for callback data, and set its frame andbounds.
//! Default values:
//! * `bounds` : origin (0, 0) and a size equal to the frame that is passed in.
//! * `clips` : `true`
//! * `hidden` : `false`
//! * `update_proc` : `NULL` (draws nothing)
//! @param frame The frame at which the layer should be initialized.
//! @param data_size The size (in bytes) of memory to allocate for callback data.
//! @see \ref layer_create()
//! @see \ref layer_set_frame()
//! @see \ref layer_set_bounds()
//! @return A pointer to the layer. `NULL` if the layer could not be created
Layer* layer_create_with_data(GRect frame, size_t data_size);
//! Destroys a layer previously created by layer_create
void layer_destroy(Layer* layer);
//! Marks the complete layer as "dirty", awaiting to be asked by the system to redraw itself.
//! Typically, this function is called whenever state has changed that affects what the layer
//! is displaying.
//! * The layer's `.update_proc` will not be called before this function returns,
//! but will be called asynchronously, shortly.
//! * Internally, a call to this function will schedule a re-render of the window that the
//! layer belongs to. In effect, all layers in that window's layer hierarchy will be asked to redraw.
//! * If an earlier re-render request is still pending, this function is a no-op.
//! @param layer The layer to mark dirty
void layer_mark_dirty(Layer *layer);
//! Sets the layer's render function.
//! The system will call the `update_proc` automatically when the layer needs to redraw itself, see
//! also \ref layer_mark_dirty().
//! @param layer Pointer to the layer structure.
//! @param update_proc Pointer to the function that will be called when the layer needs to be rendered.
//! Typically, one performs a series of drawing commands in the implementation of the `update_proc`,
//! see \ref Drawing, \ref PathDrawing and \ref TextDrawing.
void layer_set_update_proc(Layer *layer, LayerUpdateProc update_proc);
//! Sets the frame of the layer, which is it's bounding box relative to the coordinate
//! system of its parent layer.
//! The size of the layer's bounds will be extended automatically, so that the bounds
//! cover the new frame.
//! @param layer The layer for which to set the frame
//! @param frame The new frame
//! @see \ref layer_set_bounds()
void layer_set_frame(Layer *layer, GRect frame);
//! Gets the frame of the layer, which is it's bounding box relative to the coordinate
//! system of its parent layer.
//! If the frame has changed, \ref layer_mark_dirty() will be called automatically.
//! @param layer The layer for which to get the frame
//! @return The frame of the layer
//! @see layer_set_frame
GRect layer_get_frame(const Layer *layer);
//! Sets the bounds of the layer, which is it's bounding box relative to its frame.
//! If the bounds has changed, \ref layer_mark_dirty() will be called automatically.
//! @param layer The layer for which to set the bounds
//! @param bounds The new bounds
//! @see \ref layer_set_frame()
void layer_set_bounds(Layer *layer, GRect bounds);
//! Gets the bounds of the layer
//! @param layer The layer for which to get the bounds
//! @return The bounds of the layer
//! @see layer_set_bounds
GRect layer_get_bounds(const Layer *layer);
//! Gets the window that the layer is currently attached to.
//! @param layer The layer for which to get the window
//! @return The window that this layer is currently attached to, or `NULL` if it has
//! not been added to a window's layer hierarchy.
//! @see \ref window_get_root_layer()
//! @see \ref layer_add_child()
struct Window *layer_get_window(const Layer *layer);
//! Removes the layer from its current parent layer
//! If removed successfully, the child's parent layer will be marked dirty
//! automatically.
//! @param child The layer to remove
void layer_remove_from_parent(Layer *child);
//! Removes child layers from given layer
//! If removed successfully, the child's parent layer will be marked dirty
//! automatically.
//! @param parent The layer from which to remove all child layers
void layer_remove_child_layers(Layer *parent);
//! Adds the child layer to a given parent layer, making it appear
//! in front of its parent and in front of any existing child layers
//! of the parent.
//! If the child layer was already part of a layer hierarchy, it will
//! be removed from its old parent first.
//! If added successfully, the parent (and children) will be marked dirty
//! automatically.
//! @param parent The layer to which to add the child layer
//! @param child The layer to add to the parent layer
void layer_add_child(Layer *parent, Layer *child);
//! Inserts the layer as a sibling behind another layer.
//! The below_layer has to be a child of a parent layer,
//! otherwise this function will be a noop.
//! If inserted successfully, the parent (and children) will be marked dirty
//! automatically.
//! @param layer_to_insert The layer to insert into the hierarchy
//! @param below_sibling_layer The layer that will be used as the sibling layer
//! above which the insertion will take place
void layer_insert_below_sibling(Layer *layer_to_insert, Layer *below_sibling_layer);
//! Inserts the layer as a sibling in front of another layer.
//! The above_layer has to be a child of a parent layer,
//! otherwise this function will be a noop.
//! If inserted successfully, the parent (and children) will be marked dirty
//! automatically.
//! @param layer_to_insert The layer to insert into the hierarchy
//! @param above_sibling_layer The layer that will be used as the sibling layer
//! below which the insertion will take place
void layer_insert_above_sibling(Layer *layer_to_insert, Layer *above_sibling_layer);
//! Sets the visibility of the layer.
//! If the visibility has changed, \ref layer_mark_dirty() will be called automatically
//! on the parent layer.
//! @param layer The layer for which to set the visibility
//! @param hidden Supply `true` to make the layer hidden, or `false` to make it
//! non-hidden.
void layer_set_hidden(Layer *layer, bool hidden);
//! Gets the visibility of the layer.
//! @param layer The layer for which to get the visibility
//! @return True if the layer is hidden, false if it is not hidden.
bool layer_get_hidden(const Layer *layer);
//! Sets whether clipping is enabled for the layer. If enabled, whatever the layer _and
//! its children_ will draw using their `.update_proc` callbacks, will be clipped by the
//! this layer's frame.
//! If the clipping has changed, \ref layer_mark_dirty() will be called automatically.
//! @param layer The layer for which to set the clipping property
//! @param clips Supply `true` to make the layer clip to its frame, or `false`
//! to make it non-clipping.
void layer_set_clips(Layer *layer, bool clips);
//! Gets whether clipping is enabled for the layer. If enabled, whatever the layer _and
//! its children_ will draw using their `.update_proc` callbacks, will be clipped by the
//! this layer's frame.
//! @param layer The layer for which to get the clipping property
//! @return True if clipping is enabled for the layer, false if clipping is not enabled for
//! the layer.
bool layer_get_clips(const Layer *layer);
//! Gets the data from a layer that has been created with an extra data region.
//! @param layer The layer to get the data region from.
//! @return A void pointer to the data region.
void* layer_get_data(const Layer *layer);
//! @} // group Layer
//! @addtogroup Window
//! \brief The basic building block of the user interface
//!
//! Windows are the top-level elements in the UI hierarchy and the basic building blocks for a Pebble
//! UI. A single window is always displayed at a time on Pebble, with the exception of when animating
//! from one window to the other, which, in that case, is managed by the window stack. You can stack
//! windows on top of each other, but only the topmost window will be visible.
//!
//! Users wearing a Pebble typically interact with the content and media displayed in a window, clicking
//! and pressing buttons on the watch, depending on what they see and wish to respond to in a window.
//!
//! Windows serve to display a hierarchy of layers on the screen and handle user input. When a window is
//! visible, its root Layer (and all its child layers) are drawn onto the screen automatically.
//!
//! You need a window, which always fills the entire screen, to display images, text, and graphics in
//! your Pebble app. A layer by itself doesn’t display on Pebble; it must be in the current window’s
//! layer hierarchy to be visible.
//!
//! The Window Stack serves as the global manager of what window is presented and makes sure that input
//! events are forwarded to the topmost window.
//!
//! Refer to the \htmlinclude UiFramework.html (chapter "Window") for a conceptual
//! overview of Window, the Window Stack and relevant code examples.
//! @{
struct Window;
typedef struct Window Window;
//! Function signature for a handler that deals with transition events of a window.
//! @see WindowHandlers
//! @see \ref window_set_window_handlers()
typedef void (*WindowHandler)(struct Window *window);
//! WindowHandlers
//! These handlers are called by the \ref WindowStack as windows get pushed on / popped.
//! All these handlers use \ref WindowHandler as their function signature.
//! @see \ref window_set_window_handlers()
//! @see \ref WindowStack
typedef struct WindowHandlers {
//! Called when the window is pushed to the screen when it's not loaded.
//! This is a good moment to do the layout of the window.
WindowHandler load;
//! Called when the window comes on the screen (again). E.g. when
//! second-top-most window gets revealed (again) after popping the top-most
//! window, but also when the window is pushed for the first time. This is a
//! good moment to start timers related to the window, or reset the UI, etc.
WindowHandler appear;
//! Called when the window leaves the screen, e.g. when another window
//! is pushed, or this window is popped. Good moment to stop timers related
//! to the window.
WindowHandler disappear;
//! Called when the window is deinited, but could be used in the future to
//! free resources bound to windows that are not on screen.
WindowHandler unload;
} WindowHandlers;
//! Creates a new Window on the heap and initalizes it with the default values.
//!
//! * Background color : `GColorWhite`
//! * Root layer's `update_proc` : function that fills the window's background using `background_color`.
//! * Full screen : no
//! * `click_config_provider` : `NULL`
//! * `window_handlers` : all `NULL`
//! * `status_bar_icon` : `NULL` (none)
//! @return A pointer to the window. `NULL` if the window could not
//! be created
Window* window_create(void);
//! Destroys a Window previously created by window_create.
void window_destroy(Window* window);
//! Sets the click configuration provider callback function on the window.
//! This will automatically setup the input handlers of the window as well to use
//! the click recognizer subsystem.
//! @param window The window for which to set the click config provider
//! @param click_config_provider The callback that will be called to configure the click recognizers with the window
//! @see Clicks
//! @see ClickConfigProvider
void window_set_click_config_provider(Window *window, ClickConfigProvider click_config_provider);
//! Same as window_set_click_config_provider(), but will assign a custom context pointer
//! (instead of the window pointer) that will be passed into the ClickHandler click event handlers.
//! @param window The window for which to set the click config provider
//! @param click_config_provider The callback that will be called to configure the click recognizers with the window
//! @param context Pointer to application specific data that will be passed to the click configuration provider callback (defaults to the window).
//! @see Clicks
//! @see window_set_click_config_provider
void window_set_click_config_provider_with_context(Window *window, ClickConfigProvider click_config_provider, void *context);
//! Gets the current click configuration provider of the window.
//! @param window The window for which to get the click config provider
ClickConfigProvider window_get_click_config_provider(const Window *window);
//! Gets the current click configuration provider context of the window.
//! @param window The window for which to get the click config provider context
void *window_get_click_config_context(Window *window);
//! Sets the window handlers of the window.
//! These handlers get called e.g. when the user enters or leaves the window.
//! @param window The window for which to set the window handlers
//! @param handlers The handlers for the specified window
//! @see \ref WindowHandlers
void window_set_window_handlers(Window *window, WindowHandlers handlers);
//! Gets the root Layer of the window.
//! The root layer is the layer at the bottom of the layer hierarchy for this window.
//! It is the window's "canvas" if you will. By default, the root layer only draws
//! a solid fill with the window's background color.
//! @param window The window for which to get the root layer
//! @return The window's root layer
struct Layer* window_get_root_layer(const Window *window);
//! Sets the background color of the window, which is drawn automatically by the
//! root layer of the window.
//! @param window The window for which to set the background color
//! @param background_color The new background color
//! @see \ref window_get_root_layer()
void window_set_background_color(Window *window, GColor background_color);
//! Gets whether the window has been loaded.
//! If a window is loaded, its `.load` handler has been called (and the `.unload` handler
//! has not been called since).
//! @return true if the window is currently loaded or false if not.
//! @param window The window to query its loaded status
//! @see \ref WindowHandlers
bool window_is_loaded(Window *window);
//! Sets a pointer to developer-supplied data that the window uses, to
//! provide a means to access the data at later times in one of the window event handlers.
//! @see window_get_user_data
//! @param window The window for which to set the user data
//! @param data A pointer to user data.
void window_set_user_data(Window *window, void *data);
//! Gets the pointer to developer-supplied data that was previously
//! set using window_set_user_data().
//! @see window_set_user_data
//! @param window The window for which to get the user data
void* window_get_user_data(const Window *window);
//! Subscribe to single click events.
//! @note Must be called from the \ref ClickConfigProvider.
//! @note \ref window_single_click_subscribe() and \ref window_single_repeating_click_subscribe() conflict, and cannot both be used on the same button.
//! @note When there is a multi_click and/or long_click setup, there will be a delay before the single click
//! @param button_id The button events to subscribe to.
//! @param handler The \ref ClickHandler to fire on this event.
//! handler will get fired. On the other hand, when there is no multi_click nor long_click setup, the single click handler will fire directly on button down.
//! @see ButtonId
//! @see Clicks
//! @see window_single_repeating_click_subscribe
void window_single_click_subscribe(ButtonId button_id, ClickHandler handler);
//! Subscribe to single click event, with a repeat interval. A single click is detected every time "repeat_interval_ms" has been reached.
//! @note Must be called from the \ref ClickConfigProvider.
//! @note \ref window_single_click_subscribe() and \ref window_single_repeating_click_subscribe() conflict, and cannot both be used on the same button.
//! @note The back button cannot be overridden with a repeating click.
//! @param button_id The button events to subscribe to.
//! @param repeat_interval_ms When holding down, how many milliseconds before the handler is fired again.
//! A value of 0ms means "no repeat timer". The minimum is 30ms, and values below will be disregarded.
//! If there is a long-click handler subscribed on this button, `repeat_interval_ms` will not be used.
//! @param handler The \ref ClickHandler to fire on this event.
//! @see window_single_click_subscribe
void window_single_repeating_click_subscribe(ButtonId button_id, uint16_t repeat_interval_ms, ClickHandler handler);
//! Subscribe to multi click events.
//! @note Must be called from the \ref ClickConfigProvider.
//! @param button_id The button events to subscribe to.
//! @param min_clicks Minimum number of clicks before handler is fired. Defaults to 2.
//! @param max_clicks Maximum number of clicks after which the click counter is reset. A value of 0 means use "min" also as "max".
//! @param timeout The delay after which a sequence of clicks is considered finished, and the click counter is reset. A value of 0 means to use the system default 300ms.
//! @param last_click_only Defaults to false. When true, only the handler for the last multi-click is called.
//! @param handler The \ref ClickHandler to fire on this event. Fired for multi-clicks, as "filtered" by the `last_click_only`, `min`, and `max` parameters.
void window_multi_click_subscribe(ButtonId button_id, uint8_t min_clicks, uint8_t max_clicks, uint16_t timeout, bool last_click_only, ClickHandler handler);
//! Subscribe to long click events.
//! @note Must be called from the \ref ClickConfigProvider.
//! @note The back button cannot be overridden with a long click.
//! @param button_id The button events to subscribe to.
//! @param delay_ms Milliseconds after which "handler" is fired. A value of 0 means to use the system default 500ms.
//! @param down_handler The \ref ClickHandler to fire as soon as the button has been held for `delay_ms`. This may be NULL to have no down handler.
//! @param up_handler The \ref ClickHandler to fire on the release of a long click. This may be NULL to have no up handler.
void window_long_click_subscribe(ButtonId button_id, uint16_t delay_ms, ClickHandler down_handler, ClickHandler up_handler);
//! Subscribe to raw click events.
//! @note Must be called from within the \ref ClickConfigProvider.
//! @note The back button cannot be overridden with a raw click.
//! @param button_id The button events to subscribe to.
//! @param down_handler The \ref ClickHandler to fire as soon as the button has been pressed. This may be NULL to have no down handler.
//! @param up_handler The \ref ClickHandler to fire on the release of the button. This may be NULL to have no up handler.
//! @param context If this context is not NULL, it will override the general context.
void window_raw_click_subscribe(ButtonId button_id, ClickHandler down_handler, ClickHandler up_handler, void *context);
//! Set the context that will be passed to handlers for the given button's events. By default the context passed to handlers
//! is equal to the \ref ClickConfigProvider context (defaults to the window).
//! @note Must be called from within the \ref ClickConfigProvider.
//! @param button_id The button to set the context for.
//! @param context Set the context that will be passed to handlers for the given button's events.
void window_set_click_context(ButtonId button_id, void *context);
//! @} // group Window
//! @addtogroup WindowStack Window Stack
//! \brief The multiple window manager
//!
//! In Pebble OS, the window stack serves as the global manager of what window is presented,
//! ensuring that input events are forwarded to the topmost window.
//! The navigation model of Pebble centers on the concept of a vertical “stack” of windows, similar
//! to mobile app interactions.
//!
//! In working with the Window Stack API, the basic operations include push and pop. When an app wants to
//! display a new window, it pushes a new window onto the stack. This appears like a window sliding in
//! from the right. As an app is closed, the window is popped off the stack and disappears.
//!
//! For more complicated operations, involving multiple windows, you can determine which windows reside
//! on the stack, using window_stack_contains_window() and remove any specific window, using window_stack_remove().
//!
//! Refer to the \htmlinclude UiFramework.html (chapter "Window Stack") for a conceptual overview
//! of the window stack and relevant code examples.
//!
//! Also see the \ref WindowHandlers of a \ref Window for the callbacks that can be added to a window
//! in order to act upon window stack transitions.
//!
//! @{
//! Pushes the given window on the window navigation stack,
//! on top of the current topmost window of the app.
//! @param window The window to push on top
//! @param animated Pass in `true` to animate the push using a sliding animation,
//! or `false` to skip the animation.
void window_stack_push(Window *window, bool animated);
//! Pops the topmost window on the navigation stack
//! @param animated See \ref window_stack_remove()
//! @return The window that is popped, or NULL if there are no windows to pop.
Window* window_stack_pop(bool animated);
//! Pops all windows.
//! See \ref window_stack_remove() for a description of the `animated` parameter and notes.
void window_stack_pop_all(const bool animated);
//! Removes a given window from the window stack
//! that belongs to the app task.
//! @note If there are no windows for the app left on the stack, the app
//! will be killed by the system, shortly. To avoid this, make sure
//! to push another window shortly after or before removing the last window.
//! @param window The window to remove. If the window is NULL or if it
//! is not on the stack, this function is a no-op.
//! @param animated Pass in `true` to animate the removal of the window using
//! a side-to-side sliding animation to reveal the next window.
//! This is only used in case the window happens to be on top of the window
//! stack (thus visible).
//! @return True if window was successfully removed, false otherwise.
bool window_stack_remove(Window *window, bool animated);
//! Gets the topmost window on the stack that belongs to the app.
//! @return The topmost window on the stack that belongs to the app or
//! NULL if no app window could be found.
Window* window_stack_get_top_window(void);
//! Checks if the window is on the window stack
//! @param window The window to look for on the window stack
//! @return true if the window is currently on the window stack.
bool window_stack_contains_window(Window *window);
//! @} // group WindowStack
//! @addtogroup Animation
//! \brief Abstract framework to create arbitrary animations
//!
//! The Animation framework provides your Pebble app with an base layer to create arbitrary
//! animations. The simplest way to work with animations is to use the layer frame
//! \ref PropertyAnimation, which enables you to move a Layer around on the screen.
//! Using animation_set_implementation(), you can implement a custom animation.
//!
//! Refer to the \htmlinclude UiFramework.html (chapter "Animation") for a conceptual overview
//! of the animation framework and on how to write custom animations.
//! @{
struct Animation;
typedef struct Animation Animation;
//! The type used to represent how far an animation has progressed. This is passed to the
//! animation's update handler
typedef int32_t AnimationProgress;
//! Values that are used to indicate the different animation curves,
//! which determine the speed at which the animated value(s) change(s).
typedef enum {
//! Linear curve: the velocity is constant.
AnimationCurveLinear = 0,
//! Bicubic ease-in: accelerate from zero velocity
AnimationCurveEaseIn = 1,
//! Bicubic ease-in: decelerate to zero velocity
AnimationCurveEaseOut = 2,
//! Bicubic ease-in-out: accelerate from zero velocity, decelerate to zero velocity
AnimationCurveEaseInOut = 3,
AnimationCurveDefault = AnimationCurveEaseInOut,
//! Custom (user-provided) animation curve
AnimationCurveCustomFunction = 4,
//! User-provided interpolation function
AnimationCurveCustomInterpolationFunction = 5,
// Two more Reserved for forward-compatibility use.
AnimationCurve_Reserved1 = 6,
AnimationCurve_Reserved2 = 7,
} AnimationCurve;
//! Creates a new Animation on the heap and initalizes it with the default values.
//!
//! * Duration: 250ms,
//! * Curve: \ref AnimationCurveEaseInOut (ease-in-out),
//! * Delay: 0ms,
//! * Handlers: `{NULL, NULL}` (none),
//! * Context: `NULL` (none),
//! * Implementation: `NULL` (no implementation),
//! * Scheduled: no
//! @return A pointer to the animation. `NULL` if the animation could not
//! be created
Animation * animation_create(void);
//! Destroys an Animation previously created by animation_create.
//! @return true if successful, false on failure
bool animation_destroy(Animation *animation);
//! Constant to indicate "infinite" duration.
//! This can be used with \ref animation_set_duration() to indicate that the animation
//! should run indefinitely. This is useful when implementing for example a frame-by-frame
//! simulation that does not have a clear ending (e.g. a game).
//! @note Note that `distance_normalized` parameter that is passed
//! into the `.update` implementation is meaningless in when an infinite duration is used.
//! @note This can be returned by animation_get_duration (if the play count is infinite)
#define ANIMATION_DURATION_INFINITE UINT32_MAX
//! Constant to indicate infinite play count.
//! Can be passed to \ref animation_set_play_count() to repeat indefinitely.
//! @note This can be returned by \ref animation_get_play_count().
#define ANIMATION_PLAY_COUNT_INFINITE UINT32_MAX
//! The normalized distance at the start of the animation.
#define ANIMATION_NORMALIZED_MIN 0
//! The normalized distance at the end of the animation.
#define ANIMATION_NORMALIZED_MAX 65535
Animation *animation_clone(Animation *from);
//! Create a new sequence animation from a list of 2 or more other animations. The returned
//! animation owns the animations that were provided as arguments and no further write operations
//! on those handles are allowed. The variable length argument list must be terminated with a NULL
//! ptr
//! @note the maximum number of animations that can be supplied to this method is 20
//! @param animation_a the first required component animation
//! @param animation_b the second required component animation
//! @param animation_c either the third component, or NULL if only adding 2 components
//! @return The newly created sequence animation
Animation *animation_sequence_create(Animation *animation_a, Animation *animation_b,
Animation *animation_c, ...);
//! An alternate form of animation_sequence_create() that accepts an array of other animations.
//! @note the maximum number of elements allowed in animation_array is 256
//! @param animation_array an array of component animations to include
//! @param array_len the number of elements in the animation_array
//! @return The newly created sequence animation
Animation *animation_sequence_create_from_array(Animation **animation_array, uint32_t array_len);
//! Create a new spawn animation from a list of 2 or more other animations. The returned
//! animation owns the animations that were provided as arguments and no further write operations
//! on those handles are allowed. The variable length argument list must be terminated with a NULL
//! ptr
//! @note the maximum number of animations that can be supplied to this method is 20
//! @param animation_a the first required component animation
//! @param animation_b the second required component animation
//! @param animation_c either the third component, or NULL if only adding 2 components
//! @return The newly created spawn animation or NULL on failure
Animation *animation_spawn_create(Animation *animation_a, Animation *animation_b,
Animation *animation_c, ...);
//! An alternate form of animation_spawn_create() that accepts an array of other animations.
//! @note the maximum number of elements allowed in animation_array is 256
//! @param animation_array an array of component animations to include
//! @param array_len the number of elements in the animation_array
//! @return The newly created spawn animation or NULL on failure
Animation *animation_spawn_create_from_array(Animation **animation_array, uint32_t array_len);
//! Seek to a specific location in the animation. Only forward seeking is allowed. Returns true
//! if successful, false if the passed in seek location is invalid.
//! @param animation the animation for which to set the elapsed.
//! @param elapsed_ms the new elapsed time in milliseconds
//! @return true if successful, false if the requested elapsed is invalid.
bool animation_set_elapsed(Animation *animation, uint32_t elapsed_ms);
//! Get the current location in the animation.
//! @note The animation must be scheduled to get the elapsed time. If it is not schedule,
//! this method will return false.
//! @param animation The animation for which to fetch the elapsed.
//! @param[out] elapsed_ms pointer to variable that will contain the elapsed time in milliseconds
//! @return true if successful, false on failure
bool animation_get_elapsed(Animation *animation, int32_t *elapsed_ms);
//! Set an animation to run in reverse (or forward)
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation the animation to operate on
//! @param reverse set to true to run in reverse, false to run forward
//! @return true if successful, false on failure
bool animation_set_reverse(Animation *animation, bool reverse);
//! Get the reverse setting of an animation
//! @param animation The animation for which to get the setting
//! @return the reverse setting
bool animation_get_reverse(Animation *animation);
//! Set an animation to play N times. The default is 1.
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation the animation to set the play count of
//! @param play_count number of times to play this animation. Set to ANIMATION_PLAY_COUNT_INFINITE
//! to make an animation repeat indefinitely.
//! @return true if successful, false on failure
bool animation_set_play_count(Animation *animation, uint32_t play_count);
//! Get the play count of an animation
//! @param animation The animation for which to get the setting
//! @return the play count
uint32_t animation_get_play_count(Animation *animation);
//! Sets the time in milliseconds that an animation takes from start to finish.
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation The animation for which to set the duration.
//! @param duration_ms The duration in milliseconds of the animation. This excludes
//! any optional delay as set using \ref animation_set_delay().
//! @return true if successful, false on failure
bool animation_set_duration(Animation *animation, uint32_t duration_ms);
//! Get the static duration of an animation from start to end (ignoring how much has already
//! played, if any).
//! @param animation The animation for which to get the duration
//! @param include_delay if true, include the delay time
//! @param include_play_count if true, incorporate the play_count
//! @return the duration, in milliseconds. This includes any optional delay a set using
//! \ref animation_set_delay.
uint32_t animation_get_duration(Animation *animation, bool include_delay, bool include_play_count);
//! Sets an optional delay for the animation.
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation The animation for which to set the delay.
//! @param delay_ms The delay in milliseconds that the animation system should
//! wait from the moment the animation is scheduled to starting the animation.
//! @return true if successful, false on failure
bool animation_set_delay(Animation *animation, uint32_t delay_ms);
//! Get the delay of an animation in milliseconds
//! @param animation The animation for which to get the setting
//! @return the delay in milliseconds
uint32_t animation_get_delay(Animation *animation);
//! Sets the animation curve for the animation.
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation The animation for which to set the curve.
//! @param curve The type of curve.
//! @see AnimationCurve
//! @return true if successful, false on failure
bool animation_set_curve(Animation *animation, AnimationCurve curve);
//! Gets the animation curve for the animation.
//! @param animation The animation for which to get the curve.
//! @return The type of curve.
AnimationCurve animation_get_curve(Animation *animation);
//! The function pointer type of a custom animation curve.
//! @param linear_distance The linear normalized animation distance to be curved.
//! @see animation_set_custom_curve
typedef AnimationProgress (*AnimationCurveFunction)(AnimationProgress linear_distance);
//! Sets a custom animation curve function.
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation The animation for which to set the curve.
//! @param curve_function The custom animation curve function.
//! @see AnimationCurveFunction
//! @return true if successful, false on failure
bool animation_set_custom_curve(Animation *animation, AnimationCurveFunction curve_function);
//! Gets the custom animation curve function for the animation.
//! @param animation The animation for which to get the curve.
//! @return The custom animation curve function for the given animation. NULL if not set.
AnimationCurveFunction animation_get_custom_curve(Animation *animation);
//! The function pointer type of the handler that will be called when an animation is started,
//! just before updating the first frame of the animation.
//! @param animation The animation that was started.
//! @param context The pointer to custom, application specific data, as set using
//! \ref animation_set_handlers()
//! @note This is called after any optional delay as set by \ref animation_set_delay() has expired.
//! @see animation_set_handlers
typedef void (*AnimationStartedHandler)(Animation *animation, void *context);
//! The function pointer type of the handler that will be called when the animation is stopped.
//! @param animation The animation that was stopped.
//! @param finished True if the animation was stopped because it was finished normally,
//! or False if the animation was stopped prematurely, because it was unscheduled before finishing.
//! @param context The pointer to custom, application specific data, as set using
//! \ref animation_set_handlers()
//! @see animation_set_handlers
//! \note
//! This animation (i.e.: the `animation` parameter) may be destroyed here.
//! It is not recommended to unschedule or destroy a **different** Animation within this
//! Animation's `stopped` handler.
typedef void (*AnimationStoppedHandler)(Animation *animation, bool finished, void *context);
//! The handlers that will get called when an animation starts and stops.
//! See documentation with the function pointer types for more information.
//! @see animation_set_handlers
typedef struct AnimationHandlers {
//! The handler that will be called when an animation is started.
AnimationStartedHandler started;
//! The handler that will be called when an animation is stopped.
AnimationStoppedHandler stopped;
} AnimationHandlers;
//! Sets the callbacks for the animation.
//! Often an application needs to run code at the start or at the end of an animation.
//! Using this function is possible to register callback functions with an animation,
//! that will get called at the start and end of the animation.
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation The animation for which to set up the callbacks.
//! @param callbacks The callbacks.
//! @param context A pointer to application specific data, that will be passed as an argument by
//! the animation subsystem when a callback is called.
//! @return true if successful, false on failure
bool animation_set_handlers(Animation *animation, AnimationHandlers callbacks, void *context);
//! Gets the application-specific callback context of the animation.
//! This `void` pointer is passed as an argument when the animation system calls AnimationHandlers
//! callbacks. The context pointer can be set to point to any application specific data using
//! \ref animation_set_handlers().
//! @param animation The animation.
//! @see animation_set_handlers
void *animation_get_context(Animation *animation);
//! Schedules the animation. Call this once after configuring an animation to get it to
//! start running.
//!
//! If the animation's implementation has a `.setup` callback it will get called before
//! this function returns.
//!
//! @note If the animation was already scheduled,
//! it will first unschedule it and then re-schedule it again.
//! Note that in that case, the animation's `.stopped` handler, the implementation's
//! `.teardown` and `.setup` will get called, due to the unscheduling and scheduling.
//! @param animation The animation to schedule.
//! @see \ref animation_unschedule()
//! @return true if successful, false on failure
bool animation_schedule(Animation *animation);
//! Unschedules the animation, which in effect stops the animation.
//! @param animation The animation to unschedule.
//! @note If the animation was not yet finished, unscheduling it will
//! cause its `.stopped` handler to get called, with the "finished" argument set to false.
//! @note If the animation is not scheduled or NULL, calling this routine is
//! effectively a no-op
//! @see \ref animation_schedule()
//! @return true if successful, false on failure
bool animation_unschedule(Animation *animation);
//! Unschedules all animations of the application.
//! @see animation_unschedule
void animation_unschedule_all(void);
//! @return True if the animation was scheduled, or false if it was not.
//! @note An animation will be scheduled when it is running and not finished yet.
//! An animation that has finished is automatically unscheduled.
//! For convenience, passing in a NULL animation argument will simply return false
//! @param animation The animation for which to get its scheduled state.
//! @see animation_schedule
//! @see animation_unschedule
bool animation_is_scheduled(Animation *animation);
//! Pointer to function that (optionally) prepares the animation for running.
//! This callback is called when the animation is added to the scheduler.
//! @param animation The animation that needs to be set up.
//! @see animation_schedule
//! @see AnimationTeardownImplementation
typedef void (*AnimationSetupImplementation)(Animation *animation);
//! Pointer to function that updates the animation according to the given normalized progress.
//! This callback will be called repeatedly by the animation scheduler whenever the animation needs
//! to be updated.
//! @param animation The animation that needs to update; gets passed in by the animation framework.
//! @param progress The current normalized progress; gets passed in by the animation
//! framework for each animation frame.
//! The value \ref ANIMATION_NORMALIZED_MIN represents the start and \ref ANIMATION_NORMALIZED_MAX
//! represents the end. Values outside this range (generated by a custom curve function) can be used
//! to implement features like a bounce back effect, where the progress exceeds the desired final
//! value before returning to complete the animation.
//! When using a system provided curve function, each frame during the animation will have a
//! progress value between \ref ANIMATION_NORMALIZED_MIN and \ref ANIMATION_NORMALIZED_MAX based on
//! the animation duration and the \ref AnimationCurve.
//! For example, say an animation was scheduled at t = 1.0s, has a delay of 1.0s, a duration of 2.0s
//! and a curve of AnimationCurveLinear. Then the .update callback will get called on t = 2.0s with
//! distance_normalized = \ref ANIMATION_NORMALIZED_MIN. For each frame thereafter until t = 4.0s,
//! the update callback will get called where distance_normalized is (\ref ANIMATION_NORMALIZED_MIN
//! + (((\ref ANIMATION_NORMALIZED_MAX - \ref ANIMATION_NORMALIZED_MIN) * t) / duration)).
//! Other system animation curve functions will result in a non-linear relation between
//! distance_normalized and time.
typedef void (*AnimationUpdateImplementation)(Animation *animation,
const AnimationProgress progress);
//! Pointer to function that (optionally) cleans up the animation.
//! This callback is called when the animation is removed from the scheduler.
//! In case the `.setup` implementation
//! allocated any memory, this is a good place to release that memory again.
//! @param animation The animation that needs to be teared down.
//! @see animation_unschedule
//! @see AnimationSetupImplementation
typedef void (*AnimationTeardownImplementation)(Animation *animation);
//! The 3 callbacks that implement a custom animation.
//! Only the `.update` callback is mandatory, `.setup` and `.teardown` are optional.
//! See the documentation with the function pointer typedefs for more information.
//!
//! @note The `.setup` callback is called immediately after scheduling the animation,
//! regardless if there is a delay set for that animation using \ref animation_set_delay().
//!
//! The diagram below illustrates the order in which callbacks can be expected to get called
//! over the life cycle of an animation. It also illustrates where the implementation of
//! different animation callbacks are intended to be “living”.
//! ![](animations.png)
//!
//! @see AnimationSetupImplementation
//! @see AnimationUpdateImplementation
//! @see AnimationTeardownImplementation
typedef struct AnimationImplementation {
//! Called by the animation system when an animation is scheduled, to prepare it for running.
//! This callback is optional and can be left `NULL` when not needed.
AnimationSetupImplementation setup;
//! Called by the animation system when the animation needs to calculate the next animation frame.
//! This callback is mandatory and should not be left `NULL`.
AnimationUpdateImplementation update;
//! Called by the animation system when an animation is unscheduled, to clean up after it has run.
//! This callback is optional and can be left `NULL` when not needed.
AnimationTeardownImplementation teardown;
} AnimationImplementation;
//! Sets the implementation of the custom animation.
//! When implementing custom animations, use this function to specify what functions need to be
//! called to for the setup, frame update and teardown of the animation.
//! @note Trying to set an attribute when an animation is immutable will return false (failure). An
//! animation is immutable once it has been added to a sequence or spawn animation or has been
//! scheduled.
//! @param animation The animation for which to set the implementation.
//! @param implementation The structure with function pointers to the implementation of the setup,
//! update and teardown functions.
//! @see AnimationImplementation
//! @return true if successful, false on failure
bool animation_set_implementation(Animation *animation,
const AnimationImplementation *implementation);
//! Gets the implementation of the custom animation.
//! @param animation The animation for which to get the implementation.
//! @see AnimationImplementation
//! @return NULL if animation implementation has not been setup.
const AnimationImplementation* animation_get_implementation(Animation *animation);
//! @addtogroup PropertyAnimation
//! \brief A ProperyAnimation animates the value of a "property" of a "subject" over time.
//!
//! <h3>Animating a Layer's frame property</h3>
//! Currently there is only one specific type of property animation offered off-the-shelf, namely
//! one to change the frame (property) of a layer (subject), see \ref
//! property_animation_create_layer_frame().
//!
//! <h3>Implementing a custom PropertyAnimation</h3>
//! It is fairly simple to create your own variant of a PropertyAnimation.
//!
//! Please refer to \htmlinclude UiFramework.html (chapter "Property Animations") for a conceptual
//! overview of the animation framework and make sure you understand the underlying \ref Animation,
//! in case you are not familiar with it, before trying to implement a variation on
//! PropertyAnimation.
//!
//! To implement a custom property animation, use \ref property_animation_create() and provide a
//! function pointers to the accessors (getter and setter) and setup, update and teardown callbacks
//! in the implementation argument. Note that the type of property to animate with \ref
//! PropertyAnimation is limited to int16_t, GPoint or GRect.
//!
//! For each of these types, there are implementations provided for the necessary `.update` handler
//! of the animation: see \ref property_animation_update_int16(), \ref
//! property_animation_update_gpoint() and \ref property_animation_update_grect().
//! These update functions expect the `.accessors` to conform to the following interface:
//! Any getter needs to have the following function signature: `__type__ getter(void *subject);`
//! Any setter needs to have to following function signature: `void setter(void *subject,
//! __type__ value);`
//! See \ref Int16Getter, \ref Int16Setter, \ref GPointGetter, \ref GPointSetter,
//! \ref GRectGetter, \ref GRectSetter for the typedefs that accompany the update fuctions.
//!
//! \code{.c}
//! static const PropertyAnimationImplementation my_implementation = {
//! .base = {
//! // using the "stock" update callback:
//! .update = (AnimationUpdateImplementation) property_animation_update_gpoint,
//! },
//! .accessors = {
//! // my accessors that get/set a GPoint from/onto my subject:
//! .setter = { .gpoint = my_layer_set_corner_point, },
//! .getter = { .gpoint = (const GPointGetter) my_layer_get_corner_point, },
//! },
//! };
//! static PropertyAnimation* s_my_animation_ptr = NULL;
//! static GPoint s_to_point = GPointZero;
//! ...
//! // Use NULL as 'from' value, this will make the animation framework call the getter
//! // to get the current value of the property and use that as the 'from' value:
//! s_my_animation_ptr = property_animation_create(&my_implementation, my_layer, NULL, &s_to_point);
//! animation_schedule(property_animation_get_animation(s_my_animation_ptr));
//! \endcode
//! @{
struct PropertyAnimationAccessors;
typedef struct PropertyAnimationAccessors PropertyAnimationAccessors;
struct PropertyAnimationImplementation;
typedef struct PropertyAnimationImplementation PropertyAnimationImplementation;
struct PropertyAnimation;
typedef struct PropertyAnimation PropertyAnimation;
//! Convenience function to create and initialize a property animation that animates the frame of a
//! Layer. It sets up the PropertyAnimation to use \ref layer_set_frame() and \ref layer_get_frame()
//! as accessors and uses the `layer` parameter as the subject for the animation.
//! The same defaults are used as with \ref animation_create().
//! @param layer the layer that will be animated
//! @param from_frame the frame that the layer should animate from
//! @param to_frame the frame that the layer should animate to
//! @note Pass in `NULL` as one of the frame arguments to have it set automatically to the layer's
//! current frame. This will result in a call to \ref layer_get_frame() to get the current frame of
//! the layer.
//! @return A handle to the property animation. `NULL` if animation could not be created
PropertyAnimation *property_animation_create_layer_frame(struct Layer *layer, GRect *from_frame,
GRect *to_frame);
//! Convenience function to create and initialize a property animation that animates the bound's
//! origin of a Layer. It sets up the PropertyAnimation to use layer_set_bounds() and
//! layer_get_bounds() as accessors and uses the `layer` parameter as the subject for the animation.
//! The same defaults are used as with \ref animation_create().
//! @param layer the layer that will be animated
//! @param from_origin the origin that the bounds should animate from
//! @param to_origin the origin that the layer should animate to
//! @return A handle to the property animation. `NULL` if animation could not be created
PropertyAnimation *property_animation_create_bounds_origin(struct Layer *layer, GPoint *from,
GPoint *to);
//! Creates a new PropertyAnimation on the heap and and initializes it with the specified values.
//! The same defaults are used as with \ref animation_create().
//! If the `from_value` or the `to_value` is `NULL`, the getter accessor will be called to get the
//! current value of the property and be used instead.
//! @param implementation Pointer to the implementation of the animation. In most cases, it makes
//! sense to pass in a `static const` struct pointer.
//! @param subject Pointer to the "subject" being animated. This will be passed in when the getter/
//! setter accessors are called,
//! see \ref PropertyAnimationAccessors, \ref GPointSetter, and friends. The value of this pointer
//! will be copied into the `.subject` field of the PropertyAnimation struct.
//! @param from_value Pointer to the value that the subject should animate from
//! @param to_value Pointer to the value that the subject should animate to
//! @note Pass in `NULL` as one of the value arguments to have it set automatically to the subject's
//! current property value, as returned by the getter function. Also note that passing in `NULL` for
//! both `from_value` and `to_value`, will result in the animation having the same from- and to-
//! values, effectively not doing anything.
//! @return A handle to the property animation. `NULL` if animation could not be created
PropertyAnimation* property_animation_create(const PropertyAnimationImplementation *implementation,
void *subject, void *from_value, void *to_value);
//! Destroy a property animation allocated by property_animation_create() or relatives.
//! @param property_animation the return value from property_animation_create
void property_animation_destroy(PropertyAnimation* property_animation);
//! Default update callback for a property animations to update a property of type int16_t.
//! Assign this function to the `.base.update` callback field of your
//! PropertyAnimationImplementation, in combination with a `.getter` and `.setter` accessors of
//! types \ref Int16Getter and \ref Int16Setter.
//! The implementation of this function will calculate the next value of the animation and call the
//! setter to set the new value upon the subject.
//! @param property_animation The property animation for which the update is requested.
//! @param distance_normalized The current normalized distance. See \ref
//! AnimationUpdateImplementation
//! @note This function is not supposed to be called "manually", but will be called automatically
//! when the animation is being run.
void property_animation_update_int16(PropertyAnimation *property_animation,
const uint32_t distance_normalized);
//! Default update callback for a property animations to update a property of type uint32_t.
//! Assign this function to the `.base.update` callback field of your
//! PropertyAnimationImplementation, in combination with a `.getter` and `.setter` accessors of
//! types \ref UInt32Getter and \ref UInt32Setter.
//! The implementation of this function will calculate the next value of the animation and call the
//! setter to set the new value upon the subject.
//! @param property_animation The property animation for which the update is requested.
//! @param distance_normalized The current normalized distance. See \ref
//! AnimationUpdateImplementation
//! @note This function is not supposed to be called "manually", but will be called automatically
//! when the animation is being run.
void property_animation_update_uint32(PropertyAnimation *property_animation,
const uint32_t distance_normalized);
//! Default update callback for a property animations to update a property of type GPoint.
//! Assign this function to the `.base.update` callback field of your
//! PropertyAnimationImplementation,
//! in combination with a `.getter` and `.setter` accessors of types \ref GPointGetter and \ref
//! GPointSetter.
//! The implementation of this function will calculate the next point of the animation and call the
//! setter to set the new point upon the subject.
//! @param property_animation The property animation for which the update is requested.
//! @param distance_normalized The current normalized distance. See \ref
//! AnimationUpdateImplementation
//! @note This function is not supposed to be called "manually", but will be called automatically
//! when the animation is being run.
void property_animation_update_gpoint(PropertyAnimation *property_animation,
const uint32_t distance_normalized);
//! Default update callback for a property animations to update a property of type GRect.
//! Assign this function to the `.base.update` callback field of your
//! PropertyAnimationImplementation, in combination with a `.getter` and `.setter` accessors of
//! types \ref GRectGetter and \ref GRectSetter. The implementation of this function will calculate
//! the next rectangle of the animation and call the setter to set the new rectangle upon the
//! subject.
//! @param property_animation The property animation for which the update is requested.
//! @param distance_normalized The current normalized distance. See \ref
//! AnimationUpdateImplementation
//! @note This function is not supposed to be called "manually", but will be called automatically
//! when the animation is being run.
void property_animation_update_grect(PropertyAnimation *property_animation,
const uint32_t distance_normalized);
//! Work-around for function pointer return type GPoint to avoid
//! tripping the pre-processor to use the equally named GPoint define
typedef GPoint GPointReturn;
//! Work-around for function pointer return type GRect to avoid
//! tripping the pre-processor to use the equally named GRect define
typedef GRect GRectReturn;
//! Function signature of a setter function to set a property of type int16_t onto the subject.
//! @see \ref property_animation_update_int16()
//! @see \ref PropertyAnimationAccessors
typedef void (*Int16Setter)(void *subject, int16_t int16);
//! Function signature of a getter function to get the current property of type int16_t of the
//! subject.
//! @see \ref property_animation_create()
//! @see \ref PropertyAnimationAccessors
typedef int16_t (*Int16Getter)(void *subject);
//! Function signature of a setter function to set a property of type uint32_t onto the subject.
//! @see \ref property_animation_update_int16()
//! @see \ref PropertyAnimationAccessors
typedef void (*UInt32Setter)(void *subject, uint32_t uint32);
//! Function signature of a getter function to get the current property of type uint32_t of the
//! subject.
//! @see \ref property_animation_create()
//! @see \ref PropertyAnimationAccessors
typedef uint32_t (*UInt32Getter)(void *subject);
//! Function signature of a setter function to set a property of type GPoint onto the subject.
//! @see \ref property_animation_update_gpoint()
//! @see \ref PropertyAnimationAccessors
typedef void (*GPointSetter)(void *subject, GPoint gpoint);
//! Function signature of a getter function to get the current property of type GPoint of the subject.
//! @see \ref property_animation_create()
//! @see \ref PropertyAnimationAccessors
typedef GPointReturn (*GPointGetter)(void *subject);
//! Function signature of a setter function to set a property of type GRect onto the subject.
//! @see \ref property_animation_update_grect()
//! @see \ref PropertyAnimationAccessors
typedef void (*GRectSetter)(void *subject, GRect grect);
//! Function signature of a getter function to get the current property of type GRect of the subject.
//! @see \ref property_animation_create()
//! @see \ref PropertyAnimationAccessors
typedef GRectReturn (*GRectGetter)(void *subject);
//! Function signature of a setter function to set a property of type GColor8 onto the subject.
//! @see \ref property_animation_update_gcolor8()
//! @see \ref PropertyAnimationAccessors
typedef void (*GColor8Setter)(void *subject, GColor8 gcolor);
//! Function signature of a getter function to get the current property of type GColor8 of the
//! subject.
//! @see \ref property_animation_create()
//! @see \ref PropertyAnimationAccessors
typedef GColor8 (*GColor8Getter)(void *subject);
//! Data structure containing the setter and getter function pointers that the property animation
//! should use.
//! The specified setter function will be used by the animation's update callback. <br/> Based on
//! the type of the property (int16_t, GPoint or GRect), the accompanying update callback should be
//! used, see \ref property_animation_update_int16(), \ref property_animation_update_gpoint() and
//! \ref property_animation_update_grect(). <br/>
//! The getter function is used when the animation is initialized, to assign the current value of
//! the subject's property as "from" or "to" value, see \ref property_animation_create().
typedef struct PropertyAnimationAccessors {
//! Function pointer to the implementation of the function that __sets__ the updated property
//! value. This function will be called repeatedly for each animation frame.
//! @see PropertyAnimationAccessors
union {
//! Use if the property to animate is of int16_t type
Int16Setter int16;
//! Use if the property to animate is of GPoint type
GPointSetter gpoint;
//! Use if the property to animate is of GRect type
GRectSetter grect;
//! Use if the property to animate is of GColor8 type
GColor8Setter gcolor8;
//! Use if the property to animate is of uint32_t type
UInt32Setter uint32;
} setter;
//! Function pointer to the implementation of the function that __gets__ the current property
//! value. This function will be called during \ref property_animation_create(), to get the current
//! property value, in case the `from_value` or `to_value` argument is `NULL`.
//! @see PropertyAnimationAccessors
union {
//! Use if the property to animate is of int16_t type
Int16Getter int16;
//! Use if the property to animate is of GPoint type
GPointGetter gpoint;
//! Use if the property to animate is of GRect type
GRectGetter grect;
//! Use if the property to animate is of GColor8 type
GColor8Getter gcolor8;
//! Use if the property to animate is of uint32_t type
UInt32Getter uint32;
} getter;
} PropertyAnimationAccessors;
//! Data structure containing a collection of function pointers that form the implementation of the
//! property animation.
//! See the code example at the top (\ref PropertyAnimation).
typedef struct PropertyAnimationImplementation {
//! The "inherited" fields from the Animation "base class".
AnimationImplementation base;
//! The accessors to set/get the property to be animated.
PropertyAnimationAccessors accessors;
} PropertyAnimationImplementation;
//! Convenience function to retrieve an animation instance from a property animation instance
//! @param property_animation The property animation
//! @return The \ref Animation within this PropertyAnimation
Animation *property_animation_get_animation(PropertyAnimation *property_animation);
//! Convenience function to clone a property animation instance
//! @param property_animation The property animation
//! @return A clone of the original Animation
#define property_animation_clone(property_animation) \
(PropertyAnimation *)animation_clone((Animation *)property_animation)
//! Convenience function to retrieve the 'from' GRect value from property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr The value will be retrieved into this pointer
//! @return true on success, false on failure
#define property_animation_get_from_grect(property_animation, value_ptr) \
property_animation_from(property_animation, value_ptr, sizeof(GRect), false)
//! Convenience function to set the 'from' GRect value of property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer to the new value
//! @return true on success, false on failure
#define property_animation_set_from_grect(property_animation, value_ptr) \
property_animation_from(property_animation, value_ptr, sizeof(GRect), true)
//! Convenience function to retrieve the 'from' GPoint value from property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr The value will be retrieved into this pointer
//! @return true on success, false on failure
#define property_animation_get_from_gpoint(property_animation, value_ptr) \
property_animation_from(property_animation, value_ptr, sizeof(GPoint), false)
//! Convenience function to set the 'from' GPoint value of property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer to the new value
//! @return true on success, false on failure
#define property_animation_set_from_gpoint(property_animation, value_ptr) \
property_animation_from(property_animation, value_ptr, sizeof(GPoint), true)
//! Convenience function to retrieve the 'from' int16_t value from property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr The value will be retrieved into this pointer
//! @return true on success, false on failure
#define property_animation_get_from_int16(property_animation, value_ptr) \
property_animation_from(property_animation, value_ptr, sizeof(int16_t), false)
//! Convenience function to set the 'from' int16_t value of property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer to the new value
//! @return true on success, false on failure
#define property_animation_set_from_int16(property_animation, value_ptr) \
property_animation_from(property_animation, value_ptr, sizeof(int16_t), true)
//! Convenience function to retrieve the 'to' GRect value from property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr The value will be retrieved into this pointer
//! @return true on success, false on failure
#define property_animation_get_to_grect(property_animation, value_ptr) \
property_animation_to(property_animation, value_ptr, sizeof(GRect), false)
//! Convenience function to set the 'to' GRect value of property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer to the new value
//! @return true on success, false on failure
#define property_animation_set_to_grect(property_animation, value_ptr) \
property_animation_to(property_animation, value_ptr, sizeof(GRect), true)
//! Convenience function to retrieve the 'to' GPoint value from property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr The value will be retrieved into this pointer
//! @return true on success, false on failure
#define property_animation_get_to_gpoint(property_animation, value_ptr) \
property_animation_to(property_animation, value_ptr, sizeof(GPoint), false)
//! Convenience function to set the 'to' GPoint value of property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer to the new value
//! @return true on success, false on failure
#define property_animation_set_to_gpoint(property_animation, value_ptr) \
property_animation_to(property_animation, value_ptr, sizeof(GPoint), true)
//! Convenience function to retrieve the 'to' int16_t value from property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr The value will be retrieved into this pointer
//! @return true on success, false on failure
#define property_animation_get_to_int16(property_animation, value_ptr) \
property_animation_to(property_animation, value_ptr, sizeof(int16_t), false)
//! Convenience function to set the 'to' int16_t value of property animation handle
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer to the new value
//! @return true on success, false on failure
#define property_animation_set_to_int16(property_animation, value_ptr) \
property_animation_to(property_animation, value_ptr, sizeof(int16_t), true)
//! Retrieve the subject of a property animation
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer used to store the subject of this property animation
//! @return The subject of this PropertyAnimation
#define property_animation_get_subject(property_animation, value_ptr) \
property_animation_subject(property_animation, value_ptr, false)
//! Set the subject of a property animation
//! @param property_animation The PropertyAnimation to be accessed
//! @param value_ptr Pointer to the new subject value
#define property_animation_set_subject(property_animation, value_ptr) \
property_animation_subject(property_animation, value_ptr, true)
//! Helper function used by the property_animation_get|set_subject macros
//! @param property_animation Handle to the property animation
//! @param subject The subject to get or set.
//! @param set true to set new subject, false to retrieve existing value
//! @return true if successful, false on failure (usually a bad animation_h)
bool property_animation_subject(PropertyAnimation *property_animation, void **subject, bool set);
//! Helper function used by the property_animation_get|set_from_.* macros
//! @param property_animation Handle to the property animation
//! @param from Pointer to the value
//! @param size Size of the from value
//! @param set true to set new value, false to retrieve existing one
//! @return true if successful, false on failure (usually a bad animation_h)
bool property_animation_from(PropertyAnimation *property_animation, void *from, size_t size,
bool set);
//! Helper function used by the property_animation_get|set_to_.* macros
//! @param property_animation handle to the property animation
//! @param to Pointer to the value
//! @param size Size of the to value
//! @param set true to set new value, false to retrieve existing one
//! @return true if successful, false on failure (usually a bad animation_h)
bool property_animation_to(PropertyAnimation *property_animation, void *to, size_t size,
bool set);
//! @} // group PropertyAnimation
//! @} // group Animation
//! @addtogroup Layer
//! @{
//! @addtogroup TextLayer
//! \brief Layer that displays and formats a text string.
//!
//! ![](text_layer.png)
//! The geometric information (bounds, frame) of the Layer
//! is used as the "box" in which the text is drawn. The \ref TextLayer also has a number of
//! other properties that influence how the text is drawn. Most important of these properties are:
//! a pointer to the string to draw itself, the font, the text color, the background color of the
//! layer, the overflow mode and alignment of the text inside the layer.
//! @see Layer
//! @see TextDrawing
//! @see Fonts
//! @{
struct TextLayer;
typedef struct TextLayer TextLayer;
//! Creates a new TextLayer on the heap and initializes it with the default values.
//!
//! * Font: Raster Gothic 14-point Boldface (system font)
//! * Text Alignment: \ref GTextAlignmentLeft
//! * Text color: \ref GColorBlack
//! * Background color: \ref GColorWhite
//! * Clips: `true`
//! * Hidden: `false`
//! * Caching: `false`
//!
//! The text layer is automatically marked dirty after this operation.
//! @param frame The frame with which to initialze the TextLayer
//! @return A pointer to the TextLayer. `NULL` if the TextLayer could not
//! be created
TextLayer* text_layer_create(GRect frame);
//! Destroys a TextLayer previously created by text_layer_create.
void text_layer_destroy(TextLayer* text_layer);
//! Gets the "root" Layer of the text layer, which is the parent for the sub-
//! layers used for its implementation.
//! @param text_layer Pointer to the TextLayer for which to get the "root" Layer
//! @return The "root" Layer of the text layer.
Layer* text_layer_get_layer(TextLayer *text_layer);
//! Sets the pointer to the string where the TextLayer is supposed to find the text
//! at a later point in time, when it needs to draw itself.
//! @param text_layer The TextLayer of which to set the text
//! @param text The new text to set onto the TextLayer. This must be a null-terminated and valid UTF-8 string!
//! @note The string is not copied, so its buffer most likely cannot be stack allocated,
//! but is recommended to be a buffer that is long-lived, at least as long as the TextLayer
//! is part of a visible Layer hierarchy.
//! @see text_layer_get_text
void text_layer_set_text(TextLayer *text_layer, const char *text);
//! Gets the pointer to the string that the TextLayer is using.
//! @param text_layer The TextLayer for which to get the text
//! @see text_layer_set_text
const char* text_layer_get_text(TextLayer *text_layer);
//! Sets the background color of the bounding box that will be drawn behind the text
//! @param text_layer The TextLayer of which to set the background color
//! @param color The new \ref GColor to set the background to
//! @see text_layer_set_text_color
void text_layer_set_background_color(TextLayer *text_layer, GColor color);
//! Sets the color of text that will be drawn
//! @param text_layer The TextLayer of which to set the text color
//! @param color The new \ref GColor to set the text color to
//! @see text_layer_set_background_color
void text_layer_set_text_color(TextLayer *text_layer, GColor color);
//! Sets the line break mode of the TextLayer
//! @param text_layer The TextLayer of which to set the overflow mode
//! @param line_mode The new \ref GTextOverflowMode to set
void text_layer_set_overflow_mode(TextLayer *text_layer, GTextOverflowMode line_mode);
//! Sets the font of the TextLayer
//! @param text_layer The TextLayer of which to set the font
//! @param font The new \ref GFont for the TextLayer
//! @see fonts_get_system_font
//! @see fonts_load_custom_font
void text_layer_set_font(TextLayer *text_layer, GFont font);
//! Sets the alignment of the TextLayer
//! @param text_layer The TextLayer of which to set the alignment
//! @param text_alignment The new text alignment for the TextLayer
//! @see GTextAlignment
void text_layer_set_text_alignment(TextLayer *text_layer, GTextAlignment text_alignment);
//! Calculates the size occupied by the current text of the TextLayer
//! @param text_layer the TextLayer for which to calculate the text's size
//! @return The size occupied by the current text of the TextLayer
GSize text_layer_get_content_size(TextLayer *text_layer);
//! Update the size of the text layer
//! This is a convenience function to update the frame of the TextLayer.
//! @param text_layer The TextLayer of which to set the size
//! @param max_size The new size for the TextLayer
void text_layer_set_size(TextLayer *text_layer, const GSize max_size);
//! @} // group TextLayer
//! @addtogroup ScrollLayer
//! \brief Layer that scrolls its contents, animated.
//!
//! ![](scroll_layer.png)
//! <h3>Key Points</h3>
//! * Facilitates vertical scrolling of a layer sub-hierarchy zero or more
//! arbitrary layers. The example image shows a scroll layer containing one
//! large TextLayer.
//! * Shadows to indicate that there is more content are automatically drawn
//! on top of the content. When the end of the scroll layer is reached, the
//! shadow will automatically be retracted.
//! * Scrolling from one offset to another is animated implicitly by default.
//! * The scroll layer contains a "content" sub-layer, which is the layer that
//! is actually moved up an down. Any layer that is a child of this "content"
//! sub-layer, will be moved as well. Effectively, an entire layout of layers
//! can be scrolled this way. Use the convenience function
//! \ref scroll_layer_add_child() to add child layers to the "content" sub-layer.
//! * The scroll layer needs to be informed of the total size of the contents,
//! in order to calculate from and to what point it should be able to scroll.
//! Use \ref scroll_layer_set_content_size() to set the size of the contents.
//! * The button behavior is set up, using the convenience function
//! \ref scroll_layer_set_click_config_onto_window(). This will associate the
//! UP and DOWN buttons with scrolling up and down.
//! * The SELECT button can be configured by installing a click configuration
//! provider using \ref scroll_layer_set_callbacks().
//! * To scroll programatically to a certain offset, use
//! \ref scroll_layer_set_content_offset().
//! * It is possible to get called back for each scrolling increment, by
//! installing the `.content_offset_changed_handler` callback using
//! \ref scroll_layer_set_callbacks().
//! * Only vertical scrolling is supported at the moment.
//! @{
struct ScrollLayer;
typedef struct ScrollLayer ScrollLayer;
//! Function signature for the `.content_offset_changed_handler` callback.
typedef void (*ScrollLayerCallback)(struct ScrollLayer *scroll_layer, void *context);
//! All the callbacks that the ScrollLayer exposes for use by applications.
//! @note The context parameter can be set using scroll_layer_set_context() and
//! gets passed in as context with all of these callbacks.
typedef struct ScrollLayerCallbacks {
//! Provider function to set up the SELECT button handlers. This will be
//! called after the scroll layer has configured the click configurations for
//! the up/down buttons, so it can also be used to modify the default up/down
//! scrolling behavior.
ClickConfigProvider click_config_provider;
//! Called every time the the content offset changes. During a scrolling
//! animation, it will be called for each intermediary offset as well
ScrollLayerCallback content_offset_changed_handler;
} ScrollLayerCallbacks;
//! Creates a new ScrollLayer on the heap and initalizes it with the default values:
//! * Clips: `true`
//! * Hidden: `false`
//! * Content size: `frame.size`
//! * Content offset: \ref GPointZero
//! * Callbacks: None (`NULL` for each one)
//! * Callback context: `NULL`
//! @return A pointer to the ScrollLayer. `NULL` if the ScrollLayer could not
//! be created
ScrollLayer* scroll_layer_create(GRect frame);
//! Destroys a ScrollLayer previously created by scroll_layer_create.
void scroll_layer_destroy(ScrollLayer *scroll_layer);
//! Gets the "root" Layer of the scroll layer, which is the parent for the sub-
//! layers used for its implementation.
//! @param scroll_layer Pointer to the ScrollLayer for which to get the "root" Layer
//! @return The "root" Layer of the scroll layer.
Layer* scroll_layer_get_layer(const ScrollLayer *scroll_layer);
//! Adds the child layer to the content sub-layer of the ScrollLayer.
//! This will make the child layer part of the scrollable contents.
//! The content sub-layer of the ScrollLayer will become the parent of the
//! child layer.
//! @param scroll_layer The ScrollLayer to which to add the child layer.
//! @param child The Layer to add to the content sub-layer of the ScrollLayer.
//! @note You may need to update the size of the scrollable contents using
//! \ref scroll_layer_set_content_size().
void scroll_layer_add_child(ScrollLayer *scroll_layer, Layer *child);
//! Convenience function to set the \ref ClickConfigProvider callback on the
//! given window to scroll layer's internal click config provider. This internal
//! click configuration provider, will set up the default UP & DOWN
//! scrolling behavior.
//! This function calls \ref window_set_click_config_provider_with_context to
//! accomplish this.
//!
//! If you application has set a `.click_config_provider`
//! callback using \ref scroll_layer_set_callbacks(), this will be called
//! by the internal click config provider, after configuring the UP & DOWN
//! buttons. This allows your application to configure the SELECT button
//! behavior and optionally override the UP & DOWN
//! button behavior. The callback context for the SELECT click recognizer is
//! automatically set to the scroll layer's context (see
//! \ref scroll_layer_set_context() ). This context is passed into
//! \ref ClickHandler callbacks. For the UP and DOWN buttons, the scroll layer
//! itself is passed in by default as the callback context in order to deal with
//! those buttons presses to scroll up and down automatically.
//! @param scroll_layer The ScrollLayer that needs to receive click events.
//! @param window The window for which to set the click configuration.
//! @see \ref Clicks
//! @see window_set_click_config_provider_with_context
void scroll_layer_set_click_config_onto_window(ScrollLayer *scroll_layer, struct Window *window);
//! Sets the callbacks that the scroll layer exposes.
//! The `context` as set by \ref scroll_layer_set_context() is passed into each
//! of the callbacks. See \ref ScrollLayerCallbacks for the different callbacks.
//! @note If the `context` is NULL, a pointer to scroll_layer is used
//! as context parameter instead when calling callbacks.
//! @param scroll_layer The ScrollLayer for which to assign new callbacks.
//! @param callbacks The new callbacks.
void scroll_layer_set_callbacks(ScrollLayer *scroll_layer, ScrollLayerCallbacks callbacks);
//! Sets a new callback context. This context is passed into the scroll layer's
//! callbacks and also the \ref ClickHandler for the SELECT button.
//! If `NULL` or not set, the context defaults to a pointer to the ScrollLayer
//! itself.
//! @param scroll_layer The ScrollLayer for which to assign the new callback
//! context.
//! @param context The new callback context.
//! @see scroll_layer_set_click_config_onto_window
//! @see scroll_layer_set_callbacks
void scroll_layer_set_context(ScrollLayer *scroll_layer, void *context);
//! Scrolls to the given offset, optionally animated.
//! @note When scrolling down, the offset's `.y` decrements. When scrolling up,
//! the offset's `.y` increments. If scrolled completely to the top, the offset
//! is \ref GPointZero.
//! @note The `.x` field must be `0`. Horizontal scrolling is not supported.
//! @param scroll_layer The ScrollLayer for which to set the content offset
//! @param offset The final content offset
//! @param animated Pass in `true` to animate to the new content offset, or
//! `false` to set the new content offset without animating.
//! @see scroll_layer_get_content_offset
void scroll_layer_set_content_offset(ScrollLayer *scroll_layer, GPoint offset, bool animated);
//! Gets the point by which the contents are offset.
//! @param scroll_layer The ScrollLayer for which to get the content offset
//! @see scroll_layer_set_content_offset
GPoint scroll_layer_get_content_offset(ScrollLayer *scroll_layer);
//! Sets the size of the contents layer. This determines the area that is
//! scrollable. At the moment, this needs to be set "manually" and is not
//! derived from the geometry of the contents layers.
//! @param scroll_layer The ScrollLayer for which to set the content size.
//! @param size The new content size.
//! @see scroll_layer_get_content_size
void scroll_layer_set_content_size(ScrollLayer *scroll_layer, GSize size);
//! Gets the size of the contents layer.
//! @param scroll_layer The ScrollLayer for which to get the content size
//! @see scroll_layer_set_content_size
GSize scroll_layer_get_content_size(const ScrollLayer *scroll_layer);
//! Set the frame of the scroll layer and adjusts the internal layers' geometry
//! accordingly. The scroll layer is marked dirty automatically.
//! @param scroll_layer The ScrollLayer for which to set the frame
//! @param frame The new frame
void scroll_layer_set_frame(ScrollLayer *scroll_layer, GRect frame);
//! The click handlers for the UP button that the scroll layer will install as
//! part of \ref scroll_layer_set_click_config_onto_window().
//! @note This handler is exposed, in case one wants to implement an alternative
//! handler for the UP button, as a way to invoke the default behavior.
//! @param recognizer The click recognizer for which the handler is called
//! @param context A void pointer to the ScrollLayer that is the context of the click event
void scroll_layer_scroll_up_click_handler(ClickRecognizerRef recognizer, void *context);
//! The click handlers for the DOWN button that the scroll layer will install as
//! part of \ref scroll_layer_set_click_config_onto_window().
//! @note This handler is exposed, in case one wants to implement an alternative
//! handler for the DOWN button, as a way to invoke the default behavior.
//! @param recognizer The click recognizer for which the handler is called
//! @param context A void pointer to the ScrollLayer that is the context of the click event
void scroll_layer_scroll_down_click_handler(ClickRecognizerRef recognizer, void *context);
//! Sets the visibility of the scroll layer shadow.
//! If the visibility has changed, \ref layer_mark_dirty() will be called automatically
//! on the scroll layer.
//! @param scroll_layer The scroll layer for which to set the shadow visibility
//! @param hidden Supply `true` to make the shadow hidden, or `false` to make it
//! non-hidden.
void scroll_layer_set_shadow_hidden(ScrollLayer *scroll_layer, bool hidden);
//! Gets the visibility of the scroll layer shadow.
//! @param scroll_layer The scroll layer for which to get the visibility
//! @return True if the shadow is hidden, false if it is not hidden.
bool scroll_layer_get_shadow_hidden(const ScrollLayer *scroll_layer);
//! @} // group ScrollLayer
//! @addtogroup MenuLayer
//! \brief Layer that displays a standard list menu. Data is provided using
//! callbacks.
//!
//! ![](menu_layer.png)
//! <h3>Key Points</h3>
//! * The familiar list-style menu widget, as used throughout the Pebble user
//! interface.
//! * Built on top of \ref ScrollLayer, inheriting all its goodness like
//! animated scrolling, automatic "more content" shadow indicators, etc.
//! * All data needed to render the menu is requested on-demand via callbacks,
//! to avoid the need to keep a lot of data in memory.
//! * Support for "sections". A section is a group of items, visually separated
//! by a header with the name at the top of the section.
//! * Variable heights: each menu item cell and each section header can have
//! its own height. The heights are provided by callbacks.
//! * Deviation from the Layer system for cell drawing: Each menu item does
//! _not_ have its own Layer (to minimize memory usage). Instead, a
//! drawing callback is set onto the \ref MenuLayer that is responsible
//! for drawing each menu item. The \ref MenuLayer will call this callback for each
//! menu item that is visible and needs to be rendered.
//! * Cell and header drawing can be customized by implementing a custom drawing
//! callback.
//! * A few "canned" menu cell drawing functions are provided for convenience,
//! which support the default menu cell layout with a title, optional subtitle
//! and icon.
//!
//! For short, static list menus, consider using \ref SimpleMenuLayer.
//! @{
//! Section drawing function to draw a basic section cell with the title, subtitle, and icon of the section.
//! Call this function inside the `.draw_row` callback implementation, see \ref MenuLayerCallbacks.
//! @param ctx The destination graphics context
//! @param cell_layer The layer of the cell to draw
//! @param title If non-null, draws a title in larger text (24 points, bold
//! Raster Gothic system font).
//! @param subtitle If non-null, draws a subtitle in smaller text (18 points,
//! Raster Gothic system font). If `NULL`, the title will be centered vertically
//! inside the menu cell.
//! @param icon If non-null, draws an icon to the left of the text. If `NULL`,
//! the icon will be omitted and the leftover space is used for the title and
//! subtitle.
void menu_cell_basic_draw(GContext* ctx,
const Layer *cell_layer,
const char *title,
const char *subtitle,
GBitmap *icon);
//! Cell drawing function to draw a basic menu cell layout with title, subtitle
//! Cell drawing function to draw a menu cell layout with only one big title.
//! Call this function inside the `.draw_row` callback implementation, see
//! \ref MenuLayerCallbacks.
//! @param ctx The destination graphics context
//! @param cell_layer The layer of the cell to draw
//! @param title If non-null, draws a title in larger text (28 points, bold
//! Raster Gothic system font).
void menu_cell_title_draw(GContext* ctx,
const Layer *cell_layer,
const char *title);
//! Section header drawing function to draw a basic section header cell layout
//! with the title of the section.
//! Call this function inside the `.draw_header` callback implementation, see
//! \ref MenuLayerCallbacks.
//! @param ctx The destination graphics context
//! @param cell_layer The layer of the cell to draw
//! @param title If non-null, draws the title in small text (14 points, bold
//! Raster Gothic system font).
void menu_cell_basic_header_draw(GContext* ctx,
const Layer *cell_layer,
const char *title);
//! Default section header height in pixels
#define MENU_CELL_BASIC_HEADER_HEIGHT ((const int16_t) 16)
#define MENU_INDEX_NOT_FOUND ((const uint16_t) ~0)
//! Data structure to represent an menu item's position in a menu, by specifying
//! the section index and the row index within that section.
typedef struct MenuIndex {
//! The index of the section
uint16_t section;
//! The index of the row within the section with index `.section`
uint16_t row;
} MenuIndex;
//! Macro to create a MenuIndex
#define MenuIndex(section, row) ((MenuIndex){ (section), (row) })
//! Comparator function to determine the order of two MenuIndex values.
//! @param a Pointer to the menu index of the first item
//! @param b Pointer to the menu index of the second item
//! @return 0 if A and B are equal, 1 if A has a higher section & row
//! combination than B or else -1
int16_t menu_index_compare(const MenuIndex *a, const MenuIndex *b);
typedef struct MenuCellSpan {
int16_t y;
int16_t h;
int16_t sep;
MenuIndex index;
} MenuCellSpan;
struct MenuLayer;
typedef struct MenuLayer MenuLayer;
//! Function signature for the callback to get the number of sections in a menu.
//! @param menu_layer The \ref MenuLayer for which the data is requested
//! @param callback_context The callback context
//! @return The number of sections in the menu
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef uint16_t (*MenuLayerGetNumberOfSectionsCallback)(struct MenuLayer *menu_layer,
void *callback_context);
//! Function signature for the callback to get the number of rows in a
//! given section in a menu.
//! @param menu_layer The \ref MenuLayer for which the data is requested
//! @param section_index The index of the section of the menu for which the
//! number of items it contains is requested
//! @param callback_context The callback context
//! @return The number of rows in the given section in the menu
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef uint16_t (*MenuLayerGetNumberOfRowsInSectionsCallback)(struct MenuLayer *menu_layer,
uint16_t section_index,
void *callback_context);
//! Function signature for the callback to get the height of the menu cell
//! at a given index.
//! @param menu_layer The \ref MenuLayer for which the data is requested
//! @param cell_index The MenuIndex for which the cell height is requested
//! @param callback_context The callback context
//! @return The height of the cell at the given MenuIndex
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef int16_t (*MenuLayerGetCellHeightCallback)(struct MenuLayer *menu_layer,
MenuIndex *cell_index,
void *callback_context);
//! Function signature for the callback to get the height of the section header
//! at a given section index.
//! @param menu_layer The \ref MenuLayer for which the data is requested
//! @param section_index The index of the section for which the header height is
//! requested
//! @param callback_context The callback context
//! @return The height of the section header at the given section index
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef int16_t (*MenuLayerGetHeaderHeightCallback)(struct MenuLayer *menu_layer,
uint16_t section_index,
void *callback_context);
//! Function signature for the callback to get the height of the separator
//! at a given index.
//! @param menu_layer The \ref MenuLayer for which the data is requested
//! @param cell_index The MenuIndex for which the cell height is requested
//! @param callback_context The callback context
//! @return The height of the separator at the given MenuIndex
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef int16_t (*MenuLayerGetSeparatorHeightCallback)(struct MenuLayer *menu_layer,
MenuIndex *cell_index,
void *callback_context);
//! Function signature for the callback to render the menu cell at a given
//! MenuIndex.
//! @param ctx The destination graphics context to draw into
//! @param cell_layer The cell's layer, containing the geometry of the cell
//! @param cell_index The MenuIndex of the cell that needs to be drawn
//! @param callback_context The callback context
//! @note The `cell_layer` argument is provided to make it easy to re-use an
//! `.update_proc` implementation in this callback. Only the bounds and frame
//! of the `cell_layer` are actually valid and other properties should be
//! ignored.
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef void (*MenuLayerDrawRowCallback)(GContext* ctx,
const Layer *cell_layer,
MenuIndex *cell_index,
void *callback_context);
//! Function signature for the callback to render the section header at a given
//! section index.
//! @param ctx The destination graphics context to draw into
//! @param cell_layer The header cell's layer, containing the geometry of the
//! header cell
//! @param section_index The section index of the section header that needs to
//! be drawn
//! @param callback_context The callback context
//! @note The `cell_layer` argument is provided to make it easy to re-use an
//! `.update_proc` implementation in this callback. Only the bounds and frame
//! of the `cell_layer` are actually valid and other properties should be
//! ignored.
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef void (*MenuLayerDrawHeaderCallback)(GContext* ctx,
const Layer *cell_layer,
uint16_t section_index,
void *callback_context);
//! Function signature for the callback to render the separator at a given
//! MenuIndex.
//! @param ctx The destination graphics context to draw into
//! @param cell_layer The cell's layer, containing the geometry of the cell
//! @param cell_index The MenuIndex of the separator that needs to be drawn
//! @param callback_context The callback context
//! @note The `cell_layer` argument is provided to make it easy to re-use an
//! `.update_proc` implementation in this callback. Only the bounds and frame
//! of the `cell_layer` are actually valid and other properties should be
//! ignored.
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef void (*MenuLayerDrawSeparatorCallback)(GContext* ctx,
const Layer *cell_layer,
MenuIndex *cell_index,
void *callback_context);
//! Function signature for the callback to handle the event that a user hits
//! the SELECT button.
//! @param menu_layer The \ref MenuLayer for which the selection event occured
//! @param cell_index The MenuIndex of the cell that is selected
//! @param callback_context The callback context
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef void (*MenuLayerSelectCallback)(struct MenuLayer *menu_layer,
MenuIndex *cell_index,
void *callback_context);
//! Function signature for the callback to handle a change in the current
//! selected item in the menu.
//! @param menu_layer The \ref MenuLayer for which the selection event occured
//! @param new_index The MenuIndex of the new item that is selected now
//! @param old_index The MenuIndex of the old item that was selected before
//! @param callback_context The callback context
//! @see \ref menu_layer_set_callbacks()
//! @see \ref MenuLayerCallbacks
typedef void (*MenuLayerSelectionChangedCallback)(struct MenuLayer *menu_layer,
MenuIndex new_index,
MenuIndex old_index,
void *callback_context);
//! Function signature for the callback which allows or changes selection behavior of the menu.
//! In order to change the cell that should be selected, modify the passed in new_index.
//! Preventing the selection from changing, new_index can be assigned the value of old_index.
//! @param menu_layer The \ref MenuLayer for which the selection event that occured
//! @param new_index Pointer to the index that the MenuLayer is going to change selection to.
//! @param old_index The index that is being unselected.
//! @param callback_context The callback context
//! @note \ref menu_layer_set_selected_index will not trigger this callback when
//! the selection changes, but \ref menu_layer_set_selected_next will.
typedef void (*MenuLayerSelectionWillChangeCallback)(struct MenuLayer *menu_layer,
MenuIndex *new_index,
MenuIndex old_index,
void *callback_context);
//! Function signature for the callback which draws the menu's background.
//! The background is underneath the cells of the menu, and is visible in the
//! padding below the bottom cell, or if a cell's background color is set to \ref GColorClear.
//! @param ctx The destination graphics context to draw into.
//! @param bg_layer The background's layer, containing the geometry of the background.
//! @param highlight Whether this should be rendered as highlighted or not. Highlight style
//! should match the highlight style of cells, since this color can be used for animating selection.
typedef void (*MenuLayerDrawBackgroundCallback)(GContext* ctx,
const Layer *bg_layer,
bool highlight,
void *callback_context);
//! Data structure containing all the callbacks of a \ref MenuLayer.
typedef struct MenuLayerCallbacks {
//! Callback that gets called to get the number of sections in the menu.
//! This can get called at various moments throughout the life of a menu.
//! @note When `NULL`, the number of sections defaults to 1.
MenuLayerGetNumberOfSectionsCallback get_num_sections;
//! Callback that gets called to get the number of rows in a section. This
//! can get called at various moments throughout the life of a menu.
//! @note Must be set to a valid callback; `NULL` causes undefined behavior.
MenuLayerGetNumberOfRowsInSectionsCallback get_num_rows;
//! Callback that gets called to get the height of a cell.
//! This can get called at various moments throughout the life of a menu.
//! @note When `NULL`, the default height of 44 pixels is used.
MenuLayerGetCellHeightCallback get_cell_height;
//! Callback that gets called to get the height of a section header.
//! This can get called at various moments throughout the life of a menu.
//! @note When `NULL`, the defaults height of 0 pixels is used. This disables
//! section headers.
MenuLayerGetHeaderHeightCallback get_header_height;
//! Callback that gets called to render a menu item.
//! This gets called for each menu item, every time it needs to be
//! re-rendered.
//! @note Must be set to a valid callback; `NULL` causes undefined behavior.
MenuLayerDrawRowCallback draw_row;
//! Callback that gets called to render a section header.
//! This gets called for each section header, every time it needs to be
//! re-rendered.
//! @note Must be set to a valid callback, unless `.get_header_height` is
//! `NULL`. Causes undefined behavior otherwise.
MenuLayerDrawHeaderCallback draw_header;
//! Callback that gets called when the user triggers a click with the SELECT
//! button.
//! @note When `NULL`, click events for the SELECT button are ignored.
MenuLayerSelectCallback select_click;
//! Callback that gets called when the user triggers a long click with the
//! SELECT button.
//! @note When `NULL`, long click events for the SELECT button are ignored.
MenuLayerSelectCallback select_long_click;
//! Callback that gets called whenever the selection changes.
//! @note When `NULL`, selection change events are ignored.
MenuLayerSelectionChangedCallback selection_changed;
//! Callback that gets called to get the height of a separator
//! This can get called at various moments throughout the life of a menu.
//! @note When `NULL`, the default height of 1 is used.
MenuLayerGetSeparatorHeightCallback get_separator_height;
//! Callback that gets called to render a separator.
//! This gets called for each separator, every time it needs to be
//! re-rendered.
//! @note Must be set to a valid callback, unless `.get_separator_height` is
//! `NULL`. Causes undefined behavior otherwise.
MenuLayerDrawSeparatorCallback draw_separator;
//! Callback that gets called before the selected cell changes.
//! This gets called before the selected item in the MenuLayer is changed,
//! and will allow for the selected cell to be overridden.
//! This allows for skipping cells in the menu, locking selection onto a given item,
MenuLayerSelectionWillChangeCallback selection_will_change;
//! Callback that gets called before any cells are drawn.
//! This supports two states, either highlighted or not highlighted.
//! If highlighted is specified, it is expected to be colored in the same
//! style as the menu's cells are.
//! If this callback is not specified, it will default to the colors set with
//! \ref menu_layer_set_normal_colors and \ref menu_layer_set_highlight_colors.
MenuLayerDrawBackgroundCallback draw_background;
} MenuLayerCallbacks;
//! Creates a new \ref MenuLayer on the heap and initalizes it with the default values.
//!
//! * Clips: `true`
//! * Hidden: `false`
//! * Content size: `frame.size`
//! * Content offset: \ref GPointZero
//! * Callbacks: None (`NULL` for each one)
//! * Callback context: `NULL`
//! * After the relevant callbacks are called to populate the menu, the item at MenuIndex(0, 0)
//! will be selected initially.
//! @return A pointer to the \ref MenuLayer. `NULL` if the \ref MenuLayer could not
//! be created
MenuLayer* menu_layer_create(GRect frame);
//! Destroys a \ref MenuLayer previously created by menu_layer_create.
void menu_layer_destroy(MenuLayer* menu_layer);
//! Gets the "root" Layer of the \ref MenuLayer, which is the parent for the sub-
//! layers used for its implementation.
//! @param menu_layer Pointer to the MenuLayer for which to get the "root" Layer
//! @return The "root" Layer of the \ref MenuLayer.
Layer* menu_layer_get_layer(const MenuLayer *menu_layer);
//! Gets the ScrollLayer of the \ref MenuLayer, which is the layer responsible for
//! the scrolling of the \ref MenuLayer.
//! @param menu_layer Pointer to the \ref MenuLayer for which to get the ScrollLayer
//! @return The ScrollLayer of the \ref MenuLayer.
ScrollLayer* menu_layer_get_scroll_layer(const MenuLayer *menu_layer);
//! Sets the callbacks for the MenuLayer.
//! @param menu_layer Pointer to the \ref MenuLayer for which to set the callbacks
//! and callback context.
//! @param callback_context The new callback context. This is passed into each
//! of the callbacks and can be set to point to application provided data.
//! @param callbacks The new callbacks for the \ref MenuLayer. The storage for this
//! data structure must be long lived. Therefore, it cannot be stack-allocated.
//! @see MenuLayerCallbacks
void menu_layer_set_callbacks(MenuLayer *menu_layer,
void *callback_context,
MenuLayerCallbacks callbacks);
//! Convenience function to set the \ref ClickConfigProvider callback on the
//! given window to the \ref MenuLayer internal click config provider. This internal
//! click configuration provider, will set up the default UP & DOWN
//! scrolling / menu item selection behavior.
//! This function calls \ref scroll_layer_set_click_config_onto_window to
//! accomplish this.
//!
//! Click and long click events for the SELECT button can be handled by
//! installing the appropriate callbacks using \ref menu_layer_set_callbacks().
//! This is a deviation from the usual click configuration provider pattern.
//! @param menu_layer The \ref MenuLayer that needs to receive click events.
//! @param window The window for which to set the click configuration.
//! @see \ref Clicks
//! @see \ref window_set_click_config_provider_with_context()
//! @see \ref scroll_layer_set_click_config_onto_window()
void menu_layer_set_click_config_onto_window(MenuLayer *menu_layer,
struct Window *window);
//! Values to specify how a (selected) row should be aligned relative to the
//! visible area of the \ref MenuLayer.
typedef enum {
//! Don't align or update the scroll offset of the \ref MenuLayer.
MenuRowAlignNone,
//! Scroll the contents of the \ref MenuLayer in such way that the selected row
//! is centered relative to the visible area.
MenuRowAlignCenter,
//! Scroll the contents of the \ref MenuLayer in such way that the selected row
//! is at the top of the visible area.
MenuRowAlignTop,
//! Scroll the contents of the \ref MenuLayer in such way that the selected row
//! is at the bottom of the visible area.
MenuRowAlignBottom,
} MenuRowAlign;
//! Selects the next or previous item, relative to the current selection.
//! @param menu_layer The \ref MenuLayer for which to select the next item
//! @param up Supply `false` to select the next item in the list (downwards),
//! or `true` to select the previous item in the list (upwards).
//! @param scroll_align The alignment of the new selection
//! @param animated Supply `true` to animate changing the selection, or `false`
//! to change the selection instantly.
//! @note If there is no next/previous item, this function is a no-op.
void menu_layer_set_selected_next(MenuLayer *menu_layer,
bool up,
MenuRowAlign scroll_align,
bool animated);
//! Selects the item with given \ref MenuIndex.
//! @param menu_layer The \ref MenuLayer for which to change the selection
//! @param index The index of the item to select
//! @param scroll_align The alignment of the new selection
//! @param animated Supply `true` to animate changing the selection, or `false`
//! to change the selection instantly.
//! @note If the section and/or row index exceeds the avaible number of sections
//! or resp. rows, the exceeding index/indices will be capped, effectively
//! selecting the last section and/or row, resp.
void menu_layer_set_selected_index(MenuLayer *menu_layer,
MenuIndex index, MenuRowAlign scroll_align,
bool animated);
//! Gets the MenuIndex of the currently selection menu item.
//! @param menu_layer The \ref MenuLayer for which to get the current selected index.
//! @see menu_cell_layer_is_highlighted
//! @note This function should not be used to determine whether a cell should be
//! highlighted or not. See \ref menu_cell_layer_is_highlighted for more
//! information.
MenuIndex menu_layer_get_selected_index(const MenuLayer *menu_layer);
//! Reloads the data of the menu. This causes the menu to re-request the menu
//! item data, by calling the relevant callbacks.
//! The current selection and scroll position will not be changed. See the
//! note with \ref menu_layer_set_selected_index() for the behavior if the
//! old selection is no longer valid.
//! @param menu_layer The \ref MenuLayer for which to reload the data.
void menu_layer_reload_data(MenuLayer *menu_layer);
//! Returns whether or not the given cell layer is highlighted.
//! Using this for determining highlight behaviour is preferable to using
//! \ref menu_layer_get_selected_index. Row drawing callbacks may be invoked multiple
//! times with a different highlight status on the same cell in order to handle partially
//! highlighted cells during animation.
//! @param cell_layer The \ref Layer for the cell to check highlight status.
//! @return true if the given cell layer is highlighted in the menu.
bool menu_cell_layer_is_highlighted(const Layer *cell_layer);
//! Set the default colors to be used for cells when it is in a normal state (not highlighted).
//! The GContext's text and fill colors will be set appropriately prior to calling the `.draw_row`
//! callback.
//! If this function is not explicitly called on a \ref MenuLayer, it will default to white
//! background with black foreground.
//! @param menu_layer The \ref MenuLayer for which to set the colors.
//! @param background The color to be used for the background of the cell.
//! @param foreground The color to be used for the foreground and text of the cell.
//! @see \ref menu_layer_set_highlight_colors
void menu_layer_set_normal_colors(MenuLayer *menu_layer, GColor background, GColor foreground);
//! Set the default colors to be used for cells when it is in a highlighted state.
//! The GContext's text and fill colors will be set appropriately prior to calling the `.draw_row`
//! callback.
//! If this function is not explicitly called on a \ref MenuLayer, it will default to black
//! background with white foreground.
//! @param menu_layer The \ref MenuLayer for which to set the colors.
//! @param background The color to be used for the background of the cell.
//! @param foreground The color to be used for the foreground and text of the cell.
//! @see \ref menu_layer_set_normal_colors
void menu_layer_set_highlight_colors(MenuLayer *menu_layer, GColor background, GColor foreground);
//! This enables or disables padding at the bottom of the \ref MenuLayer.
//! Padding at the bottom of the layer keeps the bottom item from being at the very bottom of the
//! screen.
//! Padding is turned on by default for all MenuLayers.
//! The color of the padded area will be the background color set using
//! \ref menu_layer_set_normal_colors(). To color the padding a different color, use
//! \ref MenuLayerDrawBackgroundCallback.
//! @param menu_layer The menu layer for which to enable or disable the padding.
//! @param enable True = enable padding, False = disable padding.
void menu_layer_pad_bottom_enable(MenuLayer *menu_layer, bool enable);
//! @} // group MenuLayer
//! @addtogroup SimpleMenuLayer
//! \brief Wrapper around \ref MenuLayer, that uses static data to display a
//! list menu.
//!
//! ![](simple_menu_layer.png)
//! @{
struct SimpleMenuLayer;
typedef struct SimpleMenuLayer SimpleMenuLayer;
//! Function signature for the callback to handle the event that a user hits
//! the SELECT button.
//! @param index The row index of the item
//! @param context The callback context
typedef void (*SimpleMenuLayerSelectCallback)(int index, void *context);
//! Data structure containing the information of a menu item.
typedef struct {
//! The title of the menu item. Required.
const char *title;
//! The subtitle of the menu item. Optional, leave `NULL` if unused.
const char *subtitle;
//! The icon of the menu item. Optional, leave `NULL` if unused.
GBitmap *icon;
//! The callback that needs to be called upon a click on the SELECT button.
//! Optional, leave `NULL` if unused.
SimpleMenuLayerSelectCallback callback;
} SimpleMenuItem;
//! Data structure containing the information of a menu section.
typedef struct {
//! Title of the section. Optional, leave `NULL` if unused.
const char *title;
//! Array of items in the section.
const SimpleMenuItem *items;
//! Number of items in the `.items` array.
uint32_t num_items;
} SimpleMenuSection;
//! Creates a new SimpleMenuLayer on the heap and initializes it.
//! It also sets the internal click configuration provider onto given window.
//! @param frame The frame at which to initialize the menu
//! @param window The window onto which to set the click configuration provider
//! @param sections Array with sections that need to be displayed in the menu
//! @param num_sections The number of sections in the `sections` array.
//! @param callback_context Pointer to application specific data, that is passed
//! into the callbacks.
//! @note The `sections` array is not deep-copied and can therefore not be stack
//! allocated, but needs to be backed by long-lived storage.
//! @note This function does not add the menu's layer to the window.
//! @return A pointer to the SimpleMenuLayer. `NULL` if the SimpleMenuLayer could not
//! be created
SimpleMenuLayer* simple_menu_layer_create(GRect frame, Window *window,
const SimpleMenuSection *sections, int32_t num_sections, void *callback_context);
//! Destroys a SimpleMenuLayer previously created by simple_menu_layer_create.
void simple_menu_layer_destroy(SimpleMenuLayer* menu_layer);
//! Gets the "root" Layer of the simple menu layer, which is the parent for the
//! sub-layers used for its implementation.
//! @param simple_menu Pointer to the SimpleMenuLayer for which to get the
//! "root" Layer
//! @return The "root" Layer of the menu layer.
Layer* simple_menu_layer_get_layer(const SimpleMenuLayer *simple_menu);
//! Gets the row index of the currently selection menu item.
//! @param simple_menu The SimpleMenuLayer for which to get the current
//! selected row index.
int simple_menu_layer_get_selected_index(const SimpleMenuLayer *simple_menu);
//! Selects the item in the first section at given row index.
//! @param simple_menu The SimpleMenuLayer for which to change the selection
//! @param index The row index of the item to select
//! @param animated Supply `true` to animate changing the selection, or `false`
//! to change the selection instantly.
void simple_menu_layer_set_selected_index(SimpleMenuLayer *simple_menu, int32_t index, bool animated);
//! @param simple_menu The \ref SimpleMenuLayer to get the \ref MenuLayer from.
//! @return The \ref MenuLayer.
MenuLayer *simple_menu_layer_get_menu_layer(SimpleMenuLayer *simple_menu);
//! @} // group SimpleMenuLayer
//! @addtogroup ActionBarLayer
//! \brief Vertical, bar-shaped control widget on the right edge of the window
//!
//! ![](action_bar_layer.png)
//! ActionBarLayer is a Layer that displays a bar on the right edge of the
//! window. The bar can contain up to 3 icons, each corresponding with one of
//! the buttons on the right side of the watch. The purpose of each icon is
//! to provide a hint (feed-forward) to what action a click on the respective
//! button will cause.
//!
//! The action bar is useful when there are a few (up to 3) main actions that
//! are desirable to be able to take quickly, literally with one press of a
//! button.
//!
//! <h3>More actions</h3>
//! If there are more than 3 actions the user might want to take:
//! * Try assigning the top and bottom icons of the action bar to the two most
//! immediate actions and use the middle icon to push a Window with a MenuLayer
//! with less immediate actions.
//! * Secondary actions that are not vital, can be "hidden" under a long click.
//! Try to group similar actions to one button. For example, in a Music app,
//! a single click on the top button is tied to the action to jump to the
//! previous track. Holding that same button means seek backwards.
//!
//! <h3>Directionality mapping</h3>
//! When the top and bottom buttons are used to control navigating through
//! a (potentially virtual, non-visible) list of items, follow this guideline:
//! * Tie the top button to the action that goes to the _previous_ item in the
//! list, for example "jump to previous track" in a Music app.
//! * Tie the bottom button to the action that goes to the _next_ item in the
//! list, for example "jump to next track" in a Music app.
//!
//! <h3>Geometry</h3>
//! * The action bar is 30 pixels wide. Use the \ref ACTION_BAR_WIDTH define.
//! * Icons should not be wider than 28 pixels, or taller than 18 pixels.
//! It is recommended to use a size of around 15 x 15 pixels for the "visual core" of the icon,
//! and extending or contracting where needed.
//! <h3>Example Code</h3>
//! The code example below shows how to do the initial setup of the action bar
//! in a window's `.load` handler.
//! Configuring the button actions is similar to the process when using
//! \ref window_set_click_config_provider(). See \ref Clicks for more
//! information.
//!
//! \code{.c}
//! ActionBarLayer *action_bar;
//!
//! // The implementation of my_next_click_handler and my_previous_click_handler
//! // is omitted for the sake of brevity. See the Clicks reference docs.
//!
//! void click_config_provider(void *context) {
//! window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) my_next_click_handler);
//! window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler) my_previous_click_handler);
//! }
//!
//! void window_load(Window *window) {
//! ...
//! // Initialize the action bar:
//! action_bar = action_bar_layer_create();
//! // Associate the action bar with the window:
//! action_bar_layer_add_to_window(action_bar, window);
//! // Set the click config provider:
//! action_bar_layer_set_click_config_provider(action_bar,
//! click_config_provider);
//!
//! // Set the icons:
//! // The loading of the icons is omitted for brevity... See gbitmap_create_with_resource()
//! action_bar_layer_set_icon_animated(action_bar, BUTTON_ID_UP, my_icon_previous, true);
//! action_bar_layer_set_icon_animated(action_bar, BUTTON_ID_DOWN, my_icon_next, true);
//! }
//! \endcode
//! @{
//! The width of the action bar in pixels.
#define ACTION_BAR_WIDTH 30
//! The maximum number of action bar items.
#define NUM_ACTION_BAR_ITEMS 3
typedef enum {
ActionBarLayerIconPressAnimationNone = 0,
ActionBarLayerIconPressAnimationMoveLeft,
ActionBarLayerIconPressAnimationMoveUp,
ActionBarLayerIconPressAnimationMoveRight,
ActionBarLayerIconPressAnimationMoveDown,
} ActionBarLayerIconPressAnimation;
struct ActionBarLayer;
typedef struct ActionBarLayer ActionBarLayer;
//! Creates a new ActionBarLayer on the heap and initalizes it with the default values.
//! * Background color: \ref GColorBlack
//! * No click configuration provider (`NULL`)
//! * No icons
//! * Not added to / associated with any window, thus not catching any button input yet.
//! @return A pointer to the ActionBarLayer. `NULL` if the ActionBarLayer could not
//! be created
ActionBarLayer* action_bar_layer_create(void);
//! Destroys a ActionBarLayer previously created by action_bar_layer_create
void action_bar_layer_destroy(ActionBarLayer *action_bar_layer);
//! Gets the "root" Layer of the action bar layer, which is the parent for the sub-
//! layers used for its implementation.
//! @param action_bar_layer Pointer to the ActionBarLayer for which to get the "root" Layer
//! @return The "root" Layer of the action bar layer.
Layer* action_bar_layer_get_layer(ActionBarLayer *action_bar_layer);
//! Sets the context parameter, which will be passed in to \ref ClickHandler
//! callbacks and the \ref ClickConfigProvider callback of the action bar.
//! @note By default, a pointer to the action bar itself is passed in, if the
//! context has not been set or if it has been set to `NULL`.
//! @param action_bar The action bar for which to assign the new context
//! @param context The new context
//! @see action_bar_layer_set_click_config_provider()
//! @see \ref Clicks
void action_bar_layer_set_context(ActionBarLayer *action_bar, void *context);
//! Sets the click configuration provider callback of the action bar.
//! In this callback your application can associate handlers to the different
//! types of click events for each of the buttons, see \ref Clicks.
//! @note If the action bar had already been added to a window and the window
//! is currently on-screen, the click configuration provider will be called
//! before this function returns. Otherwise, it will be called by the system
//! when the window becomes on-screen.
//! @note The `.raw` handlers cannot be used without breaking the automatic
//! highlighting of the segment of the action bar that for which a button is
//! @see action_bar_layer_set_icon()
//! @param action_bar The action bar for which to assign a new click
//! configuration provider
//! @param click_config_provider The new click configuration provider
void action_bar_layer_set_click_config_provider(ActionBarLayer *action_bar, ClickConfigProvider click_config_provider);
//! Sets an action bar icon onto one of the 3 slots as identified by `button_id`.
//! Only \ref BUTTON_ID_UP, \ref BUTTON_ID_SELECT and \ref BUTTON_ID_DOWN can be
//! used. The transition will not be animated.
//! Whenever an icon is set, the click configuration provider will be
//! called, to give the application the opportunity to reconfigure the button
//! interaction.
//! @param action_bar The action bar for which to set the new icon
//! @param button_id The identifier of the button for which to set the icon
//! @param icon Pointer to the \ref GBitmap icon
//! @see action_bar_layer_set_icon_animated()
//! @see action_bar_layer_set_icon_press_animation()
//! @see action_bar_layer_set_click_config_provider()
void action_bar_layer_set_icon(ActionBarLayer *action_bar, ButtonId button_id, const GBitmap *icon);
//! Convenience function to clear out an existing icon.
//! All it does is call `action_bar_layer_set_icon(action_bar, button_id, NULL)`
//! @param action_bar The action bar for which to clear an icon
//! @param button_id The identifier of the button for which to clear the icon
//! @see action_bar_layer_set_icon()
void action_bar_layer_clear_icon(ActionBarLayer *action_bar, ButtonId button_id);
//! Adds the action bar's layer on top of the window's root layer. It also
//! adjusts the layout of the action bar to match the geometry of the window it
//! gets added to.
//! Lastly, it calls \ref window_set_click_config_provider_with_context() on
//! the window to set it up to work with the internal callback and raw click
//! handlers of the action bar, to enable the highlighting of the section of the
//! action bar when the user presses a button.
//! @note After this call, do not use
//! \ref window_set_click_config_provider_with_context() with the window that
//! the action bar has been added to (this would de-associate the action bar's
//! click config provider and context). Instead use
//! \ref action_bar_layer_set_click_config_provider() and
//! \ref action_bar_layer_set_context() to register the click configuration
//! provider to configure the buttons actions.
//! @note It is advised to call this is in the window's `.load` or `.appear`
//! handler. Make sure to call \ref action_bar_layer_remove_from_window() in the
//! window's `.unload` or `.disappear` handler.
//! @note Adding additional layers to the window's root layer after this calll
//! can occlude the action bar.
//! @param action_bar The action bar to associate with the window
//! @param window The window with which the action bar is to be associated
void action_bar_layer_add_to_window(ActionBarLayer *action_bar, struct Window *window);
//! Removes the action bar from the window and unconfigures the window's
//! click configuration provider. `NULL` is set as the window's new click config
//! provider and also as its callback context. If it has not been added to a
//! window before, this function is a no-op.
//! @param action_bar The action bar to de-associate from its current window
void action_bar_layer_remove_from_window(ActionBarLayer *action_bar);
//! Sets the background color of the action bar. Defaults to \ref GColorBlack.
//! The action bar's layer is automatically marked dirty.
//! @param action_bar The action bar of which to set the background color
//! @param background_color The new background color
void action_bar_layer_set_background_color(ActionBarLayer *action_bar, GColor background_color);
//! Sets an action bar icon onto one of the 3 slots as identified by `button_id`.
//! Only \ref BUTTON_ID_UP, \ref BUTTON_ID_SELECT and \ref BUTTON_ID_DOWN can be
//! used. Optionally, if `animated` is `true`, the transition will be animated.
//! Whenever an icon is set, the click configuration provider will be called,
//! to give the application the opportunity to reconfigure the button interaction.
//! @param action_bar The action bar for which to set the new icon
//! @param button_id The identifier of the button for which to set the icon
//! @param icon Pointer to the \ref GBitmap icon
//! @param animated True = animate the transition, False = do not animate the transition
//! @see action_bar_layer_set_icon()
//! @see action_bar_layer_set_icon_press_animation()
//! @see action_bar_layer_set_click_config_provider()
void action_bar_layer_set_icon_animated(ActionBarLayer *action_bar, ButtonId button_id,
const GBitmap *icon, bool animated);
//! Sets the animation to use while a button is pressed on an ActionBarLayer.
//! By default we use ActionBarLayerIconPressAnimationMoveLeft
//! @param action_bar The action bar for which to set the press animation
//! @param button_id The button for which to set the press animation
//! @param animation The animation to use.
//! @see action_bar_layer_set_icon_animated()
//! @see action_bar_layer_set_click_config_provider()
void action_bar_layer_set_icon_press_animation(ActionBarLayer *action_bar, ButtonId button_id,
ActionBarLayerIconPressAnimation animation);
//! @} // group ActionBarLayer
//! @addtogroup StatusBarLayer
//! \brief Layer that serves as a configurable status bar.
//! @{
struct StatusBarLayer;
typedef struct StatusBarLayer StatusBarLayer;
//! Values that are used to indicate the different status bar separator modes.
typedef enum {
//! No separator will be shown.
StatusBarLayerSeparatorModeNone = 0,
//! The default mode. A dotted separator at the bottom of the status bar.
StatusBarLayerSeparatorModeDotted = 1,
} StatusBarLayerSeparatorMode;
//! The fixed height of the status bar, including separator height
#define STATUS_BAR_LAYER_HEIGHT 16
//! Creates a new StatusBarLayer on the heap and initializes it with the default values.
//!
//! * Text color: \ref GColorBlack
//! * Background color: \ref GColorWhite
//! * Frame: `GRect(0, 0, screen_width, STATUS_BAR_LAYER_HEIGHT)`
//! The status bar is automatically marked dirty after this operation.
//! You can call \ref layer_set_frame() to create a StatusBarLayer of a different width.
//!
//! \code{.c}
//! // Change the status bar width to make space for the action bar
//! int16_t width = layer_get_bounds(root_layer).size.w - ACTION_BAR_WIDTH;
//! GRect frame = GRect(0, 0, width, STATUS_BAR_LAYER_HEIGHT);
//! layer_set_frame(status_bar_layer_get_layer(status_bar), frame);
//! layer_add_child(root_layer, status_bar_layer_get_layer(status_bar));
//! \endcode
//! @return A pointer to the StatusBarLayer, which will be allocated to the heap,
//! `NULL` if the StatusBarLayer could not be created
StatusBarLayer *status_bar_layer_create(void);
//! Destroys a StatusBarLayer previously created by status_bar_layer_create.
//! @param status_bar_layer The StatusBarLayer to destroy
void status_bar_layer_destroy(StatusBarLayer *status_bar_layer);
//! Gets the "root" Layer of the status bar, which is the parent for the sub-
//! layers used for its implementation.
//! @param status_bar_layer Pointer to the StatusBarLayer for which to get the "root" Layer
//! @return The "root" Layer of the status bar.
//! @note The result is always equal to `(Layer *) status_bar_layer`.
Layer *status_bar_layer_get_layer(StatusBarLayer *status_bar_layer);
//! Gets background color of StatusBarLayer
//! @param status_bar_layer The StatusBarLayer of which to get the color
//! @return GColor of background color property
GColor status_bar_layer_get_background_color(const StatusBarLayer *status_bar_layer);
//! Gets foreground color of StatusBarLayer
//! @param status_bar_layer The StatusBarLayer of which to get the color
//! @return GColor of foreground color property
GColor status_bar_layer_get_foreground_color(const StatusBarLayer *status_bar_layer);
//! Sets the background and foreground colors of StatusBarLayer
//! @param status_bar_layer The StatusBarLayer of which to set the colors
//! @param background The new \ref GColor to set for background
//! @param foreground The new \ref GColor to set for text and other foreground elements
void status_bar_layer_set_colors(StatusBarLayer *status_bar_layer, GColor background,
GColor foreground);
//! Sets the mode of the StatusBarLayer separator, to help divide it from content
//! @param status_bar_layer The StatusBarLayer of which to set the separator mode
//! @param mode Determines the separator mode
void status_bar_layer_set_separator_mode(StatusBarLayer *status_bar_layer,
StatusBarLayerSeparatorMode mode);
//! @} // group StatusBarLayer
//! @addtogroup BitmapLayer
//! \brief Layer that displays a bitmap image.
//!
//! ![](bitmap_layer.png)
//! BitmapLayer is a Layer subtype that draws a GBitmap within its frame. It uses an alignment property
//! to specify how to position the bitmap image within its frame. Optionally, when the
//! background color is not GColorClear, it draws a solid background color behind the
//! bitmap image, filling areas of the frame that are not covered by the bitmap image.
//! Lastly, using the compositing mode property of the BitmapLayer, determines the way the
//! bitmap image is drawn on top of what is underneath it (either the background color, or
//! the layers beneath it).
//!
//! <h3>Inside the Implementation</h3>
//! The implementation of BitmapLayer is fairly straightforward and relies heavily on the
//! functionality as exposed by the core drawing functions (see \ref Drawing).
//! \ref BitmapLayer's drawing callback uses \ref graphics_draw_bitmap_in_rect()
//! to perform the actual drawing of the \ref GBitmap. It uses \ref grect_align() to perform
//! the layout of the image and it uses \ref graphics_fill_rect() to draw the background plane.
//! @{
struct BitmapLayer;
typedef struct BitmapLayer BitmapLayer;
//! Creates a new bitmap layer on the heap and initalizes it the default values.
//!
//! * Bitmap: `NULL` (none)
//! * Background color: \ref GColorClear
//! * Compositing mode: \ref GCompOpAssign
//! * Clips: `true`
//! @return A pointer to the BitmapLayer. `NULL` if the BitmapLayer could not
//! be created
BitmapLayer* bitmap_layer_create(GRect frame);
//! Destroys a window previously created by bitmap_layer_create
void bitmap_layer_destroy(BitmapLayer* bitmap_layer);
//! Gets the "root" Layer of the bitmap layer, which is the parent for the sub-
//! layers used for its implementation.
//! @param bitmap_layer Pointer to the BitmapLayer for which to get the "root" Layer
//! @return The "root" Layer of the bitmap layer.
Layer* bitmap_layer_get_layer(const BitmapLayer *bitmap_layer);
//! Gets the pointer to the bitmap image that the BitmapLayer is using.
//!
//! @param bitmap_layer The BitmapLayer for which to get the bitmap image
//! @return A pointer to the bitmap image that the BitmapLayer is using
const GBitmap* bitmap_layer_get_bitmap(BitmapLayer *bitmap_layer);
//! Sets the bitmap onto the BitmapLayer. The bitmap is set by reference (no deep
//! copy), thus the caller of this function has to make sure the bitmap is kept
//! in memory.
//!
//! The bitmap layer is automatically marked dirty after this operation.
//! @param bitmap_layer The BitmapLayer for which to set the bitmap image
//! @param bitmap The new \ref GBitmap to set onto the BitmapLayer
void bitmap_layer_set_bitmap(BitmapLayer *bitmap_layer, const GBitmap *bitmap);
//! Sets the alignment of the image to draw with in frame of the BitmapLayer.
//! The aligment parameter specifies which edges of the bitmap should overlap
//! with the frame of the BitmapLayer.
//! If the bitmap is smaller than the frame of the BitmapLayer, the background
//! is filled with the background color.
//!
//! The bitmap layer is automatically marked dirty after this operation.
//! @param bitmap_layer The BitmapLayer for which to set the aligment
//! @param alignment The new alignment for the image inside the BitmapLayer
void bitmap_layer_set_alignment(BitmapLayer *bitmap_layer, GAlign alignment);
//! Sets the background color of bounding box that will be drawn behind the image
//! of the BitmapLayer.
//!
//! The bitmap layer is automatically marked dirty after this operation.
//! @param bitmap_layer The BitmapLayer for which to set the background color
//! @param color The new \ref GColor to set the background to
void bitmap_layer_set_background_color(BitmapLayer *bitmap_layer, GColor color);
//! Sets the compositing mode of how the bitmap image is composited onto the
//! BitmapLayer's background plane, or how it is composited onto what has been
//! drawn beneath the BitmapLayer.
//!
//! The compositing mode only affects the drawing of the bitmap and not the
//! drawing of the background color.
//!
//! For Aplite, there is no notion of "transparency" in the graphics system. However, the effect of
//! transparency can be created by masking and using compositing modes.
//!
//! For Basalt, when drawing \ref GBitmap images, \ref GCompOpSet will be required to apply any
//! transparency.
//!
//! The bitmap layer is automatically marked dirty after this operation.
//! @param bitmap_layer The BitmapLayer for which to set the compositing mode
//! @param mode The compositing mode to set
//! @see See \ref GCompOp for visual examples of the different compositing modes.
void bitmap_layer_set_compositing_mode(BitmapLayer *bitmap_layer, GCompOp mode);
//! @} // group BitmapLayer
//! @addtogroup RotBitmapLayer
//! \brief Layer that displays a rotated bitmap image.
//!
//! A RotBitmapLayer is like a \ref BitmapLayer but has the ability to be rotated (by default, around its center). The amount of rotation
//! is specified using \ref rot_bitmap_layer_set_angle() or \ref rot_bitmap_layer_increment_angle(). The rotation argument
//! to those functions is specified as an amount of clockwise rotation, where the value 0x10000 represents a full 360 degree
//! rotation and 0 represent no rotation, and it scales linearly between those values, just like \ref sin_lookup.
//!
//! The center of rotation in the source bitmap is always placed at the center of the RotBitmapLayer and the size of the RotBitmapLayer
//! is automatically calculated so that the entire Bitmap can fit in at all rotation angles.
//!
//! For example, if the image is 10px wide and high, the RotBitmapLayer will be 14px wide ( sqrt(10^2+10^2) ).
//!
//! By default, the center of rotation in the source bitmap is the center of the bitmap but you can call \ref rot_bitmap_set_src_ic() to change the
//! center of rotation.
//!
//! @note RotBitmapLayer has performance limitations that can degrade user
//! experience (see \ref graphics_draw_rotated_bitmap). Use sparingly.
//! @{
struct RotBitmapLayer;
typedef struct RotBitmapLayer RotBitmapLayer;
//! Creates a new RotBitmapLayer on the heap and initializes it with the default values:
//! * Angle: 0
//! * Compositing mode: \ref GCompOpAssign
//! * Corner clip color: \ref GColorClear
//!
//! @param bitmap The bitmap to display in this RotBitmapLayer
//! @return A pointer to the RotBitmapLayer. `NULL` if the RotBitmapLayer could not
//! be created
RotBitmapLayer* rot_bitmap_layer_create(GBitmap *bitmap);
//! Destroys a RotBitmapLayer and frees all associated memory.
//! @note It is the developer responsibility to free the \ref GBitmap.
//! @param bitmap The RotBitmapLayer to destroy.
void rot_bitmap_layer_destroy(RotBitmapLayer *bitmap);
//! Defines what color to use in areas that are not covered by the source bitmap.
//! By default this is \ref GColorClear.
//! @param bitmap The RotBitmapLayer on which to change the corner clip color
//! @param color The corner clip color
void rot_bitmap_layer_set_corner_clip_color(RotBitmapLayer *bitmap, GColor color);
//! Sets the rotation angle of this RotBitmapLayer
//! @param bitmap The RotBitmapLayer on which to change the rotation
//! @param angle Rotation is an integer between 0 (no rotation) and 0x10000 (360 degree rotation). @see sin_lookup()
void rot_bitmap_layer_set_angle(RotBitmapLayer *bitmap, int32_t angle);
//! Change the rotation angle of this RotBitmapLayer
//! @param bitmap The RotBitmapLayer on which to change the rotation
//! @param angle_change The rotation angle change
void rot_bitmap_layer_increment_angle(RotBitmapLayer *bitmap, int32_t angle_change);
//! Defines the only point that will not be affected by the rotation in the source bitmap.
//!
//! For example, if you pass GPoint(0, 0), the image will rotate around the top-left corner.
//!
//! This point is always projected at the center of the RotBitmapLayer. Calling this function
//! automatically adjusts the width and height of the RotBitmapLayer so that
//! the entire bitmap can fit inside the layer at all rotation angles.
//!
//! @param bitmap The RotBitmapLayer on which to change the rotation
//! @param ic The only point in the original image that will not be affected by the rotation.
void rot_bitmap_set_src_ic(RotBitmapLayer *bitmap, GPoint ic);
//! Sets the compositing mode of how the bitmap image is composited onto what has been drawn beneath the
//! RotBitmapLayer.
//! By default this is \ref GCompOpAssign.
//! The RotBitmapLayer is automatically marked dirty after this operation.
//! @param bitmap The RotBitmapLayer on which to change the rotation
//! @param mode The compositing mode to set
//! @see \ref GCompOp for visual examples of the different compositing modes.
void rot_bitmap_set_compositing_mode(RotBitmapLayer *bitmap, GCompOp mode);
//! @} // group RotBitmapLayer
//! @} // group Layer
//! @addtogroup Window
//! @{
//! @addtogroup NumberWindow
//! \brief A ready-made Window prompting the user to pick a number
//!
//! ![](number_window.png)
//! @{
struct NumberWindow;
typedef struct NumberWindow NumberWindow;
//! Function signature for NumberWindow callbacks.
typedef void (*NumberWindowCallback)(struct NumberWindow *number_window, void *context);
//! Data structure containing all the callbacks for a NumberWindow.
typedef struct {
//! Callback that gets called as the value is incremented.
//! Optional, leave `NULL` if unused.
NumberWindowCallback incremented;
//! Callback that gets called as the value is decremented.
//! Optional, leave `NULL` if unused.
NumberWindowCallback decremented;
//! Callback that gets called as the value is confirmed, in other words the
//! SELECT button is clicked.
//! Optional, leave `NULL` if unused.
NumberWindowCallback selected;
} NumberWindowCallbacks;
//! Creates a new NumberWindow on the heap and initalizes it with the default values.
//!
//! @param label The title or prompt to display in the NumberWindow. Must be long-lived and cannot be stack-allocated.
//! @param callbacks The callbacks
//! @param callback_context Pointer to application specific data that is passed
//! @note The number window is not pushed to the window stack. Use \ref window_stack_push() to do this.
//! @return A pointer to the NumberWindow. `NULL` if the NumberWindow could not
//! be created
NumberWindow* number_window_create(const char *label, NumberWindowCallbacks callbacks, void *callback_context);
//! Destroys a NumberWindow previously created by number_window_create.
void number_window_destroy(NumberWindow* number_window);
//! Sets the text of the title or prompt label.
//! @param numberwindow Pointer to the NumberWindow for which to set the label
//! text
//! @param label The new label text. Must be long-lived and cannot be
//! stack-allocated.
void number_window_set_label(NumberWindow *numberwindow, const char *label);
//! Sets the maximum value this field can hold
//! @param numberwindow Pointer to the NumberWindow for which to set the maximum
//! value
//! @param max The maximum value
void number_window_set_max(NumberWindow *numberwindow, int32_t max);
//! Sets the minimum value this field can hold
//! @param numberwindow Pointer to the NumberWindow for which to set the minimum
//! value
//! @param min The minimum value
void number_window_set_min(NumberWindow *numberwindow, int32_t min);
//! Sets the current value of the field
//! @param numberwindow Pointer to the NumberWindow for which to set the current
//! value
//! @param value The new current value
void number_window_set_value(NumberWindow *numberwindow, int32_t value);
//! Sets the amount by which to increment/decrement by on a button click
//! @param numberwindow Pointer to the NumberWindow for which to set the step
//! increment
//! @param step The new step increment
void number_window_set_step_size(NumberWindow *numberwindow, int32_t step);
//! Gets the current value
//! @param numberwindow Pointer to the NumberWindow for which to get the current
//! value
//! @return The current value
int32_t number_window_get_value(const NumberWindow *numberwindow);
//! Gets the "root" Window of the number window
//! @param numberwindow Pointer to the NumberWindow for which to get the "root" Window
//! @return The "root" Window of the number window.
Window *number_window_get_window(NumberWindow *numberwindow);
//! @} // group NumberWindow
//! @} // group Window
//! @addtogroup Vibes
//! \brief Controlling the vibration motor
//!
//! The Vibes API provides calls that let you control Pebble’s vibration motor.
//!
//! The vibration motor can be used as a visceral mechanism for giving immediate feedback to the user.
//! You can use it to highlight important moments in games, or to draw the attention of the user.
//! However, you should use the vibration feature sparingly, because sustained use will rapidly deplete Pebble’s battery,
//! and vibrating Pebble too much and too often can become annoying for users.
//! @note When using these calls, if there is an ongoing vibration,
//! calling any of the functions to emit (another) vibration will have no effect.
//! @{
/** Data structure describing a vibration pattern.
A pattern consists of at least 1 vibe-on duration, optionally followed by
alternating vibe-off + vibe-on durations. Each segment may have a different duration.
Example code:
\code{.c}
// Vibe pattern: ON for 200ms, OFF for 100ms, ON for 400ms:
static const uint32_t const segments[] = { 200, 100, 400 };
VibePattern pat = {
.durations = segments,
.num_segments = ARRAY_LENGTH(segments),
};
vibes_enqueue_custom_pattern(pat);
\endcode
@see vibes_enqueue_custom_pattern
*/
typedef struct {
/**
Pointer to an array of segment durations, measured in milli-seconds.
The maximum allowed duration is 10000ms.
*/
const uint32_t *durations;
/**
The length of the array of durations.
*/
uint32_t num_segments;
} VibePattern;
//! Cancel any in-flight vibe patterns; this is a no-op if there is no
//! on-going vibe.
void vibes_cancel(void);
//! Makes the watch emit one short vibration.
void vibes_short_pulse(void);
//! Makes the watch emit one long vibration.
void vibes_long_pulse(void);
//! Makes the watch emit two brief vibrations.
//!
void vibes_double_pulse(void);
//! Makes the watch emit a ‘custom’ vibration pattern.
//! @param pattern An arbitrary vibration pattern
//! @see VibePattern
void vibes_enqueue_custom_pattern(VibePattern pattern);
//! @} // group Vibes
//! @addtogroup Light Light
//! \brief Controlling Pebble's backlight
//!
//! The Light API provides you with functions to turn on Pebble’s backlight or
//! put it back into automatic control. You can trigger the backlight and schedule a timer
//! to automatically disable the backlight after a short delay, which is the preferred
//! method of interacting with the backlight.
//! @{
//! Trigger the backlight and schedule a timer to automatically disable the backlight
//! after a short delay. This is the preferred method of interacting with the backlight.
void light_enable_interaction(void);
//! Turn the watch's backlight on or put it back into automatic control.
//! Developers should take care when calling this function, keeping Pebble's backlight on for long periods of time
//! will rapidly deplete the battery.
//! @param enable Turn the backlight on if `true`, otherwise `false` to put it back into automatic control.
void light_enable(bool enable);
//! @} // group Light
//! @} // group UI
//! @addtogroup Profiling
//! @{
//! @} // group Profiling
//! @addtogroup StandardC Standard C
//! @{
//! @addtogroup StandardTime Time
//! \brief Standard system time functions
//!
//! This module contains standard time functions and formatters for printing.
//! Note that Pebble now supports both local time and UTC time
//! (including timezones and daylight savings time).
//! Most of these functions are part of the C standard library which is documented at
//! https://sourceware.org/newlib/libc.html#Timefns
//! @{
#define TZ_LEN 6
//! Callback type for tick timer events
//! @param tick_time the time at which the tick event was triggered
//! @param units_changed which unit change triggered this tick event
struct tm {
int tm_sec; /*!< Seconds. [0-60] (1 leap second) */
int tm_min; /*!< Minutes. [0-59] */
int tm_hour; /*!< Hours. [0-23] */
int tm_mday; /*!< Day. [1-31] */
int tm_mon; /*!< Month. [0-11] */
int tm_year; /*!< Years since 1900 */
int tm_wday; /*!< Day of week. [0-6] */
int tm_yday; /*!< Days in year.[0-365] */
int tm_isdst; /*!< DST. [-1/0/1] */
int tm_gmtoff; /*!< Seconds east of UTC */
char tm_zone[TZ_LEN]; /*!< Timezone abbreviation */
};
//! Format the time value at tm according to fmt and place the result in a buffer s of size max
//! @param s A preallocation char array of size max
//! @param maxsize the size of the array s
//! @param format a formatting string
//! @param tm_p A pointer to a struct tm containing a broken out time value
//! @return The number of bytes placed in the array s, not including the null byte,
//! 0 if the value does not fit.
int strftime(char* s, size_t maxsize, const char* format, const struct tm* tm_p);
//! convert the time value pointed at by clock to a struct tm which contains the time
//! adjusted for the local timezone
//! @param timep A pointer to an object of type time_t that contains a time value
//! @return A pointer to a struct tm containing the broken out time value adjusted
//! for the local timezone
struct tm *localtime(const time_t *timep);
//! convert the time value pointed at by clock to a struct tm
//! which contains the time expressed in Coordinated Universal Time (UTC)
//! @param timep A pointer to an object of type time_t that contains a time value
//! @return A pointer to a struct tm containing Coordinated Universal Time (UTC)
struct tm *gmtime(const time_t *timep);
//! convert the broken-down time structure to a timestamp
//! expressed in Coordinated Universal Time (UTC)
//! @param tb A pointer to an object of type tm that contains broken-down time
//! @return The number of seconds since epoch, January 1st 1970
time_t mktime(struct tm *tb);
//! Obtain the number of seconds since epoch.
//! Note that the epoch is not adjusted for Timezones and Daylight Savings.
//! @param tloc Optionally points to an address of a time_t variable to store the time in.
//! If you only want to use the return value, you may pass NULL into tloc instead
//! @return The number of seconds since epoch, January 1st 1970
time_t time(time_t *tloc);
//! Obtain the number of seconds elapsed between beginning and end represented as a double.
//! @param end A time_t variable representing some number of seconds since epoch, January 1st 1970
//! @param beginning A time_t variable representing some number of seconds since epoch,
//! January 1st 1970. Note that end should be greater than beginning, but this is not enforced.
//! @return The number of seconds elapsed between beginning and end.
//! @note Pebble uses software floating point emulation. Including this function which returns a
//! double will significantly increase the size of your binary. We recommend directly
//! subtracting both timestamps to calculate a time difference.
//! \code{.c}
//! int difference = ts1 - ts2;
//! \endcode
double difftime(time_t end, time_t beginning);
//! Obtain the number of seconds and milliseconds part since the epoch.
//! This is a non-standard C function provided for convenience.
//! @param tloc Optionally points to an address of a time_t variable to store the time in.
//! You may pass NULL into tloc if you don't need a time_t variable to be set
//! with the seconds since the epoch
//! @param out_ms Optionally points to an address of a uint16_t variable to store the
//! number of milliseconds since the last second in.
//! If you only want to use the return value, you may pass NULL into out_ms instead
//! @return The number of milliseconds since the last second
uint16_t time_ms(time_t *t_utc, uint16_t *out_ms);
//! @} // group StandardTime
//! @} // group StandardC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment