Skip to content

Commit 6ca5162

Browse files
Merge pull request #60 from dreamer-coding/add_input_api_key_binding
2 parents bb7cdf0 + 2da4e31 commit 6ca5162

File tree

4 files changed

+447
-0
lines changed

4 files changed

+447
-0
lines changed

code/logic/fossil/io/input.h

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,85 @@ void fossil_io_show_progress(int progress);
172172
*/
173173
int fossil_io_gets(char *buffer, size_t size);
174174

175+
/**
176+
* @brief Type for representing a key binding.
177+
*/
178+
typedef void (*fossil_io_action_callback_t)(void);
179+
180+
typedef struct {
181+
int key_code; /**< The integer key code (e.g., ASCII or special code). */
182+
const char *action; /**< The action string associated with this key. */
183+
fossil_io_action_callback_t callback; /**< Optional function to call when key is pressed. */
184+
} fossil_io_keybinding_t;
185+
186+
/**
187+
* @brief Registers a new key binding.
188+
*
189+
* Adds a key-action mapping to the input library.
190+
*
191+
* @param key_code The integer key code to bind.
192+
* @param action The action string associated with the key.
193+
* @return 0 on success, non-zero on failure (e.g., duplicate key).
194+
*/
195+
int fossil_io_register_keybinding(int key_code, const char *action);
196+
197+
/**
198+
* @brief Removes an existing key binding.
199+
*
200+
* @param key_code The key code of the binding to remove.
201+
* @return 0 on success, non-zero if the key was not found.
202+
*/
203+
int fossil_io_unregister_keybinding(int key_code);
204+
205+
/**
206+
* Registers a new key binding with optional callback.
207+
*/
208+
int fossil_io_register_keybinding_with_callback(int key_code, const char *action, fossil_io_action_callback_t cb);
209+
210+
/**
211+
* @brief Retrieves the action associated with a key code.
212+
*
213+
* @param key_code The key code to look up.
214+
* @return The action string if found, NULL otherwise.
215+
*/
216+
const char* fossil_io_get_keybinding_action(int key_code);
217+
218+
/**
219+
* @brief Processes a key event.
220+
*
221+
* Checks if a key has a registered action and triggers it if found.
222+
*
223+
* @param key_code The key code that was pressed.
224+
* @return 1 if an action was triggered, 0 if no binding exists.
225+
*/
226+
int fossil_io_process_keybinding(int key_code);
227+
228+
/**
229+
* @brief Lists all currently registered key bindings.
230+
*
231+
* @param bindings Array of fossil_io_keybinding_t to populate.
232+
* @param max_bindings Maximum number of bindings to populate.
233+
* @return Number of bindings populated in the array.
234+
*/
235+
size_t fossil_io_list_keybindings(fossil_io_keybinding_t *bindings, size_t max_bindings);
236+
237+
/**
238+
* @brief Clears all registered key bindings.
239+
*
240+
* Frees any internal memory and removes all key-action mappings.
241+
*/
242+
void fossil_io_clear_keybindings(void);
243+
175244
#ifdef __cplusplus
176245
}
177246

247+
#include <cstddef>
248+
#include <string>
249+
#include <vector>
250+
#include <utility>
251+
#include <cstdarg>
252+
#include <istream>
253+
178254
/**
179255
* Namespace for the Fossil Logic I/O library.
180256
*/
@@ -397,6 +473,82 @@ namespace fossil {
397473
return input_stream;
398474
}
399475

476+
/**
477+
* Registers a key binding with an associated action string.
478+
*
479+
* @param key_code The integer key code.
480+
* @param action The action string to associate with the key.
481+
* @return 0 on success, non-zero on failure.
482+
*/
483+
static int register_keybinding(int key_code, const std::string &action) {
484+
return fossil_io_register_keybinding(key_code, action.c_str());
485+
}
486+
487+
/**
488+
* Registers a key binding with an optional callback function.
489+
*
490+
* @param key_code The integer key code.
491+
* @param action The action string to associate with the key.
492+
* @param callback Function pointer to execute when the key is pressed.
493+
* @return 0 on success, non-zero on failure.
494+
*/
495+
static int register_keybinding_callback(int key_code, const std::string &action, void (*callback)()) {
496+
return fossil_io_register_keybinding_with_callback(key_code, action.c_str(), callback);
497+
}
498+
499+
/**
500+
* Unregisters a key binding.
501+
*
502+
* @param key_code The key code of the binding to remove.
503+
* @return 0 on success, non-zero if the key was not found.
504+
*/
505+
static int unregister_keybinding(int key_code) {
506+
return fossil_io_unregister_keybinding(key_code);
507+
}
508+
509+
/**
510+
* Processes a key event.
511+
*
512+
* @param key_code The key code to process.
513+
* @return 1 if an action was triggered, 0 if no binding exists.
514+
*/
515+
static int process_keybinding(int key_code) {
516+
return fossil_io_process_keybinding(key_code);
517+
}
518+
519+
/**
520+
* Retrieves the action associated with a key code.
521+
*
522+
* @param key_code The key code to look up.
523+
* @return The action string if found, empty string otherwise.
524+
*/
525+
static std::string get_keybinding_action(int key_code) {
526+
const char *action = fossil_io_get_keybinding_action(key_code);
527+
return action ? std::string(action) : std::string();
528+
}
529+
530+
/**
531+
* Lists all registered key bindings.
532+
*
533+
* @return A vector of key-action pairs representing all bindings.
534+
*/
535+
static std::vector<std::pair<int, std::string>> list_keybindings() {
536+
std::vector<std::pair<int, std::string>> bindings;
537+
fossil_io_keybinding_t arr[256];
538+
size_t count = fossil_io_list_keybindings(arr, 256);
539+
for (size_t i = 0; i < count; i++) {
540+
bindings.emplace_back(arr[i].key_code, arr[i].action ? arr[i].action : "");
541+
}
542+
return bindings;
543+
}
544+
545+
/**
546+
* Clears all registered key bindings.
547+
*/
548+
static void clear_keybindings() {
549+
fossil_io_clear_keybindings();
550+
}
551+
400552
};
401553

402554
}

code/logic/input.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
#include "fossil/io/input.h"
1515
#include "fossil/io/output.h"
16+
#include "fossil/io/cstring.h"
1617

1718
#include <ctype.h>
1819
#include <stdio.h>
@@ -315,3 +316,100 @@ int fossil_io_gets(char *buffer, size_t size) {
315316
}
316317
return 0; // Success
317318
}
319+
320+
#define MAX_KEYBINDINGS 256
321+
static fossil_io_keybinding_t _keybindings[MAX_KEYBINDINGS];
322+
static size_t _num_keybindings = 0;
323+
324+
int fossil_io_register_keybinding(int key_code, const char *action) {
325+
if (!action || _num_keybindings >= MAX_KEYBINDINGS) return 1;
326+
327+
// Check for duplicate
328+
for (size_t i = 0; i < _num_keybindings; i++) {
329+
if (_keybindings[i].key_code == key_code) return 2; // Duplicate key
330+
}
331+
332+
_keybindings[_num_keybindings].key_code = key_code;
333+
_keybindings[_num_keybindings].action = fossil_io_cstring_dup(action);
334+
if (!_keybindings[_num_keybindings].action) return 3; // Allocation failure
335+
336+
_num_keybindings++;
337+
return 0;
338+
}
339+
340+
int fossil_io_unregister_keybinding(int key_code) {
341+
for (size_t i = 0; i < _num_keybindings; i++) {
342+
if (_keybindings[i].key_code == key_code) {
343+
free((void*)_keybindings[i].action);
344+
// Shift remaining bindings
345+
for (size_t j = i; j < _num_keybindings - 1; j++) {
346+
_keybindings[j] = _keybindings[j + 1];
347+
}
348+
_num_keybindings--;
349+
return 0;
350+
}
351+
}
352+
return 1; // Key not found
353+
}
354+
355+
const char* fossil_io_get_keybinding_action(int key_code) {
356+
for (size_t i = 0; i < _num_keybindings; i++) {
357+
if (_keybindings[i].key_code == key_code) {
358+
return _keybindings[i].action;
359+
}
360+
}
361+
return NULL;
362+
}
363+
364+
/**
365+
* Registers a new key binding with optional callback.
366+
*/
367+
int fossil_io_register_keybinding_with_callback(int key_code, const char *action, fossil_io_action_callback_t cb) {
368+
if (!action || _num_keybindings >= MAX_KEYBINDINGS) return 1;
369+
370+
// Check for duplicate key
371+
for (size_t i = 0; i < _num_keybindings; i++) {
372+
if (_keybindings[i].key_code == key_code) return 2; // Duplicate
373+
}
374+
375+
_keybindings[_num_keybindings].key_code = key_code;
376+
_keybindings[_num_keybindings].action = fossil_io_cstring_dup(action);
377+
_keybindings[_num_keybindings].callback = cb;
378+
if (!_keybindings[_num_keybindings].action) return 3; // Allocation failed
379+
380+
_num_keybindings++;
381+
return 0;
382+
}
383+
384+
/**
385+
* Enhanced key processing that calls registered callback.
386+
*/
387+
int fossil_io_process_keybinding(int key_code) {
388+
for (size_t i = 0; i < _num_keybindings; i++) {
389+
if (_keybindings[i].key_code == key_code) {
390+
if (_keybindings[i].callback) {
391+
_keybindings[i].callback(); // Execute callback
392+
} else {
393+
printf("Action triggered: %s\n", _keybindings[i].action);
394+
}
395+
return 1; // Action processed
396+
}
397+
}
398+
return 0; // No binding
399+
}
400+
401+
size_t fossil_io_list_keybindings(fossil_io_keybinding_t *bindings, size_t max_bindings) {
402+
size_t count = (max_bindings < _num_keybindings) ? max_bindings : _num_keybindings;
403+
for (size_t i = 0; i < count; i++) {
404+
bindings[i].key_code = _keybindings[i].key_code;
405+
bindings[i].action = _keybindings[i].action;
406+
}
407+
return count;
408+
}
409+
410+
void fossil_io_clear_keybindings(void) {
411+
for (size_t i = 0; i < _num_keybindings; i++) {
412+
free((void*)_keybindings[i].action);
413+
}
414+
_num_keybindings = 0;
415+
}

code/tests/cases/test_input.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,91 @@ FOSSIL_TEST(c_test_io_getc) {
219219
fclose(input_stream.file);
220220
}
221221

222+
FOSSIL_TEST(c_test_io_register_keybinding_success) {
223+
fossil_io_clear_keybindings();
224+
int result = fossil_io_register_keybinding(42, "jump");
225+
ASSUME_ITS_EQUAL_I32(0, result);
226+
const char *action = fossil_io_get_keybinding_action(42);
227+
ASSUME_ITS_EQUAL_CSTR("jump", action);
228+
fossil_io_clear_keybindings();
229+
}
230+
231+
FOSSIL_TEST(c_test_io_register_keybinding_duplicate) {
232+
fossil_io_clear_keybindings();
233+
fossil_io_register_keybinding(42, "jump");
234+
int result = fossil_io_register_keybinding(42, "run");
235+
ASSUME_ITS_EQUAL_I32(2, result); // Duplicate key
236+
fossil_io_clear_keybindings();
237+
}
238+
239+
FOSSIL_TEST(c_test_io_unregister_keybinding_success) {
240+
fossil_io_clear_keybindings();
241+
fossil_io_register_keybinding(42, "jump");
242+
int result = fossil_io_unregister_keybinding(42);
243+
ASSUME_ITS_EQUAL_I32(0, result);
244+
const char *action = fossil_io_get_keybinding_action(42);
245+
ASSUME_ITS_CNULL(action);
246+
fossil_io_clear_keybindings();
247+
}
248+
249+
FOSSIL_TEST(c_test_io_unregister_keybinding_not_found) {
250+
fossil_io_clear_keybindings();
251+
int result = fossil_io_unregister_keybinding(99);
252+
ASSUME_ITS_EQUAL_I32(1, result); // Key not found
253+
fossil_io_clear_keybindings();
254+
}
255+
256+
// Callback for keybinding test
257+
static int cb_called = 0;
258+
static void cb(void) { cb_called = 1; }
259+
260+
FOSSIL_TEST(c_test_io_register_keybinding_with_callback_success) {
261+
fossil_io_clear_keybindings();
262+
cb_called = 0;
263+
int result = fossil_io_register_keybinding_with_callback(55, "shoot", cb);
264+
ASSUME_ITS_EQUAL_I32(0, result);
265+
fossil_io_process_keybinding(55);
266+
ASSUME_ITS_EQUAL_I32(1, cb_called);
267+
fossil_io_clear_keybindings();
268+
}
269+
270+
FOSSIL_TEST(c_test_io_get_keybinding_action_not_found) {
271+
fossil_io_clear_keybindings();
272+
const char *action = fossil_io_get_keybinding_action(123);
273+
ASSUME_ITS_CNULL(action);
274+
fossil_io_clear_keybindings();
275+
}
276+
277+
FOSSIL_TEST(c_test_io_process_keybinding_no_binding) {
278+
fossil_io_clear_keybindings();
279+
int result = fossil_io_process_keybinding(77);
280+
ASSUME_ITS_EQUAL_I32(0, result);
281+
fossil_io_clear_keybindings();
282+
}
283+
284+
FOSSIL_TEST(c_test_io_list_keybindings_populates_array) {
285+
fossil_io_clear_keybindings();
286+
fossil_io_register_keybinding(1, "up");
287+
fossil_io_register_keybinding(2, "down");
288+
fossil_io_keybinding_t bindings[4];
289+
size_t count = fossil_io_list_keybindings(bindings, 4);
290+
ASSUME_ITS_EQUAL_I32(2, (int)count);
291+
ASSUME_ITS_EQUAL_I32(1, bindings[0].key_code);
292+
ASSUME_ITS_EQUAL_CSTR("up", bindings[0].action);
293+
ASSUME_ITS_EQUAL_I32(2, bindings[1].key_code);
294+
ASSUME_ITS_EQUAL_CSTR("down", bindings[1].action);
295+
fossil_io_clear_keybindings();
296+
}
297+
298+
FOSSIL_TEST(c_test_io_clear_keybindings_removes_all) {
299+
fossil_io_register_keybinding(1, "up");
300+
fossil_io_register_keybinding(2, "down");
301+
fossil_io_clear_keybindings();
302+
fossil_io_keybinding_t bindings[2];
303+
size_t count = fossil_io_list_keybindings(bindings, 2);
304+
ASSUME_ITS_EQUAL_I32(0, (int)count);
305+
}
306+
222307
// * * * * * * * * * * * * * * * * * * * * * * * *
223308
// * Fossil Logic Test Pool
224309
// * * * * * * * * * * * * * * * * * * * * * * * *
@@ -242,6 +327,16 @@ FOSSIL_TEST_GROUP(c_input_tests) {
242327
FOSSIL_TEST_ADD(c_input_suite, c_test_io_validate_is_length_valid);
243328
FOSSIL_TEST_ADD(c_input_suite, c_test_io_validate_is_length_invalid);
244329
FOSSIL_TEST_ADD(c_input_suite, c_test_io_getc);
330+
331+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_register_keybinding_success);
332+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_register_keybinding_duplicate);
333+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_unregister_keybinding_success);
334+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_unregister_keybinding_not_found);
335+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_register_keybinding_with_callback_success);
336+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_get_keybinding_action_not_found);
337+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_process_keybinding_no_binding);
338+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_list_keybindings_populates_array);
339+
FOSSIL_TEST_ADD(c_input_suite, c_test_io_clear_keybindings_removes_all);
245340

246341
FOSSIL_TEST_REGISTER(c_input_suite);
247342
}

0 commit comments

Comments
 (0)