Skip to content

Commit

Permalink
Merge pull request #8 from dreamer-coding/keyboard_handler
Browse files Browse the repository at this point in the history
Keyboard handler
  • Loading branch information
dreamer-coding authored Jan 28, 2025
2 parents aeb80e5 + ddbbdd7 commit b880673
Show file tree
Hide file tree
Showing 8 changed files with 542 additions and 2 deletions.
1 change: 1 addition & 0 deletions code/logic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(TEST_CODE
output.c
soap.c
stream.c
keyboard.c
)

# Create the library target
Expand Down
1 change: 1 addition & 0 deletions code/logic/fossil/io/framework.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define FOSSIL_IO_FRAMEWORK_H

// Include the necessary headers
#include "keyboard.h"
#include "output.h"
#include "input.h"
#include "error.h"
Expand Down
145 changes: 145 additions & 0 deletions code/logic/fossil/io/keyboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* -----------------------------------------------------------------------------
* Project: Fossil Logic
*
* This file is part of the Fossil Logic project, which aims to develop high-
* performance, cross-platform applications and libraries. The code contained
* herein is subject to the terms and conditions defined in the project license.
*
* Author: Michael Gene Brockus (Dreamer)
*
* Copyright (C) 2024 Fossil Logic. All rights reserved.
* -----------------------------------------------------------------------------
*/
#ifndef FOSSIL_IO_KEYBOARD_H
#define FOSSIL_IO_KEYBOARD_H

#include <stdint.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

// Define a keyboard event structure
typedef struct {
int shift; // 1 if Shift is pressed, 0 otherwise
int ctrl; // 1 if Ctrl is pressed, 0 otherwise
int alt; // 1 if Alt is pressed, 0 otherwise
char key; // The character of the key pressed
} fossil_io_keyboard_event_t;

// Define a callback type for key events
typedef void (*fossil_io_keyboard_callback_t)(fossil_io_keyboard_event_t event);

/**
* Initialize the keyboard library.
* Sets up any platform-specific configurations.
*/
void fossil_io_keyboard_init(void);

/**
* Shut down the keyboard library.
* Cleans up any platform-specific configurations.
*/
void fossil_io_keyboard_shutdown(void);

/**
* Clear all keybindings from the library.
*/
void fossil_io_keyboard_clear_bindings(void);

/**
* Register a keybinding with the library.
*
* @param event The keyboard event to bind to.
* @param callback The callback function to call when the event occurs.
*/
void fossil_io_keyboard_register_binding(fossil_io_keyboard_event_t event, fossil_io_keyboard_callback_t callback);

/**
* Unregister a keybinding with the library.
*
* @param event The keyboard event to unbind.
*/
void fossil_io_keyboard_unregister_binding(fossil_io_keyboard_event_t event);

/**
* Poll for keyboard events and trigger any registered callbacks.
* This function should be called in the main loop of the application.
*/
void fossil_io_keyboard_poll_events(void);

#ifdef __cplusplus
}

/**
* C++ wrapper for the SOAP API.
*/
namespace fossil {

/**
* Namespace for I/O operations.
*/
namespace io {
/**
* Class for interacting with the keyboard.
*/
class keyboard {
public:
/**
* Initialize the keyboard library.
* Sets up any platform-specific configurations.
*/
static void init() {
fossil_io_keyboard_init();
}

/**
* Shut down the keyboard library.
* Cleans up any platform-specific configurations.
*/
static void shutdown() {
fossil_io_keyboard_shutdown();
}

/**
* Clear all keybindings from the library.
*/
static void clear_bindings() {
fossil_io_keyboard_clear_bindings();
}

/**
* Register a keybinding with the library.
*
* @param event The keyboard event to bind to.
* @param callback The callback function to call when the event occurs.
*/
static void register_binding(fossil_io_keyboard_event_t event, fossil_io_keyboard_callback_t callback) {
fossil_io_keyboard_register_binding(event, callback);
}

/**
* Unregister a keybinding with the library.
*
* @param event The keyboard event to unbind.
*/
static void unregister_binding(fossil_io_keyboard_event_t event) {
fossil_io_keyboard_unregister_binding(event);
}

/**
* Poll for keyboard events and trigger any registered callbacks.
* This function should be called in the main loop of the application.
*/
static void poll_events() {
fossil_io_keyboard_poll_events();
}
};
}
}

#endif

#endif /* FOSSIL_IO_FRAMEWORK_H */
179 changes: 179 additions & 0 deletions code/logic/keyboard.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* -----------------------------------------------------------------------------
* Project: Fossil Logic
*
* This file is part of the Fossil Logic project, which aims to develop high-
* performance, cross-platform applications and libraries. The code contained
* herein is subject to the terms and conditions defined in the project license.
*
* Author: Michael Gene Brockus (Dreamer)
*
* Copyright (C) 2024 Fossil Logic. All rights reserved.
* -----------------------------------------------------------------------------
*/
#include "fossil/io/keyboard.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(_WIN32) || defined(_WIN64)
#include <conio.h>
#include <windows.h>
#else
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h> // For fd_set, FD_ZERO, FD_SET, select
#include <sys/time.h> // For struct timeval
#include <sys/types.h> // For system types
#endif

#define MAX_KEYBINDS 256

typedef struct {
fossil_io_keyboard_event_t event;
fossil_io_keyboard_callback_t callback;
} fossil_io_keyboard_binding_t;

typedef struct {
fossil_io_keyboard_binding_t bindings[MAX_KEYBINDS];
size_t count;
} fossil_io_keyboard_manager_t;

static fossil_io_keyboard_manager_t keyboard_manager = { .count = 0 };

#if defined(_WIN32) || defined(_WIN64)

static int fossil_io_keyboard_is_key_pressed(void) {
return _kbhit();
}

static fossil_io_keyboard_event_t fossil_io_keyboard_get_event(void) {
fossil_io_keyboard_event_t event = {0};
int key = _getch();

// Check for extended keys (e.g., arrow keys, function keys)
if (key == 0 || key == 224) {
key = _getch(); // Fetch the actual key code
}

// Check modifiers
event.shift = GetKeyState(VK_SHIFT) & 0x8000;
event.ctrl = GetKeyState(VK_CONTROL) & 0x8000;
event.alt = GetKeyState(VK_MENU) & 0x8000;
event.key = (char)key;

return event;
}

#else
static struct termios old_termios, new_termios;

static void fossil_io_keyboard_enable_raw_mode(void) {
tcgetattr(STDIN_FILENO, &old_termios);
new_termios = old_termios;
new_termios.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
}

static void fossil_io_keyboard_disable_raw_mode(void) {
tcsetattr(STDIN_FILENO, TCSANOW, &old_termios);
}

static int fossil_io_keyboard_is_key_pressed(void) {
struct timeval timeout = {0L, 0L};
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
return select(STDIN_FILENO + 1, &fds, NULL, NULL, &timeout) > 0;
}

static fossil_io_keyboard_event_t fossil_io_keyboard_get_event(void) {
fossil_io_keyboard_event_t event = {0};
char c;
read(STDIN_FILENO, &c, 1);

event.key = c;

// Modifier keys (POSIX doesn't have direct access to key states; approximation)
if (c == 27) { // Alt key (ESC sequence)
event.alt = 1;
read(STDIN_FILENO, &c, 1);
event.key = c;
} else if (c < 32) { // Ctrl key (control characters)
event.ctrl = 1;
event.key = c + 96; // Map to lowercase ASCII
} else if (c == 127) { // Backspace key with Ctrl
event.ctrl = 1;
event.key = 8;
}

return event;
}
#endif

void fossil_io_keyboard_init(void) {
#if defined(_WIN32) || defined(_WIN64)
// Windows doesn't require explicit setup for raw mode.
#else
fossil_io_keyboard_enable_raw_mode();
atexit(fossil_io_keyboard_disable_raw_mode);
#endif
}

void fossil_io_keyboard_shutdown(void) {
#if defined(_WIN32) || defined(_WIN64)
// Windows doesn't require explicit cleanup for raw mode.
#else
fossil_io_keyboard_disable_raw_mode();
#endif
}

void fossil_io_keyboard_clear_bindings(void) {
keyboard_manager.count = 0;
}

void fossil_io_keyboard_register_binding(fossil_io_keyboard_event_t event, fossil_io_keyboard_callback_t callback) {
if (keyboard_manager.count < MAX_KEYBINDS) {
keyboard_manager.bindings[keyboard_manager.count].event = event;
keyboard_manager.bindings[keyboard_manager.count].callback = callback;
keyboard_manager.count++;
} else {
fprintf(stderr, "Max keybindings reached.\n");
}
}

void fossil_io_keyboard_unregister_binding(fossil_io_keyboard_event_t event) {
for (size_t i = 0; i < keyboard_manager.count; ++i) {
if (keyboard_manager.bindings[i].event.key == event.key &&
keyboard_manager.bindings[i].event.shift == event.shift &&
keyboard_manager.bindings[i].event.ctrl == event.ctrl &&
keyboard_manager.bindings[i].event.alt == event.alt) {
for (size_t j = i; j < keyboard_manager.count - 1; ++j) {
keyboard_manager.bindings[j] = keyboard_manager.bindings[j + 1];
}
keyboard_manager.count--;
return;
}
}
fprintf(stderr, "No matching keybinding to unregister.\n");
}

void fossil_io_keyboard_poll_events(void) {
if (fossil_io_keyboard_is_key_pressed()) {
fossil_io_keyboard_event_t event = fossil_io_keyboard_get_event();

for (size_t i = 0; i < keyboard_manager.count; ++i) {
fossil_io_keyboard_binding_t *binding = &keyboard_manager.bindings[i];
if (binding->event.key == event.key &&
binding->event.shift == event.shift &&
binding->event.ctrl == event.ctrl &&
binding->event.alt == event.alt) {
if (binding->callback) {
binding->callback(event);
}
break;
}
}
}
}
2 changes: 1 addition & 1 deletion code/logic/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ dir = include_directories('.')


fossil_io_lib = library('fossil-io',
files('input.c', 'output.c', 'error.c', 'soap.c', 'stream.c'),
files('input.c', 'output.c', 'error.c', 'soap.c', 'stream.c', 'keyboard.c'),
install: true,
include_directories: dir)

Expand Down
Loading

0 comments on commit b880673

Please sign in to comment.