DungeonCrawl
Loading...
Searching...
No Matches
input_handler.c
Go to the documentation of this file.
1
5#include "input_handler.h"
6
8#include "../io_handler.h"// Include io_handler.h to access global nc
9
10#include <stdbool.h>
11#include <stdlib.h>
12#include <string.h>
13#include <time.h>
14
15#ifndef CLOCK_MONOTONIC
16 #define CLOCK_MONOTONIC 1// Fallback for platforms without CLOCK_MONOTONIC
17#endif
18
19// Define platform-specific key event type
20#ifdef __APPLE__
21 #define KEY_EVENT NCTYPE_PRESS
22#else
23 #define KEY_EVENT NCTYPE_UNKNOWN
24#endif
25
26// Define the minimum time between key presses (milliseconds)
27#define KEY_DEBOUNCE_TIME_MS 20
28
29// Structure to track input timing for debouncing
30typedef struct {
31 struct timespec last_key_time;
32 bool first_key;
34
35// Global input timing tracker
36static input_timing_t input_timing = {
37 .first_key = true};
38
49static long get_time_diff_ms(struct timespec* start, struct timespec* end) {
50 return (end->tv_sec - start->tv_sec) * 1000 +
51 (end->tv_nsec - start->tv_nsec) / 1000000;
52}
53
62input_t translate_input(const ncinput* raw_input) {
63 if (!raw_input) {
64 return INPUT_NONE;
65 }
66
67 // Handle special case for Ctrl+C to quit
68 if (raw_input->id == 'c' && (raw_input->modifiers & NCKEY_MOD_CTRL)) {
69 return INPUT_QUIT;
70 }
71
72 // Check if this is a key event (allow both NCTYPE_UNKNOWN and NCTYPE_PRESS)
73 if (raw_input->evtype == NCTYPE_PRESS || raw_input->evtype == NCTYPE_UNKNOWN) {
74 // Arrow keys for navigation
75 if (raw_input->id == NCKEY_UP) return INPUT_UP;
76 if (raw_input->id == NCKEY_DOWN) return INPUT_DOWN;
77 if (raw_input->id == NCKEY_LEFT) return INPUT_LEFT;
78 if (raw_input->id == NCKEY_RIGHT) return INPUT_RIGHT;
79
80 // Enter key for confirmation
81 if (raw_input->id == NCKEY_ENTER || raw_input->id == ' ') return INPUT_CONFIRM;
82
83 // Cancel key (C)
84 if (raw_input->id == 'c' || raw_input->id == 'C') return INPUT_CANCEL;
85
86 // Menu key (M)
87 if (raw_input->id == 'm' || raw_input->id == 'M') return INPUT_MENU;
88
89 // Stats key (L)
90 if (raw_input->id == 'l' || raw_input->id == 'L') return INPUT_STATS;
91
92 // Inventory key (I)
93 if (raw_input->id == 'i' || raw_input->id == 'I') return INPUT_INVENTORY;
94 }
95
96 // If we didn't match anything, return INPUT_NONE
97 return INPUT_NONE;
98}
99
100bool init_input_handler(struct notcurses* notcurses_ptr) {
101 if (!notcurses_ptr) {
102 log_msg(ERROR, "input_handler", "Null Notcurses instance provided");
103 return false;
104 }
105
106 // Assign to the global variable
107 nc = notcurses_ptr;
108
109 // Initialize input timing
110 input_timing.first_key = true;
111
112 return true;
113}
114
125static bool should_process_key() {
126 struct timespec current_time;
127 clock_gettime(CLOCK_MONOTONIC, &current_time);
128
129 if (input_timing.first_key) {
130 input_timing.first_key = false;
131 input_timing.last_key_time = current_time;
132 return true;
133 }
134
135 long time_diff = get_time_diff_ms(&input_timing.last_key_time, &current_time);
136
137 if (time_diff < KEY_DEBOUNCE_TIME_MS) {
138 // Not enough time has passed, ignore this key
139 return false;
140 }
141
142 // Update the last key time
143 input_timing.last_key_time = current_time;
144 return true;
145}
146
148 if (!event || !nc) {
149 log_msg(ERROR, "input_handler", "Null event pointer or uninitialized handler");
150 return false;
151 }
152
153 // Get input directly using notcurses
154 ncinput raw_input;
155 memset(&raw_input, 0, sizeof(ncinput));
156
157 // Set default values in case we return early
158 event->type = INPUT_NONE;
159 memset(&event->raw_input, 0, sizeof(ncinput));
160
161 // Loop until we get a valid input or notcurses returns an error
162 while (true) {
163 uint32_t ret = notcurses_get_blocking(nc, &raw_input);
164 if (ret <= 0) {
165 // Error or no input
166 return false;
167 }
168
169 // Debounce - if we're getting keys too fast, ignore some
170 if (!should_process_key()) {
171 continue;
172 }
173
174 // Translate and fill the event structure
175 event->type = translate_input(&raw_input);
176 event->raw_input = raw_input;
177 return true;
178 }
179}
180
182 if (!event || !nc) {
183 log_msg(ERROR, "input_handler", "Null event pointer or uninitialized handler");
184 return false;
185 }
186
187 // Get input directly using notcurses
188 ncinput raw_input;
189 memset(&raw_input, 0, sizeof(ncinput));
190
191 // Set default values in case we return early
192 event->type = INPUT_NONE;
193 memset(&event->raw_input, 0, sizeof(ncinput));
194
195 uint32_t ret = notcurses_get_nblock(nc, &raw_input);
196 if (ret <= 0) {
197 // No input available
198 return false;
199 }
200
201 // Debounce - if we're getting keys too fast, ignore some
202 if (!should_process_key()) {
203 return false;
204 }
205
206 // Translate and fill the event structure
207 event->type = translate_input(&raw_input);
208 event->raw_input = raw_input;
209 return true;
210}
211
213 nc = NULL;
214}
bool get_input_blocking(input_event_t *event)
Get the next input event (blocking)
input_t translate_input(const ncinput *raw_input)
Translate a raw Notcurses input to a logical input type.
bool init_input_handler(struct notcurses *notcurses_ptr)
Initialize the input handler.
bool get_input_nonblocking(input_event_t *event)
Get the next input event (non-blocking)
void shutdown_input_handler(void)
Shutdown the input handler.
Exposes functions for working with input.
input_t
Enumeration of all possible input actions in the game.
Definition input_types.h:17
Exposes functions for the IO-Handler.
void log_msg(const log_level_t level, const char *module, const char *format,...)
Logs a formatted message with a specified log level and module.
Definition logger.c:246
Header file for logging functionality of the game.
Structure for advanced input events with context data.
Definition input_types.h:43