mirror of
https://github.com/google/pebble.git
synced 2025-04-30 15:21:41 -04:00
183 lines
6.3 KiB
C
183 lines
6.3 KiB
C
/*
|
|
* 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 "unobstructed_area_service.h"
|
|
#include "unobstructed_area_service_private.h"
|
|
|
|
#include "applib/app.h"
|
|
#include "applib/graphics/framebuffer.h"
|
|
#include "kernel/events.h"
|
|
#include "kernel/pbl_malloc.h"
|
|
#include "process_state/app_state/app_state.h"
|
|
#include "syscall/syscall_internal.h"
|
|
#include "system/passert.h"
|
|
|
|
static void prv_handle_unobstructed_area_event(PebbleEvent *event, void *context);
|
|
static void prv_origin_y_to_area(int16_t origin_y, GRect *area_out);
|
|
|
|
void unobstructed_area_service_init(UnobstructedAreaState *state, int16_t current_y) {
|
|
PBL_ASSERTN(state);
|
|
*state = (UnobstructedAreaState) {};
|
|
prv_origin_y_to_area(current_y, &state->area);
|
|
state->event_info = (EventServiceInfo) {
|
|
.type = PEBBLE_UNOBSTRUCTED_AREA_EVENT,
|
|
.handler = prv_handle_unobstructed_area_event,
|
|
.context = state,
|
|
};
|
|
event_service_client_subscribe(&state->event_info);
|
|
}
|
|
|
|
void unobstructed_area_service_deinit(UnobstructedAreaState *state) {
|
|
PBL_ASSERTN(state);
|
|
event_service_client_unsubscribe(&state->event_info);
|
|
}
|
|
|
|
static void prv_put_area_event(UnobstructedAreaEventType type, int16_t current_y,
|
|
int16_t final_y, AnimationProgress progress) {
|
|
PebbleEvent event = {
|
|
.type = PEBBLE_UNOBSTRUCTED_AREA_EVENT,
|
|
.unobstructed_area = {
|
|
.type = type,
|
|
.current_y = current_y,
|
|
.final_y = final_y,
|
|
.progress = progress,
|
|
},
|
|
};
|
|
event_put(&event);
|
|
}
|
|
|
|
static void prv_clip_area(GRect *area_out) {
|
|
PBL_ASSERTN(area_out);
|
|
const GRect display_frame = { .size = framebuffer_get_size(app_state_get_framebuffer()) };
|
|
grect_clip(area_out, &display_frame);
|
|
}
|
|
|
|
//! Currently, the unobstructed area is derived from the origin of the obstruction.
|
|
//! This is equivalent to the height of the unobstructed area.
|
|
static void prv_origin_y_to_area(int16_t origin_y, GRect *area_out) {
|
|
PBL_ASSERTN(area_out);
|
|
*area_out = (GRect) {
|
|
.size = { DISP_COLS, origin_y },
|
|
};
|
|
prv_clip_area(area_out);
|
|
}
|
|
|
|
void unobstructed_area_service_will_change(int16_t current_y, int16_t final_y) {
|
|
prv_put_area_event(UnobstructedAreaEventType_WillChange, current_y, final_y,
|
|
ANIMATION_NORMALIZED_MIN);
|
|
}
|
|
|
|
void unobstructed_area_service_change(int16_t current_y, int16_t final_y,
|
|
AnimationProgress progress) {
|
|
prv_put_area_event(UnobstructedAreaEventType_Change, current_y, final_y, progress);
|
|
}
|
|
|
|
void unobstructed_area_service_did_change(int16_t final_y) {
|
|
prv_put_area_event(UnobstructedAreaEventType_DidChange, final_y, final_y,
|
|
ANIMATION_NORMALIZED_MAX);
|
|
}
|
|
|
|
static void prv_save_event_area(UnobstructedAreaState *state, PebbleEvent *event) {
|
|
GRect area;
|
|
prv_origin_y_to_area(event->unobstructed_area.current_y, &area);
|
|
state->area = area;
|
|
prv_clip_area(&state->area);
|
|
}
|
|
|
|
static void prv_call_will_change(UnobstructedAreaState *state, PebbleEvent *event) {
|
|
if (state->is_changing) {
|
|
return;
|
|
}
|
|
// Always call the will handler even if the app restarts or starts out of sync with
|
|
// the animation and has had its app state reinitialized. Set `is_changing` to keep track.
|
|
state->is_changing = true;
|
|
if (state->handlers.will_change) {
|
|
GRect final_area;
|
|
prv_origin_y_to_area(event->unobstructed_area.final_y, &final_area);
|
|
state->handlers.will_change(final_area, state->context);
|
|
}
|
|
}
|
|
|
|
static void prv_handle_will_change_event(PebbleEvent *event, void *context) {
|
|
UnobstructedAreaState *state = context;
|
|
// It is the producer's responsibility not to overlap unobstructed area changes.
|
|
PBL_ASSERTN(!state->is_changing);
|
|
prv_call_will_change(state, event);
|
|
}
|
|
|
|
static void prv_handle_change_event(PebbleEvent *event, void *context) {
|
|
UnobstructedAreaState *state = context;
|
|
prv_call_will_change(state, event);
|
|
if (state->handlers.change) {
|
|
state->handlers.change(event->unobstructed_area.progress, state->context);
|
|
}
|
|
}
|
|
|
|
static void prv_handle_did_change_event(PebbleEvent *event, void *context) {
|
|
UnobstructedAreaState *state = context;
|
|
prv_call_will_change(state, event);
|
|
state->is_changing = false;
|
|
if (state->handlers.did_change) {
|
|
state->handlers.did_change(state->context);
|
|
}
|
|
}
|
|
|
|
static void prv_handle_unobstructed_area_event(PebbleEvent *event, void *context) {
|
|
UnobstructedAreaState *state = context;
|
|
GRect previous_area = state->area;
|
|
prv_save_event_area(state, event);
|
|
if (!grect_equal(&previous_area, &state->area)) {
|
|
app_request_render();
|
|
}
|
|
switch (event->unobstructed_area.type) {
|
|
case UnobstructedAreaEventType_WillChange:
|
|
prv_handle_will_change_event(event, context);
|
|
break;
|
|
case UnobstructedAreaEventType_Change:
|
|
prv_handle_change_event(event, context);
|
|
break;
|
|
case UnobstructedAreaEventType_DidChange:
|
|
prv_handle_did_change_event(event, context);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void unobstructed_area_service_subscribe(UnobstructedAreaState *state,
|
|
const UnobstructedAreaHandlers *handlers,
|
|
void *context) {
|
|
PBL_ASSERTN(state && handlers);
|
|
state->handlers = *handlers;
|
|
state->context = context;
|
|
}
|
|
|
|
void unobstructed_area_service_unsubscribe(UnobstructedAreaState *state) {
|
|
PBL_ASSERTN(state);
|
|
state->handlers = (UnobstructedAreaHandlers) {};
|
|
}
|
|
|
|
void unobstructed_area_service_get_area(UnobstructedAreaState *state, GRect *area_out) {
|
|
if (state && area_out) {
|
|
*area_out = state->area;
|
|
}
|
|
}
|
|
|
|
void app_unobstructed_area_service_subscribe(UnobstructedAreaHandlers handlers, void *context) {
|
|
unobstructed_area_service_subscribe(app_state_get_unobstructed_area_state(), &handlers, context);
|
|
}
|
|
|
|
void app_unobstructed_area_service_unsubscribe(void) {
|
|
unobstructed_area_service_unsubscribe(app_state_get_unobstructed_area_state());
|
|
}
|