mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 04:41:26 +04:00
[FL-3927] FuriThread stdin (#3979)
* feat: FuriThread stdin * ci: fix f18 * feat: stdio callback context Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
@@ -129,12 +129,12 @@ void furi_string_swap(FuriString* string_1, FuriString* string_2);
|
||||
|
||||
/** Move string_2 content to string_1.
|
||||
*
|
||||
* Set the string to the other one, and destroy the other one.
|
||||
* Copy data from one string to another and destroy the source.
|
||||
*
|
||||
* @param string_1 The FuriString instance 1
|
||||
* @param string_2 The FuriString instance 2
|
||||
* @param destination The destination FuriString
|
||||
* @param source The source FuriString
|
||||
*/
|
||||
void furi_string_move(FuriString* string_1, FuriString* string_2);
|
||||
void furi_string_move(FuriString* destination, FuriString* source);
|
||||
|
||||
/** Compute a hash for the string.
|
||||
*
|
||||
|
||||
@@ -23,12 +23,17 @@
|
||||
|
||||
#define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t))
|
||||
|
||||
typedef struct FuriThreadStdout FuriThreadStdout;
|
||||
|
||||
struct FuriThreadStdout {
|
||||
typedef struct {
|
||||
FuriThreadStdoutWriteCallback write_callback;
|
||||
FuriString* buffer;
|
||||
};
|
||||
void* context;
|
||||
} FuriThreadStdout;
|
||||
|
||||
typedef struct {
|
||||
FuriThreadStdinReadCallback read_callback;
|
||||
FuriString* unread_buffer; // <! stores data from `ungetc` and friends
|
||||
void* context;
|
||||
} FuriThreadStdin;
|
||||
|
||||
struct FuriThread {
|
||||
StaticTask_t container;
|
||||
@@ -55,6 +60,7 @@ struct FuriThread {
|
||||
size_t heap_size;
|
||||
|
||||
FuriThreadStdout output;
|
||||
FuriThreadStdin input;
|
||||
|
||||
// Keep all non-alignable byte types in one place,
|
||||
// this ensures that the size of this structure is minimal
|
||||
@@ -136,6 +142,7 @@ static void furi_thread_body(void* context) {
|
||||
|
||||
static void furi_thread_init_common(FuriThread* thread) {
|
||||
thread->output.buffer = furi_string_alloc();
|
||||
thread->input.unread_buffer = furi_string_alloc();
|
||||
|
||||
FuriThread* parent = NULL;
|
||||
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
||||
@@ -245,6 +252,7 @@ void furi_thread_free(FuriThread* thread) {
|
||||
}
|
||||
|
||||
furi_string_free(thread->output.buffer);
|
||||
furi_string_free(thread->input.unread_buffer);
|
||||
free(thread);
|
||||
}
|
||||
|
||||
@@ -710,13 +718,22 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {
|
||||
|
||||
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {
|
||||
if(thread->output.write_callback != NULL) {
|
||||
thread->output.write_callback(data, size);
|
||||
thread->output.write_callback(data, size, thread->output.context);
|
||||
} else {
|
||||
furi_log_tx((const uint8_t*)data, size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t
|
||||
__furi_thread_stdin_read(FuriThread* thread, char* data, size_t size, FuriWait timeout) {
|
||||
if(thread->input.read_callback != NULL) {
|
||||
return thread->input.read_callback(data, size, timeout, thread->input.context);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
|
||||
FuriString* buffer = thread->output.buffer;
|
||||
size_t size = furi_string_size(buffer);
|
||||
@@ -727,19 +744,33 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
__furi_thread_stdout_flush(thread);
|
||||
thread->output.write_callback = callback;
|
||||
}
|
||||
|
||||
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
return thread->output.write_callback;
|
||||
}
|
||||
|
||||
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
return thread->input.read_callback;
|
||||
}
|
||||
|
||||
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
__furi_thread_stdout_flush(thread);
|
||||
thread->output.write_callback = callback;
|
||||
thread->output.context = context;
|
||||
}
|
||||
|
||||
void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
thread->input.read_callback = callback;
|
||||
thread->input.context = context;
|
||||
}
|
||||
|
||||
size_t furi_thread_stdout_write(const char* data, size_t size) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
@@ -772,6 +803,31 @@ int32_t furi_thread_stdout_flush(void) {
|
||||
return __furi_thread_stdout_flush(thread);
|
||||
}
|
||||
|
||||
size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
|
||||
size_t from_buffer = MIN(furi_string_size(thread->input.unread_buffer), size);
|
||||
size_t from_input = size - from_buffer;
|
||||
size_t from_input_actual =
|
||||
__furi_thread_stdin_read(thread, buffer + from_buffer, from_input, timeout);
|
||||
memcpy(buffer, furi_string_get_cstr(thread->input.unread_buffer), from_buffer);
|
||||
furi_string_right(thread->input.unread_buffer, from_buffer);
|
||||
|
||||
return from_buffer + from_input_actual;
|
||||
}
|
||||
|
||||
void furi_thread_stdin_unread(char* buffer, size_t size) {
|
||||
FuriThread* thread = furi_thread_get_current();
|
||||
furi_check(thread);
|
||||
|
||||
FuriString* new_buf = furi_string_alloc(); // there's no furi_string_alloc_set_strn :(
|
||||
furi_string_set_strn(new_buf, buffer, size);
|
||||
furi_string_cat(new_buf, thread->input.unread_buffer);
|
||||
furi_string_free(thread->input.unread_buffer);
|
||||
thread->input.unread_buffer = new_buf;
|
||||
}
|
||||
|
||||
void furi_thread_suspend(FuriThreadId thread_id) {
|
||||
furi_check(thread_id);
|
||||
|
||||
|
||||
@@ -74,8 +74,23 @@ typedef int32_t (*FuriThreadCallback)(void* context);
|
||||
*
|
||||
* @param[in] data pointer to the data to be written to the standard out
|
||||
* @param[in] size size of the data in bytes
|
||||
* @param[in] context optional context
|
||||
*/
|
||||
typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size);
|
||||
typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size, void* context);
|
||||
|
||||
/**
|
||||
* @brief Standard input callback function pointer type
|
||||
*
|
||||
* The function to be used as a standard input callback MUST follow this signature.
|
||||
*
|
||||
* @param[out] buffer buffer to read data into
|
||||
* @param[in] size maximum number of bytes to read into the buffer
|
||||
* @param[in] timeout how long to wait for (in ticks) before giving up
|
||||
* @param[in] context optional context
|
||||
* @returns number of bytes that was actually read into the buffer
|
||||
*/
|
||||
typedef size_t (
|
||||
*FuriThreadStdinReadCallback)(char* buffer, size_t size, FuriWait timeout, void* context);
|
||||
|
||||
/**
|
||||
* @brief State change callback function pointer type.
|
||||
@@ -468,13 +483,30 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id);
|
||||
*/
|
||||
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void);
|
||||
|
||||
/**
|
||||
* @brief Get the standard input callback for the current thead.
|
||||
*
|
||||
* @return pointer to the standard in callback function
|
||||
*/
|
||||
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void);
|
||||
|
||||
/** Set standard output callback for the current thread.
|
||||
*
|
||||
* @param[in] callback pointer to the callback function or NULL to clear
|
||||
* @param[in] context context to be passed to the callback
|
||||
*/
|
||||
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback);
|
||||
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context);
|
||||
|
||||
/** Set standard input callback for the current thread.
|
||||
*
|
||||
* @param[in] callback pointer to the callback function or NULL to clear
|
||||
* @param[in] context context to be passed to the callback
|
||||
*/
|
||||
void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context);
|
||||
|
||||
/** Write data to buffered standard output.
|
||||
*
|
||||
* @note You can also use the standard C `putc`, `puts`, `printf` and friends.
|
||||
*
|
||||
* @param[in] data pointer to the data to be written
|
||||
* @param[in] size data size in bytes
|
||||
@@ -489,6 +521,29 @@ size_t furi_thread_stdout_write(const char* data, size_t size);
|
||||
*/
|
||||
int32_t furi_thread_stdout_flush(void);
|
||||
|
||||
/** Read data from the standard input
|
||||
*
|
||||
* @note You can also use the standard C `getc`, `gets` and friends.
|
||||
*
|
||||
* @param[in] buffer pointer to the buffer to read data into
|
||||
* @param[in] size how many bytes to read into the buffer
|
||||
* @param[in] timeout how long to wait for (in ticks) before giving up
|
||||
* @return number of bytes that was actually read
|
||||
*/
|
||||
size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout);
|
||||
|
||||
/** Puts data back into the standard input buffer
|
||||
*
|
||||
* `furi_thread_stdin_read` will return the bytes in the same order that they
|
||||
* were supplied to this function.
|
||||
*
|
||||
* @note You can also use the standard C `ungetc`.
|
||||
*
|
||||
* @param[in] buffer pointer to the buffer to get data from
|
||||
* @param[in] size how many bytes to read from the buffer
|
||||
*/
|
||||
void furi_thread_stdin_unread(char* buffer, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Suspend a thread.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user