diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index ec0535bfd..36267b480 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -55,6 +55,16 @@ void gui_input_events_callback(const void* value, void* ctx) { furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT); } +void gui_ascii_events_callback(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + + Gui* gui = ctx; + + furi_message_queue_put(gui->ascii_queue, value, FuriWaitForever); + furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_ASCII); +} + // Only Fullscreen supports vertical display for now static bool gui_redraw_fs(Gui* gui) { canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); @@ -375,6 +385,35 @@ static void gui_input(Gui* gui, InputEvent* input_event) { gui_unlock(gui); } +static void gui_ascii(Gui* gui, AsciiEvent* ascii_event) { + furi_assert(gui); + furi_assert(ascii_event); + + gui_lock(gui); + + do { + if(gui->direct_draw) { + break; + } + + ViewPort* view_port = NULL; + + if(gui->lockdown) { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } else { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } + + if(view_port) { + view_port_ascii(view_port, ascii_event); + } + } while(false); + + gui_unlock(gui); +} + void gui_lock(Gui* gui) { furi_assert(gui); furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk); @@ -598,9 +637,13 @@ Gui* gui_alloc() { // Input gui->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); gui->input_events = furi_record_open(RECORD_INPUT_EVENTS); + gui->ascii_queue = furi_message_queue_alloc(8, sizeof(AsciiEvent)); + gui->ascii_events = furi_record_open(RECORD_ASCII_EVENTS); furi_check(gui->input_events); furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui); + furi_check(gui->ascii_events); + furi_pubsub_subscribe(gui->ascii_events, gui_ascii_events_callback, gui); Storage* storage = furi_record_open(RECORD_STORAGE); gui_add_view_port(gui, storage->sd_gui.view_port, GuiLayerStatusBarLeft); @@ -626,6 +669,14 @@ int32_t gui_srv(void* p) { gui_input(gui, &input_event); } } + // Process and dispatch ascii + if(flags & GUI_THREAD_FLAG_ASCII) { + // Process till queue become empty + AsciiEvent ascii_event; + while(furi_message_queue_get(gui->ascii_queue, &ascii_event, 0) == FuriStatusOk) { + gui_ascii(gui, &ascii_event); + } + } // Process and dispatch draw call if(flags & GUI_THREAD_FLAG_DRAW) { // Clear flags that arrived on input step diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h index c7aa5ddda..e2df59350 100644 --- a/applications/services/gui/gui_i.h +++ b/applications/services/gui/gui_i.h @@ -40,7 +40,8 @@ #define GUI_THREAD_FLAG_DRAW (1 << 0) #define GUI_THREAD_FLAG_INPUT (1 << 1) -#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT) +#define GUI_THREAD_FLAG_ASCII (1 << 2) +#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_ASCII) ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); @@ -75,6 +76,9 @@ struct Gui { ViewPort* ongoing_input_view_port; uint16_t hide_statusbar_count; + + FuriMessageQueue* ascii_queue; + FuriPubSub* ascii_events; }; /** Find enabled ViewPort in ViewPortArray diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index 316f5c612..b2fa7f58c 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -27,6 +27,11 @@ void view_set_input_callback(View* view, ViewInputCallback callback) { view->input_callback = callback; } +void view_set_ascii_callback(View* view, ViewAsciiCallback callback) { + furi_assert(view); + view->ascii_callback = callback; +} + void view_set_custom_callback(View* view, ViewCustomCallback callback) { furi_assert(view); view->custom_callback = callback; @@ -156,6 +161,15 @@ bool view_input(View* view, InputEvent* event) { } } +bool view_ascii(View* view, AsciiEvent* event) { + furi_assert(view); + if(view->ascii_callback) { + return view->ascii_callback(event, view->context); + } else { + return false; + } +} + bool view_custom(View* view, uint32_t event) { furi_assert(view); if(view->custom_callback) { diff --git a/applications/services/gui/view.h b/applications/services/gui/view.h index 7a2003a63..e50f33e17 100644 --- a/applications/services/gui/view.h +++ b/applications/services/gui/view.h @@ -48,6 +48,14 @@ typedef void (*ViewDrawCallback)(Canvas* canvas, void* model); */ typedef bool (*ViewInputCallback)(InputEvent* event, void* context); +/** View Ascii callback + * @param event, pointer to ascii event data + * @param context, pointer to context + * @return true if event handled, false if event ignored + * @warning called from GUI thread + */ +typedef bool (*ViewAsciiCallback)(AsciiEvent* event, void* context); + /** View Custom callback * @param event, number of custom event * @param context, pointer to context @@ -122,6 +130,13 @@ void view_set_draw_callback(View* view, ViewDrawCallback callback); */ void view_set_input_callback(View* view, ViewInputCallback callback); +/** Set View Ascii callback + * + * @param view View instance + * @param callback ascii callback + */ +void view_set_ascii_callback(View* view, ViewAsciiCallback callback); + /** Set View Custom callback * * @param view View instance diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 87b07a87c..933a7f017 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -10,6 +10,8 @@ ViewDispatcher* view_dispatcher_alloc() { view_dispatcher->view_port, view_dispatcher_draw_callback, view_dispatcher); view_port_input_callback_set( view_dispatcher->view_port, view_dispatcher_input_callback, view_dispatcher); + view_port_ascii_callback_set( + view_dispatcher->view_port, view_dispatcher_ascii_callback, view_dispatcher); view_port_enabled_set(view_dispatcher->view_port, false); ViewDict_init(view_dispatcher->views); @@ -89,6 +91,8 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) { break; } else if(message.type == ViewDispatcherMessageTypeInput) { view_dispatcher_handle_input(view_dispatcher, &message.input); + } else if(message.type == ViewDispatcherMessageTypeAscii) { + view_dispatcher_handle_ascii(view_dispatcher, &message.ascii); } else if(message.type == ViewDispatcherMessageTypeCustomEvent) { view_dispatcher_handle_custom_event(view_dispatcher, message.custom_event); } @@ -233,6 +237,24 @@ void view_dispatcher_input_callback(InputEvent* event, void* context) { } } +bool view_dispatcher_ascii_callback(AsciiEvent* event, void* context) { + // Due to queue we cannot know ahead of time if event is consumed + // So instead ViewDispatcher tells ViewPort that all events are consumed + // Then ViewDispatcher handles fallbacks the same way as ViewPort would have done + ViewDispatcher* view_dispatcher = context; + if(view_dispatcher->queue) { + ViewDispatcherMessage message; + message.type = ViewDispatcherMessageTypeAscii; + message.ascii = *event; + furi_check( + furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == + FuriStatusOk); + } else { + view_dispatcher_handle_ascii(view_dispatcher, event); + } + return true; +} + void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event) { // Check input complementarity uint8_t key_bit = (1 << event->key); @@ -290,6 +312,48 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } } +void view_dispatcher_handle_ascii(ViewDispatcher* view_dispatcher, AsciiEvent* event) { + // Deliver event + if(view_dispatcher->current_view) { + // Dispatch ascii to current view + bool is_consumed = view_ascii(view_dispatcher->current_view, event); + + // Navigate if ascii is not consumed + if(!is_consumed) { + InputKey fallback_key = InputKeyMAX; + switch(event->value) { + case AsciiValueBS: // Backspace + case AsciiValueESC: // Escape + fallback_key = InputKeyBack; + break; + case AsciiValueDC1: // Up + case AsciiValueDC2: // Down + case AsciiValueDC3: // Right + case AsciiValueDC4: // Left + fallback_key = InputKeyUp + (event->value - AsciiValueDC1); + break; + case AsciiValueCR: // Enter + fallback_key = InputKeyOk; + break; + default: + break; + } + if(fallback_key != InputKeyMAX) { + // Fallback to directional input, needs press-short-release complementarity + InputEvent fallback_event = { + .key = fallback_key, + .type = InputTypePress, + }; + view_dispatcher_handle_input(view_dispatcher, &fallback_event); + fallback_event.type = InputTypeShort; + view_dispatcher_handle_input(view_dispatcher, &fallback_event); + fallback_event.type = InputTypeRelease; + view_dispatcher_handle_input(view_dispatcher, &fallback_event); + } + } + } +} + void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher) { if(view_dispatcher->tick_event_callback) { view_dispatcher->tick_event_callback(view_dispatcher->event_context); diff --git a/applications/services/gui/view_dispatcher_i.h b/applications/services/gui/view_dispatcher_i.h index f30a84e6b..73d275896 100644 --- a/applications/services/gui/view_dispatcher_i.h +++ b/applications/services/gui/view_dispatcher_i.h @@ -34,6 +34,7 @@ struct ViewDispatcher { typedef enum { ViewDispatcherMessageTypeInput, + ViewDispatcherMessageTypeAscii, ViewDispatcherMessageTypeCustomEvent, ViewDispatcherMessageTypeStop, } ViewDispatcherMessageType; @@ -42,6 +43,7 @@ typedef struct { ViewDispatcherMessageType type; union { InputEvent input; + AsciiEvent ascii; uint32_t custom_event; }; } ViewDispatcherMessage; @@ -52,9 +54,15 @@ void view_dispatcher_draw_callback(Canvas* canvas, void* context); /** ViewPort Input Callback */ void view_dispatcher_input_callback(InputEvent* event, void* context); +/** ViewPort Ascii Callback */ +bool view_dispatcher_ascii_callback(AsciiEvent* event, void* context); + /** Input handler */ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event); +/** Ascii handler */ +void view_dispatcher_handle_ascii(ViewDispatcher* view_dispatcher, AsciiEvent* event); + /** Tick handler */ void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher); diff --git a/applications/services/gui/view_holder.c b/applications/services/gui/view_holder.c index 7ab0a8e1a..ea89405c0 100644 --- a/applications/services/gui/view_holder.c +++ b/applications/services/gui/view_holder.c @@ -19,6 +19,7 @@ struct ViewHolder { static void view_holder_draw_callback(Canvas* canvas, void* context); static void view_holder_input_callback(InputEvent* event, void* context); +static bool view_holder_ascii_callback(AsciiEvent* event, void* context); ViewHolder* view_holder_alloc() { ViewHolder* view_holder = malloc(sizeof(ViewHolder)); @@ -26,6 +27,7 @@ ViewHolder* view_holder_alloc() { view_holder->view_port = view_port_alloc(); view_port_draw_callback_set(view_holder->view_port, view_holder_draw_callback, view_holder); view_port_input_callback_set(view_holder->view_port, view_holder_input_callback, view_holder); + view_port_ascii_callback_set(view_holder->view_port, view_holder_ascii_callback, view_holder); view_port_enabled_set(view_holder->view_port, false); return view_holder; @@ -156,3 +158,15 @@ static void view_holder_input_callback(InputEvent* event, void* context) { } } } + +static bool view_holder_ascii_callback(AsciiEvent* event, void* context) { + ViewHolder* view_holder = context; + + bool is_consumed = false; + + if(view_holder->view) { + is_consumed = view_ascii(view_holder->view, event); + } + + return is_consumed; +} diff --git a/applications/services/gui/view_i.h b/applications/services/gui/view_i.h index 3e895bd94..dcbe75097 100644 --- a/applications/services/gui/view_i.h +++ b/applications/services/gui/view_i.h @@ -29,6 +29,8 @@ struct View { void* model; void* context; + + ViewAsciiCallback ascii_callback; }; /** IconAnimation tie callback */ @@ -43,6 +45,9 @@ void view_draw(View* view, Canvas* canvas); /** Input Callback for View dispatcher */ bool view_input(View* view, InputEvent* event); +/** Ascii Callback for View dispatcher */ +bool view_ascii(View* view, AsciiEvent* event); + /** Custom Callback for View dispatcher */ bool view_custom(View* view, uint32_t event); diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 25f670a7c..e4c228364 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -174,6 +174,17 @@ void view_port_input_callback_set( furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } +void view_port_ascii_callback_set( + ViewPort* view_port, + ViewPortAsciiCallback callback, + void* context) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->ascii_callback = callback; + view_port->ascii_callback_context = context; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + void view_port_update(ViewPort* view_port) { furi_assert(view_port); @@ -228,6 +239,53 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); } +void view_port_ascii(ViewPort* view_port, AsciiEvent* event) { + furi_assert(view_port); + furi_assert(event); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + furi_check(view_port->gui); + + bool is_consumed = false; + if(view_port->ascii_callback) { + is_consumed = view_port->ascii_callback(event, view_port->ascii_callback_context); + } + + if(!is_consumed) { + InputKey fallback_key = InputKeyMAX; + switch(event->value) { + case AsciiValueBS: // Backspace + case AsciiValueESC: // Escape + fallback_key = InputKeyBack; + break; + case AsciiValueDC1: // Up + case AsciiValueDC2: // Down + case AsciiValueDC3: // Right + case AsciiValueDC4: // Left + fallback_key = InputKeyUp + (event->value - AsciiValueDC1); + break; + case AsciiValueCR: // Enter + fallback_key = InputKeyOk; + break; + default: + break; + } + if(fallback_key != InputKeyMAX) { + // Fallback to directional input, needs press-short-release complementarity + InputEvent fallback_event = { + .key = fallback_key, + .type = InputTypePress, + }; + view_port_input(view_port, &fallback_event); + fallback_event.type = InputTypeShort; + view_port_input(view_port, &fallback_event); + fallback_event.type = InputTypeRelease; + view_port_input(view_port, &fallback_event); + } + } + + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { furi_assert(view_port); furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 752fc46ba..225d9bf1a 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -32,6 +32,12 @@ typedef void (*ViewPortDrawCallback)(Canvas* canvas, void* context); */ typedef void (*ViewPortInputCallback)(InputEvent* event, void* context); +/** ViewPort Ascii callback + * @return true if event handled, false if event ignored + * @warning called from GUI thread + */ +typedef bool (*ViewPortAsciiCallback)(AsciiEvent* event, void* context); + /** ViewPort allocator * * always returns view_port or stops system if not enough memory. @@ -88,6 +94,10 @@ void view_port_input_callback_set( ViewPort* view_port, ViewPortInputCallback callback, void* context); +void view_port_ascii_callback_set( + ViewPort* view_port, + ViewPortAsciiCallback callback, + void* context); /** Emit update signal to GUI system. * diff --git a/applications/services/gui/view_port_i.h b/applications/services/gui/view_port_i.h index 444e1a27c..bb69d48ee 100644 --- a/applications/services/gui/view_port_i.h +++ b/applications/services/gui/view_port_i.h @@ -22,6 +22,9 @@ struct ViewPort { ViewPortInputCallback input_callback; void* input_callback_context; + + ViewPortAsciiCallback ascii_callback; + void* ascii_callback_context; }; /** Set GUI reference. @@ -50,3 +53,12 @@ void view_port_draw(ViewPort* view_port, Canvas* canvas); * @param event pointer to input event */ void view_port_input(ViewPort* view_port, InputEvent* event); + +/** Process ascii. Calls ascii callback. + * + * To be used by GUI, called on ascii dispatch. + * + * @param view_port ViewPort instance + * @param event pointer to ascii event + */ +void view_port_ascii(ViewPort* view_port, AsciiEvent* event); diff --git a/applications/services/gui/view_stack.c b/applications/services/gui/view_stack.c index 0a106f1ba..0149c96fb 100644 --- a/applications/services/gui/view_stack.c +++ b/applications/services/gui/view_stack.c @@ -16,6 +16,7 @@ struct ViewStack { static void view_stack_draw(Canvas* canvas, void* model); static bool view_stack_input(InputEvent* event, void* context); +static bool view_stack_ascii(AsciiEvent* event, void* context); static void view_stack_update_callback(View* view_top_or_bottom, void* context) { furi_assert(view_top_or_bottom); @@ -69,6 +70,7 @@ ViewStack* view_stack_alloc(void) { view_allocate_model(view_stack->view, ViewModelTypeLocking, sizeof(ViewStackModel)); view_set_draw_callback(view_stack->view, view_stack_draw); view_set_input_callback(view_stack->view, view_stack_input); + view_set_ascii_callback(view_stack->view, view_stack_ascii); view_set_context(view_stack->view, view_stack); view_set_enter_callback(view_stack->view, view_stack_enter); view_set_exit_callback(view_stack->view, view_stack_exit); @@ -121,6 +123,25 @@ static bool view_stack_input(InputEvent* event, void* context) { return consumed; } +static bool view_stack_ascii(AsciiEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + ViewStack* view_stack = context; + + bool consumed = false; + ViewStackModel* model = view_get_model(view_stack->view); + for(int i = MAX_VIEWS - 1; i >= 0; i--) { + if(model->views[i] && view_ascii(model->views[i], event)) { + consumed = true; + break; + } + } + view_commit_model(view_stack->view, false); + + return consumed; +} + void view_stack_add_view(ViewStack* view_stack, View* view) { furi_assert(view_stack); furi_assert(view); diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 211d3ca16..bcbee0d56 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -77,6 +77,8 @@ int32_t input_srv(void* p) { input->thread_id = furi_thread_get_current_id(); input->event_pubsub = furi_pubsub_alloc(); furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); + input->ascii_pubsub = furi_pubsub_alloc(); + furi_record_create(RECORD_ASCII_EVENTS, input->ascii_pubsub); #if INPUT_DEBUG furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); diff --git a/applications/services/input/input.h b/applications/services/input/input.h index a62e84569..303074108 100644 --- a/applications/services/input/input.h +++ b/applications/services/input/input.h @@ -12,6 +12,7 @@ extern "C" { #endif #define RECORD_INPUT_EVENTS "input_events" +#define RECORD_ASCII_EVENTS "ascii_events" #define INPUT_SEQUENCE_SOURCE_HARDWARE (0u) #define INPUT_SEQUENCE_SOURCE_SOFTWARE (1u) @@ -40,6 +41,48 @@ typedef struct { InputType type; } InputEvent; +typedef enum { + AsciiValueNUL = 0x00, // NULL + _AsciiValueSOH = 0x01, // Start of Heading + _AsciiValueSTX = 0x02, // Start of Text + _AsciiValueETX = 0x03, // End of Text + _AsciiValueEOT = 0x04, // End of Transmission + _AsciiValueENQ = 0x05, // Enquiry + _AsciiValueACK = 0x06, // Acknowledgement + _AsciiValueBEL = 0x07, // Bell + AsciiValueBS = 0x08, // Backspace + _AsciiValueTAB = 0x09, // Horizontal Tab + _AsciiValueLF = 0x0A, // Line Feed + _AsciiValueVT = 0x0B, // Vertical Tab + _AsciiValueFF = 0x0C, // Form Feed + AsciiValueCR = 0x0D, // Carriage Return + _AsciiValueSO = 0x0E, // Shift Out + _AsciiValueSI = 0x0F, // Shift In + _AsciiValueDLE = 0x10, // Data Link Escape + AsciiValueDC1 = 0x11, // Device Control 1 + AsciiValueDC2 = 0x12, // Device Control 2 + AsciiValueDC3 = 0x13, // Device Control 3 + AsciiValueDC4 = 0x14, // Device Control 4 + _AsciiValueNAK = 0x15, // Negative Acknowledgement + _AsciiValueSYN = 0x16, // Synchronous Idle + _AsciiValueETB = 0x17, // End of Transmission Block + _AsciiValueCAN = 0x18, // Cancel + _AsciiValueEM = 0x19, // End of Medium + _AsciiValueSUB = 0x1A, // Substitute + AsciiValueESC = 0x1B, // Escape + _AsciiValueSF = 0x1C, // File Separator + _AsciiValueGS = 0x1D, // Group Separator + _AsciiValueRS = 0x1E, // Record Separator + _AsciiValueUS = 0x1F, // Unit Separator + // Printable Ascii 0x20-0x7E + _AsciiValueDEL = 0x7F, // Delete +} AsciiValue; + +/** Ascii Event, dispatches with FuriPubSub */ +typedef struct { + uint8_t value; +} AsciiEvent; + /** Get human readable input key name * @param key - InputKey * @return string diff --git a/applications/services/input/input_i.h b/applications/services/input/input_i.h index e5a5b7539..206f9f2d0 100644 --- a/applications/services/input/input_i.h +++ b/applications/services/input/input_i.h @@ -36,6 +36,8 @@ typedef struct { InputPinState* pin_states; Cli* cli; volatile uint32_t counter; + + FuriPubSub* ascii_pubsub; } Input; /** Input press timer callback */ diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index f3f370256..1d21f449d 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3415,6 +3415,7 @@ Function,+,view_free,void,View* Function,+,view_free_model,void,View* Function,+,view_get_model,void*,View* Function,+,view_port_alloc,ViewPort*, +Function,+,view_port_ascii_callback_set,void,"ViewPort*, ViewPortAsciiCallback, void*" Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* @@ -3427,6 +3428,7 @@ Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" Function,+,view_port_update,void,ViewPort* +Function,+,view_set_ascii_callback,void,"View*, ViewAsciiCallback" Function,+,view_set_context,void,"View*, void*" Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback"