1// This file is part of SmallBASIC
2//
3// Support for keyboard event handling
4// eg: sub foo: end: DEFINEKEY "s", foo 'run foo when s pressed
5//
6// This program is distributed under the terms of the GPL v2.0 or later
7// Download the GNU Public License (GPL) from www.gnu.org
8//
9// Copyright(C) 2010 Chris Warren-Smith. [http://tinyurl.com/ja2ss]
10
11#include "common/sys.h"
12#include "common/var.h"
13#include "common/smbas.h"
14#include "common/pproc.h"
15#include "common/keymap.h"
16
17// Keyboard buffer
18uint32_t keybuff[PCKBSIZE];
19int keyhead;
20int keytail;
21
22typedef struct key_map_s key_map_s;
23
24/**
25 * keyboard event handler
26 */
27struct key_map_s {
28 key_map_s *next; // next structure element
29 bcip_t ip; // handler location
30 int key; // key definition
31};
32
33key_map_s *keymap = 0;
34
35/**
36 * Prepare task_t exec.keymap for keymap handling at program init
37 */
38void keymap_init() {
39 keymap = 0;
40}
41
42/**
43 * Handler for keymap_free()
44 */
45void keymap_delete(key_map_s *km) {
46 if (km) {
47 keymap_delete(km->next);
48 free(km);
49 }
50}
51
52/**
53 * Cleanup task_t exec.keymap at program termination
54 */
55void keymap_free() {
56 keymap_delete(keymap);
57 keymap = 0;
58}
59
60/**
61 * DEFINEKEY command handler to add a keymap
62 */
63void keymap_add(uint32_t key, bcip_t ip) {
64 key_map_s *km = (key_map_s *)malloc(sizeof(key_map_s));
65 km->next = 0;
66 km->ip = ip;
67 km->key = key;
68
69 // add the new mapping onto the linked list
70 key_map_s *head = keymap;
71 if (!head) {
72 keymap = km;
73 } else {
74 while (head->next) {
75 head = head->next;
76 }
77 head->next = km;
78 }
79}
80
81/**
82 * DEFINEKEY key, 0
83 */
84void keymap_remove(uint32_t key, int level) {
85 key_map_s *head = keymap;
86 key_map_s *prev = head;
87
88 while (head) {
89 if (head->key == key) {
90 key_map_s *current = head;
91 prev->next = head->next;
92 head = head->next;
93 free(current);
94 if (current == prev) {
95 prev = head;
96 }
97 if (current == keymap) {
98 keymap = head;
99 }
100 } else {
101 prev = head;
102 head = head->next;
103 }
104 }
105}
106
107/**
108 * invokes the handler for the given key
109 */
110int keymap_invoke(uint32_t key) {
111 int result = 0;
112 key_map_s *head = keymap;
113 while (head) {
114 if (head->key == key) {
115 bcip_t ip = prog_ip; // store current ip
116 prog_ip = head->ip; // jump to keymap ip
117 bc_loop(1); // invoke the keymap code
118 prog_ip = ip; // restore the current ip
119 result = 1; // key was consumed
120 }
121 head = head->next;
122 }
123 return result;
124}
125
126/**
127 * returns whether a key has been pressed
128 */
129int keymap_kbhit() {
130 return (keytail != keyhead);
131}
132
133/**
134 * returns the last key without removing it
135 */
136int keymap_kbpeek() {
137 return keybuff[keyhead];
138}
139
140/**
141 * clear keyboard buffer
142 */
143void dev_clrkb() {
144 keyhead = keytail = 0;
145}
146
147/**
148 * stores a key in keyboard buffer
149 */
150void dev_pushkey(uint32_t key) {
151 keybuff[keytail] = key;
152 keytail++;
153 if (keytail >= PCKBSIZE) {
154 keytail = 0;
155 }
156
157 keymap_invoke(key);
158}
159
160/**
161 * returns true if there is an key in keyboard buffer
162 */
163int dev_kbhit() {
164 if (keytail != keyhead) {
165 return 1;
166 }
167
168 // conserve battery power
169 int code = dev_events(2);
170
171 if (code < 0) {
172 brun_break();
173 }
174 return (keytail != keyhead);
175}
176
177/**
178 * returns the next key in keyboard buffer (and removes it)
179 */
180long int dev_getch() {
181 while ((dev_kbhit() == 0) && (prog_error == 0)) {
182 int evc = dev_events(2);
183 if (evc < 0 || prog_error) {
184 return 0xFFFF;
185 }
186 }
187
188 int ch = keybuff[keyhead];
189 keyhead++;
190 if (keyhead >= PCKBSIZE) {
191 keyhead = 0;
192 }
193 return ch;
194}
195
196void timer_free(timer_s *timer) {
197 if (timer) {
198 timer_free(timer->next);
199 free(timer);
200 }
201}
202
203void timer_add(var_num_t interval, bcip_t ip) {
204 timer_s *timer = (timer_s *)malloc(sizeof (timer_s));
205 timer->next = NULL;
206 timer->ip = ip;
207 timer->interval = interval;
208 timer->value = 0;
209 timer->active = 0;
210
211 // add the new timer onto the linked list
212 timer_s* head = prog_timer;
213 if (!head) {
214 prog_timer = timer;
215 } else {
216 while (head->next) {
217 head = head->next;
218 }
219 head->next = timer;
220 }
221}
222
223void timer_run(uint32_t now) {
224 timer_s *timer = prog_timer;
225 while (timer) {
226 if (timer->value == 0) {
227 // start timer
228 timer->value = now + timer->interval;
229 } else if (now > timer->value && !timer->active) {
230 // timer expired
231 timer->active = 1;
232 bcip_t ip = prog_ip;
233 prog_ip = timer->ip;
234 bc_loop(1);
235 prog_ip = ip;
236
237 // reset for next interval
238 timer->value = now + timer->interval;
239 timer->active = 0;
240 }
241 timer = timer->next;
242 }
243}
244