synapsis
synapsis

Reputation: 15

How to get current keyboard cursor position in Linux terminal

I am dealing with an issue in Ubuntu. I want to get current keyboard cursor position in terminal via Gcc any assist...

Upvotes: 1

Views: 1306

Answers (2)

Thomas Dickey
Thomas Dickey

Reputation: 54583

Most terminals support a cursor-position report feature, because it is in ECMA-48.

It is documented in xterm's control-sequences:

CSI Ps n Device Status Report (DSR).
...
Ps = 6 ⇒ Report Cursor Position (CPR) [row;column].
Result is CSI r ; c R

where CSI is defined

ESC [
Control Sequence Introducer (CSI is 0x9b).

The resize program uses this feature for instance, to find the actual screen size. It does that by telling the terminal to move the cursor to row 9999, column 9999 and getting the response from ESC[6n (see code):

static const char *const getsize[EMULATIONS] =
{
    ESCAPE("7") ESCAPE("[r") ESCAPE("[9999;9999H") ESCAPE("[6n"),
    ESCAPE("[18t"),
};

There is no standard terminfo capability for the feature, but ncurses uses u6/u7 to represent it:

# INTERPRETATION OF USER CAPABILITIES
#
# The System V Release 4 and XPG4 terminfo format defines ten string
# capabilities for use by applications, <u0>...<u9>.   In this file, we use
# certain of these capabilities to describe functions which are not covered
# by terminfo.  The mapping is as follows:
#
#       u9      terminal enquire string (equiv. to ANSI/ECMA-48 DA)
#       u8      terminal answerback description
#       u7      cursor position request (equiv. to VT100/ANSI/ECMA-48 DSR 6)
#       u6      cursor position report (equiv. to ANSI/ECMA-48 CPR)
#
# The terminal enquire string <u9> should elicit an answerback response
# from the terminal.  Common values for <u9> will be ^E (on older ASCII
# terminals) or \E[c (on newer VT100/ANSI/ECMA-48-compatible terminals).
#
# The cursor position request (<u7>) string should elicit a cursor position
# report.  A typical value (for VT100 terminals) is \E[6n.

so that programs which use the terminfo database can check if the feature is supported for a given terminal.

Upvotes: 2

rici
rici

Reputation: 241911

"Terminal" is a program, or more accurately a description of a large class of programs, which implements a graphical interface emulating an external terminal (which would have been connected to your computer via a serial cable, or in some similar fashion). Your program communicates with the terminal emulator through a kind of bidirectional pipe (a "pseudoterminal") implemented by the operating system; to your program it looks like a pair of ordinary streams (stdin and stdout).

Linux itself has a terminal emulator, called "the console", which can be used instead of a window manager. Few programmers use it these days, but it's still there if you want to experiment. The console is a "terminal" (and there are usually several of them which you can switch between using a control+function key). As you might expect from the words "terminal" and "pseudoterminal", these basically look the same to your application.

There are a ton of details, which I'm skipping over because it would take a book to describe the whole thing.

The only connection between your program and the terminal (or pseudoterminal) is that you can send it a stream of characters, and you can receive a stream of characters from it. There is no other communication. There's no hidden operating system interface, because the terminal emulator is not part of the operating system. It's not even part of the window manager. It's just another userland application running without special privileges, just like your application.

You often want to do things other than just send characters to the output device. Maybe you want to clear the screen, or move the cursor to another location, or change the colour of the text or the background. All of these things are done by sending specially coded sequences interspersed with the text you're displaying. The operating system doesn't mediate or verify these sequences, and there's no definitive standard for how the terminal emulator should interpret them, but there is common framework which most terminal emulators conform to, to some extent, which makes it possible to actually write code which doesn't need to know exactly which terminal emulator is being used at the moment. The terminfo library is commonly used to describe the available terminals; by convention, the environment variable TERM contains the name of the relevant terminfo configuration, and that configuration can be used to create concrete control sequence strings suitable for the configured terminal [Note 1].

Now let's get back to your initial question: "how do I find out the current cursor location?" That's one of a small number of possible queries, which are also implemented as control sequences. Specifically, you send the terminal a control sequnce which asks it where the cursor is (usually the four characters \x1B[6n) and the terminal eventually replies with a control sequence which might look something like \x1B12,7R meaning that the cursor was on row 12 at column 7 at the moment that the control sequence was sent [Note 2]. So you could use terminfo to help you send the query and then attempt to parse the reply when it comes.

Note that the response is not synchronous with the query, since the user could be typing while the query is sent. (However, the response is sent as a contiguous sequence.) So part of the parsing process is disentangling the user input from the query response.

My guess is that you don't actually want to do all that work. In most cases, if you want to write a console application which does something less boring than just write output sequentially to a terminal window, you should use ncurses (also maintained by Thomas Dickey) or some other similar library. Ncurses takes full responsibility for maintaining the console image, jumping through the necessary hoops to communicate with the terminal emulator; one of its features is to keep track of the current cursor position [Note 3].

Another option, if you are only trying to provide better line editing and tab completion, is to use the GNU Readline library, or similar interfaces available for other operating systems.


Notes

  1. This might or might not be the terminal you're actually using, since TERM is just an environment variable. You could set it yourself if you wanted it.

  2. I took those codes from man 4 console_codes; another good source of information is Thomas Dickey's terse list of code sequences understood by xterm.

  3. As far as I know, Ncurses does not use the cursor-position query to figure out where the cursor is on the screen. It maintains its own copy of the screen being displayed, which includes the current cursor position. You can use the macro getyx() to ask for what it considers the current cursor position.

Upvotes: 4

Related Questions