From 200f709092beb94a239a1df8e5c45a486fc52470 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Mon, 6 Nov 2023 10:56:55 +0100 Subject: [PATCH] Documentation: migrate "NxTerm Example" from wiki link: https://cwiki.apache.org/confluence/display/NUTTX/NxTerm+Example --- .../applications/examples/nxterm/index.rst | 174 ++++++++++++++++++ .../examples/nxterm/nxtermexample.png | Bin 0 -> 15366 bytes 2 files changed, 174 insertions(+) create mode 100644 Documentation/applications/examples/nxterm/nxtermexample.png diff --git a/Documentation/applications/examples/nxterm/index.rst b/Documentation/applications/examples/nxterm/index.rst index 5deb2d749f..de3744d99a 100644 --- a/Documentation/applications/examples/nxterm/index.rst +++ b/Documentation/applications/examples/nxterm/index.rst @@ -2,6 +2,9 @@ ``nxterm`` Display NuttShell (NSH) as NX Console ================================================ +.. figure:: nxtermexample.png + :align: center + This directory contains yet another version of the NuttShell (NSH). This version uses the NX console device defined in ``include/nuttx/nx/nxterm.h`` for output. the result is that the NSH input still come from the standard console input @@ -45,3 +48,174 @@ The following configuration options can be selected to customize the test: - ``CONFIG_EXAMPLES_NXTERM_SERVERPRIO`` – The server priority. Default: ``120``. - ``CONFIG_EXAMPLES_NXTERM_LISTENERPRIO`` – The priority of the event listener thread. Default: ``80``. + +Initialization +============== + +NX Server +--------- + +The NxTerm example initializes the NX Server through the following steps: + +* Calls ``boardctl(BOARDIOC_NX_START, 0)`` to start the NX server, then +* Calls ``nx_connect()`` to connect to the NX server. +* It also creates a separate thread at entry ``nxterm_listener()`` to listen for + NX server events. + +Window Creation +--------------- + +The Nxterm Example then initializes the Windows: + +* Calls ``nxtk_openwindow()`` to create a bordered window, +* Calls ``nxtk_setposition()`` and ``nxtk_setsize()`` to position the window in + the display, +* Calls ``nxtk_opentoolbar()`` to create a toolbar sub-window on the main window + (This toolbar window is not used by the example, it is just for illustration). + +A more practical use case for, say, a handheld device with a single NxTerm display +would be to use the background window. The background is a window like any other +with these special properties: It cannot be moved; it is always positioned at (0,0). +It cannot be resized; it is always the full size of the display. And it cannot be +raised, it is always the lowest windows in the z-axis. + +NxTerm Driver +------------- + +And binds the Nxterm driver to the permit drawing in the window. This is done when it: + +* Calls ``boardctl(BOARDIOC_NXTERM, (uintptr_t)&nxcreate)`` + +Console Task +------------ + +Finally, it sets up the NxTerm and starts the console task: + +* Opens the NxTerm driver, +* It then re-directs stdout and stderr to the NxTerm driver. This will cause all + standard output to be rendered into the main windows, and +* It then starts a separate console daemon that inherits the re-directed output + and exits. + +Character I/O +============= + +Normal Keyboard Input +--------------------- + +Keyboard and mouse inputs are received by the application through window callbacks, +just like with the Xorg X server. Some listener needs to inject the keyboard input +via ``nx_kbdin()`` (``libs/libnx/nx_kbdin.c``) which sends a message containing the +keyboard input to the NX server. The NX server will forward that keyboard input to +the window that has focus. + +The Window application listens for NX server events by calling ``nx_eventhandler()`` +(``libs/libnx/nx_eventhandler``) on another listener thread. If the window has +focus when the key press is entered, ``nx_eventhandler()`` will forward the key +press information to the registered window event handler. + +In ``apps/examples/nxterm``, ``nxterm_listener.c`` is the thread that drives +``nx_eventhandler()``. The window NX keyboard callback is the function ``nxwndo_kbdin()`` +in ``nxterm_wndo.c``. That callback function is just a stub that writes the keyboard +data to stdout. It is a stub because keyboard input is not received from the NxTerm +in this example. + +The apps/examples/nxterm/ Kludge +-------------------------------- + +``apps/examples/nxterm`` does not do things in the normal way. NSH does not receive +keyboard input from NX; it gets keyboard input directly from the default stdin which +is probably not a keyboard at all but more likely the host PC serial console. This is +okay because only a single window is used and that example does not need the help of +NX to select the window that has focus. + +Re-direction of stdout and stderr +--------------------------------- + +stdin in and stderr are re-directed to the NxTerm character driver in ``nxterm_main.c`` +just before starting the console task. That logic looks like this: + +.. code-block:: C + + /* Now re-direct stdout and stderr so that they use the NX console driver. + * Note that stdin is retained (file descriptor 0, probably the serial + * console). + */ + + printf("nxterm_main: Starting the console task\n"); + + fflush(stdout); + fflush(stderr); + + fclose(stdout); + fclose(stderr); + + dup2(fd, 1); + dup2(fd, 2); + +Note that stdin is not re-directed in this example! This means that keyboard +input does not come from the NxTerm driver but, rather, from whatever input +device was previously configured for stdin, often a serial console. + +There is a configuration option that determines if NxTerm receives keyboard +input or not: ``CONFIG_NXTERM_NXKBDIN``, For this NxTerm example, that option can be disabled. + +What Is BOARDIOC_NXTERM_IOCTL and Where Is It Used? +--------------------------------------------------- + +The ``boardctl()`` command ``BOARDIOC_NXTERM_IOCTL`` allows an application to +inject keyboard data into NX for forwarding to the window with focus. +In ``apps/examples/nxterm``, the ``BOARDIOC_NXTERM_IOCTL`` is only called for the +case of a redraw event. A redraw event may happen when a window above the current +window is moved and the text is exposed. + +If you use only a single window for the NxTerm example, then that window will +always have focus. It will always have focus and will never be redrawn and the +``BOARDIOC_NXTERM_IOCTL`` will never be used (Unless, perhaps, you choose to implement +pop-up error messages or menus on top of the NxTerm window). + +Redraw callbacks will not be received even in a multi-window configuration if you +use per-window frame buffers, either. In that case, the system will automatically +redraw windows as needed using the per-window frame buffer shadow memory. This is +controlled by the option ``CONFIG_NX_RAMBACKED``. This option is recommended for +performance reasons if you have sufficient memory to support it. + +Character Data Flow: Keyboard to Display +========================================= + +Character Data Flow in apps/examples/nxterm +------------------------------------------- + +* Character input driver receives input +* NSH receives input on stdin and processes it (stdin is not redirected) +* Data is output to stdout (redirected to the NxTerm driver) + +In this simple, single-window case, ``BOARDIOC_NXTERM_IOCTL`` will never be used. + +Character Data Flow in the Generic Window Case +---------------------------------------------- + +See, for an example, ``apps/graphics/nxwm/src/cnxterm.cxx``. In this case, the +behavior will change, depending on the selection of ``CONFIG_NXTERM_NXKBDIN``: +If ``CONFIG_NXTERM_NXKBDIN`` is not selected, then the behavior will be similar +to ``apps/examples/nxterm``; stdin will not be redirected an keyboard input will +come directly from the the system console. + +But is ``CONFIG_NXTERM_NXKBDIN`` is select, NSH's stdin will be re-redirected to +the to the NxTerm character driver. Keyboard input will arrive on stdin from the +NxTerm driver rather than from the system console. The following sequence describes +the keyboard input in this latter case: + +* Character input driver receives input, +* Some keyboard listener thread receives input and injects it into NX via + a call to ``nx_kbdin()``, +* NX sends an event to the registered ``kbdin()`` method of the window that has + focus, providing the keyboard input to the window application. In this case, + the window application of interest is the window bound to the NxTerm character + driver by the application. The ``kbin()`` callback provides the focused keyboard + input to the NxTerm driver via ``boardctl(BOARDIOC_NXTERM_IOCTL, (uintptr_t)&iocargs)``, +* The NxTerm character driver receives keyboard data, buffers it, and provides + that keyboard input for the next read operation, +* NSH receives input on stdin which was re-directed to the NxTerm character driver. + NSH processes the input, and +* ,NSH outputs data to stdout which was re-directed to the NxTerm character driver. diff --git a/Documentation/applications/examples/nxterm/nxtermexample.png b/Documentation/applications/examples/nxterm/nxtermexample.png new file mode 100644 index 0000000000000000000000000000000000000000..22335488798abc647dcfff084f67ac3d518dd9f5 GIT binary patch literal 15366 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