Console input
This commit is contained in:
@@ -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()
|
@@ -10,6 +10,5 @@
|
||||
#include "engine/engine.h"
|
||||
|
||||
void cmdQuit(const consolecmdexec_t *exec) {
|
||||
consolePrint("Quitting...");
|
||||
ENGINE.running = false;
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
||||
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
|
@@ -9,10 +9,12 @@
|
||||
#include "consolevar.h"
|
||||
#include "consolecmd.h"
|
||||
|
||||
#if DUSK_CONSOLE_TERMIOS
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#if DUSK_CONSOLE_POSIX
|
||||
#include "thread/thread.h"
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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);
|
||||
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
|
@@ -27,4 +27,5 @@ void engineUpdate(void) {
|
||||
}
|
||||
|
||||
void engineDispose(void) {
|
||||
consoleDispose();
|
||||
}
|
17
src/main.c
17
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;
|
||||
}
|
@@ -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.");
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user