blob: 2744b1cb8502cb58d7eefef9a48c86d1c7b0b0b4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <assert.h>
#include "os/mynewt.h"
#include "button/button.h"
#define _BUTTON_FSM_INIT 0
#define _BUTTON_FSM_PRESSED 1
#define _BUTTON_FSM_WAIT_DBLPRESSED 2
#define _BUTTON_FSM_DBLPRESSED 3
#define _BUTTON_FSM_HOLD_OR_REPEAT 4
#define _BUTTON_RELEASED 0
#define _BUTTON_PRESSED 1
#define _BUTTON_TIMEOUT 2
struct button_event {
struct os_event os_event;
uint8_t type;
uint8_t flags;
};
static struct os_eventq *button_internal_evq = NULL;
static struct os_eventq *button_callback_default_evq = NULL;
#define BUTTON_POST_STATE_EVENT(button, flags) \
button_post_event(button, BUTTON_STATE_CHANGED, flags)
#define BUTTON_POST_ACTION_EVENT(button, flags) \
button_post_event(button, BUTTON_ACTION, flags)
static struct button_event button_event[MYNEWT_VAL(BUTTON_EVENT_MAX)] = { 0 };
static button_callback_t button_callback = NULL;
static struct button_event *
button_alloc_event(void)
{
int i;
for (i = 0 ; i < MYNEWT_VAL(BUTTON_EVENT_MAX) ; i++) {
if (!OS_EVENT_QUEUED(&button_event[i].os_event)) {
return &button_event[i];
}
}
return NULL;
}
/**
* Process generated event, using the user defined callback.
*
* @param ev generated event
*/
static void
button_event_handler(struct os_event *ev)
{
struct button_event *b_ev;
button_t *button;
b_ev = (struct button_event *)ev;
button = (button_t *)ev->ev_arg;
button_callback(button->id, b_ev->type, b_ev->flags);
}
/**
* Post an event to the default queue.
*
* @param button button generating the event
* @param type type of event (state or action)
* @param flags event description (BUTTON_FLG_*)
*
* @return -1 unable to post event (not enough buffer)
* 0 event posted
* 1 event not posted due to filtering
*/
static int
button_post_event(button_t *button, uint8_t type, uint8_t flags)
{
struct button_event *evt;
#if MYNEWT_VAL(BUTTON_USE_FILTERING)
if (button->filter.enabled) {
uint8_t filter = 0xff;
switch(type) {
#if MYNEWT_VAL(BUTTON_EMIT_STATE_CHANGED)
case BUTTON_STATE_CHANGED:
filter = button->filter.state;
break;
#endif
#if MYNEWT_VAL(BUTTON_EMIT_ACTION)
case BUTTON_ACTION:
filter = button->filter.action;
break;
#endif
}
if (~filter & flags) {
return 1;
}
}
#endif
evt = button_alloc_event();
if (evt) {
evt->os_event.ev_cb = button_event_handler;
evt->os_event.ev_arg = button;
evt->type = type;
evt->flags = flags;
#if MYNEWT_VAL(BUTTON_USE_PER_BUTTON_CALLBACK_EVENTQ) > 0
os_eventq_put(button->eventq, &evt->os_event);
#else
os_eventq_put(button_callback_default_evq, &evt->os_event);
#endif
return 0;
} else {
button->state |= BUTTON_FLG_MISSED;
return -1;
}
}
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
/**
* Perform button emulation.
*
* @param button the button to emulate
*/
static void
button_emulating(button_t *button)
{
bool pressed = true;
bool released = true;
for (button_t **from = button->emulated ; *from ; from++) {
if ((*from)->state & BUTTON_FLG_PRESSED) {
released = false;
} else {
pressed = false;
}
}
if (pressed != !released)
return;
if (pressed || (released && button->emulating)) {
button->emulating = pressed;
button_set_low_level_state(button, pressed);
}
}
/**
* Process children of a button. Used for emulating button.
*
* @param button the button to look for children
*/
static void
button_process_children(button_t *button)
{
if (button->children == NULL)
return;
for (button_t **child = button->children ; *child ; child++)
if ((*child)->emulated)
button_emulating(*child);
}
/**
* Check if one of the children is using the action, and so
* we shouldn't generate one for ourself.
*
* @param button the button to look for children
*/
static bool
button_stealed_action(button_t *button)
{
if (button->children == NULL)
return false;
for (button_t **child = button->children ; *child ; child++)
if ((*child)->emulated && (*child)->emulating)
return true;
return false;
}
#endif
/**
* Use low level action, to generated button state and action.
* This one deal with simple state/action, where only pressed/released/click
* are considered.
*
* @param button button to process
* @param action low level action associated to the button
* (_BUTTON_PRESSED, _BUTTON_RELEASED)
*/
static void
button_exec_simple(button_t *button, int action)
{
assert(action != _BUTTON_TIMEOUT);
switch(action) {
case _BUTTON_PRESSED:
button->state = BUTTON_FLG_PRESSED;
break;
case _BUTTON_RELEASED:
#if MYNEWT_VAL(BUTTON_EMIT_ACTION)
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
if (!button_stealed_action(button))
#endif
BUTTON_POST_ACTION_EVENT(button, button->state);
#endif
button->state &= ~BUTTON_FLG_PRESSED;
break;
}
#if MYNEWT_VAL(BUTTON_EMIT_STATE_CHANGED)
BUTTON_POST_STATE_EVENT(button, button->state);
#endif
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
button_process_children(button);
#endif
}
#if MYNEWT_VAL(BUTTON_USE_DOUBLE) || \
MYNEWT_VAL(BUTTON_USE_LONG ) || \
MYNEWT_VAL(BUTTON_USE_REPEAT)
/**
* Use low level action, to generated button state and action.
* This one deal with complexe action, such as generating double click,
* long click, repeating action, ...
*
* @param button the button to process
* @param action low level action associated to the button
* (_BUTTON_PRESSED, _BUTTON_RELEASED, _BUTTON_TIMEOUT)
*/
static void
button_exec_fsm(button_t *button, int action)
{
struct os_callout *callout;
callout = &button->callout;
switch (button->fsm_state) {
case _BUTTON_FSM_INIT:
switch(action) {
case _BUTTON_PRESSED:
goto do_pressed;
case _BUTTON_RELEASED:
goto do_nothing;
case _BUTTON_TIMEOUT:
goto do_assert;
}
break;
case _BUTTON_FSM_PRESSED:
switch(action) {
case _BUTTON_PRESSED:
goto do_nothing;
case _BUTTON_RELEASED:
#if MYNEWT_VAL(BUTTON_USE_DOUBLE)
if (button->mode & BUTTON_FLG_DOUBLED) {
goto to_wait_dblpressed;
} else {
#endif
goto do_release;
#if MYNEWT_VAL(BUTTON_USE_DOUBLE)
}
#endif
case _BUTTON_TIMEOUT:
#if MYNEWT_VAL(BUTTON_USE_LONG)
if (button->mode & BUTTON_FLG_LONG) {
goto do_longpress;
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
} else if (button->mode & BUTTON_FLG_REPEATING) {
goto do_repeat;
#endif
} else {
goto do_assert;
}
#elif MYNEWT_VAL(BUTTON_USE_REPEAT)
goto do_repeat;
#else
goto do_assert;
#endif
}
break;
#if MYNEWT_VAL(BUTTON_USE_DOUBLE)
case _BUTTON_FSM_WAIT_DBLPRESSED:
switch(action) {
case _BUTTON_TIMEOUT:
goto do_release;
case _BUTTON_PRESSED:
goto do_dblpressed;
case _BUTTON_RELEASED:
goto do_nothing;
}
break;
case _BUTTON_FSM_DBLPRESSED:
switch(action) {
case _BUTTON_TIMEOUT :
#if MYNEWT_VAL(BUTTON_USE_LONG)
if (button->mode & BUTTON_FLG_LONG) {
goto do_longpress;
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
} else if (button->mode & BUTTON_FLG_REPEATING) {
goto do_repeat;
#endif
} else {
goto do_assert;
}
#elif MYNEWT_VAL(BUTTON_USE_REPEAT)
goto do_repeat;
#else
goto do_assert;
#endif
case _BUTTON_RELEASED: goto do_release;
case _BUTTON_PRESSED : goto do_nothing;
}
break;
#endif
#if MYNEWT_VAL(BUTTON_USE_LONG ) || \
MYNEWT_VAL(BUTTON_USE_REPEAT)
case _BUTTON_FSM_HOLD_OR_REPEAT:
switch(action) {
case _BUTTON_TIMEOUT:
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
if (button->mode & BUTTON_FLG_REPEATING) {
goto do_repeat;
} else {
#endif
goto do_assert;
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
}
#endif
case _BUTTON_RELEASED:
goto do_release;
case _BUTTON_PRESSED:
goto do_nothing;
}
break;
#endif
}
do_assert:
assert(0);
do_nothing:
return;
do_pressed:
#if MYNEWT_VAL(BUTTON_USE_LONG)
if (button->mode & BUTTON_FLG_LONG) {
os_callout_reset(callout, MYNEWT_VAL(BUTTON_LONGHOLD_TICKS));
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
} else if (button->mode & BUTTON_FLG_REPEATING) {
os_callout_reset(callout, MYNEWT_VAL(BUTTON_REPEAT_FIRST_TICKS));
#endif
}
#elif MYNEWT_VAL(BUTTON_USE_REPEAT)
os_callout_reset(callout, MYNEWT_VAL(BUTTON_REPEAT_FIRST_TICKS));
#endif
button->state = BUTTON_FLG_PRESSED;
#if MYNEWT_VAL(BUTTON_EMIT_STATE_CHANGED)
BUTTON_POST_STATE_EVENT(button, button->state);
#endif
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
button_process_children(button);
#endif
button->fsm_state = _BUTTON_FSM_PRESSED;
return;
#if MYNEWT_VAL(BUTTON_USE_DOUBLE)
to_wait_dblpressed:
os_callout_reset(callout, MYNEWT_VAL(BUTTON_DBLCLICK_TICKS));
button->fsm_state = _BUTTON_FSM_WAIT_DBLPRESSED;
return;
do_dblpressed:
#if MYNEWT_VAL(BUTTON_USE_LONG)
if (button->mode & BUTTON_FLG_LONG) {
os_callout_reset(callout, MYNEWT_VAL(BUTTON_LONGHOLD_TICKS));
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
} else if (button->mode & BUTTON_FLG_REPEATING) {
os_callout_reset(callout, MYNEWT_VAL(BUTTON_REPEAT_FIRST_TICKS));
#endif
} else {
os_callout_stop(callout);
}
#elif MYNEWT_VAL(BUTTON_USE_REPEAT)
os_callout_reset(callout, MYNEWT_VAL(BUTTON_REPEAT_FIRST_TICKS));
#else
os_callout_stop(callout);
#endif
button->state |= BUTTON_FLG_DOUBLED;
#if MYNEWT_VAL(BUTTON_EMIT_STATE_CHANGED)
BUTTON_POST_STATE_EVENT(button, button->state);
#endif
button->fsm_state = _BUTTON_FSM_DBLPRESSED;
return;
#endif
#if MYNEWT_VAL(BUTTON_USE_LONG)
do_longpress:
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
if (button->mode & BUTTON_FLG_REPEATING)
os_callout_reset(callout, MYNEWT_VAL(BUTTON_REPEAT_FIRST_TICKS));
#endif
button->state |= BUTTON_FLG_LONG;
#if MYNEWT_VAL(BUTTON_EMIT_STATE_CHANGED)
BUTTON_POST_STATE_EVENT(button, button->state);
#endif
button->fsm_state = _BUTTON_FSM_HOLD_OR_REPEAT;
return;
#endif
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
do_repeat:
os_callout_reset(callout, MYNEWT_VAL(BUTTON_REPEAT_TICKS));
#if MYNEWT_VAL(BUTTON_EMIT_ACTION)
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
if (!button_stealed_action(button))
#endif
BUTTON_POST_ACTION_EVENT(button, button->state);
#endif
if (!(button->state & BUTTON_FLG_REPEATING)) {
button->state |= BUTTON_FLG_REPEATING;
#if MYNEWT_VAL(BUTTON_EMIT_STATE_CHANGED)
BUTTON_POST_STATE_EVENT(button, button->state);
#endif
}
return;
#endif
do_release:
os_callout_stop(callout);
#if MYNEWT_VAL(BUTTON_EMIT_ACTION)
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
if (!button_stealed_action(button)) {
#endif
#if MYNEWT_VAL(BUTTON_USE_REPEAT)
if (!(button->state & BUTTON_FLG_REPEATING))
#endif
BUTTON_POST_ACTION_EVENT(button, button->state);
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
}
#endif
#endif
button->state &= ~BUTTON_FLG_PRESSED;
#if MYNEWT_VAL(BUTTON_EMIT_STATE_CHANGED)
BUTTON_POST_STATE_EVENT(button, button->state);
#endif
#if MYNEWT_VAL(BUTTON_USE_EMULATION)
button_process_children(button);
#endif
button->fsm_state = _BUTTON_FSM_INIT;
return;
}
/**
* Callout function used for processing timeout.
* Used for processing complexe action.
*
* @param ev event from callout
*/
static void
button_fsm_callout(struct os_event *ev)
{
button_t *button;
button = (button_t *)ev->ev_arg;
button_exec_fsm(button, _BUTTON_TIMEOUT);
}
#endif
void
button_init(button_t *buttons, unsigned int count, button_callback_t cb)
{
#if MYNEWT_VAL(BUTTON_USE_DOUBLE) || \
MYNEWT_VAL(BUTTON_USE_LONG ) || \
MYNEWT_VAL(BUTTON_USE_REPEAT) || \
MYNEWT_VAL(BUTTON_USE_PER_BUTTON_CALLBACK_EVENTQ) > 0
unsigned int i;
#endif
if (button_internal_evq == NULL) {
button_internal_evq = os_eventq_dflt_get();
}
if (button_callback_default_evq == NULL) {
button_callback_default_evq = os_eventq_dflt_get();
}
button_callback = cb;
#if MYNEWT_VAL(BUTTON_USE_DOUBLE) || \
MYNEWT_VAL(BUTTON_USE_LONG ) || \
MYNEWT_VAL(BUTTON_USE_REPEAT)
for (i = 0 ; i < count ; i++) {
button_t *button = &buttons[i];
os_callout_init(&button->callout, button_internal_evq,
button_fsm_callout, button);
}
#endif
#if MYNEWT_VAL(BUTTON_USE_PER_BUTTON_CALLBACK_EVENTQ) > 0
for (i = 0 ; i < count ; i++) {
button_t *button = &buttons[i];
if (button->eventq == NULL) {
button->eventq = button_callback_default_evq;
}
}
#endif
}
void
button_set_low_level_state(button_t *button, bool pressed)
{
int action;
action = pressed ? _BUTTON_PRESSED : _BUTTON_RELEASED;
#if MYNEWT_VAL(BUTTON_USE_DOUBLE) || \
MYNEWT_VAL(BUTTON_USE_LONG ) || \
MYNEWT_VAL(BUTTON_USE_REPEAT)
if (button->mode & ~(BUTTON_FLG_PRESSED)) {
button_exec_fsm(button, action);
} else {
button_exec_simple(button, action);
}
#else
button_exec_simple(button, action);
#endif
}
void
button_internal_evq_set(struct os_eventq *evq)
{
button_internal_evq = evq;
}
void
button_callback_default_evq_set(struct os_eventq *evq)
{
button_callback_default_evq = evq;
}