summaryrefslogtreecommitdiff
path: root/users/pcewing/key_repeater.c
blob: 6c77d45b5f56205cae3f866e470ceaf10d9f91f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
 * Copyright 2022 Paul Ewing
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "key_repeater.h"
#include <stdlib.h>
#include QMK_KEYBOARD_H

enum key_repeater_state {
    KR_DISABLED,
    KR_WAITING,
    KR_BUTTON_DOWN,

    __KR_STATE_COUNT
};

struct key_repeater_t {
    int key;
    uint32_t key_duration_min;
    uint32_t key_duration_max;
    uint32_t wait_duration_min;
    uint32_t wait_duration_max;

    enum key_repeater_state state;
    uint32_t previous_button_down;
    uint32_t previous_button_up;
    uint32_t key_duration;
    uint32_t wait_duration;
};

// Utility functions
uint32_t get_rand(uint32_t min, uint32_t max);

// State handler function table
void kr_waiting(struct key_repeater_t *kr);
void kr_button_down(struct key_repeater_t *kr);

typedef void (*kr_state_handler)(struct key_repeater_t *kr);
static kr_state_handler kr_state_handlers[] = {
    [KR_DISABLED] = NULL,
    [KR_WAITING] = kr_waiting,
    [KR_BUTTON_DOWN] = kr_button_down,
};

struct key_repeater_t* kr_new(struct key_repeater_config_t* cfg) {
    struct key_repeater_t* kr = (struct key_repeater_t*)malloc(sizeof(struct key_repeater_t));

    if (cfg) {
        kr->key = cfg->key;
        kr->key_duration_min = cfg->key_duration_min;
        kr->key_duration_max = cfg->key_duration_max;
        kr->wait_duration_min = cfg->wait_duration_min;
        kr->wait_duration_max = cfg->wait_duration_max;
    } else {
        kr->key = KC_NO;
        kr->key_duration_min = 0;
        kr->key_duration_max = 0;
        kr->wait_duration_min = 0;
        kr->wait_duration_max = 0;
    }
 
    kr->state = KR_DISABLED;
    kr->previous_button_down = 0;
    kr->previous_button_up = 0;
    kr->key_duration = 0;
    kr->wait_duration = 0;
    
    return kr;
}

void kr_free(struct key_repeater_t **kr) {
    if (kr && *kr) {
        free(*kr);
        *kr = NULL;
    }
}

void kr_enable(struct key_repeater_t *kr) {
    if (!kr || kr->key == KC_NO) {
        return;
    }

    if (kr->state != KR_DISABLED) {
        return;
    }
    kr->state = KR_WAITING;
    kr->previous_button_down = 0;
    kr->previous_button_up = 0;
    kr->key_duration = 0;
    kr->wait_duration = 0;
}

void kr_disable(struct key_repeater_t *kr) {
    if (!kr || kr->key == KC_NO) {
        return;
    }

    if (kr->state == KR_BUTTON_DOWN) {
        unregister_code(kr->key);
    }
    kr->state = KR_DISABLED;
}

void kr_poll(struct key_repeater_t *kr) {
    if (!kr || kr->key == KC_NO) {
        return;
    }

    kr_state_handler handler = kr_state_handlers[kr->state];
    if (handler) {
        (handler)(kr);
    }
}

void kr_waiting(struct key_repeater_t *kr) {
    if (!kr || kr->key == KC_NO) {
        return;
    }

    uint32_t now = timer_read32();
    if (now > (kr->previous_button_up + kr->wait_duration)) {
        kr->state = KR_BUTTON_DOWN;
        kr->previous_button_down = now;
        if (kr->key_duration_min == kr->key_duration_max) {
            kr->key_duration = kr->key_duration_min;
        } else {
            kr->key_duration = get_rand(kr->key_duration_min, kr->key_duration_max);
        }
        register_code(kr->key);
    }
}

void kr_button_down(struct key_repeater_t *kr) {
    if (!kr || kr->key == KC_NO) {
        return;
    }

    uint32_t now = timer_read32();
    if (now > (kr->previous_button_down + kr->key_duration)) {
        kr->state = KR_WAITING;
        kr->previous_button_up = now;
        if (kr->wait_duration_min == kr->wait_duration_max) {
            kr->wait_duration = kr->wait_duration_min;
        } else {
            kr->wait_duration = get_rand(kr->wait_duration_min, kr->wait_duration_max);
        }
        unregister_code(kr->key);
    }
}

// Return a random number between min and max; assumes that the random number
// generator has already been seeded
uint32_t get_rand(uint32_t min, uint32_t max) {
    return (rand() % (max - min)) + min;
}