mirror of
https://github.com/google/pebble.git
synced 2025-07-12 17:32:18 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
84
src/fw/kernel/ui/kernel_ui.c
Normal file
84
src/fw/kernel/ui/kernel_ui.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "kernel_ui.h"
|
||||
|
||||
#include "kernel/kernel_applib_state.h"
|
||||
#include "kernel/pebble_tasks.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
#include "services/common/compositor/compositor.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/ui/animation_private.h"
|
||||
|
||||
static GContext s_kernel_grahics_context;
|
||||
|
||||
T_STATIC ContentIndicatorsBuffer s_kernel_content_indicators_buffer;
|
||||
|
||||
static TimelineItemActionSource s_kernel_current_timeline_item_action_source;
|
||||
|
||||
void kernel_ui_init(void) {
|
||||
graphics_context_init(&s_kernel_grahics_context, compositor_get_framebuffer(),
|
||||
GContextInitializationMode_System);
|
||||
animation_private_state_init(kernel_applib_get_animation_state());
|
||||
content_indicator_init_buffer(&s_kernel_content_indicators_buffer);
|
||||
s_kernel_current_timeline_item_action_source = TimelineItemActionSourceModalNotification;
|
||||
}
|
||||
|
||||
GContext* kernel_ui_get_graphics_context(void) {
|
||||
PBL_ASSERT_TASK(PebbleTask_KernelMain);
|
||||
|
||||
return &s_kernel_grahics_context;
|
||||
}
|
||||
|
||||
GContext *graphics_context_get_current_context(void) {
|
||||
if (pebble_task_get_current() == PebbleTask_App) {
|
||||
return app_state_get_graphics_context();
|
||||
} else {
|
||||
return kernel_ui_get_graphics_context();
|
||||
}
|
||||
}
|
||||
|
||||
ContentIndicatorsBuffer *kernel_ui_get_content_indicators_buffer(void) {
|
||||
PBL_ASSERT_TASK(PebbleTask_KernelMain);
|
||||
|
||||
return &s_kernel_content_indicators_buffer;
|
||||
}
|
||||
|
||||
ContentIndicatorsBuffer *content_indicator_get_current_buffer(void) {
|
||||
if (pebble_task_get_current() == PebbleTask_App) {
|
||||
return app_state_get_content_indicators_buffer();
|
||||
} else {
|
||||
return kernel_ui_get_content_indicators_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
TimelineItemActionSource kernel_ui_get_current_timeline_item_action_source(void) {
|
||||
if (pebble_task_get_current() == PebbleTask_App) {
|
||||
return app_state_get_current_timeline_item_action_source();
|
||||
} else {
|
||||
return s_kernel_current_timeline_item_action_source;
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_ui_set_current_timeline_item_action_source(TimelineItemActionSource current_source) {
|
||||
if (pebble_task_get_current() == PebbleTask_App) {
|
||||
app_state_set_current_timeline_item_action_source(current_source);
|
||||
} else {
|
||||
s_kernel_current_timeline_item_action_source = current_source;
|
||||
}
|
||||
}
|
34
src/fw/kernel/ui/kernel_ui.h
Normal file
34
src/fw/kernel/ui/kernel_ui.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/ui/content_indicator_private.h"
|
||||
#include "services/normal/timeline/timeline_actions.h"
|
||||
|
||||
void kernel_ui_init(void);
|
||||
|
||||
GContext* kernel_ui_get_graphics_context(void);
|
||||
|
||||
GContext *graphics_context_get_current_context(void);
|
||||
|
||||
ContentIndicatorsBuffer *kernel_ui_get_content_indicators_buffer(void);
|
||||
|
||||
ContentIndicatorsBuffer *content_indicator_get_current_buffer(void);
|
||||
|
||||
TimelineItemActionSource kernel_ui_get_current_timeline_item_action_source(void);
|
||||
void kernel_ui_set_current_timeline_item_action_source(TimelineItemActionSource current_source);
|
582
src/fw/kernel/ui/modals/modal_manager.c
Normal file
582
src/fw/kernel/ui/modals/modal_manager.c
Normal file
|
@ -0,0 +1,582 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "modal_manager.h"
|
||||
|
||||
#include "applib/ui/app_window_click_glue.h"
|
||||
#include "applib/ui/click_internal.h"
|
||||
#include "applib/ui/window.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/window_stack.h"
|
||||
#include "applib/ui/window_stack_animation.h"
|
||||
#include "applib/ui/window_stack_private.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private.h"
|
||||
#include "console/prompt.h"
|
||||
#include "kernel/panic.h"
|
||||
#include "kernel/events.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
#include "services/common/compositor/compositor_transitions.h"
|
||||
#include "shell/normal/app_idle_timeout.h"
|
||||
#include "shell/normal/watchface.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/profiler.h"
|
||||
#include "util/list.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
|
||||
typedef struct ModalContext {
|
||||
WindowStack window_stack;
|
||||
} ModalContext;
|
||||
|
||||
typedef struct UpdateContext {
|
||||
ModalPriority highest_idx;
|
||||
ModalProperty properties;
|
||||
} UpdateContext;
|
||||
|
||||
static void prv_update_modal_stacks(UpdateContext *context);
|
||||
|
||||
// Static State
|
||||
///////////////////
|
||||
static ModalContext s_modal_window_stacks[NumModalPriorities];
|
||||
|
||||
static ClickManager s_modal_window_click_manager;
|
||||
|
||||
static ModalPriority s_modal_min_priority = ModalPriorityMin;
|
||||
|
||||
// Used to help us keep track various modal properties in aggregate, such as existence.
|
||||
// Initialize the default to being equivalent to having no modals.
|
||||
static ModalProperty s_current_modal_properties = ModalPropertyDefault;
|
||||
|
||||
// Used to decide the compositor transition after a modal is already removed from the stack
|
||||
static ModalPriority s_last_highest_modal_priority = ModalPriorityInvalid;
|
||||
|
||||
// Private API
|
||||
////////////////////
|
||||
static bool prv_has_visible_window(ModalContext *context, void *unused) {
|
||||
const bool empty = (context->window_stack.list_head == NULL);
|
||||
const bool filtered_out = (context < &s_modal_window_stacks[s_modal_min_priority]);
|
||||
return (!empty && !filtered_out);
|
||||
}
|
||||
|
||||
static bool prv_has_transition_window(ModalContext *context) {
|
||||
Window *window = window_stack_get_top_window(&context->window_stack);
|
||||
return (window && (context > &s_modal_window_stacks[ModalPriorityDiscreet]));
|
||||
}
|
||||
|
||||
static bool prv_has_opaque_window(ModalContext *context) {
|
||||
Window *window = window_stack_get_top_window(&context->window_stack);
|
||||
return (window && !window->is_transparent);
|
||||
}
|
||||
|
||||
static bool prv_has_focusable_window(ModalContext *context) {
|
||||
Window *window = window_stack_get_top_window(&context->window_stack);
|
||||
return (window && !window->is_unfocusable);
|
||||
}
|
||||
|
||||
static bool prv_has_visible_focusable_window(ModalContext *context, void *unused) {
|
||||
return (prv_has_visible_window(context, NULL) && prv_has_focusable_window(context));
|
||||
}
|
||||
|
||||
static void prv_send_will_focus_event(bool in_focus) {
|
||||
static bool s_focus_lost = true;
|
||||
if (s_focus_lost == in_focus) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_focus_lost = in_focus;
|
||||
|
||||
PebbleEvent event = {
|
||||
.type = PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT,
|
||||
.app_focus = {
|
||||
.in_focus = in_focus,
|
||||
}
|
||||
};
|
||||
event_put(&event);
|
||||
}
|
||||
|
||||
// Public API
|
||||
////////////////////
|
||||
void modal_manager_init(void) {
|
||||
// Don't touch s_modal_window_stacks or s_modal_min_priority, it's valid for someone to have
|
||||
// disabled modals using modal_manager_set_enabled before we've initialized and we should
|
||||
// honour that setting.
|
||||
|
||||
click_manager_init(&s_modal_window_click_manager);
|
||||
}
|
||||
|
||||
void modal_manager_set_min_priority(ModalPriority priority) {
|
||||
s_modal_min_priority = priority;
|
||||
for (int i = 0; i < priority; i++) {
|
||||
window_stack_lock_push(&s_modal_window_stacks[i].window_stack);
|
||||
}
|
||||
for (int i = priority; i < NumModalPriorities; ++i) {
|
||||
window_stack_unlock_push(&s_modal_window_stacks[i].window_stack);
|
||||
}
|
||||
}
|
||||
|
||||
bool modal_manager_get_enabled(void) {
|
||||
return s_modal_min_priority < ModalPriorityMax;
|
||||
}
|
||||
|
||||
ClickManager *modal_manager_get_click_manager(void) {
|
||||
return &s_modal_window_click_manager;
|
||||
}
|
||||
|
||||
static WindowStack *prv_find_window_stack(ModalContextFilterCallback callback, void *data) {
|
||||
for (ModalPriority idx = NumModalPriorities - 1; idx >= ModalPriorityMin; idx--) {
|
||||
if (callback(&s_modal_window_stacks[idx], data)) {
|
||||
return &s_modal_window_stacks[idx].window_stack;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WindowStack *modal_manager_find_window_stack(ModalContextFilterCallback filter_cb, void *ctx) {
|
||||
return prv_find_window_stack(filter_cb, ctx);
|
||||
}
|
||||
|
||||
WindowStack *modal_manager_get_window_stack(ModalPriority priority) {
|
||||
PBL_ASSERTN((priority > ModalPriorityInvalid) && (priority < NumModalPriorities));
|
||||
ModalContext *context = &s_modal_window_stacks[priority];
|
||||
return &context->window_stack;
|
||||
}
|
||||
|
||||
Window *modal_manager_get_top_window(void) {
|
||||
WindowStack *stack = prv_find_window_stack(prv_has_visible_window, NULL);
|
||||
return window_stack_get_top_window(stack);
|
||||
}
|
||||
|
||||
static void prv_pop_stacks_in_range(ModalPriority low, ModalPriority high) {
|
||||
// Discreet modals are transparent and unfocusable, they are not meant to be popped when
|
||||
// requesting opaque focusable modals to pop.
|
||||
for (ModalPriority priority = MAX(low, ModalPriorityDiscreet + 1); priority <= high;
|
||||
priority++) {
|
||||
ModalContext *m_context = &s_modal_window_stacks[priority];
|
||||
window_stack_pop_all(&m_context->window_stack, true /* animated */);
|
||||
}
|
||||
}
|
||||
|
||||
void modal_manager_pop_all(void) {
|
||||
prv_pop_stacks_in_range(ModalPriorityMin, NumModalPriorities - 1);
|
||||
}
|
||||
|
||||
void modal_manager_pop_all_below_priority(ModalPriority priority) {
|
||||
prv_pop_stacks_in_range(ModalPriorityMin, priority - 1);
|
||||
}
|
||||
|
||||
static const CompositorTransition *prv_get_compositor_transition(bool modal_is_destination) {
|
||||
bool is_top_discreet;
|
||||
if (modal_is_destination) {
|
||||
Window *window =
|
||||
window_stack_get_top_window(&s_modal_window_stacks[ModalPriorityDiscreet].window_stack);
|
||||
is_top_discreet = (window && (window == modal_manager_get_top_window()));
|
||||
} else {
|
||||
is_top_discreet = (s_last_highest_modal_priority == ModalPriorityDiscreet);
|
||||
}
|
||||
return is_top_discreet ? NULL : compositor_modal_transition_to_modal_get(modal_is_destination);
|
||||
}
|
||||
|
||||
static void prv_handle_app_to_modal_transition_visible(void) {
|
||||
// The last event resulted in a modal window being pushed where we didn't have any before.
|
||||
// Start the animation!
|
||||
compositor_transition(prv_get_compositor_transition(true /* modal_is_destination */));
|
||||
}
|
||||
|
||||
static void prv_handle_modal_to_app_transition_visible(void) {
|
||||
compositor_transition(prv_get_compositor_transition(false /* modal_is_destination */));
|
||||
}
|
||||
|
||||
static void prv_handle_app_to_modal_transition_hidden_and_unfocused(void) {
|
||||
#if !RECOVERY_FW && !SHELL_SDK
|
||||
app_idle_timeout_pause();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prv_handle_modal_to_app_transition_hidden_and_unfocused(void) {
|
||||
#if !RECOVERY_FW && !SHELL_SDK
|
||||
app_idle_timeout_resume();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prv_handle_app_to_modal_transition_focus(void) {
|
||||
#if !RECOVERY_FW && (!SHELL_SDK || CAPABILITY_HAS_SDK_SHELL4)
|
||||
watchface_reset_click_manager();
|
||||
#endif
|
||||
|
||||
// Let the underlying window know it has lost focus if this is the first modal
|
||||
// window to show up.
|
||||
prv_send_will_focus_event(false /* in_focus */);
|
||||
}
|
||||
|
||||
static void prv_handle_modal_to_app_transition_focus(void) {
|
||||
// There are no more modal windows, so we need to cleanup the modal window state.
|
||||
click_manager_clear(modal_manager_get_click_manager());
|
||||
|
||||
prv_send_will_focus_event(true /* in_focus */);
|
||||
}
|
||||
|
||||
void modal_manager_event_loop_upkeep(void) {
|
||||
if (!modal_manager_get_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateContext update;
|
||||
prv_update_modal_stacks(&update);
|
||||
const ModalProperty last_properties = s_current_modal_properties;
|
||||
s_current_modal_properties = update.properties;
|
||||
|
||||
const bool is_modal_transitionable = (update.properties & ModalProperty_CompositorTransitions);
|
||||
const bool was_modal_transitionable = (last_properties & ModalProperty_CompositorTransitions);
|
||||
if (!was_modal_transitionable && is_modal_transitionable) {
|
||||
// We now have a window visible when we didn't have one before, start the transition.
|
||||
prv_handle_app_to_modal_transition_visible();
|
||||
} else if (was_modal_transitionable && !is_modal_transitionable) {
|
||||
// This event resulted in our last visible modal window being popped, let's transition away.
|
||||
prv_handle_modal_to_app_transition_visible();
|
||||
}
|
||||
|
||||
const bool is_modal_unfocused = (update.properties & ModalProperty_Unfocused);
|
||||
const bool was_modal_unfocused = (last_properties & ModalProperty_Unfocused);
|
||||
if (was_modal_unfocused && !is_modal_unfocused) {
|
||||
// We now have a modal window focused when we didn't have one before, start the transition.
|
||||
prv_handle_app_to_modal_transition_focus();
|
||||
} else if (!was_modal_unfocused && is_modal_unfocused) {
|
||||
// This event resulted in our last focusable modal window being popped, let's transition away.
|
||||
prv_handle_modal_to_app_transition_focus();
|
||||
}
|
||||
|
||||
const bool is_app_hidden_and_unfocused =
|
||||
(!(update.properties & ModalProperty_Transparent) && !is_modal_unfocused);
|
||||
const bool was_app_hidden_and_unfocused =
|
||||
(!(last_properties & ModalProperty_Transparent) && !was_modal_unfocused);
|
||||
if (!was_app_hidden_and_unfocused && is_app_hidden_and_unfocused) {
|
||||
// The app is now obstructed by an opaque modal and lost focus to a modal, idle.
|
||||
prv_handle_app_to_modal_transition_hidden_and_unfocused();
|
||||
} else if (was_app_hidden_and_unfocused && !is_app_hidden_and_unfocused) {
|
||||
// The app now either is obstructed only by transparent modals or gained focus, resume.
|
||||
prv_handle_modal_to_app_transition_hidden_and_unfocused();
|
||||
}
|
||||
|
||||
// We have modal windows and we should render them, either because they asked to or because
|
||||
// they recently became the top window in their respective modal stacks and haven't noticed yet.
|
||||
// See the handling for off screen windows in prv_render_modal_stack.
|
||||
if (update.properties & ModalProperty_RenderRequested) {
|
||||
compositor_modal_render_ready();
|
||||
}
|
||||
|
||||
s_last_highest_modal_priority = update.highest_idx;
|
||||
}
|
||||
|
||||
typedef struct IterContext {
|
||||
Window *current_top_window;
|
||||
ModalPriority current_idx;
|
||||
ModalPriority first_visible_idx;
|
||||
ModalPriority first_transition_idx;
|
||||
ModalPriority first_focus_idx;
|
||||
ModalPriority first_opaque_idx;
|
||||
} IterContext;
|
||||
|
||||
typedef bool (*ModalContextIterCallback)(ModalContext *modal, IterContext *iter, void *data);
|
||||
|
||||
static void prv_each_modal_stack(ModalContextIterCallback callback, void *data) {
|
||||
IterContext iter = {
|
||||
.first_visible_idx = ModalPriorityInvalid,
|
||||
.first_transition_idx = ModalPriorityInvalid,
|
||||
.first_focus_idx = ModalPriorityInvalid,
|
||||
.first_opaque_idx = ModalPriorityInvalid,
|
||||
};
|
||||
for (ModalPriority idx = NumModalPriorities - 1; idx >= ModalPriorityMin; idx--) {
|
||||
ModalContext *context = &s_modal_window_stacks[idx];
|
||||
if (!prv_has_visible_window(context, NULL)) {
|
||||
continue;
|
||||
} else if (iter.first_visible_idx == ModalPriorityInvalid) {
|
||||
iter.first_visible_idx = idx;
|
||||
}
|
||||
if (prv_has_transition_window(context) && (iter.first_transition_idx == ModalPriorityInvalid)) {
|
||||
iter.first_transition_idx = idx;
|
||||
}
|
||||
if (prv_has_focusable_window(context) && (iter.first_focus_idx == ModalPriorityInvalid)) {
|
||||
iter.first_focus_idx = idx;
|
||||
}
|
||||
if (prv_has_opaque_window(context) && (iter.first_opaque_idx == ModalPriorityInvalid)) {
|
||||
iter.first_opaque_idx = idx;
|
||||
}
|
||||
}
|
||||
if (iter.first_visible_idx != ModalPriorityInvalid) {
|
||||
for (ModalPriority idx = ModalPriorityMin; idx < NumModalPriorities; idx++) {
|
||||
ModalContext *modal = &s_modal_window_stacks[idx];
|
||||
WindowStack *stack = &modal->window_stack;
|
||||
iter.current_idx = idx;
|
||||
iter.current_top_window = window_stack_get_top_window(stack);
|
||||
if (!iter.current_top_window) {
|
||||
continue;
|
||||
}
|
||||
const bool should_continue = callback(modal, &iter, data);
|
||||
if (!should_continue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool prv_update_modal_stack_callback(ModalContext *modal, IterContext *iter, void *data) {
|
||||
UpdateContext *ctx = data;
|
||||
Window *window = iter->current_top_window;
|
||||
|
||||
// Handle window state changes
|
||||
const bool is_visible = (iter->current_idx >= iter->first_opaque_idx);
|
||||
if (!window->on_screen && is_visible) {
|
||||
// We've been exposed by a higher priority modal window stack emptying out, become on screen
|
||||
// now.
|
||||
window_set_on_screen(window, true /* new on screen */, true /* call handlers */);
|
||||
}
|
||||
|
||||
// Setting on-screen can configure the click, but if this is a window below a transparent
|
||||
// window that just disappeared, it was already on screen and may need its click configured.
|
||||
const bool is_focused = (iter->current_idx == iter->first_focus_idx);
|
||||
if (!window->is_click_configured && is_focused) {
|
||||
// Input is now exposed by a higher priority modal window stack emptying out, gain input
|
||||
window_setup_click_config_provider(window);
|
||||
} else if (window->is_click_configured && !is_focused) {
|
||||
// A different modal window now has focus
|
||||
window->is_click_configured = false;
|
||||
}
|
||||
|
||||
// Set the last highest visible modal priority
|
||||
if (is_visible) {
|
||||
ctx->highest_idx = iter->current_idx;
|
||||
}
|
||||
|
||||
// Update properties based on state changes
|
||||
// If this callback was called, there exists a modal
|
||||
ctx->properties |= ModalProperty_Exists;
|
||||
|
||||
if (iter->current_idx > ModalPriorityDiscreet) {
|
||||
// There is a modal window that has a compositor transition
|
||||
ctx->properties |= ModalProperty_CompositorTransitions;
|
||||
}
|
||||
|
||||
if (is_visible) {
|
||||
if (!window->is_transparent) {
|
||||
// There is a visible opaque window, remove the transparent property
|
||||
ctx->properties &= ~ModalProperty_Transparent;
|
||||
}
|
||||
if (window->is_render_scheduled) {
|
||||
// There is a visible window that will render, add the render requested property
|
||||
ctx->properties |= ModalProperty_RenderRequested;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_focused) {
|
||||
// There is a modal with focus, remove the unfocused property
|
||||
ctx->properties &= ~ModalProperty_Unfocused;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prv_render_modal_stack_callback(ModalContext *modal, IterContext *iter, void *data) {
|
||||
if (iter->current_idx < iter->first_opaque_idx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GContext *ctx = data;
|
||||
WindowStack *stack = &modal->window_stack;
|
||||
Window *window = iter->current_top_window;
|
||||
|
||||
if (window_stack_is_animating(stack) &&
|
||||
stack->transition_context.implementation &&
|
||||
stack->transition_context.implementation->render) {
|
||||
// a lot of safety guards to make sure the transition can do render by its own
|
||||
WindowTransitioningContext *const transition_context = &stack->transition_context;
|
||||
transition_context->implementation->render(transition_context, ctx);
|
||||
} else {
|
||||
PROFILER_NODE_START(render_modal);
|
||||
window_render(window, ctx);
|
||||
PROFILER_NODE_STOP(render_modal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_update_modal_stacks(UpdateContext *context) {
|
||||
context->highest_idx = ModalPriorityInvalid;
|
||||
context->properties = ModalPropertyDefault;
|
||||
prv_each_modal_stack(prv_update_modal_stack_callback, context);
|
||||
}
|
||||
|
||||
|
||||
ModalProperty modal_manager_get_properties(void) {
|
||||
return modal_manager_get_enabled() ? s_current_modal_properties : ModalPropertyDefault;
|
||||
}
|
||||
|
||||
void modal_manager_render(GContext *ctx) {
|
||||
PBL_ASSERTN(ctx);
|
||||
prv_each_modal_stack(prv_render_modal_stack_callback, ctx);
|
||||
}
|
||||
|
||||
typedef struct VisibleContext {
|
||||
Window *window;
|
||||
bool visible;
|
||||
} VisibleContext;
|
||||
|
||||
static bool prv_is_window_visible_callback(ModalContext *modal, IterContext *iter, void *data) {
|
||||
if (iter->current_idx < iter->first_opaque_idx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VisibleContext *ctx = data;
|
||||
ctx->visible = (ctx->window == iter->current_top_window);
|
||||
return !ctx->visible;
|
||||
}
|
||||
|
||||
bool modal_manager_is_window_visible(Window *window) {
|
||||
VisibleContext context = { .window = window };
|
||||
prv_each_modal_stack(prv_is_window_visible_callback, &context);
|
||||
return context.visible;
|
||||
}
|
||||
|
||||
typedef struct FocusedContext {
|
||||
Window *window;
|
||||
bool focused;
|
||||
} FocusedContext;
|
||||
|
||||
static bool prv_is_window_focused_callback(ModalContext *modal, IterContext *iter, void *data) {
|
||||
FocusedContext *ctx = data;
|
||||
ctx->focused = ((iter->current_top_window == ctx->window) &&
|
||||
(iter->current_idx == iter->first_focus_idx));
|
||||
return !ctx->focused;
|
||||
}
|
||||
|
||||
bool modal_manager_is_window_focused(Window *window) {
|
||||
FocusedContext context = { .window = window };
|
||||
prv_each_modal_stack(prv_is_window_focused_callback, &context);
|
||||
return context.focused;
|
||||
}
|
||||
|
||||
static Window *prv_get_visible_focused_window(void) {
|
||||
WindowStack *stack = prv_find_window_stack(prv_has_visible_focusable_window, NULL);
|
||||
return window_stack_get_top_window(stack);
|
||||
}
|
||||
|
||||
void modal_manager_handle_button_event(PebbleEvent *event) {
|
||||
ClickManager *click_manager = modal_manager_get_click_manager();
|
||||
switch (event->type) {
|
||||
case PEBBLE_BUTTON_DOWN_EVENT: {
|
||||
// If we get a button event, it must also be for the top modal window.
|
||||
Window *window = prv_get_visible_focused_window();
|
||||
// Ensure that this function isn't being called when a modal window
|
||||
// is not present.
|
||||
PBL_ASSERTN(window);
|
||||
ButtonId id = event->button.button_id;
|
||||
if (id == BUTTON_ID_BACK && !window->overrides_back_button) {
|
||||
window_stack_remove(window, true /* animated */);
|
||||
} else {
|
||||
click_recognizer_handle_button_down(&click_manager->recognizers[id]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PEBBLE_BUTTON_UP_EVENT: {
|
||||
ButtonId id = event->button.button_id;
|
||||
click_recognizer_handle_button_up(&click_manager->recognizers[id]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
PBL_CROAK("Invalid event type: %u", event->type);
|
||||
}
|
||||
}
|
||||
|
||||
void modal_window_push(Window *window, ModalPriority priority, bool animated) {
|
||||
// Note: We do not have to adjust the `animated` argument to consider whether
|
||||
// this window is higher priority than the current visible window stack, as
|
||||
// this is taken care of by the transition context handlers in `window_stack.c`
|
||||
window_stack_push(modal_manager_get_window_stack(priority), window, animated);
|
||||
}
|
||||
|
||||
// Commands
|
||||
////////////////////////////
|
||||
|
||||
typedef struct WindowStackInfoContext {
|
||||
SemaphoreHandle_t interlock;
|
||||
WindowStackDump *dumps[NumModalPriorities];
|
||||
size_t counts[NumModalPriorities];
|
||||
bool disabled;
|
||||
} WindowStackInfoContext;
|
||||
|
||||
static void prv_modal_window_stack_info_cb(void *ctx) {
|
||||
WindowStackInfoContext *info = ctx;
|
||||
if (modal_manager_get_enabled()) {
|
||||
for (ModalPriority priority = 0;
|
||||
priority < NumModalPriorities;
|
||||
++priority) {
|
||||
WindowStack *window_stack = modal_manager_get_window_stack(priority);
|
||||
info->counts[priority] = window_stack_dump(window_stack,
|
||||
&info->dumps[priority]);
|
||||
}
|
||||
} else {
|
||||
info->disabled = true;
|
||||
}
|
||||
xSemaphoreGive(info->interlock);
|
||||
}
|
||||
|
||||
void command_modal_stack_info(void) {
|
||||
WindowStackInfoContext info = {
|
||||
.interlock = xSemaphoreCreateBinary(),
|
||||
};
|
||||
if (!info.interlock) {
|
||||
prompt_send_response("Couldn't allocate semaphore for modal stack");
|
||||
return;
|
||||
}
|
||||
|
||||
launcher_task_add_callback(prv_modal_window_stack_info_cb, &info);
|
||||
xSemaphoreTake(info.interlock, portMAX_DELAY);
|
||||
vSemaphoreDelete(info.interlock);
|
||||
|
||||
prompt_send_response("Modal Stack, top to bottom:");
|
||||
|
||||
char buffer[128];
|
||||
for (ModalPriority priority = NumModalPriorities - 1;
|
||||
priority > ModalPriorityInvalid;
|
||||
--priority) {
|
||||
prompt_send_response_fmt(buffer, sizeof(buffer), "Priority: %d (%zu)",
|
||||
priority, info.counts[priority]);
|
||||
if (info.counts[priority] > 0 && !info.dumps[priority]) {
|
||||
prompt_send_response("Couldn't allocate buffers for modal stack data");
|
||||
} else {
|
||||
for (size_t i = 0; i < info.counts[priority]; ++i) {
|
||||
prompt_send_response_fmt(buffer, sizeof(buffer), "window %p <%s>",
|
||||
info.dumps[priority][i].addr,
|
||||
info.dumps[priority][i].name);
|
||||
}
|
||||
}
|
||||
kernel_free(info.dumps[priority]);
|
||||
}
|
||||
}
|
||||
|
||||
void modal_manager_reset(void) {
|
||||
for (ModalPriority idx = ModalPriorityInvalid + 1; idx < NumModalPriorities; idx++) {
|
||||
memset(&s_modal_window_stacks[idx], 0, sizeof(ModalContext));
|
||||
}
|
||||
|
||||
s_modal_min_priority = ModalPriorityDiscreet;
|
||||
|
||||
modal_manager_init();
|
||||
}
|
176
src/fw/kernel/ui/modals/modal_manager.h
Normal file
176
src/fw/kernel/ui/modals/modal_manager.h
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "modal_manager_private.h"
|
||||
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/ui/click_internal.h"
|
||||
#include "applib/ui/window.h"
|
||||
#include "applib/ui/window_stack_animation.h"
|
||||
#include "applib/ui/window_stack_private.h"
|
||||
#include "kernel/events.h"
|
||||
|
||||
struct ModalContext;
|
||||
typedef struct ModalContext ModalContext;
|
||||
|
||||
typedef bool (*ModalContextFilterCallback)(ModalContext *context, void *data);
|
||||
|
||||
//! This defines the priorities for the various modals. The order in which they are
|
||||
//! defined is specified by the interruption policy and determine who is able to interrupt
|
||||
//! whom. If a modal has a higher priority than another modal, it is able to interrupt
|
||||
//! that modal. Before adding anything to here, consider how it would affect the interrupt
|
||||
//! policy.
|
||||
//! Interruption Policy:
|
||||
//! https://docs.google.com/presentation/d/1xIjOR9kh4jcBYonzRstdtFQW0ZNMoFZZwYOUMN2JHNI
|
||||
typedef enum ModalPriority {
|
||||
//! Invalid priority for a modal.
|
||||
ModalPriorityInvalid = -1,
|
||||
//! Min priority, all modals are below or equal to this priority.
|
||||
ModalPriorityMin = 0,
|
||||
//! Discreet mode for watchface overlay information such as Timeline Peek.
|
||||
//! This priority should be used for modals that meant to display above the watchface without
|
||||
//! completely obstructing the watchface. For this reason, Discreet modal windows do not have
|
||||
//! compositor transitions because partial obstruction requires notifying the app of the
|
||||
//! unobstructed regions which only the modal window is able to derive.
|
||||
ModalPriorityDiscreet = ModalPriorityMin,
|
||||
//! Priority for generic one-off windows such as the battery charging window.
|
||||
//! This priority should be used for any modals that don't necessarily care how
|
||||
//! they are shown, just that they are shown.
|
||||
ModalPriorityGeneric,
|
||||
//! Priority used for displaying the phone ui after a phone call has been answered.
|
||||
//! Note: Notifications should always be able to subvert this.
|
||||
ModalPriorityPhone,
|
||||
//! Priority used for displaying notifications.
|
||||
ModalPriorityNotification,
|
||||
//! Priority used for displaying alerts. Alerts are important, but shouldn't
|
||||
//! impact the user of the watch.
|
||||
ModalPriorityAlert,
|
||||
//! Priority used for displaying the voice screen for recording. Ensure this one is
|
||||
//! always kept second to last.
|
||||
ModalPriorityVoice,
|
||||
//! Priority for displaying time-sensitive/critical windows which provide information
|
||||
//! on things that may affect the user's watch experience. However, these should never
|
||||
//! prevent an alarm from displaying.
|
||||
ModalPriorityCritical,
|
||||
//! Priority used for displaying wake up events such as alarms.
|
||||
ModalPriorityAlarm,
|
||||
//! Max priority, all modals are below this priority
|
||||
ModalPriorityMax,
|
||||
NumModalPriorities = ModalPriorityMax,
|
||||
} ModalPriority;
|
||||
|
||||
typedef enum ModalProperty {
|
||||
ModalProperty_None = 0,
|
||||
//! Whether there exists a modal in a modal stack on screen.
|
||||
ModalProperty_Exists = (1 << 0),
|
||||
//! Whether there exists a modal in a modal stack on screen that uses compositor transitions.
|
||||
ModalProperty_CompositorTransitions = (1 << 1),
|
||||
//! Whether there exists a modal that requested to render.
|
||||
ModalProperty_RenderRequested = (1 << 2),
|
||||
//! Whether all modal stacks are transparent. Having no modal is treated as transparent.
|
||||
ModalProperty_Transparent = (1 << 3),
|
||||
//! Whether all modal stacks pass input through. Having no modal is treated as unfocused.
|
||||
ModalProperty_Unfocused = (1 << 4),
|
||||
//! The default properties equivalent to there being no modal windows.
|
||||
ModalPropertyDefault = (ModalProperty_Transparent | ModalProperty_Unfocused),
|
||||
} ModalProperty;
|
||||
|
||||
//! Initializes the modal window state. This should be called before any
|
||||
//! Modal applications attempt to push windows.
|
||||
void modal_manager_init(void);
|
||||
|
||||
//! Sets whether modal windows are enabled.
|
||||
//! @param enabled Boolean indicating whether enabled or disabled
|
||||
//! Note that this is usable before modal_manager_init is called and modal_manager_init will not
|
||||
//! reset this state.
|
||||
void modal_manager_set_min_priority(ModalPriority priority);
|
||||
|
||||
//! Gets whether modal windows are enabled.
|
||||
// @returns boolean indicating if modals are enabled
|
||||
bool modal_manager_get_enabled(void);
|
||||
|
||||
//! Returns the ClickManager for the modal windows.
|
||||
//! @returns pointer to a \ref ClickManager
|
||||
ClickManager *modal_manager_get_click_manager(void);
|
||||
|
||||
//! Returns the first \ref WindowStack to pass the given filter callback.
|
||||
//! Iterates down from the highest priority to the lowest priority.
|
||||
//! @param filter_cb The \ref ModalContextFilterCallback
|
||||
//! @param context Context to pass to the callback
|
||||
//! @returns pointer to a \ref WindowStack
|
||||
WindowStack *modal_manager_find_window_stack(ModalContextFilterCallback filter_cb, void *ctx);
|
||||
|
||||
//! Returns the stack with the given window priority.
|
||||
//! If the passed priority is invalid, raises an assertion.
|
||||
//! @param priority The \ref ModalPriority of the desired \ref WindowStack
|
||||
//! @returns pointer to a \ref WindowStack
|
||||
WindowStack *modal_manager_get_window_stack(ModalPriority priority);
|
||||
|
||||
//! Returns the \ref Window of the current visible stack if there is one,
|
||||
//! otherwise NULL.
|
||||
//! @returns Pointer to a \ref Window
|
||||
Window *modal_manager_get_top_window(void);
|
||||
|
||||
//! Handles a button press event for the Modal Window. Raises an
|
||||
//! assertion if the event is not a click event.
|
||||
//! @param event The \ref PebbleEvent to handle.
|
||||
void modal_manager_handle_button_event(PebbleEvent *event);
|
||||
|
||||
//! Pops all windows from all modal stacks.
|
||||
void modal_manager_pop_all(void);
|
||||
|
||||
//! Pops all windows from modal stacks with priorities less than the given priority
|
||||
//! @param the max priorirty stack to pop all windows from
|
||||
void modal_manager_pop_all_below_priority(ModalPriority priority);
|
||||
|
||||
//! Called from the kernel event loop between events to handle any changes that have been made
|
||||
//! to the modal window stacks.
|
||||
void modal_manager_event_loop_upkeep(void);
|
||||
|
||||
//! Enumerates through the modal stacks and returns the flattened properties of all stacks
|
||||
//! combined. For example, if all modals are transparent, the Transparent property will be
|
||||
//! returned. Flattened meaning the entire stack is considered in aggregate. For example, if any
|
||||
//! one modal is opaque, the Transparent property won't be returned.
|
||||
ModalProperty modal_manager_get_properties(void);
|
||||
|
||||
//! Renders the highest priority top opaque window and all windows with higher priority.
|
||||
//! @param ctx The \ref GContext in which to render.
|
||||
//! @return Modal properties such as whether the modals are transparent or unfocusable
|
||||
void modal_manager_render(GContext *ctx);
|
||||
|
||||
//! Determines whether the given modal window is visible. Use window_manager_is_window_visible if
|
||||
//! both app windows and modal windows need to be considered.
|
||||
//! @param window The modal window to determine whether it is visible.
|
||||
//! @return true if the modal window is visible, false otherwise
|
||||
bool modal_manager_is_window_visible(Window *window);
|
||||
|
||||
//! Determines whether the given modal window is focused. Use window_manager_is_window_focused if
|
||||
//! both app windows and modal windows need to be considered.
|
||||
//! @param window The modal window to determine whether it is focused.
|
||||
//! @return true if the modal window is focused, false otherwise
|
||||
bool modal_manager_is_window_focused(Window *window);
|
||||
|
||||
//! Wrapper to call \ref window_stack_push() with the appropriate stack for
|
||||
//! the given \ref ModalPriority
|
||||
//! @param window The window to push onto the stack
|
||||
//! @param priority The priority of the window stack to push to
|
||||
//! @param animated `True` for animated, otherwise `False`
|
||||
void modal_window_push(Window *window, ModalPriority priority, bool animated);
|
||||
|
||||
//! Reset the modal manager state. Useful for unit testing.
|
||||
void modal_manager_reset(void);
|
18
src/fw/kernel/ui/modals/modal_manager_private.h
Normal file
18
src/fw/kernel/ui/modals/modal_manager_private.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
382
src/fw/kernel/ui/system_icons.h
Normal file
382
src/fw/kernel/ui/system_icons.h
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
// GBitmap + pixel data generated by bitmapgen.py:
|
||||
|
||||
/** Status Bar Icons */
|
||||
|
||||
static const uint8_t s_status_icon_launcher_pixels[] = {
|
||||
0xdf, 0x07, 0x00, 0x00, 0x8f, 0x07, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x04, 0x00, 0x00, 0x71, 0x04, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x71, 0x04, 0x00, 0x00, 0x71, 0x04, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_launcher_bitmap = {
|
||||
.addr = (void*) &s_status_icon_launcher_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 11, .h = 10 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_sms_pixels[] = {
|
||||
0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0xc7, 0x03, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0xf7, 0x03, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_sms_bitmap = {
|
||||
.addr = (void*) &s_status_icon_sms_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 10, .h = 9 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_bluetooth_pixels[] = {
|
||||
0x77, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x55, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x57, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_bluetooth_bitmap = {
|
||||
.addr = (void*) &s_status_icon_bluetooth_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 7, .h = 11 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_settings_pixels[] = {
|
||||
0x3f, 0x03, 0x00, 0x00, 0xbf, 0x03, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x8f, 0x03, 0x00, 0x00, 0xc7, 0x03, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf2, 0x03, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0xf7, 0x03, 0x00, 0x00, 0xf3, 0x03, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_settings_bitmap = {
|
||||
.addr = (void*) &s_status_icon_settings_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 10, .h = 10 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_phone_pixels[] = {
|
||||
0xfd, 0x07, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0xf9, 0x07, 0x00, 0x00, 0xf1, 0x07, 0x00, 0x00, 0xe3, 0x07, 0x00, 0x00, 0xc7, 0x06, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x0f, 0x04, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x7f, 0x04, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_phone_bitmap = {
|
||||
.addr = (void*) &s_status_icon_phone_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 11, .h = 11 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_music_pixels[] = {
|
||||
0x3f, 0x00, 0x00, 0x00, 0xc7, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0xf7, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x10, 0x01, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_music_bitmap = {
|
||||
.addr = (void*) &s_status_icon_music_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 9, .h = 10 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_silent_pixels[] = {
|
||||
0x7f, 0x03, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0x9f, 0x04, 0x00, 0x00, 0x43, 0x06, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x23, 0x07, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, 0x4b, 0x07, 0x00, 0x00, 0x27, 0x07, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x13, 0x07, 0x00, 0x00, 0x39, 0x07, 0x00, 0x00, 0x7c, 0x07, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_silent_bitmap = {
|
||||
.addr = (void*) &s_status_icon_silent_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 11, .h = 11 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_quiet_time_status_icon_pixels[] = {
|
||||
0x03, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0xfc, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x01, 0x02, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_quiet_time_status_icon_bitmap = {
|
||||
.addr = (void*) &s_quiet_time_status_icon_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 10, .h = 10 },
|
||||
},
|
||||
};
|
||||
|
||||
/** Action Bar Icons */
|
||||
|
||||
static const uint8_t s_bar_icon_actions_pixels[] = {
|
||||
0xff, 0xf7, 0x00, 0x00, 0xff, 0xe7, 0x00, 0x00, 0x83, 0xc7, 0x00, 0x00, 0xfd, 0x81, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x7d, 0x00, 0x00, 0x00, 0x1d, 0x80, 0x00, 0x00, 0x9d, 0xc7, 0x00, 0x00, 0xed, 0xe7, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0xed, 0xf7, 0x00, 0x00, 0xfd, 0xbf, 0x00, 0x00, 0xfd, 0xbf, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_actions_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_actions_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 16, .h = 12 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_phone_pixels[] = {
|
||||
0xf3, 0x7f, 0x00, 0x00, 0xe1, 0x7f, 0x00, 0x00, 0xc1, 0x7f, 0x00, 0x00, 0xc1, 0x7f, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0xe1, 0x7f, 0x00, 0x00, 0xe3, 0x7f, 0x00, 0x00, 0xe3, 0x7f, 0x00, 0x00, 0xc7, 0x7f, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x87, 0x7f, 0x00, 0x00, 0x0f, 0x67, 0x00, 0x00, 0x1f, 0x40, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, /* bytes 32 - 48 */
|
||||
0xff, 0x00, 0x00, 0x00, 0xff, 0x43, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_phone_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_phone_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 15, .h = 14 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_x_pixels[] = {
|
||||
0xfb, 0x0d, 0x00, 0x00, 0xf1, 0x08, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x03, 0x0c, 0x00, 0x00, 0x07, 0x0e, 0x00, 0x00, 0x07, 0x0e, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x01, 0x08, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xf1, 0x08, 0x00, 0x00, 0xfb, 0x0d, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_x_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_x_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 12, .h = 12 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_check_pixels[] = {
|
||||
0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x01, 0x00, /* bytes 0 - 16 */
|
||||
0x00, 0xf8, 0x00, 0x00, 0x04, 0x7c, 0x00, 0x00, 0x0e, 0x3e, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0xbe, 0x0f, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, /* bytes 32 - 48 */
|
||||
0xe0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_check_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_check_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 18, .h = 14 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_up_pixels[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0xfc, 0x03, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_up_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_up_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 12, .h = 7 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_down_pixels[] = {
|
||||
0xff, 0x0f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0xf0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_down_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_down_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 12, .h = 7 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_yes_pixels[] = {
|
||||
0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x71, 0x04, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x71, 0x04, 0x00, 0x00, 0x71, 0x04, 0x00, 0x00, 0x23, 0x06, 0x00, 0x00, 0x23, 0x06, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x27, 0x07, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x8f, 0x07, 0x00, 0x00, /* bytes 32 - 48 */
|
||||
0x8f, 0x07, 0x00, 0x00, 0x8f, 0x07, 0x00, 0x00, 0x8f, 0x07, 0x00, 0x00, 0x8f, 0x07, 0x00, 0x00, /* bytes 48 - 64 */
|
||||
0x8f, 0x07, 0x00, 0x00, 0x8f, 0x07, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_yes_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_yes_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 11, .h = 18 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_no_pixels[] = {
|
||||
0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, /* bytes 32 - 48 */
|
||||
0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, /* bytes 48 - 64 */
|
||||
0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_no_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_no_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 10, .h = 18 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_bar_icon_snooze_pixels[] = {
|
||||
0x3f, 0x00, 0x00, 0x00, 0xff, 0x0b, 0x00, 0x00, 0xff, 0x0d, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xc7, 0x0f, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0xf1, 0x0f, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_bar_icon_snooze_bitmap = {
|
||||
.addr = (void*) &s_bar_icon_snooze_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 12, .h = 12 },
|
||||
},
|
||||
};
|
||||
|
||||
// Battery Icons
|
||||
////////////////////////////////////////////////////////////
|
||||
static const uint8_t s_status_battery_empty_pixels[] = {
|
||||
0xff, 0x3f, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x01, 0x60, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_battery_empty_bitmap = {
|
||||
.addr = (void*) &s_status_battery_empty_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 15, .h = 8 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_battery_charging_pixels[] = {
|
||||
0x88, 0xff, 0x1f, 0x00, 0x8c, 0x00, 0x10, 0x00, 0x86, 0x00, 0x30, 0x00, 0x87, 0x00, 0x30, 0x00, /* bytes 0 - 16 */
|
||||
0x9c, 0x00, 0x30, 0x00, 0x8c, 0x00, 0x30, 0x00, 0x86, 0x00, 0x10, 0x00, 0x82, 0xff, 0x1f, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_battery_charging_bitmap = {
|
||||
.addr = (void*) &s_status_battery_charging_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 22, .h = 8 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_battery_charged_pixels[] = {
|
||||
0x0f, 0x3c, 0x00, 0x00, 0xc1, 0x21, 0x00, 0x00, 0xe1, 0x67, 0x00, 0x00, 0xfd, 0x61, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0xfd, 0x61, 0x00, 0x00, 0xe1, 0x67, 0x00, 0x00, 0xc1, 0x21, 0x00, 0x00, 0x0f, 0x3c, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_battery_charged_bitmap = {
|
||||
.addr = (void*) &s_status_battery_charged_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 15, .h = 8 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_phone_only_pixels[] = {
|
||||
0x41, 0x02, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x24, 0x00, 0x00, 0x00, 0x13, 0x02, 0x00, 0x00, 0xc9, 0x03, 0x00, 0x00, 0xe4, 0x03, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0xf6, 0x03, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_phone_only_bitmap = {
|
||||
.addr = (void*) &s_status_icon_phone_only_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 10, .h = 9 },
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t s_status_icon_airplane_mode_pixels[] = {
|
||||
0xcf, 0x0f, 0x00, 0x00, 0x9f, 0x0f, 0x00, 0x00, 0x1c, 0x0f, 0x00, 0x00, 0x39, 0x0e, 0x00, 0x00, /* bytes 0 - 16 */
|
||||
0x01, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x39, 0x0e, 0x00, 0x00, /* bytes 16 - 32 */
|
||||
0x1c, 0x0f, 0x00, 0x00, 0x9f, 0x0f, 0x00, 0x00, 0xcf, 0x0f, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const GBitmap s_status_icon_airplane_mode_bitmap = {
|
||||
.addr = (void*) &s_status_icon_airplane_mode_pixels,
|
||||
.row_size_bytes = 4,
|
||||
.info_flags = 0x1000,
|
||||
.bounds = {
|
||||
.origin = { .x = 0, .y = 0 },
|
||||
.size = { .w = 12, .h = 11 },
|
||||
},
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue