10e552da7SchristosUtilities 20e552da7Schristos========= 30e552da7Schristos 40e552da7SchristosThis chapter catalogues tools and techniques which are useful for common tasks. 50e552da7SchristosThe `libev man page`_ already covers some patterns which can be adopted to 60e552da7Schristoslibuv through simple API changes. It also covers parts of the libuv API that 70e552da7Schristosdon't require entire chapters dedicated to them. 80e552da7Schristos 90e552da7SchristosTimers 100e552da7Schristos------ 110e552da7Schristos 120e552da7SchristosTimers invoke the callback after a certain time has elapsed since the timer was 130e552da7Schristosstarted. libuv timers can also be set to invoke at regular intervals instead of 140e552da7Schristosjust once. 150e552da7Schristos 160e552da7SchristosSimple use is to init a watcher and start it with a ``timeout``, and optional ``repeat``. 170e552da7SchristosTimers can be stopped at any time. 180e552da7Schristos 190e552da7Schristos.. code-block:: c 200e552da7Schristos 210e552da7Schristos uv_timer_t timer_req; 220e552da7Schristos 230e552da7Schristos uv_timer_init(loop, &timer_req); 240e552da7Schristos uv_timer_start(&timer_req, callback, 5000, 2000); 250e552da7Schristos 260e552da7Schristoswill start a repeating timer, which first starts 5 seconds (the ``timeout``) after the execution 270e552da7Schristosof ``uv_timer_start``, then repeats every 2 seconds (the ``repeat``). Use: 280e552da7Schristos 290e552da7Schristos.. code-block:: c 300e552da7Schristos 310e552da7Schristos uv_timer_stop(&timer_req); 320e552da7Schristos 330e552da7Schristosto stop the timer. This can be used safely from within the callback as well. 340e552da7Schristos 350e552da7SchristosThe repeat interval can be modified at any time with:: 360e552da7Schristos 370e552da7Schristos uv_timer_set_repeat(uv_timer_t *timer, int64_t repeat); 380e552da7Schristos 390e552da7Schristoswhich will take effect **when possible**. If this function is called from 400e552da7Schristosa timer callback, it means: 410e552da7Schristos 420e552da7Schristos* If the timer was non-repeating, the timer has already been stopped. Use 430e552da7Schristos ``uv_timer_start`` again. 440e552da7Schristos* If the timer is repeating, the next timeout has already been scheduled, so 450e552da7Schristos the old repeat interval will be used once more before the timer switches to 460e552da7Schristos the new interval. 470e552da7Schristos 480e552da7SchristosThe utility function:: 490e552da7Schristos 500e552da7Schristos int uv_timer_again(uv_timer_t *) 510e552da7Schristos 520e552da7Schristosapplies **only to repeating timers** and is equivalent to stopping the timer 530e552da7Schristosand then starting it with both initial ``timeout`` and ``repeat`` set to the 540e552da7Schristosold ``repeat`` value. If the timer hasn't been started it fails (error code 550e552da7Schristos``UV_EINVAL``) and returns -1. 560e552da7Schristos 570e552da7SchristosAn actual timer example is in the :ref:`reference count section 580e552da7Schristos<reference-count>`. 590e552da7Schristos 600e552da7Schristos.. _reference-count: 610e552da7Schristos 620e552da7SchristosEvent loop reference count 630e552da7Schristos-------------------------- 640e552da7Schristos 650e552da7SchristosThe event loop only runs as long as there are active handles. This system 660e552da7Schristosworks by having every handle increase the reference count of the event loop 670e552da7Schristoswhen it is started and decreasing the reference count when stopped. It is also 680e552da7Schristospossible to manually change the reference count of handles using:: 690e552da7Schristos 700e552da7Schristos void uv_ref(uv_handle_t*); 710e552da7Schristos void uv_unref(uv_handle_t*); 720e552da7Schristos 730e552da7SchristosThese functions can be used to allow a loop to exit even when a watcher is 740e552da7Schristosactive or to use custom objects to keep the loop alive. 750e552da7Schristos 760e552da7SchristosThe latter can be used with interval timers. You might have a garbage collector 770e552da7Schristoswhich runs every X seconds, or your network service might send a heartbeat to 780e552da7Schristosothers periodically, but you don't want to have to stop them along all clean 790e552da7Schristosexit paths or error scenarios. Or you want the program to exit when all your 800e552da7Schristosother watchers are done. In that case just unref the timer immediately after 810e552da7Schristoscreation so that if it is the only watcher running then ``uv_run`` will still 820e552da7Schristosexit. 830e552da7Schristos 840e552da7SchristosThis is also used in node.js where some libuv methods are being bubbled up to 850e552da7Schristosthe JS API. A ``uv_handle_t`` (the superclass of all watchers) is created per 860e552da7SchristosJS object and can be ref/unrefed. 870e552da7Schristos 880e552da7Schristos.. rubric:: ref-timer/main.c 890e552da7Schristos.. literalinclude:: ../../code/ref-timer/main.c 90*5f2f4271Schristos :language: c 910e552da7Schristos :linenos: 920e552da7Schristos :lines: 5-8, 17- 930e552da7Schristos :emphasize-lines: 9 940e552da7Schristos 950e552da7SchristosWe initialize the garbage collector timer, then immediately ``unref`` it. 960e552da7SchristosObserve how after 9 seconds, when the fake job is done, the program 970e552da7Schristosautomatically exits, even though the garbage collector is still running. 980e552da7Schristos 990e552da7SchristosIdler pattern 1000e552da7Schristos------------- 1010e552da7Schristos 1020e552da7SchristosThe callbacks of idle handles are invoked once per event loop. The idle 1030e552da7Schristoscallback can be used to perform some very low priority activity. For example, 1040e552da7Schristosyou could dispatch a summary of the daily application performance to the 1050e552da7Schristosdevelopers for analysis during periods of idleness, or use the application's 1060e552da7SchristosCPU time to perform SETI calculations :) An idle watcher is also useful in 1070e552da7Schristosa GUI application. Say you are using an event loop for a file download. If the 1080e552da7SchristosTCP socket is still being established and no other events are present your 1090e552da7Schristosevent loop will pause (**block**), which means your progress bar will freeze 1100e552da7Schristosand the user will face an unresponsive application. In such a case queue up and 1110e552da7Schristosidle watcher to keep the UI operational. 1120e552da7Schristos 1130e552da7Schristos.. rubric:: idle-compute/main.c 1140e552da7Schristos.. literalinclude:: ../../code/idle-compute/main.c 115*5f2f4271Schristos :language: c 1160e552da7Schristos :linenos: 1170e552da7Schristos :lines: 5-9, 34- 1180e552da7Schristos :emphasize-lines: 13 1190e552da7Schristos 1200e552da7SchristosHere we initialize the idle watcher and queue it up along with the actual 1210e552da7Schristosevents we are interested in. ``crunch_away`` will now be called repeatedly 1220e552da7Schristosuntil the user types something and presses Return. Then it will be interrupted 1230e552da7Schristosfor a brief amount as the loop deals with the input data, after which it will 1240e552da7Schristoskeep calling the idle callback again. 1250e552da7Schristos 1260e552da7Schristos.. rubric:: idle-compute/main.c 1270e552da7Schristos.. literalinclude:: ../../code/idle-compute/main.c 128*5f2f4271Schristos :language: c 1290e552da7Schristos :linenos: 1300e552da7Schristos :lines: 10-19 1310e552da7Schristos 1320e552da7Schristos.. _baton: 1330e552da7Schristos 1340e552da7SchristosPassing data to worker thread 1350e552da7Schristos----------------------------- 1360e552da7Schristos 1370e552da7SchristosWhen using ``uv_queue_work`` you'll usually need to pass complex data through 1380e552da7Schristosto the worker thread. The solution is to use a ``struct`` and set 1390e552da7Schristos``uv_work_t.data`` to point to it. A slight variation is to have the 1400e552da7Schristos``uv_work_t`` itself as the first member of this struct (called a baton [#]_). 1410e552da7SchristosThis allows cleaning up the work request and all the data in one free call. 1420e552da7Schristos 1430e552da7Schristos.. code-block:: c 1440e552da7Schristos :linenos: 1450e552da7Schristos :emphasize-lines: 2 1460e552da7Schristos 1470e552da7Schristos struct ftp_baton { 1480e552da7Schristos uv_work_t req; 1490e552da7Schristos char *host; 1500e552da7Schristos int port; 1510e552da7Schristos char *username; 1520e552da7Schristos char *password; 1530e552da7Schristos } 1540e552da7Schristos 1550e552da7Schristos.. code-block:: c 1560e552da7Schristos :linenos: 1570e552da7Schristos :emphasize-lines: 2 1580e552da7Schristos 1590e552da7Schristos ftp_baton *baton = (ftp_baton*) malloc(sizeof(ftp_baton)); 1600e552da7Schristos baton->req.data = (void*) baton; 1610e552da7Schristos baton->host = strdup("my.webhost.com"); 1620e552da7Schristos baton->port = 21; 1630e552da7Schristos // ... 1640e552da7Schristos 1650e552da7Schristos uv_queue_work(loop, &baton->req, ftp_session, ftp_cleanup); 1660e552da7Schristos 1670e552da7SchristosHere we create the baton and queue the task. 1680e552da7Schristos 1690e552da7SchristosNow the task function can extract the data it needs: 1700e552da7Schristos 1710e552da7Schristos.. code-block:: c 1720e552da7Schristos :linenos: 1730e552da7Schristos :emphasize-lines: 2, 12 1740e552da7Schristos 1750e552da7Schristos void ftp_session(uv_work_t *req) { 1760e552da7Schristos ftp_baton *baton = (ftp_baton*) req->data; 1770e552da7Schristos 1780e552da7Schristos fprintf(stderr, "Connecting to %s\n", baton->host); 1790e552da7Schristos } 1800e552da7Schristos 1810e552da7Schristos void ftp_cleanup(uv_work_t *req) { 1820e552da7Schristos ftp_baton *baton = (ftp_baton*) req->data; 1830e552da7Schristos 1840e552da7Schristos free(baton->host); 1850e552da7Schristos // ... 1860e552da7Schristos free(baton); 1870e552da7Schristos } 1880e552da7Schristos 1890e552da7SchristosWe then free the baton which also frees the watcher. 1900e552da7Schristos 1910e552da7SchristosExternal I/O with polling 1920e552da7Schristos------------------------- 1930e552da7Schristos 1940e552da7SchristosUsually third-party libraries will handle their own I/O, and keep track of 1950e552da7Schristostheir sockets and other files internally. In this case it isn't possible to use 1960e552da7Schristosthe standard stream I/O operations, but the library can still be integrated 1970e552da7Schristosinto the libuv event loop. All that is required is that the library allow you 1980e552da7Schristosto access the underlying file descriptors and provide functions that process 1990e552da7Schristostasks in small increments as decided by your application. Some libraries though 2000e552da7Schristoswill not allow such access, providing only a standard blocking function which 2010e552da7Schristoswill perform the entire I/O transaction and only then return. It is unwise to 2020e552da7Schristosuse these in the event loop thread, use the :ref:`threadpool` instead. Of 2030e552da7Schristoscourse, this will also mean losing granular control on the library. 2040e552da7Schristos 2050e552da7SchristosThe ``uv_poll`` section of libuv simply watches file descriptors using the 2060e552da7Schristosoperating system notification mechanism. In some sense, all the I/O operations 2070e552da7Schristosthat libuv implements itself are also backed by ``uv_poll`` like code. Whenever 2080e552da7Schristosthe OS notices a change of state in file descriptors being polled, libuv will 2090e552da7Schristosinvoke the associated callback. 2100e552da7Schristos 2110e552da7SchristosHere we will walk through a simple download manager that will use libcurl_ to 2120e552da7Schristosdownload files. Rather than give all control to libcurl, we'll instead be 2130e552da7Schristosusing the libuv event loop, and use the non-blocking, async multi_ interface to 2140e552da7Schristosprogress with the download whenever libuv notifies of I/O readiness. 2150e552da7Schristos 2160e552da7Schristos.. _libcurl: https://curl.haxx.se/libcurl/ 2170e552da7Schristos.. _multi: https://curl.haxx.se/libcurl/c/libcurl-multi.html 2180e552da7Schristos 2190e552da7Schristos.. rubric:: uvwget/main.c - The setup 2200e552da7Schristos.. literalinclude:: ../../code/uvwget/main.c 221*5f2f4271Schristos :language: c 2220e552da7Schristos :linenos: 223*5f2f4271Schristos :lines: 1-9,142- 2240e552da7Schristos :emphasize-lines: 7,21,24-25 2250e552da7Schristos 2260e552da7SchristosThe way each library is integrated with libuv will vary. In the case of 2270e552da7Schristoslibcurl, we can register two callbacks. The socket callback ``handle_socket`` 2280e552da7Schristosis invoked whenever the state of a socket changes and we have to start polling 2290e552da7Schristosit. ``start_timeout`` is called by libcurl to notify us of the next timeout 2300e552da7Schristosinterval, after which we should drive libcurl forward regardless of I/O status. 2310e552da7SchristosThis is so that libcurl can handle errors or do whatever else is required to 2320e552da7Schristosget the download moving. 2330e552da7Schristos 2340e552da7SchristosOur downloader is to be invoked as:: 2350e552da7Schristos 2360e552da7Schristos $ ./uvwget [url1] [url2] ... 2370e552da7Schristos 2380e552da7SchristosSo we add each argument as an URL 2390e552da7Schristos 2400e552da7Schristos.. rubric:: uvwget/main.c - Adding urls 2410e552da7Schristos.. literalinclude:: ../../code/uvwget/main.c 242*5f2f4271Schristos :language: c 2430e552da7Schristos :linenos: 2440e552da7Schristos :lines: 39-56 2450e552da7Schristos :emphasize-lines: 13-14 2460e552da7Schristos 2470e552da7SchristosWe let libcurl directly write the data to a file, but much more is possible if 2480e552da7Schristosyou so desire. 2490e552da7Schristos 2500e552da7Schristos``start_timeout`` will be called immediately the first time by libcurl, so 251*5f2f4271Schristosthings are set in motion. This simply starts a libuv `timer <#timers>`_ which 2520e552da7Schristosdrives ``curl_multi_socket_action`` with ``CURL_SOCKET_TIMEOUT`` whenever it 2530e552da7Schristostimes out. ``curl_multi_socket_action`` is what drives libcurl, and what we 2540e552da7Schristoscall whenever sockets change state. But before we go into that, we need to poll 2550e552da7Schristoson sockets whenever ``handle_socket`` is called. 2560e552da7Schristos 2570e552da7Schristos.. rubric:: uvwget/main.c - Setting up polling 2580e552da7Schristos.. literalinclude:: ../../code/uvwget/main.c 259*5f2f4271Schristos :language: c 2600e552da7Schristos :linenos: 2610e552da7Schristos :lines: 102-140 2620e552da7Schristos :emphasize-lines: 9,11,15,21,24 2630e552da7Schristos 2640e552da7SchristosWe are interested in the socket fd ``s``, and the ``action``. For every socket 2650e552da7Schristoswe create a ``uv_poll_t`` handle if it doesn't exist, and associate it with the 2660e552da7Schristossocket using ``curl_multi_assign``. This way ``socketp`` points to it whenever 2670e552da7Schristosthe callback is invoked. 2680e552da7Schristos 2690e552da7SchristosIn the case that the download is done or fails, libcurl requests removal of the 2700e552da7Schristospoll. So we stop and free the poll handle. 2710e552da7Schristos 2720e552da7SchristosDepending on what events libcurl wishes to watch for, we start polling with 2730e552da7Schristos``UV_READABLE`` or ``UV_WRITABLE``. Now libuv will invoke the poll callback 2740e552da7Schristoswhenever the socket is ready for reading or writing. Calling ``uv_poll_start`` 2750e552da7Schristosmultiple times on the same handle is acceptable, it will just update the events 2760e552da7Schristosmask with the new value. ``curl_perform`` is the crux of this program. 2770e552da7Schristos 2780e552da7Schristos.. rubric:: uvwget/main.c - Driving libcurl. 2790e552da7Schristos.. literalinclude:: ../../code/uvwget/main.c 280*5f2f4271Schristos :language: c 2810e552da7Schristos :linenos: 2820e552da7Schristos :lines: 81-95 2830e552da7Schristos :emphasize-lines: 2,6-7,12 2840e552da7Schristos 2850e552da7SchristosThe first thing we do is to stop the timer, since there has been some progress 2860e552da7Schristosin the interval. Then depending on what event triggered the callback, we set 2870e552da7Schristosthe correct flags. Then we call ``curl_multi_socket_action`` with the socket 2880e552da7Schristosthat progressed and the flags informing about what events happened. At this 2890e552da7Schristospoint libcurl does all of its internal tasks in small increments, and will 2900e552da7Schristosattempt to return as fast as possible, which is exactly what an evented program 2910e552da7Schristoswants in its main thread. libcurl keeps queueing messages into its own queue 2920e552da7Schristosabout transfer progress. In our case we are only interested in transfers that 2930e552da7Schristosare completed. So we extract these messages, and clean up handles whose 2940e552da7Schristostransfers are done. 2950e552da7Schristos 2960e552da7Schristos.. rubric:: uvwget/main.c - Reading transfer status. 2970e552da7Schristos.. literalinclude:: ../../code/uvwget/main.c 298*5f2f4271Schristos :language: c 2990e552da7Schristos :linenos: 3000e552da7Schristos :lines: 58-79 3010e552da7Schristos :emphasize-lines: 6,9-10,13-14 3020e552da7Schristos 3030e552da7SchristosCheck & Prepare watchers 3040e552da7Schristos------------------------ 3050e552da7Schristos 3060e552da7SchristosTODO 3070e552da7Schristos 3080e552da7SchristosLoading libraries 3090e552da7Schristos----------------- 3100e552da7Schristos 3110e552da7Schristoslibuv provides a cross platform API to dynamically load `shared libraries`_. 3120e552da7SchristosThis can be used to implement your own plugin/extension/module system and is 3130e552da7Schristosused by node.js to implement ``require()`` support for bindings. The usage is 3140e552da7Schristosquite simple as long as your library exports the right symbols. Be careful with 3150e552da7Schristossanity and security checks when loading third party code, otherwise your 3160e552da7Schristosprogram will behave unpredictably. This example implements a very simple 3170e552da7Schristosplugin system which does nothing except print the name of the plugin. 3180e552da7Schristos 3190e552da7SchristosLet us first look at the interface provided to plugin authors. 3200e552da7Schristos 3210e552da7Schristos.. rubric:: plugin/plugin.h 3220e552da7Schristos.. literalinclude:: ../../code/plugin/plugin.h 323*5f2f4271Schristos :language: c 3240e552da7Schristos :linenos: 3250e552da7Schristos 3260e552da7SchristosYou can similarly add more functions that plugin authors can use to do useful 3270e552da7Schristosthings in your application [#]_. A sample plugin using this API is: 3280e552da7Schristos 3290e552da7Schristos.. rubric:: plugin/hello.c 3300e552da7Schristos.. literalinclude:: ../../code/plugin/hello.c 331*5f2f4271Schristos :language: c 3320e552da7Schristos :linenos: 3330e552da7Schristos 3340e552da7SchristosOur interface defines that all plugins should have an ``initialize`` function 3350e552da7Schristoswhich will be called by the application. This plugin is compiled as a shared 3360e552da7Schristoslibrary and can be loaded by running our application:: 3370e552da7Schristos 3380e552da7Schristos $ ./plugin libhello.dylib 3390e552da7Schristos Loading libhello.dylib 3400e552da7Schristos Registered plugin "Hello World!" 3410e552da7Schristos 3420e552da7Schristos.. NOTE:: 3430e552da7Schristos 3440e552da7Schristos The shared library filename will be different depending on platforms. On 3450e552da7Schristos Linux it is ``libhello.so``. 3460e552da7Schristos 3470e552da7SchristosThis is done by using ``uv_dlopen`` to first load the shared library 3480e552da7Schristos``libhello.dylib``. Then we get access to the ``initialize`` function using 3490e552da7Schristos``uv_dlsym`` and invoke it. 3500e552da7Schristos 3510e552da7Schristos.. rubric:: plugin/main.c 3520e552da7Schristos.. literalinclude:: ../../code/plugin/main.c 353*5f2f4271Schristos :language: c 3540e552da7Schristos :linenos: 3550e552da7Schristos :lines: 7- 3560e552da7Schristos :emphasize-lines: 15, 18, 24 3570e552da7Schristos 3580e552da7Schristos``uv_dlopen`` expects a path to the shared library and sets the opaque 3590e552da7Schristos``uv_lib_t`` pointer. It returns 0 on success, -1 on error. Use ``uv_dlerror`` 3600e552da7Schristosto get the error message. 3610e552da7Schristos 3620e552da7Schristos``uv_dlsym`` stores a pointer to the symbol in the second argument in the third 3630e552da7Schristosargument. ``init_plugin_function`` is a function pointer to the sort of 3640e552da7Schristosfunction we are looking for in the application's plugins. 3650e552da7Schristos 3660e552da7Schristos.. _shared libraries: https://en.wikipedia.org/wiki/Shared_library#Shared_libraries 3670e552da7Schristos 3680e552da7SchristosTTY 3690e552da7Schristos--- 3700e552da7Schristos 3710e552da7SchristosText terminals have supported basic formatting for a long time, with a `pretty 3720e552da7Schristosstandardised`_ command set. This formatting is often used by programs to 3730e552da7Schristosimprove the readability of terminal output. For example ``grep --colour``. 3740e552da7Schristoslibuv provides the ``uv_tty_t`` abstraction (a stream) and related functions to 3750e552da7Schristosimplement the ANSI escape codes across all platforms. By this I mean that libuv 3760e552da7Schristosconverts ANSI codes to the Windows equivalent, and provides functions to get 3770e552da7Schristosterminal information. 3780e552da7Schristos 3790e552da7Schristos.. _pretty standardised: https://en.wikipedia.org/wiki/ANSI_escape_sequences 3800e552da7Schristos 3810e552da7SchristosThe first thing to do is to initialize a ``uv_tty_t`` with the file descriptor 3820e552da7Schristosit reads/writes from. This is achieved with:: 3830e552da7Schristos 3840e552da7Schristos int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int unused) 3850e552da7Schristos 3860e552da7SchristosThe ``unused`` parameter is now auto-detected and ignored. It previously needed 3870e552da7Schristosto be set to use ``uv_read_start()`` on the stream. 3880e552da7Schristos 3890e552da7SchristosIt is then best to use ``uv_tty_set_mode`` to set the mode to *normal* 3900e552da7Schristoswhich enables most TTY formatting, flow-control and other settings. Other_ modes 3910e552da7Schristosare also available. 3920e552da7Schristos 3930e552da7Schristos.. _Other: http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t 3940e552da7Schristos 3950e552da7SchristosRemember to call ``uv_tty_reset_mode`` when your program exits to restore the 3960e552da7Schristosstate of the terminal. Just good manners. Another set of good manners is to be 3970e552da7Schristosaware of redirection. If the user redirects the output of your command to 3980e552da7Schristosa file, control sequences should not be written as they impede readability and 3990e552da7Schristos``grep``. To check if the file descriptor is indeed a TTY, call 4000e552da7Schristos``uv_guess_handle`` with the file descriptor and compare the return value with 4010e552da7Schristos``UV_TTY``. 4020e552da7Schristos 4030e552da7SchristosHere is a simple example which prints white text on a red background: 4040e552da7Schristos 4050e552da7Schristos.. rubric:: tty/main.c 4060e552da7Schristos.. literalinclude:: ../../code/tty/main.c 407*5f2f4271Schristos :language: c 4080e552da7Schristos :linenos: 4090e552da7Schristos :emphasize-lines: 11-12,14,17,27 4100e552da7Schristos 4110e552da7SchristosThe final TTY helper is ``uv_tty_get_winsize()`` which is used to get the 4120e552da7Schristoswidth and height of the terminal and returns ``0`` on success. Here is a small 4130e552da7Schristosprogram which does some animation using the function and character position 4140e552da7Schristosescape codes. 4150e552da7Schristos 4160e552da7Schristos.. rubric:: tty-gravity/main.c 4170e552da7Schristos.. literalinclude:: ../../code/tty-gravity/main.c 418*5f2f4271Schristos :language: c 4190e552da7Schristos :linenos: 4200e552da7Schristos :emphasize-lines: 19,25,38 4210e552da7Schristos 4220e552da7SchristosThe escape codes are: 4230e552da7Schristos 4240e552da7Schristos====== ======================= 4250e552da7SchristosCode Meaning 4260e552da7Schristos====== ======================= 4270e552da7Schristos*2* J Clear part of the screen, 2 is entire screen 4280e552da7SchristosH Moves cursor to certain position, default top-left 4290e552da7Schristos*n* B Moves cursor down by n lines 4300e552da7Schristos*n* C Moves cursor right by n columns 4310e552da7Schristosm Obeys string of display settings, in this case green background (40+2), white text (30+7) 4320e552da7Schristos====== ======================= 4330e552da7Schristos 4340e552da7SchristosAs you can see this is very useful to produce nicely formatted output, or even 4350e552da7Schristosconsole based arcade games if that tickles your fancy. For fancier control you 4360e552da7Schristoscan try `ncurses`_. 4370e552da7Schristos 4380e552da7Schristos.. _ncurses: https://www.gnu.org/software/ncurses/ncurses.html 4390e552da7Schristos 4400e552da7Schristos.. versionchanged:: 1.23.1: the `readable` parameter is now unused and ignored. 4410e552da7Schristos The appropriate value will now be auto-detected from the kernel. 4420e552da7Schristos 4430e552da7Schristos---- 4440e552da7Schristos 4450e552da7Schristos.. [#] I was first introduced to the term baton in this context, in Konstantin 4460e552da7Schristos Käfer's excellent slides on writing node.js bindings -- 4470e552da7Schristos https://kkaefer.com/node-cpp-modules/#baton 4480e552da7Schristos.. [#] mfp is My Fancy Plugin 4490e552da7Schristos 4500e552da7Schristos.. _libev man page: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_OR_BOTH 451