diff --git a/dev/main.c b/dev/main.c index 053f961..540b04a 100644 --- a/dev/main.c +++ b/dev/main.c @@ -15,37 +15,40 @@ static lwbtn_btn_t btns[] = {{.arg = (void*)&keys[0]}, {.arg = (void*)&keys[1]}, {.arg = (void*)&keys[6]}, {.arg = (void*)&keys[7]}, {.arg = (void*)&keys[8]}, {.arg = (void*)&keys[9]}}; +/** + * \brief Get input state callback + * \param lw: LwBTN instance + * \param btn: Button instance + * \return `1` if button active, `0` otherwise + */ uint8_t prv_btn_get_state(struct lwbtn* lw, struct lwbtn_btn* btn) { + (void)lw; return GetAsyncKeyState(*(int*)btn->arg) < 0; } +/** + * \brief Button event + * + * \param lw: LwBTN instance + * \param btn: Button instance + * \param evt: Button event + */ void prv_btn_event(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt) { + const char* s = "unknown"; (void)lw; - printf("[%7u] State !!!. CH: %c, evt: ", (unsigned)get_tick(), *(int*)btn->arg); - switch (evt) { - case LWBTN_EVT_KEEPALIVE: { - printf("KEEPALIVE"); - break; - } - case LWBTN_EVT_ONPRESS: { - printf("ONPRESS"); - break; - } - case LWBTN_EVT_ONRELEASE: { - printf("ONRELEASE"); - break; - } - case LWBTN_EVT_ONCLICK: { - printf("ONCLICK"); - break; - } - default: { - break; - } - } - printf("\r\n"); + + /* Get event string */ + s = ((evt == LWBTN_EVT_KEEPALIVE) + ? "KEEPALIVE" + : ((evt == LWBTN_EVT_ONPRESS) + ? " ONPRESS" + : ((evt == LWBTN_EVT_ONRELEASE) ? "ONRELEASE" + : ((evt == LWBTN_EVT_ONCLICK) ? " ONCLICK" : " UNKNOWN")))); + + printf("[%7u] CH: %c, evt: %s, keep-alive cnt: %3u, click cnt: %3u\r\n", (unsigned)get_tick(), *(int*)btn->arg, s, + (unsigned)btn->keepalive.cnt, (unsigned)btn->click.cnt); } int @@ -54,14 +57,22 @@ main(void) { QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&sys_start_time); + /* Define buttons */ lwbtn_init_ex(NULL, btns, sizeof(btns) / sizeof(btns[0]), prv_btn_get_state, prv_btn_event); while (1) { + /* Process forever */ lwbtn_process_ex(NULL, get_tick()); - Sleep(10); + + /* Artificial sleep to offload win process */ + Sleep(5); } } +/** + * \brief Get current tick in ms from start of program + * \return uint32_t: Tick in ms + */ static uint32_t get_tick(void) { LONGLONG ret; diff --git a/lwbtn/src/include/lwbtn/lwbtn.h b/lwbtn/src/include/lwbtn/lwbtn.h index cf26461..04701af 100644 --- a/lwbtn/src/include/lwbtn/lwbtn.h +++ b/lwbtn/src/include/lwbtn/lwbtn.h @@ -74,44 +74,75 @@ typedef enum { LWBTN_EVT_KEEPALIVE, } lwbtn_evt_t; +/** + * \brief Button event function callback prototype + * \param[in] lw: LwBTN instance + * \param[in] btn: Button instance from array for which event occured + * \param[in] evt: Event type + */ typedef void (*lwbtn_evt_fn)(struct lwbtn* lw, struct lwbtn_btn* btn, lwbtn_evt_t evt); + +/** + * \brief Get button/input state callback function + * \param[in] lw: LwBTN instance + * \param[in] btn: Button instance from array to read state + * \return `1` when button is considered `active`, `0` otherwise + */ typedef uint8_t (*lwbtn_get_state_fn)(struct lwbtn* lw, struct lwbtn_btn* btn); /** * \brief Button/input structure */ typedef struct lwbtn_btn { - uint16_t flags; - uint8_t old_state; - uint32_t time_change; + uint16_t flags; /*!< Private button flags management */ + uint8_t old_state; /*!< Old button state - `1` means active, `0` means inactive */ + uint32_t time_change; /*!< Time in ms when button state got changed last time */ struct { - uint32_t last_time; - uint16_t cnt; - } keepalive; + uint32_t last_time; /*!< Time in ms of last send keep alive event */ + uint16_t cnt; /*!< Number of keep alive events sent after successful on-press detection. + Value is reset after on-release */ + } keepalive; /*!< Keep alive structure */ struct { - uint32_t last_time; - uint8_t consecutive_cnt; - } click; + uint32_t last_time; /*!< Time in ms of last successfully detected (not sent!) click event */ + uint8_t cnt; /*!< Number of consecutive clicks detected, respecting maximum timeout between clicks */ + } click; /*!< Click event structure */ - void* arg; + void* arg; /*!< User defined custom argument for callback function purpose */ } lwbtn_btn_t; /** * \brief LwBTN group structure */ typedef struct lwbtn { - lwbtn_btn_t* btns; - uint16_t btns_cnt; - lwbtn_evt_fn evt_fn; - lwbtn_get_state_fn get_state_fn; + lwbtn_btn_t* btns; /*!< Pointer to buttons array */ + uint16_t btns_cnt; /*!< Number of buttons in array */ + lwbtn_evt_fn evt_fn; /*!< Pointer to event function */ + lwbtn_get_state_fn get_state_fn; /*!< Pointer to get state function */ } lwbtn_t; uint8_t lwbtn_init_ex(lwbtn_t* lw, lwbtn_btn_t* btns, uint16_t btns_cnt, lwbtn_get_state_fn get_state_fn, lwbtn_evt_fn evt_fn); uint8_t lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime); +/** + * \brief Initialize LwBTN library with buttons on default button group + * \param[in] btns: Array of buttons to process + * \param[in] btns_cnt: Number of buttons to process + * \param[in] get_state_fn: Pointer to function providing button state on demand + * \param[in] evt_fn: Button event function callback + * \sa lwbtn_init_ex + */ +#define lwbtn_init(btns, btns_cnt, get_state_fn, evt_fn) lwbtn_init_ex(NULL, btns, btns_cnt, get_state_fn, evt_fn) + +/** + * \brief Periodically read button states and take appropriate actions + * \param[in] mstime: Current system time in milliseconds + * \sa lwbtn_process_ex + */ +#define lwbtn_process(mstime) lwbtn_process_ex(NULL, mstime) + /** * \} */ diff --git a/lwbtn/src/lwbtn/lwbtn.c b/lwbtn/src/lwbtn/lwbtn.c index f1764c4..f03d7af 100644 --- a/lwbtn/src/lwbtn/lwbtn.c +++ b/lwbtn/src/lwbtn/lwbtn.c @@ -112,12 +112,12 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { * Increase consecutive clicks if max not reached yet * and if time between two clicks is not too long */ - if (b->click.consecutive_cnt > 0 && b->click.consecutive_cnt < LWBTN_CLICK_MAX_CONSECUTIVE(b) + if (b->click.cnt > 0 && b->click.cnt < LWBTN_CLICK_MAX_CONSECUTIVE(b) && (mstime - b->click.last_time) < LWBTN_TIME_CLICK_MAX_MULTI(b)) { - ++b->click.consecutive_cnt; + ++b->click.cnt; } else { /* Start over - set as first click */ - b->click.consecutive_cnt = 1; + b->click.cnt = 1; } b->click.last_time = mstime; } else { @@ -130,7 +130,7 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { * * Clear then counter */ - b->click.consecutive_cnt = 0; + b->click.cnt = 0; } } } @@ -164,9 +164,9 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { * * Handle this on on-press state */ - if (b->click.consecutive_cnt > 0 && b->click.consecutive_cnt == LWBTN_CLICK_MAX_CONSECUTIVE(b)) { + if (b->click.cnt > 0 && b->click.cnt == LWBTN_CLICK_MAX_CONSECUTIVE(b)) { lw->evt_fn(lw, b, LWBTN_EVT_ONCLICK); - b->click.consecutive_cnt = 0; + b->click.cnt = 0; } /* Now start with new on-press */ @@ -202,10 +202,10 @@ lwbtn_process_ex(lwbtn_t* lw, uint32_t mstime) { * This is sent after last valid click has been detected for OnRelease event * - Reset consecutive clicks counter if time after last click is longer than maximum one */ - if (b->click.consecutive_cnt > 0) { + if (b->click.cnt > 0) { if ((mstime - b->click.last_time) >= LWBTN_TIME_CLICK_SEND_TIMEOUT(b)) { lw->evt_fn(lw, b, LWBTN_EVT_ONCLICK); - b->click.consecutive_cnt = 0; + b->click.cnt = 0; } } }