Skip to main content

Cursor Movement with CSI Sequences

CSI # BCUDCUrsor Down
CSI # CCUFCUrsor Forward
CSI # DCUBCUrsor Backward
CSI # ECNLCUrsor Next Line
CSI # FCPLCUrsor Previous Line
CSI # ICHTCursor Horizontal forward Tabulation
CSI # ZCBTCursor Backward Tabulation
CSI # GCHACursor Horizontal Absolute
CSI # ; # HCUPCUrsor Position

Today, we will continue from the previous article to explore how to move the cursor using CSI sequences. The types of CSI sequences for moving the cursor can be summarized as follows.


These are the abbreviations for CUrsor Up, CUrsor Down, CUrsor Forward, and CUrsor Backward; as the names suggest, they move the cursor up, down, forward, and backward. They take a single number as an argument; if the argument is omitted, it is treated as 1. Thus, 0x1b[A is equivalent to 0x1b[1A.

In this case, CUF and CUB move only within the same line. In other words, CUB received at the beginning of a line does not move the cursor to the previous line, and CUF received at the end of a line does not move the cursor to the next line. To achieve this behavior, you need to turn on reverse auto-wrap mode and auto-wrap mode, respectively, but support for these modes depends on the terminal implementation.


These stand for Cursor Next Line and Cursor Previous Line, respectively, and move the cursor to the next or previous line. While CUU and CUD keep the cursor's column, CNL and CPL move the cursor to the beginning of the line. In other words, CNL is a combination of CR(\r, carriage return) and CUD; CPL is a combination of CR and CUU. They also take a single number as an argument; if the argument is not provided, the default value is treated as 1.


CHT stands for Cursor Horizontal Forward Tabulation, and CBT stands for Cursor Backward Tabulation. They move the cursor to the next, and the previous tab stops, respectively.


The above sequences move the cursor from its current position to a relative position. However, CHA and CUP move the cursor to the absolute coordinates received. The difference is that CHA takes a single number as an argument and moves the cursor to the specified column in the current line, while CUP takes two numbers, a line, and a column, and moves the cursor to the specified coordinates. If no numbers are provided, the default value is treated as 1.

People familiar with programming may wonder why the default value is not 0 but 1. This is because terminals use a coordinate system that starts at (1, 1) instead of (0, 0). The top-left coordinate in the terminal is (1, 1).

So far, we have looked at how to move the cursor using CSI sequences. Freely controlling the cursor is the minimum building block for creating a TUI. Using this, you can implement various components on the terminal, not only a simple output stream that flows downward.


Popular posts from this blog

Type Conversion in Rust

Type conversion is not special in Rust. It's just a function that takes ownership of the value and returns the other type. So you can name convert functions anything. However, it's a convention to use as_ , to_ , and into_ prefixed name or to use from_ prefixed constructor. From You can create any function for type conversion. However, if you want to provide generic interfaces, you'd better implement the From trait. For instance, you should implement From<X> for Y when you want the interface that converts the X type value to the Y type value. The From trait have an associated function named from . You can call this function like From::from(x) . You also can call it like Y::from(x) if the compiler cannot infer the type of the destination type. Into From have an associated function, it makes you be able to specify the destination type. It's why From has an associated function instead of a method, but on the other hands, you cannot use it as a me

Do not use garbage collection to catch memory leak

Garbage collection is a technique that automatically releases unnecessary memory. It's very famous because many programming languages adopted garbage collection after John McCarthy implemented it in Lisp. However, there are a few people who misunderstand what garbage collection does. If you think garbage collection prevents a memory leak, unfortunately, you are one of them. Garbage collection cannot prevent a memory leak. There is no way to avoid all memory leaks if you are using Turing-complete language. To understand it you should know what a memory leak is. Wikipedia describes a memory leak as the following: a type of resource leak that occurs when a computer program incorrectly manages memory allocations in such a way that memory which is no longer needed is not released. Briefly, a memory leak is a bug that doesn't release a memory that you don't use. So it is first to find the memory which will not be used in order to detect memory leaks. Unfortunately, it i

Handling Terminal Output with Termios

As I explained in the previous article , Unix-like operating systems, for instance, OS X and Linux, use LF (line feed, 0x0A , \n ) as the newline character which moves the cursor to the beginning of the next line. However, the standard-defined behavior of LF only moves the cursor down to the next line, not to the beginning of the line. This difference is acceptable if files are always accessed through operating system-dependent applications. However, Unix-like systems have no distinction between files and input/output; this difference can be problematic when file and process input/output interact. To handle this difference, a terminal emulator post-processes the output appropriately. The c_oflag in the termios structure defined by the POSIX.1 standard controls this. The c_oflag is a flag for what post-processing the terminal should perform before displaying the received characters. The most important flag in c_oflag is OPOST . This flag determines whether or not to post-pro