blob: cc846161a45e04f7c5ef750ac0d6c6f949d9b9ef [file] [log] [blame]
/**
*******************************************************************************
*
* @file atm_vkey.h
*
* @brief Virtual key event modeling
*
* Copyright (C) Atmosic 2021-2023
*
*******************************************************************************
*/
#pragma once
#include "co_math.h"
/**
*******************************************************************************
* @defgroup ATM_DEV_VKEY Virtual key models
* @ingroup DRIVERS
* @brief ATM virtual key handling module
*
* This module contains the necessary procedure to deal with keys based user
* events. In general, application registers interesting events initially and
* provids virtual key number with pressed(or released) information when user
* operated via some physical input like key matrixes and gpio buttons. If some
* event condition matched, callback function which was registered will be called.
*
* ## Register event table
* The Application could define preferred key events by atm_vk_reg_t type and
* form them as atm_vk_reg_t array table. Each array entry could be easily
* declared by macro such as @ref VKEY_CLICK, @ref VKEY_DB_CLICK, and so on.
* By calling @ref atm_vkey_add_table function, application could add the event
* table into atm_vkey module. Application could add many event tables and each
* table adding would return a handle to application.
*
* Below is two sets of event array registered in a HID keyboard example:
* @code
* // Key event on connected state
* static const atm_vk_reg_t kbd_conn_vkeys[] = {
* VKEY_DOWN_FIRST(kbd_send_rpt, atm_vk_any),
* VKEY_DOWN_MORE (kbd_send_rpt_more, atm_vk_any),
* VKEY_UP_INTER (kbd_send_rpt_more, atm_vk_any),
* VKEY_UP_LAST (kbd_send_rpt, atm_vk_any),
* VKEY_HOLD_1KEY (kbd_hold_test1, NULL, 1000, VK_FN),
* VKEY_HOLD_2KEY (kbd_hold_test2, NULL, 1000, VK_FN, VK_F1),
* VKEY_HOLD_1KEY_CLICK(kbd_clear_all_bond, VK_U, VK_FN),
* VKEY_HOLD_2KEY_CLICK(kbd_enter_rftest, VK_ENTER, VK_FN, VK_F1),
* };
*
* // Key event on disconnected state
* static const atm_vk_reg_t kbd_unconn_vkeys[] = {
* VKEY_DOWN_FIRST(kbd_save_key, atm_vk_any),
* VKEY_DOWN_MORE (kbd_save_key_more, atm_vk_any),
* VKEY_UP_INTER (kbd_save_key_more, atm_vk_any),
* VKEY_UP_LAST (kbd_save_key, atm_vk_any),
* VKEY_HOLD_1KEY (kbd_hold_test1, NULL, 1000, VK_FN),
* VKEY_HOLD_2KEY (kbd_hold_test2, NULL, 1000, VK_FN, VK_F1),
* VKEY_HOLD_1KEY_CLICK(kbd_clear_all_bond, VK_U, VK_FN),
* VKEY_HOLD_2KEY_CLICK(kbd_enter_rftest, VK_ENTER, VK_FN, VK_F1),
* };
*
* ...
*
* // Register events for connected state
* vkey_handles[0] = atm_vkey_add_table(kbd_unconn_vkeys,
* ARRAY_LEN(kbd_unconn_vkeys), &mmi_vkey_ctx);
* // Register events for disconnected state
* vkey_handles[1] = atm_vkey_add_table(kbd_conn_vkeys,
* ARRAY_LEN(kbd_conn_vkeys), &mmi_vkey_ctx);
* @endcode
*
* ## Feed virtual key
* When application was triggered by user input, it could use @ref atm_vkey_feed
* to provide the virtual key index and its pressed or release state.
*
* Below is the example of feeding virtual key into atm_vkey module:
* @code
*
* // key matrix callback function
* static void key_matrix_event(bool pressed, uint32_t idx)
* {
* switch (atm_asm_get_current_state(MMI_S_TBL_IDX)) {
* // Feed key when state is disconnected
* case MMI_S_BOOTED:
* case MMI_S_INITING:
* case MMI_S_IDLE: {
* atm_vkey_feed(vkey_handles[0], idx, pressed);
* } break;
* ...
* // Feed key when state is connected
* case MMI_S_HID_ONLY: {
* atm_vkey_feed(vkey_handles[1], idx, pressed);
* } break;
* ...
* }
* }
* @endcode
*
* @{
*
*******************************************************************************
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ATM_VKEY_MAX
#define ATM_VKEY_MAX 32
#endif
/**
*******************************************************************************
* @brief Create double click event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_dck_reg_t.cb
* @param[in] vkey Key index to monitor. @ref atm_vk_dck_reg_t.vkey
* @param[in] max_ms Maximal triggered interval within two clicks.
* @ref atm_vk_dck_reg_t.max_ms
*
* Here is the example of monitoring double click events on key index x or
* y while the time between two clicks is shorter than 100 milliseconds:
* - Method one:
* @code
*
* // Handler for double click event
* static void vkey_any_dbclick(atm_vk_dck_evt_t *evt, void const *ctx)
* {
* DEBUG_TRACE("Key (%03u) double clicked", evt->vkey);
*
* if (evt->vkey == x) {
* // Doing things when key x was double clicked
* ...
* } else if (evt->vkey == y) {
* // Doing things when key y was double clicked
* ...
* }
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_DB_CLICK(vkey_any_dbclick, atm_vk_any, 100),
* ...
* };
* @endcode
* - Method two:
* @code
*
* // Handler for double click event of key 3
* static void vkey_x_dbclick(atm_vk_dck_evt_t *evt, void const *ctx)
* {
* DEBUG_TRACE("Key (%03u) double clicked", evt->vkey);
* // Doing things when key x was double clicked
* ...
* }
*
* // Handler for double click event of key y
* static void vkey_y_dbclick(atm_vk_dck_evt_t *evt, void const *ctx)
* {
* DEBUG_TRACE("Key (%03u) double clicked", evt->vkey);
* // Doing things when key y was double clicked
* ...
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_DB_CLICK(vkey_x_dbclick, x, 100),
* VKEY_DB_CLICK(vkey_y_dbclick, y, 100),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_DB_CLICK(fn, vkey, max_ms) { \
atm_vk_db_click, .db_click = {fn, vkey, max_ms} \
}
/**
*******************************************************************************
* @brief Create click event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_ck_reg_t.cb
* @param[in] vkey Key index to monitor. @ref atm_vk_ck_reg_t.vkey
* @param[in] min_ms Minimal triggered interval within up and down.
* @ref atm_vk_ck_reg_t.min_ms
* @param[in] max_ms Maximal triggered interval within up and down.
* @ref atm_vk_ck_reg_t.max_ms
*
* Here is the example of monitoring click event on key index x with click interval
* within 50 to 100 millisecond and 500 to 1000 millisecond correspondingly:
* - Method one
* @code
* // Handler for key x quick click event
* static void vkey_x_quick_click(atm_vk_ck_evt_t *evt, void const *ctx)
* {
* // evt->vkey shall be x
* // evt->time_ms shall be from 50 to 100.
* DEBUG_TRACE("Key (%03u) quick clicked in %ld ms", evt->vkey, evt->time_ms);
* // Do things of quick clicking
* ...
* }
*
* // Handler for key x slow click event
* static void vkey_x_slow_click(atm_vk_ck_evt_t *evt, void const *ctx)
* {
* // evt->vkey shall be x
* // evt->time_ms shall be from 500 to 1000.
* DEBUG_TRACE("Key (%03u) slow clicked in %ld ms", evt->vkey, evt->time_ms);
* // Do things of slow clicking
* ...
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_CLICK(vkey_3_quick_click, x, 50, 100),
* ...
* VKEY_CLICK(vkey_3_slow_click, x, 500, 1000),
* ...
* };
* @endcode
* - Method two
* @code
* // Handler for key x quick click event
* static void vkey_x_click(atm_vk_ck_evt_t *evt, void const *ctx)
* {
* // evt->vkey shall be x
* // evt->time_ms shall be from 50 to 1000.
* DEBUG_TRACE("Key (%03u) clicked in %ld ms", evt->vkey, evt->time_ms);
* if (evt->time_ms <= 100) {
* // Do things of quick clicking
* ...
* } else if(evt->time_ms >= 500) {
* // Do things of slow clicking
* ...
* }
* }
*
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_CLICK(vkey_x_click, x, 50, 1000),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_CLICK(fn, vkey, min_ms, max_ms) { \
atm_vk_click, .click = {fn, vkey, min_ms, max_ms} \
}
/**
*******************************************************************************
* @brief Create hold click event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_hc_reg_t.cb
* @param[in] vkey Virtual key index for clicking monitor.
* @param[in] ... Held keys' mask.
* @note VKEY_HOLD_CLICK don't support ATM_MASK_ANY
*
* Here is the example of monitoring click events on key index x clicking with
* key indexes a,b and c (where a < 32, b < 32, 32 < c < 64) held and key index
* y clicking with key index d (where d < 32) held:
* @code
*
* // Handler for key index x clicking with key indexes a,b and c held.
* static void vkey_x_click_in_abc(atm_vk_hc_evt_t *evt, void const *ctx)
* {
* // evt->vkey shall be x
* DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
* // evt->held.mask[0] should equal to (1 << a) | (1 << b)
* DEBUG_TRACE("Key held mask[0] = %l#x", evt->held.mask[0]);
* // evt->held.mask[1] should equal to (1 << (c-32))
* DEBUG_TRACE("Key held mask[1] = %l#x", evt->held.mask[1]);
* }
*
* // Handler for key index y click with key index d held.
* static void vkey_y_click_in_d(atm_vk_hc_evt_t *evt, void const *ctx)
* {
* // evt->vkey will be y
* DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
* // evt->held.mask[0] should equal to (1 << d)
* DEBUG_TRACE("Key held mask[0] = %l#x", evt->held.mask[0]);
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_HOLD_CLICK(vkey_x_click_in_abc, x, (1 << a) | (1 << b), (1 << (c - 32))),
* ...
* VKEY_HOLD_CLICK(vkey_y_click_in_d, y, (1 << d)),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_HOLD_CLICK(fn, vkey, ...) { \
atm_vk_hold_click, .hold_click = {fn, vkey, {__VA_ARGS__}} \
}
/**
*******************************************************************************
* @brief Create hold click event entry with 1 key holding.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function. This is convenient version
* of @ref VKEY_HOLD_CLICK with 1 key holding.
* @param[in] fn Callback function
* @ref atm_vk_hc_reg_t.cb
* @param[in] vkey Virtual key index for clicking monitor.
* @param[in] mk Virtual key index for holding.
*
* Here is the example of monitoring click event on key index x clicking with
* key index a held:
* @code
*
* // Handler for key x click with key index a held.
* static void vkey_x_click_with_a(atm_vk_hc_evt_t *evt, void const *ctx)
* {
* // evt->vkey will be x
* DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
* // evt->held.mask[a/32] should equal to (1 << (a%32))
* DEBUG_TRACE("Key held mask[%x] = %l#x", a/32, evt->held.mask[a/32]);
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* // If a < 32, this equals to VKEY_HOLD_CLICK(vkey_x_click_with_a, x, (1 << a))
* // If a < 64, this equals to VKEY_HOLD_CLICK(vkey_x_click_with_a, x, 0, (1 << a % 32))
* // If a < 96, this equals to VKEY_HOLD_CLICK(vkey_x_click_with_a, x, 0, 0, (1 << a % 32))
* // Etc,.
* VKEY_HOLD_1KEY_CLICK(vkey_x_click_with_a, x, a),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_HOLD_1KEY_CLICK(fn, vkey, mk) \
VKEY_HOLD_CLICK(fn, vkey, VKEY_1KEY_MASK(mk))
/**
*******************************************************************************
* @brief Create hold click event entry with 2 key holding.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function. This is the convenient version
* of @ref VKEY_HOLD_CLICK with 2 key holding.
* @param[in] fn Callback function
* @ref atm_vk_hc_reg_t.cb
* @param[in] vkey Virtual key index for clicking monitor.
* @param[in] mk1 First virtual key index for holding.
* @param[in] mk2 Second virtual key index for holding.
* Here is the example of monitoring click event on key index x clicking with
* key index a and key index b held:
* @code
*
* // Handler for key index x click with key index a and b held.
* static void vkey_x_click_with_ab(atm_vk_hc_evt_t *evt, void const *ctx)
* {
* // evt->vkey shall be x
* DEBUG_TRACE("Key (%03u) clicked", evt->vkey);
* // evt->held.mask[a/32] bit a%32 shall be 1
* // evt->held.mask[b/32] bit b%32 shall be 1
* DEBUG_TRACE("mask %d = %l#x mask %d = %l#x", a/32, evt->held.mask[a/32], b/32,
* evt->held.mask[b/32]);
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_HOLD_2KEY_CLICK(vkey_x_click_with_ab, x, a, b),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_HOLD_2KEY_CLICK(fn, vkey, mk1, mk2) \
VKEY_HOLD_CLICK(fn, vkey, VKEY_2KEY_MASK(mk1, mk2))
/**
*******************************************************************************
* @brief Create hold event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_hd_reg_t.cb
* @param[in] fn1 Callback function
* @ref atm_vk_hd_reg_t.status_cb
* @param[in] min_ms Minimal hold time to trigger the event.
* @ref atm_vk_hd_reg_t.time_ms
* @param[in] ... Held keys' mask.
*
* Here is the example of monitoring click events on key a, b, and c held for
* 500 milliseconds and key index d held for 1000 milliseconds.
* @code
*
* // Handler for key index a,b and c held.
* static void vkey_abc_hold(atm_vk_hd_evt_t *evt, void const *ctx)
* {
* // evt->held.mask[a/32] bit a%32 shall be 1
* // evt->held.mask[b/32] bit b%32 shall be 1
* // evt->held.mask[c/32] bit c%32 shall be 1
* DEBUG_TRACE("mask %d = %l#x mask %d = %l#x", a/32, evt->held.mask[a/32], b/32,
* evt->held.mask[b/32], c/32, evt->held.mask[c/32]);
* // evt->time_ms should be longer than 500 milliseconds
* DEBUG_TRACE("time = %ld", evt->time_ms);
* }
*
* // Handler for key index d held.
* static void vkey_d_hold(atm_vk_hd_evt_t *evt, void const *ctx)
* {
* // evt->held.mask[d/32] bit d%32 shall be 1
* DEBUG_TRACE("mask %d = %l#x", d/32, evt->held.mask[d/32]);
* // evt->time_ms should be longer than 1000 milliseconds
* DEBUG_TRACE("time = %ld", evt->time_ms);
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* // The (1 << (a%32)), (1 << (b%32)) and (1 << (c%32)) would be located in
* // mask a/32, b/32, c/32 respectively
* VKEY_HOLD(vkey_abc_hold, NULL, 500, ... (1 << (a%32)) ... (1 << (b%32)) ... (1 << (c%32))),
* ...
* // The (1 << (d%32)) would be located in mask d/32
* VKEY_HOLD(vkey_d_hold, NULL, 1000, ...(1 << (d%32)...),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_HOLD(fn, fn1, min_ms, ...) { \
atm_vk_hold, .hold = {fn, fn1, min_ms, {__VA_ARGS__}} \
}
/**
*******************************************************************************
* @brief Create hold event entry with 1 key holding.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function. This is convenient version of
* @ref VKEY_HOLD with 1 key holding
* @param[in] fn Callback function
* @ref atm_vk_hd_reg_t.cb
* @param[in] fn1 Callback function
* @ref atm_vk_hd_reg_t.status_cb
* @param[in] min_ms Minimal hold time to trigger the event.
* @param[in] mk Virtual key index for holding.
*
* Here is the example of monitoring click event on and key index x holding for
* 1000 ms.
* @code
*
* // Handler for key x held.
* static void vkey_x_hold(atm_vk_hd_evt_t *evt, void const *ctx)
* {
* // evt->held.mask[x/32] bit x%32 shall be 1
* DEBUG_TRACE("Holding key mask[%d] = %l#x", x/32,evt->held.mask[x/32]);
* // evt->time_ms shall be longer than 1000
* DEBUG_TRACE("time = %ld", evt->time_ms);
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_HOLD_1KEY(vkey_x_hold, NULL, 1000, x),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_HOLD_1KEY(fn, fn1, min_ms, mk) \
VKEY_HOLD(fn, fn1, min_ms, VKEY_1KEY_MASK(mk))
/**
*******************************************************************************
* @brief Create hold click event entry with 2 key holding.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function. This is convenient version
* of @ref VKEY_HOLD with 2 keys holding.
* @param[in] fn Callback function
* @ref atm_vk_hd_reg_t.cb
* @param[in] fn1 Callback function
* @ref atm_vk_hd_reg_t.status_cb
* @param[in] min_ms Minimal hold time to trigger the event.
* @param[in] mk1 First virtual key index for holding.
* @param[in] mk2 Second virtual key index for holding.
*
* Here is the example of monitoring click event on and key index x and y holding for
* 1500 ms.
* @code
*
* // Handler for key index x and y held.
* static void vkey_xy_hold(atm_vk_hd_evt_t *evt, void const *ctx)
* {
* // evt->held.mask[x/32] bit x%32 shall be 1
* DEBUG_TRACE("Holding key mask[%d] = %l#x", x/32, evt->held.mask[x/32]);
* // evt->held.mask[y/32] bit y%32 shall be 1
* DEBUG_TRACE("Holding key mask[%d] = %l#x", y/32, evt->held.mask[y/32]);
* // evt->time_ms shall be longer than 1000
* DEBUG_TRACE("time = %ld", evt->time_ms);
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_HOLD_2KEY(vkey_xy_hold, NULL, 1500, x, y),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_HOLD_2KEY(fn, fn1, min_ms, mk1, mk2) \
VKEY_HOLD(fn, fn1, min_ms, VKEY_2KEY_MASK(mk1, mk2))
/**
*******************************************************************************
* @brief Create non-first down event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_dnup_reg_t.cb
* @param[in] vkey Virtual key index for down monitor.
*
* @anchor down_more_example
* Here is the example of monitoring all kinds of down and up events and report
* them to a single callback:
* @code
*
* static void vkey_any_up_down_inter(atm_vk_dnup_inter_evt_t *evt, void const *ctx)
* {
* if(evt->eid == atm_vk_down_1st || evt->eid == atm_vk_down_more) {
* DEBUG_TRACE("vkey %d is pressed", evt->vkey);
* } else {
* DEBUG_TRACE("vkey %d is release", evt->vkey);
* }
*
* if(evt->eid == atm_vk_down_more || evt->eid == atm_vk_up_inter) {
* DEBUG_TRACE("Some key held mask:")
* for (int i = 0; i < VKEY_MASK_NUM; i++) {
* DEBUG_TRACE("mask[%d] = %lu", evt->held.mask[i]);
* }
* }
* }
*
* static void vkey_any_up_down(atm_vk_dnup_evt_t *evt, void const *ctx)
* {
* atm_vk_dnup_inter_evt_t new_evt;
* memcpy(&new_evt, &evt, sizeof(atm_vk_dnup_evt_t));
* vkey_any_up_down_inter(&new_evt, ctx);
* }
*
* // Key event array
* static const atm_vk_reg_t key_event_array[] = {
* ...
* VKEY_DOWN_MORE(vkey_any_up_down, atm_vk_any),
* VKEY_DOWN_FIRST(vkey_any_up_down_inter, atm_vk_any),
* VKEY_UP_INTER(vkey_any_up_down_inter, atm_vk_any),
* VKEY_UP_LAST(vkey_any_up_down, atm_vk_any),
* ...
* };
* @endcode
*******************************************************************************
*/
#define VKEY_DOWN_MORE(fn, vkey) { \
atm_vk_down_more, .dn = {fn, vkey} \
}
/**
*******************************************************************************
* @brief Create first down event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_dnup_reg_t.cb
* @param[in] vkey Virtual key index for down monitor.
* @note Please refer example @ref down_more_example "here"
*******************************************************************************
*/
#define VKEY_DOWN_FIRST(fn, vkey) { \
atm_vk_down_1st, .dn_fst = {fn, vkey} \
}
/**
*******************************************************************************
* @brief Create non-last up event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_dnup_reg_t.cb
* @param[in] vkey Virtual key index for up monitor.
* @note Please refer example @ref down_more_example "here"
*******************************************************************************
*/
#define VKEY_UP_INTER(fn, vkey) { \
atm_vk_up_inter, .up = {fn, vkey} \
}
/**
*******************************************************************************
* @brief Create last up event entry.
* The entry is part of a @ref atm_vk_reg_t array which is used to
* registered into @ref atm_vkey_add_table function.
* @param[in] fn Callback function
* @ref atm_vk_dnup_reg_t.cb
* @param[in] vkey Virtual key index for up monitor.
* @note Please refer example @ref down_more_example "here"
*******************************************************************************
*/
#define VKEY_UP_LAST(fn, vkey) { \
atm_vk_up_last, .up_lst = {fn, vkey} \
}
/// Internal macro used by VKEY_1KEY_MASK
#define _MK_N(n, v) ((v / 32 == n) ? CO_BIT(v % 32) : 0)
/// Internal macro used by VKEY_2KEY_MASK
#define _MK_N2(n, v1, v2) (_MK_N(n,v1) | _MK_N(n,v2))
/// Vkey index to vkey mask conversion by maximum vkey number
#define _MK_N_64(v) _MK_N(0, v), _MK_N(1, v)
#define _MK_N_96(v) _MK_N_64(v), _MK_N(2, v)
#define _MK_N_128(v) _MK_N_96(v), _MK_N(3, v)
#define _MK_N_160(v) _MK_N_128(v), _MK_N(4, v)
#define _MK_N_192(v) _MK_N_160(v), _MK_N(5, v)
#define _MK_N2_64(v1, v2) _MK_N2(0, v1, v2), _MK_N2(1, v1, v2)
#define _MK_N2_96(v1, v2) _MK_N2_64(v1, v2), _MK_N2(2, v1, v2)
#define _MK_N2_128(v1, v2) _MK_N2_96(v1, v2), _MK_N2(3, v1, v2)
#define _MK_N2_160(v1, v2) _MK_N2_128(v1, v2), _MK_N2(4, v1, v2)
#define _MK_N2_192(v1, v2) _MK_N2_160(v1, v2), _MK_N2(5, v1, v2)
#if (ATM_VKEY_MAX <= 32)
#define VKEY_1KEY_MASK(v) _MK_N(0, v)
#define VKEY_2KEY_MASK(v1, v2) _MK_N2(0, v1, v2)
#elif (ATM_VKEY_MAX <= 64)
#define VKEY_1KEY_MASK(v) _MK_N_64(v)
#define VKEY_2KEY_MASK(v1, v2) _MK_N2_64(v1, v2)
#elif (ATM_VKEY_MAX <= 96)
#define VKEY_1KEY_MASK(v) _MK_N_96(v)
#define VKEY_2KEY_MASK(v1, v2) _MK_N2_96(v1, v2)
#elif (ATM_VKEY_MAX <= 128)
#define VKEY_1KEY_MASK(v) _MK_N_128(v)
#define VKEY_2KEY_MASK(v1, v2) _MK_N2_128(v1, v2)
#elif (ATM_VKEY_MAX <= 160)
#define VKEY_1KEY_MASK(v) _MK_N_160(v)
#define VKEY_2KEY_MASK(v1, v2) _MK_N2_160(v1, v2)
#else
#define VKEY_1KEY_MASK(v) _MK_N_192(v)
#define VKEY_2KEY_MASK(v1, v2) _MK_N2_192(v1, v2)
#endif // (ATM_VKEY_MAX <= 32)
/// Check if a key is in mask array
#define VKEY_IS_MASK_HIT(m, k) (m[k / 32] & CO_BIT(k % 32))
/// Check if two mask array is equal
#define VKEY_IS_MASK_EQU(m1, m2) \
(!memcmp(m1, m2, sizeof(uint32_t) * VKEY_MASK_NUM))
/// Special number for any mask
#define ATM_MASK_ANY 0xFFFFFFFF
/// Number for vkey mask
#define VKEY_MASK_NUM CO_DIVIDE_CEIL(ATM_VKEY_MAX, 32)
/// Key event ID
typedef enum {
/// First key down event.
atm_vk_down_1st,
/// Non-first key down event. Other pressed key(s) exist.
atm_vk_down_more,
/// Key held event.
atm_vk_hold,
/// Non-last Key up event. Other pressed key(s) exist.
atm_vk_up_inter,
/// Last key up event.
atm_vk_up_last,
/// Event for key clicking.
atm_vk_click,
/// Event for key clicking with some key(s) held.
atm_vk_hold_click,
/// Event for key double clicking.
atm_vk_db_click,
} atm_vk_eid_t;
/// key index: 0 to 254 is valid index.
/// Here defined is special usage.
typedef enum {
/// any keys
atm_vk_any = 0xff
} atm_vk_idx_t;
/// Held keys
typedef struct {
/// Masks of current held keys.
uint32_t mask[VKEY_MASK_NUM];
} atm_vk_held_keys_t;
/// Event structure header
typedef struct {
/// Event id
atm_vk_eid_t eid;
union {
/// Key index have been acted. @ref atm_vk_idx_t
atm_vk_idx_t vkey;
/// Key index of uint8_t type.
uint8_t u8vkey;
};
} atm_vk_basic_evt_t;
/// Event structure for atm_vk_down_fist and atm_vk_up_last.
typedef struct {
/// Basic event
atm_vk_basic_evt_t top;
} atm_vk_dnup_evt_t;
/// Event structure for atm_vk_hold
typedef struct atm_vk_hd_evt_s {
/// Basic event
/// @note the vkey in top is unused in atm_vk_hold
atm_vk_basic_evt_t top;
/// Minimal time of holding. Unit is millisecond. Zero for ignoring.
uint16_t time_ms;
/// Current held key
atm_vk_held_keys_t held;
} atm_vk_hd_evt_t;
/// Event structure for atm_vk_hold_click, atm_vk_down_more and
/// atm_vk_up_inter
typedef struct {
/// Basic event;
atm_vk_basic_evt_t top;
/// Masks of current held key.
atm_vk_held_keys_t held;
} atm_vk_hc_evt_t, atm_vk_dnup_inter_evt_t;
/// Click and double click event structure
typedef struct atm_vk_ck_evt_s {
/// Basic event;
atm_vk_basic_evt_t top;
/// Measured time for click and double click
/// click: time between press and release.
/// double click: time between two clicks.
uint16_t time_ms;
} atm_vk_ck_evt_t, atm_vk_dck_evt_t;
/// Entry structure for hold detection.
typedef struct atm_vk_hd_reg_s {
/// callback while event happen
/// @brief This function will be called if hold condition matched.
/// @param[in] evt Event for hold and click structure
/// @param[in] ctx Context data.
/// @return Return true to keep detecting this hold. otherwise, false.
bool (*cb)(atm_vk_hd_evt_t const *evt, void const *ctx);
/// @brief This function will be called if hold key has been pressed or
/// released.
/// @param[in] pressed Indicate the hold key is pressed and a timer is
/// starting to count.
/// @param[in] ctx Context data.
void (*status_cb)(bool pressed, void const *ctx);
/// Minimal time of holding. Unit is millisecond. Zero for ignoring.
uint16_t time_ms;
/// Masks of expecting held keys.
uint32_t mask[VKEY_MASK_NUM];
} atm_vk_hd_reg_t;
/// Entry structure for hold and click
typedef struct atm_vk_hc_reg_s {
/// callback while event happen
/// @brief This function will be called if hold and click condition matched.
/// @param[in] evt Event for hold structure
/// @param[in] ctx Context data.
void (*cb)(atm_vk_hc_evt_t const *evt, void const *ctx);
/// Key index of clicking. @ref atm_vk_idx_t
uint8_t vkey;
/// Masks of expecting held keys.
uint32_t mask[VKEY_MASK_NUM];
} atm_vk_hc_reg_t;
/// Entry structure of click event
typedef struct atm_vk_ck_reg_s {
/// callback while event happen
/// @brief This function will be called if click condition matched.
/// @param[in] evt Event for click structure
/// @note min_ms and max_ms represent the real click interval and they should
/// equal.
/// @param[in] ctx Context data.
void (*cb)(atm_vk_ck_evt_t const *evt, void const *ctx);
/// Key index of clicking. @ref atm_vk_idx_t
uint8_t vkey;
/// Minimal time between press and release. zero for ignoring.
uint16_t min_ms;
/// Maximal time between press and release. zero for ignoring.
uint16_t max_ms;
} atm_vk_ck_reg_t;
/// Entry structure for double click detection
typedef struct atm_vk_dck_reg_s {
/// callback while event happen
/// @brief This function will be called if double click condition matched.
/// @param[in] evt Event for double click structure
/// @param[in] ctx Context data.
void (*cb)(atm_vk_dck_evt_t const *evt, void const *ctx);
/// Listening key index of double clicking. @ref atm_vk_idx_t
uint8_t vkey;
/// Maximal time between two clicks. zero for ignoring.
uint16_t max_ms;
} atm_vk_dck_reg_t;
/// Entry structure for down and up
typedef struct atm_vk_dnup_reg_s {
/// callback while event happen
/// @brief This function will be called if down or up condition matched.
/// @param[in] evt Event for down or up structure
/// @param[in] ctx Context data.
void (*cb)(atm_vk_dnup_evt_t const *evt, void const *ctx);
/// Key listen index for atm_vk_down_fist, atm_vk_up_last
/// @ref atm_vk_idx_t
uint8_t vkey;
} atm_vk_dnup_reg_t;
/// Event structure for non-lonely down and up
typedef struct atm_vk_dnup_inter_reg_s {
/// callback while event happen
/// @brief This function will be called if down or up condition matched.
/// @param[in] evt Event for non-lonely down or up structure
/// @param[in] ctx Context data.
void (*cb)(atm_vk_dnup_inter_evt_t const *evt, void const *ctx);
/// Key index for atm_vk_down_more, atm_vk_up_inter
/// @ref atm_vk_idx_t
uint8_t vkey;
} atm_vk_dnup_inter_reg_t;
/// Event union for table
typedef struct {
/// Event id
atm_vk_eid_t eid;
union {
/// Non-first down event
atm_vk_dnup_inter_reg_t dn;
/// Non-last up event
atm_vk_dnup_inter_reg_t up;
/// First down event
atm_vk_dnup_reg_t dn_fst;
/// Last up event
atm_vk_dnup_reg_t up_lst;
/// Hold event
atm_vk_hd_reg_t hold;
/// Hold and click event
atm_vk_hc_reg_t hold_click;
/// click event
atm_vk_ck_reg_t click;
/// double click event
atm_vk_dck_reg_t db_click;
};
} atm_vk_reg_t;
/**
*******************************************************************************
* @brief Add a virtual key event table
* @param[in] table Virtual key event table
* @param[in] ent_cnt Entry count of event table
* @param[in] ctx application context
* @return Handle of this table. This handle is used to feed virtual key while
* some user input happened.
*******************************************************************************
*/
__NONNULL(1)
struct vkll_ctx_s *atm_vkey_add_table(atm_vk_reg_t const *table, uint16_t ent_cnt,
void const *ctx);
/**
*******************************************************************************
* @brief Virtual key feed.
* When some user input happened, use this function to put virtual key into
* system.
* @param[in] handle Handle of virtual key event table.
* @param[in] vkey Virtual key to be inputted.
* @param[in] is_pressed True for key pressing. Otherwise for releasing.
*******************************************************************************
*/
__NONNULL(1)
void atm_vkey_feed(struct vkll_ctx_s *handle, uint8_t vkey, bool is_pressed);
/**
*******************************************************************************
* @brief Virtual key flush.
* @param[in] handle Handle of virtual key event table.
*******************************************************************************
*/
__NONNULL_ALL
void atm_vkey_flush(struct vkll_ctx_s *handle);
#ifdef __cplusplus
}
#endif
/// @} ATM_BTFM_PVKEY