From 048b88a411fb2b6762df454a234d50f121d12a99 Mon Sep 17 00:00:00 2001 From: Andrew Howe Date: Thu, 9 Apr 2026 21:37:36 +0100 Subject: [PATCH 1/3] fw/applib: Misc small API documentation improvements - Add module summary with code example to app_glance.h - Clarify use of elapsed timers with app_timer_reschedule() & app_timer_cancel() - Clarify app_wakeup_service_subscribe() - Clarify that bitmap transparency is off by default and how to enable it - Document return values of persist_delete() - Replace an old reference to Aplite/Basalt with black&white/colour - Fix "error: duplicate 'const' declaration specifier" in vibes.h code examples - Fix typo in dictation_session.h Signed-off-by: Andrew Howe --- src/fw/applib/app_glance.h | 42 ++++++++++++++++++++++++- src/fw/applib/app_timer.h | 2 ++ src/fw/applib/app_wakeup.h | 2 ++ src/fw/applib/graphics/gcontext.h | 1 + src/fw/applib/persist.h | 1 + src/fw/applib/ui/bitmap_layer.h | 9 +++--- src/fw/applib/ui/vibes.h | 6 ++-- src/fw/applib/voice/dictation_session.h | 2 +- 8 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/fw/applib/app_glance.h b/src/fw/applib/app_glance.h index 8ff9cc30d..adedc0f78 100644 --- a/src/fw/applib/app_glance.h +++ b/src/fw/applib/app_glance.h @@ -11,7 +11,47 @@ //! @addtogroup Foundation //! @{ //! @addtogroup AppGlance App Glance -//! \brief API for the application to modify its glance. +//! \brief API for the application to modify its "glance" i.e. app menu subtitle. +//! +//! Apps have the ability to show a temporary icon and subtitle in the app selection menu; +//! this is known as a "glance". +//! For example, the app might use it to display a preview of its current state +//! such as the currently playing song or the number of pending notifications. +//! +//! The glance is loaded with a stack of \ref AppGlanceSlice structs; only the latest slice is shown, +//! and they are set to expire at a certain time to show the next slice in the stack. +//! +//! To update the glance with a stack of slices, you must define an +//! \ref AppGlanceReloadCallback and give it to \ref app_glance_reload. +//! The implementation of your \ref AppGlanceReloadCallback should call +//! \ref app_glance_add_slice to add slices to the glance. +//! +//! The main window's unload handler is usually a good place to call \ref app_glance_reload. +//! +//! @warning \ref PBL_PLATFORM_APLITE does not support App Glance. +//! +//! Example code: +//! \code{.c} +//! #if !PBL_PLATFORM_APLITE +//! static void glance_reload_callback(AppGlanceReloadSession *session, size_t limit, void *context) { +//! AppGlanceSlice slice = { +//! .expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION, +//! .layout.icon = APP_GLANCE_SLICE_DEFAULT_ICON, +//! .layout.subtitle_template_string = "hello!", +//! }; +//! AppGlanceResult result = app_glance_add_slice(session, slice); +//! if (result != APP_GLANCE_RESULT_SUCCESS){ +//! APP_LOG(APP_LOG_LEVEL_ERROR, "app_glance_add_slice() returned %d", result); +//! } +//! } +//! #endif // !PBL_PLATFORM_APLITE +//! +//! static void main_window_unload(Window *window) { +//! #if !PBL_PLATFORM_APLITE +//! app_glance_reload(glance_reload_callback, NULL); +//! #endif // !PBL_PLATFORM_APLITE +//! } +//! \endcode //! //! @{ diff --git a/src/fw/applib/app_timer.h b/src/fw/applib/app_timer.h index f1aa6bb12..8cb69cbc4 100644 --- a/src/fw/applib/app_timer.h +++ b/src/fw/applib/app_timer.h @@ -43,6 +43,7 @@ AppTimer* app_timer_register_repeatable(uint32_t timeout_ms, void *app_timer_get_data(AppTimer *timer); //! Reschedules an already running timer for some point in the future. +//! Elapsed timers cannot be rescheduled. //! @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 @@ -50,6 +51,7 @@ 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. +//! Elapsed timers do not need to be cancelled. void app_timer_cancel(AppTimer *timer_handle); //! @} // group Timer diff --git a/src/fw/applib/app_wakeup.h b/src/fw/applib/app_wakeup.h index 1ba8fc156..990a035fd 100644 --- a/src/fw/applib/app_wakeup.h +++ b/src/fw/applib/app_wakeup.h @@ -23,6 +23,8 @@ typedef void (*WakeupHandler)(WakeupId wakeup_id, int32_t cookie); //! Registers a WakeupHandler to be called when wakeup events occur. +//! @note The handler is only called for wakeup events which occur while the app is already running; +//! use \ref launch_reason() === \ref APP_LAUNCH_WAKEUP to detect when the app was launched by a wakeup event. //! @param handler The callback that gets called when the wakeup event occurs void app_wakeup_service_subscribe(WakeupHandler handler); diff --git a/src/fw/applib/graphics/gcontext.h b/src/fw/applib/graphics/gcontext.h index 05e376193..b8536bd79 100644 --- a/src/fw/applib/graphics/gcontext.h +++ b/src/fw/applib/graphics/gcontext.h @@ -152,6 +152,7 @@ void graphics_context_set_text_color_2bit(GContext* ctx, GColor2 color); void graphics_context_set_tint_color(GContext *ctx, GColor color); //! Sets the current bitmap compositing mode of the graphics context. +//! The default mode is GCompOpAssign i.e. bitmap transparency disabled. //! @param ctx The graphics context onto which to set the compositing mode //! @param mode The new compositing mode //! @see \ref GCompOp diff --git a/src/fw/applib/persist.h b/src/fw/applib/persist.h index 78c9b5fe2..10e911879 100644 --- a/src/fw/applib/persist.h +++ b/src/fw/applib/persist.h @@ -128,6 +128,7 @@ 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. +//! @return S_TRUE if successful, E_DOES_NOT_EXIST if a value was not set, or another error value from \ref StatusCode. status_t persist_delete(const uint32_t key); //! @} // end addtogroup Storage diff --git a/src/fw/applib/ui/bitmap_layer.h b/src/fw/applib/ui/bitmap_layer.h index c7c718b1c..98df69e0b 100644 --- a/src/fw/applib/ui/bitmap_layer.h +++ b/src/fw/applib/ui/bitmap_layer.h @@ -123,6 +123,7 @@ void bitmap_layer_set_alignment(BitmapLayer *bitmap_layer, GAlign alignment); //! 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 +//! @see \ref bitmap_layer_set_compositing_mode for enabling transparency void bitmap_layer_set_background_color(BitmapLayer *bitmap_layer, GColor color); void bitmap_layer_set_background_color_2bit(BitmapLayer *bitmap_layer, GColor2 color); @@ -133,11 +134,11 @@ void bitmap_layer_set_background_color_2bit(BitmapLayer *bitmap_layer, GColor2 c //! 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 black&white platforms, 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. +//! For color platforms, when drawing \ref GBitmap images, \ref GCompOpSet is +//! 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 diff --git a/src/fw/applib/ui/vibes.h b/src/fw/applib/ui/vibes.h index ebced949f..c255e9d31 100644 --- a/src/fw/applib/ui/vibes.h +++ b/src/fw/applib/ui/vibes.h @@ -28,7 +28,7 @@ Example code: \code{.c} // Vibe pattern: ON for 200ms, OFF for 100ms, ON for 400ms: -static const uint32_t const segments[] = { 200, 100, 400 }; +static const uint32_t segments[] = { 200, 100, 400 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), @@ -56,8 +56,8 @@ typedef struct { Example code: \code{.c} // Ramp-down pattern: 100% for 200ms, 50% for 200ms, 25% for 200ms: - static const uint32_t const segments[] = { 200, 200, 200 }; - static const uint32_t const amplitudes[] = { 100, 50, 25 }; + static const uint32_t segments[] = { 200, 200, 200 }; + static const uint32_t amplitudes[] = { 100, 50, 25 }; VibePatternWithAmplitudes pat = { .durations = segments, .amplitudes = amplitudes, diff --git a/src/fw/applib/voice/dictation_session.h b/src/fw/applib/voice/dictation_session.h index 17fe2c8dc..eda945696 100644 --- a/src/fw/applib/voice/dictation_session.h +++ b/src/fw/applib/voice/dictation_session.h @@ -86,7 +86,7 @@ typedef void (*DictationSessionStatusCallback)(DictationSession *session, //! Create a dictation session. The session object can be used more than once to get a //! transcription. When a transcription is received a buffer will be allocated to store the text in -//! with a maximum size specified by \ref buffer_size. When a transcription and accepted by the user +//! with a maximum size specified by \ref buffer_size. When a transcription is accepted by the user //! or a failure of some sort occurs, the callback specified will be called with the status and the //! transcription if one was accepted. //! @param buffer_size size of buffer to allocate for the transcription text; text will be From 68c340c92defcc97f0f1ed11c24d0a5a660f2907 Mon Sep 17 00:00:00 2001 From: Andrew Howe Date: Thu, 9 Apr 2026 21:37:36 +0100 Subject: [PATCH 2/3] fw/applib: Emit PBL_LOG_ERR for all rejected BUTTON_ID_BACK handlers Signed-off-by: Andrew Howe --- src/fw/applib/ui/window.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fw/applib/ui/window.c b/src/fw/applib/ui/window.c index 705e566f7..7eb483a2b 100644 --- a/src/fw/applib/ui/window.c +++ b/src/fw/applib/ui/window.c @@ -287,6 +287,7 @@ void window_single_click_subscribe(ButtonId button_id, ClickHandler handler) { void window_single_repeating_click_subscribe(ButtonId button_id, uint16_t repeat_interval_ms, ClickHandler handler) { prv_check_is_in_click_config_provider(window_manager_get_top_window(), "subscribe"); if (button_id == BUTTON_ID_BACK) { + PBL_LOG_ERR("Cannot register BUTTON_ID_BACK repeating click handler"); return; } ClickManager *mgr = prv_get_current_click_manager(); @@ -322,6 +323,7 @@ void window_long_click_subscribe(ButtonId button_id, uint16_t delay_ms, ClickHan // long-pressing the back button a normal interaction method, and users may // unintentionally hold the button too long and force-quit the app. if (app_install_id_from_app_db(sys_process_manager_get_current_process_id())) { + PBL_LOG_ERR("Cannot register BUTTON_ID_BACK long click handler"); return; } else { Window *window = window_manager_get_top_window(); @@ -339,7 +341,7 @@ void window_long_click_subscribe(ButtonId button_id, uint16_t delay_ms, ClickHan void window_raw_click_subscribe(ButtonId button_id, ClickHandler down_handler, ClickHandler up_handler, void *context) { prv_check_is_in_click_config_provider(window_manager_get_top_window(), "subscribe"); if (button_id == BUTTON_ID_BACK) { - PBL_LOG_DBG("Cannot register BUTTON_ID_BACK raw handler"); + PBL_LOG_ERR("Cannot register BUTTON_ID_BACK raw handler"); return; } ClickManager *mgr = prv_get_current_click_manager(); From 4ce256c3ed123ed7820ffef53c800c9012d11f53 Mon Sep 17 00:00:00 2001 From: Andrew Howe Date: Thu, 9 Apr 2026 21:37:36 +0100 Subject: [PATCH 3/3] fw/applib: Add new rot_bitmap_layer_get_layer() and AppGlanceSliceLayout to API rot_bitmap_layer_get_layer() is added to match all other layer modules. The anonymous `layout` struct from AppGlanceSlice is extracted to AppGlanceSliceLayout so that its documentation can be included in docs. Signed-off-by: Andrew Howe --- src/fw/applib/app_glance.h | 17 ++++++++++------- src/fw/applib/ui/rotate_bitmap_layer.c | 4 ++++ src/fw/applib/ui/rotate_bitmap_layer.h | 10 +++++++++- src/fw/process_management/pebble_process_info.h | 3 ++- tools/generate_native_sdk/exported_symbols.json | 10 +++++++++- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/fw/applib/app_glance.h b/src/fw/applib/app_glance.h index adedc0f78..df5e52e9d 100644 --- a/src/fw/applib/app_glance.h +++ b/src/fw/applib/app_glance.h @@ -65,19 +65,22 @@ typedef uint32_t PublishedId; //! icon. #define APP_GLANCE_SLICE_DEFAULT_ICON ((PublishedId)0) -//! An app's glance can change over time as defined by zero or more app glance slices that each -//! describe the state of the app glance at a particular point in time. Slices are displayed in the -//! order they are added, and they are removed at the specified expiration time. -typedef struct AppGlanceSlice { - //! Describes how the slice should be visualized in the app's glance in the launcher. - struct { +//! Describes how the slice should be visualized in the app's glance in the launcher. +typedef struct AppGlanceSliceLayout { //! The published resource ID of the bitmap icon to display in the app's glance. Use \ref //! APP_GLANCE_SLICE_DEFAULT_ICON to use the app's default bitmap icon. PublishedId icon; //! A template string to visualize in the app's glance. The string will be copied, so it is safe //! to destroy after adding the slice to the glance. Use NULL if no string should be displayed. const char *subtitle_template_string; - } layout; +} AppGlanceSliceLayout; + +//! An app's glance can change over time as defined by zero or more app glance slices that each +//! describe the state of the app glance at a particular point in time. Slices are displayed in the +//! order they are added, and they are removed at the specified expiration time. +typedef struct AppGlanceSlice { + //! Describes how the slice should be visualized in the app's glance in the launcher. + AppGlanceSliceLayout layout; //! The UTC time after which this slice should no longer be shown in the app's glance. Use \ref //! APP_GLANCE_SLICE_NO_EXPIRATION if the slice should never expire. time_t expiration_time; diff --git a/src/fw/applib/ui/rotate_bitmap_layer.c b/src/fw/applib/ui/rotate_bitmap_layer.c index 5f67c51f4..75921efef 100644 --- a/src/fw/applib/ui/rotate_bitmap_layer.c +++ b/src/fw/applib/ui/rotate_bitmap_layer.c @@ -64,6 +64,10 @@ void rot_bitmap_layer_destroy(RotBitmapLayer *rot_bitmap_layer) { applib_free(rot_bitmap_layer); } +Layer* rot_bitmap_layer_get_layer(const RotBitmapLayer *rot_bitmap_layer) { + return &((RotBitmapLayer *)rot_bitmap_layer)->layer; +} + void rot_bitmap_layer_set_corner_clip_color(RotBitmapLayer *image, GColor color) { if (gcolor_equal(color, image->corner_clip_color)) { return; diff --git a/src/fw/applib/ui/rotate_bitmap_layer.h b/src/fw/applib/ui/rotate_bitmap_layer.h index 07c0b655c..0dbe2e03f 100644 --- a/src/fw/applib/ui/rotate_bitmap_layer.h +++ b/src/fw/applib/ui/rotate_bitmap_layer.h @@ -82,6 +82,14 @@ void rot_bitmap_layer_deinit(RotBitmapLayer *bitmap); //! @param bitmap The RotBitmapLayer to destroy. void rot_bitmap_layer_destroy(RotBitmapLayer *bitmap); +//! Gets the "root" Layer of the RotBitmapLayer, which is the parent for the sub- +//! layers used for its implementation. +//! @param rot_bitmap_layer Pointer to the RotBitmapLayer for which to get the "root" Layer +//! @return The "root" Layer of the RotBitmapLayer. +//! @internal +//! @note The result is always equal to `(Layer *) rot_bitmap_layer`. +Layer* rot_bitmap_layer_get_layer(const RotBitmapLayer *rot_bitmap_layer); + //! 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 @@ -113,7 +121,7 @@ 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. +//! By default this is \ref GCompOpAssign, i.e. transparency disabled. //! 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 diff --git a/src/fw/process_management/pebble_process_info.h b/src/fw/process_management/pebble_process_info.h index 1cf391130..32e90fba7 100644 --- a/src/fw/process_management/pebble_process_info.h +++ b/src/fw/process_management/pebble_process_info.h @@ -155,9 +155,10 @@ typedef enum { // sdk.major:0x5 .minor:0x57 -- Add moddable_createMachine (rev 90) // sdk.major:0x5 .minor:0x58 -- Add size 60 LECO font (rev 91) // sdk.major:0x5 .minor:0x59 -- Add flags to ModdableCreationRecord (rev 92) +// sdk.major:0x5 .minor:0x5a -- Add rot_bitmap_layer_get_layer() and AppGlanceSliceLayout (rev 93) #define PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR 0x5 -#define PROCESS_INFO_CURRENT_SDK_VERSION_MINOR 0x59 +#define PROCESS_INFO_CURRENT_SDK_VERSION_MINOR 0x5a // The first SDK to ship with 2.x APIs #define PROCESS_INFO_FIRST_2X_SDK_VERSION_MAJOR 0x4 diff --git a/tools/generate_native_sdk/exported_symbols.json b/tools/generate_native_sdk/exported_symbols.json index 27af1f3ad..a367c4bb4 100644 --- a/tools/generate_native_sdk/exported_symbols.json +++ b/tools/generate_native_sdk/exported_symbols.json @@ -4,7 +4,7 @@ "You should also make sure you are obeying our API design guidelines:", "https://pebbletechnology.atlassian.net/wiki/display/DEV/SDK+API+Design+Guidelines" ], - "revision" : "92", + "revision" : "93", "version" : "2.0", "files": [ "fw/drivers/ambient_light.h", @@ -1817,6 +1817,10 @@ "type": "define", "name": "APP_GLANCE_SLICE_DEFAULT_ICON", "addedRevision": "83" + }, { + "type": "type", + "name": "AppGlanceSliceLayout", + "addedRevision": "93" }, { "type": "type", "name": "AppGlanceSlice", @@ -4250,6 +4254,10 @@ "type": "function", "name": "rot_bitmap_layer_destroy", "addedRevision": "0" + }, { + "type": "function", + "name": "rot_bitmap_layer_get_layer", + "addedRevision": "93" }, { "type": "function", "name": "rot_bitmap_layer_set_corner_clip_color_2bit",