From 84f273524628cb33d3fc642a452c77e5675b6110 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Wed, 20 Aug 2025 21:23:12 -0500 Subject: [PATCH] Console input --- src/console/CMakeLists.txt | 2 +- src/console/cmd/cmdquit.h | 1 - src/console/console.c | 89 ++++++++++++++++++++++++++++++-------- src/console/console.h | 28 ++++++++---- src/engine/engine.c | 1 + src/main.c | 17 ++------ src/thread/thread.c | 24 +++++++--- src/thread/thread.h | 11 ++++- 8 files changed, 125 insertions(+), 48 deletions(-) diff --git a/src/console/CMakeLists.txt b/src/console/CMakeLists.txt index ba39dd6..4a436a7 100644 --- a/src/console/CMakeLists.txt +++ b/src/console/CMakeLists.txt @@ -18,6 +18,6 @@ add_subdirectory(cmd) if(DUSK_TARGET_SYSTEM STREQUAL "linux") target_compile_definitions(${DUSK_TARGET_NAME} PRIVATE - DUSK_CONSOLE_TERMIOS=1 + DUSK_CONSOLE_POSIX=1 ) endif() \ No newline at end of file diff --git a/src/console/cmd/cmdquit.h b/src/console/cmd/cmdquit.h index e897def..d1e3474 100644 --- a/src/console/cmd/cmdquit.h +++ b/src/console/cmd/cmdquit.h @@ -10,6 +10,5 @@ #include "engine/engine.h" void cmdQuit(const consolecmdexec_t *exec) { - consolePrint("Quitting..."); ENGINE.running = false; } \ No newline at end of file diff --git a/src/console/console.c b/src/console/console.c index 9d1088f..8cf8a3d 100644 --- a/src/console/console.c +++ b/src/console/console.c @@ -27,15 +27,12 @@ void consoleInit() { consolePrint(" = Dawn Console = "); - #if DUSK_CONSOLE_TERMIOS - // Create termios session. - struct termios newTermios; - tcgetattr(STDIN_FILENO, &CONSOLE.originalTermios); + #if DUSK_CONSOLE_POSIX - // Disable canonical mode & echo - newTermios.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &newTermios); - fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);// Set stdin to non-blocking + + threadInit(&CONSOLE.thread, consoleInputThread); + threadMutexInit(&CONSOLE.execMutex); + threadStartRequest(&CONSOLE.thread); #endif } @@ -80,6 +77,8 @@ void consolePrint(const char_t *message, ...) { } void consoleExec(const char_t *line) { + threadMutexLock(&CONSOLE.execMutex); + assertNotNull(line, "line must not be NULL"); assertTrue( CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX, @@ -279,23 +278,22 @@ void consoleExec(const char_t *line) { break; } } + + threadMutexUnlock(&CONSOLE.execMutex); } // May move these later void consoleUpdate() { - #if DUSK_CONSOLE_TERMIOS - char_t c; - for(;;) { - if(!read(STDIN_FILENO, &c, 1)) break; - putchar(c); - } + #if DUSK_CONSOLE_POSIX #endif + threadMutexLock(&CONSOLE.execMutex); for(uint32_t i = 0; i < CONSOLE.execBufferCount; i++) { consolecmdexec_t *exec = &CONSOLE.execBuffer[i]; assertNotNull(exec->cmd, "Command execution has no command."); exec->cmd->function(exec); } + threadMutexUnlock(&CONSOLE.execMutex); // #if DUSK_KEYBOARD_SUPPORT == 1 // uint8_t key; @@ -337,8 +335,63 @@ void consoleUpdate() { } void consoleDispose(void) { - // Reset termios - #if DUSK_CONSOLE_TERMIOS - tcsetattr(STDIN_FILENO, TCSANOW, &CONSOLE.originalTermios); + #if DUSK_CONSOLE_POSIX + threadStop(&CONSOLE.thread); + threadMutexDispose(&CONSOLE.execMutex); #endif -} \ No newline at end of file + + consolePrint(" = Console shutting down = "); +} + + +#if DUSK_CONSOLE_POSIX + void consoleInputThread(thread_t *thread) { + assertNotNull(thread, "Thread cannot be NULL."); + + char_t line[CONSOLE_LINE_MAX]; + size_t cap = 0; + + struct pollfd pfd = { + .fd = STDIN_FILENO, + .events = POLLIN + }; + + while(!threadShouldStop(thread) && ENGINE.running) { + int32_t rc = poll(&pfd, 1, DUSK_CONSOLE_POSIX_POLL_RATE); + + if(rc == 0) continue; + if(rc < 0) { + if(errno == EINTR) continue; // Interrupted by signal, retry + assertUnreachable("poll() failed with unexpected error."); + } + + // Check for errors or input + if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) break; + if (!(pfd.revents & POLLIN)) { + pfd.revents = 0; + continue; + } + + // Read a line from stdin + if(!fgets(line, CONSOLE_LINE_MAX, stdin)) { + if (feof(stdin)) break; + clearerr(stdin); + continue; + } + + // Did we read a full line or did it get truncated? + size_t len = strlen(line); + int32_t fullLine = len > 0 && line[len - 1] == '\n'; + + // Strip trailing newline/CR + while(len && (line[len - 1] == '\n' || line[len - 1] == '\r')) { + line[--len] = '\0'; + } + + if(len > 0) consoleExec(line); + + pfd.revents = 0; + } + } + +#endif \ No newline at end of file diff --git a/src/console/console.h b/src/console/console.h index 6286a3e..c2c5833 100644 --- a/src/console/console.h +++ b/src/console/console.h @@ -9,10 +9,12 @@ #include "consolevar.h" #include "consolecmd.h" -#if DUSK_CONSOLE_TERMIOS - #include - #include +#if DUSK_CONSOLE_POSIX + #include "thread/thread.h" + #include #include + + #define DUSK_CONSOLE_POSIX_POLL_RATE 75 #endif typedef enum { @@ -46,10 +48,10 @@ typedef struct { bool_t visible; - #if DUSK_CONSOLE_TERMIOS - struct termios originalTermios; + #if DUSK_CONSOLE_POSIX char_t inputBuffer[CONSOLE_LINE_MAX]; - int32_t inputBufferLength; + thread_t thread; + threadmutex_t execMutex; #endif } console_t; @@ -95,7 +97,8 @@ void consolePrint( ); /** - * Executes a console command. + * Executes a console command. This method is thread safe and can be called from + * any thread. * * @param line The line to execute. */ @@ -109,4 +112,13 @@ void consoleUpdate(); /** * Disposes of the console. */ -void consoleDispose(void); \ No newline at end of file +void consoleDispose(void); + +#if DUSK_CONSOLE_POSIX + /** + * Input thread handler for posix input. + * + * @param thread The thread that is running. + */ + void consoleInputThread(thread_t *thread); +#endif \ No newline at end of file diff --git a/src/engine/engine.c b/src/engine/engine.c index 84a72dc..124c5e7 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -27,4 +27,5 @@ void engineUpdate(void) { } void engineDispose(void) { + consoleDispose(); } \ No newline at end of file diff --git a/src/main.c b/src/main.c index b1d7f4d..1cb46b5 100644 --- a/src/main.c +++ b/src/main.c @@ -7,22 +7,11 @@ #include "engine/engine.h" -#include "thread/thread.h" -thread_t thread; -threadmutex_t mutex; - -void myCoolThread(thread_t *thread) { - printf("Hello from myCoolThread!\n"); -} - int main(int argc, char **argv) { - threadInit(&thread, myCoolThread); - threadStart(&thread); - threadStop(&thread); - return 0; - engineInit(); - for(;;) engineUpdate(); + do { + engineUpdate(); + } while(ENGINE.running); engineDispose(); return 0; } \ No newline at end of file diff --git a/src/thread/thread.c b/src/thread/thread.c index 2807ae6..b62b7ac 100644 --- a/src/thread/thread.c +++ b/src/thread/thread.c @@ -26,11 +26,9 @@ void threadInit(thread_t *thread, const threadcallback_t callback) { void threadStartRequest(thread_t *thread) { assertNotNull(thread, "Thread cannot be NULL."); - printf("Starting thread...\n"); threadMutexLock(&thread->stateMutex); thread->state = THREAD_STATE_STARTING; threadMutexUnlock(&thread->stateMutex); - printf("Thread marked starting.\n"); #if DUSK_THREAD_PTHREAD assertTrue(thread->threadId == 0, "Thread id not 0."); @@ -43,8 +41,6 @@ void threadStartRequest(thread_t *thread) { ); pthread_detach(thread->threadId); #endif - - printf("Thread created.\n"); } void threadStopRequest(thread_t *thread) { @@ -70,7 +66,6 @@ void threadStart(thread_t *thread) { void threadStop(thread_t *thread) { assertNotNull(thread, "Thread cannot be NULL."); - threadStopRequest(thread); threadMutexLock(&thread->stateMutex); while(thread->state != THREAD_STATE_STOPPED) { @@ -79,6 +74,25 @@ void threadStop(thread_t *thread) { threadMutexUnlock(&thread->stateMutex); } +bool_t threadShouldStop(thread_t *thread) { + bool_t state; + assertNotNull(thread, "Thread cannot be NULL."); + + threadMutexLock(&thread->stateMutex); + switch(thread->state) { + case THREAD_STATE_STOPPED: + case THREAD_STATE_STOP_REQUESTED: + state = true; + break; + + default: + state = false; + break; + } + threadMutexUnlock(&thread->stateMutex); + return state; +} + #if DUSK_THREAD_PTHREAD void * threadHandler(thread_t *thread) { assertNotNull(thread, "Thread cannot be NULL."); diff --git a/src/thread/thread.h b/src/thread/thread.h index 5f97e6d..2672b91 100644 --- a/src/thread/thread.h +++ b/src/thread/thread.h @@ -78,12 +78,21 @@ void threadStart(thread_t *thread); /** * Stops the thread, blocking until it has stopped. Does this as efficiently as - * possible depending on the threading implementation. + * possible depending on the threading implementation. Note that it is possible + * for the thread to fully COMPLETE well before this function returns. * * @param thread Pointer to the thread structure to stop. */ void threadStop(thread_t *thread); +/** + * Checks if the thread should stop, based on its state. + * + * @param thread Pointer to the thread structure to check. + * @return true if the thread should stop, false otherwise. + */ +bool_t threadShouldStop(thread_t *thread); + #if DUSK_THREAD_PTHREAD /** * Handles the thread's lifecycle for pthreads.