Hi Michael, On 3/4/19 6:04 PM, Michael Witten wrote: > This commit provides a much more comprehensive description > of virtual console capture devices, and how to use them > properly. > > The text has been written so as to be viewed comfortably in > a monospace font across 80 columns: > > MANWIDTH=80 man vcs > > It also renders well as a PDF: > > man -Tpdf vcs >/tmp/vcs.pdf I'm happy to take improvements to the page, but it would be helpful if your commit message mentioned how you verified these details. Can you add that? Thanks, Michael > Signed-off-by: Michael Witten <mfwitten@xxxxxxxxx> > --- > man4/vcs.4 | 957 ++++++++++++++++++++++++++++++++++++++++++++++------- > 1 file changed, 839 insertions(+), 118 deletions(-) > > diff --git a/man4/vcs.4 b/man4/vcs.4 > index 290d6e3b8..3eca9886f 100644 > --- a/man4/vcs.4 > +++ b/man4/vcs.4 > @@ -1,6 +1,10 @@ > +'\" t > .\" Copyright (c) 1995 James R. Van Zandt <jrv@xxxxxxxxxxxxxxx> > .\" Sat Feb 18 09:11:07 EST 1995 > .\" > +.\" Authorship is recorded in the git history; the copyrights of > +.\" each author are implied thereby. > +.\" > .\" %%%LICENSE_START(GPLv2+_DOC_FULL) > .\" This is free documentation; you can redistribute it and/or > .\" modify it under the terms of the GNU General Public License as > @@ -25,155 +29,872 @@ > .\" Modified, Sun Feb 26 15:08:05 1995, faith@xxxxxxxxxx > .\" 2007-12-17, Samuel Thibault <samuel.thibault@xxxxxxxxxxxx>: > .\" document the VT_GETHIFONTMASK ioctl > -.\" " > +.\" > +.\" Further modifications are recorded in the git history. > .TH VCS 4 2017-05-03 "Linux" "Linux Programmer's Manual" > .SH NAME > -vcs, vcsa \- virtual console memory > -.SH DESCRIPTION > -.I /dev/vcs0 > -is a character device with major number 7 and minor number > -0, usually with mode 0644 and ownership root:tty. > -It refers to the memory of the currently > -displayed virtual console terminal. > -.PP > -.I /dev/vcs[1\-63] > -are character devices for virtual console > -terminals, they have major number 7 and minor number 1 to 63, usually > -mode 0644 and ownership root:tty. > -.IR /dev/vcsa[0\-63] > -are the same, but > -using > -.IR "unsigned short" s > -(in host byte order) that include attributes, > -and prefixed with four bytes giving the screen > -dimensions and cursor position: > -.IR lines , > -.IR columns , > -.IR x , > -.IR y . > -.RI ( x > -= > -.I y > -= 0 at the top left corner of the screen.) > -.PP > -When a 512-character font is loaded, > -the 9th bit position can be fetched by applying the > -.BR ioctl (2) > +vcs, vcsa \- virtual console capture devices > +.SH SYNOPSIS > +.BR /dev/vcs [\f[BI]N\f[]] > +.RS 2 > +.PP > +For example, > +.IR /dev/vcs2 . > +This is a console capture device. > +.PP > +It is a character device that provides both read and write access > +to the \%textual values of the corresponding virtual console device, > +.I /dev/ttyN > +(for example, > +.IR /dev/tty2 ), > +thereby \[lq]capturing\[rq] its data; > +each textual value is represented by a single > +byte. > +.PP > +The device > +.IR /dev/vcs " (with" out > +a suffix number) is special; > +it captures the > +.IR current " (or\~\[lq]" controlling \[rq]) > +virtual console > +.RI ( /dev/tty0 ). > +.RE > +.PP > +.BR /dev/vcsa [\f[BI]N\f[]] > +.RS 2 > +.PP > +For example, > +.IR /dev/vcsa33 . > +This is similar to > +.IR /dev/vcs [ N ], > +but also provides additional information. > +.PP > +The initial 4 bytes of this file constitute a header that > +describes 2\~geometric characteristics: The console dimensions > +.RI ( lines ,\ columns ) > +and the cursor location > +.RI ( column ,\ line ), > +in that order. > +.PP > +The cursor location is a point in a two-dimensional, y-inverted, > +cartesian coordinate system: > +.RI ( \%column ,\~ line ") = (" x ,\~\- y ); > +the top-left corner of the console is the origin of the > +coordinate system: (0,\ 0); > +the bottom-right corner of the console is > +.RI ( \%columns "\~\-\~1, " lines \~\-\~1). > +.PP > +A textual value and its associated attributes form a > +.I cell\~pair > +that is represented in this device file as 2 consecutive bytes, > +the order of which is determined by the endianness of the kernel; > +the low-order byte provides the textual value. > +.RE > +.PP > .B VT_GETHIFONTMASK > -operation > -(available in Linux kernels 2.6.18 and above) > -on > -.IR /dev/tty[1\-63] ; > -the value is returned in the > -.I "unsigned short" > -pointed to by the third > +.IP "" 2 > +When an 8-bit textual value is not large enough to represent all > +of the available code points, this > .BR ioctl (2) > -argument. > +request may be called on a virtual console > +.RI ( /dev/ttyN ", not " /dev/vcs* ) > +to help determine what is essentially a 9th bit for the textual value, > +thereby doubling the range of possible code points. > +.SH DESCRIPTION > +.SS Console capture device > +Conceptually, a console's memory is organized as a two-dimensional > +array (or\~matrix) of \[lq]cells\[rq] (or\~\[lq]tiles\[rq]) that > +are to be displayed on a graphical screen; > +each cell records 2 important pieces of information: > +.IP \[bu] 2 > +.IR "A textual value" . > +This is a number that is intended to be interpreted as a code > +point, meaning it often identifies a symbolic character of a > +human language; > +it can be used to determine which glyph should be drawn on > +screen in the associated cell. > +This value is always stored in exactly one byte, allowing for > +at most 256 code points. > +.IP \[bu] > +.IR "A set of attributes" . > +This is a collection of bits that is intended to be interpreted > +as metadata for the cell or for the textual value. > +For example, an individual attribute bit might determine whether > +the screen should cause a cell to blink, or whether the glyph in > +a cell should be underlined; > +a contiguous group of bits might be used to specify a color for > +the glyph or even for the entire background of the cell. > +\%Furthermore, an attribute may be used to \[lq]shift\[rq] the > +interpretation of the textual value to some \[lq]higher\[rq] > +or \[lq]lower\[rq] set of code points, effectively extending > +the textual value by one more bit, which is quite useful for > +handling encodings with more than 256 code points (see the > +.B NOTES > +section for a discussion of this). > .PP > -These devices replace the screendump > -.BR ioctl (2) > -operations of > -.BR ioctl_console (2), > -so the system > -administrator can control access using filesystem permissions. > +Linux provides access to this information through what is known > +as a > +.RI \[lq] "console capture device" \[rq]; > +it is a type of > +.I special > +file that is called a \[lq]character device\[rq], which may be > +opened for reading or writing almost like any > +.I regular > +file, provided the user has adequate permissions to do so, > +of course. > +The capabilities of a special file are determined by its > +controlling kernel driver, which the file identifies by > +means of 2 numbers: > +.IP \[bu] 2 > +.IR "major number" . > +This essentially identifies the driver; > +it serves as a kind of driver index. > +.IP \[bu] > +.IR "minor number" . > +This identifies a particular set of resources that are managed by > +the driver; > +it serves as a kind of driver instantiation index. > +For instance, it is used to refer to a particular virtual console > +among multiple virtual consoles. > +Sometimes, however, the minor number is [ab]used in concert with > +the major number to select what is essentially a driver. > +.PP > +.SS VCS > +.IR /dev/vcs [ N ] > +is a console capture device; > +it has major number 7 and minor number in the range [0,63] such > +that > +.I N > +is normally the minor number; > +.I N > +is represented as a single character when in the range [1,9], and > +as two characters when in the range [10,63]; > +for example, > +.I /dev/vcs\f[BI]2\f[] > +should have minor number > +.BR 2 . > +Minor number 0 is special; > +its file should be named > +.I /dev/vcs > +(without any suffix number), and it captures the > +.IR \%current " (or\~\[lq]" controlling \[rq]) > +virtual console. > +.PP > +Such a file usually has mode > +.I 0644 > +and ownership > +.IR root:tty ; > +for details, see > +.BR chmod (1) > +and > +.BR chown (1). > .PP > -The devices for the first eight virtual consoles may be created by: > +Reading or writing a byte of this capture device provides access > +to a textual value of the corresponding virtual console > +device, > +.IR /dev/\f[BI]tty\f[]N ; > +for example, > +.IR /dev/vcs2 " captures " /dev/tty2 ", and " /dev/vcs " captures " /dev/tty0 . > +Because it is a character device, it provides only > +linear access to these values; > +the user is responsible for translating cell location information > +between the one-dimensional and two-dimensional spaces. > +.PP > +For the sake of clarity in naming, recall the following > +conventions: > +.IP \[bu] 2 > +.I /dev/console > +refers to the \[lq]system console\[rq], which is managed by the > +kernel; > +it is used for handling logins when the kernel operates in > +.IR single-user\~mode , > +and it is the device to which system messages > +may be written for diagnostic purposes. > +.IP \[bu] > +.I /dev/tty0 > +(with a 0) refers to the > +.I current > +virtual console (the one with which a process is associated); > +this is captured by > +.IR /dev/vcs " (with" out > +a 0). > +.IP \[bu] > +.IR /dev/tty " (with" out > +a 0) > +refers to the > +.I current > +TTY device; > +.RB see\~ tty (4). > +This may be the system console, a virtual console, a pseudo terminal > +.RB (see\~ pty (7)), > +a serial port, or something more exotic. > +.PP > +.SS VCSA > +.IR /dev/vcsa [ N ] > +is a console capture device; > +it is similar to > +.IR /dev/vcs [ N ], > +but has the following differences: > +.IP \[bu] 2 > +The minor number is > +.IR N+128 : > +.I /dev/vcsa > +should have minor number > +.IR 128 , > +and > +.I /dev/vcsa2 > +should have minor number > +.IR 130 . > +.IP \[bu] > +The initial 4 bytes of this file constitute a \[lq]header\[rq] > +that provides the following graphical characteristics > +(in the given order): > +.RS > +.IP "" 2 > +console dimensions > +.RI ( lines ,\ columns ) > +.br > +cursor location > +.RI ( column ,\ line ) > +.PP > +The cursor location is a point in a two-dimensional, y-inverted, > +cartesian coordinate system: > +.IP "" 2 > +.RI ( column ", " line ") = (" x ", \-" y ) > +.PP > +The top-left corner of the console is the origin of the > +coordinate system: (0,\ 0); > +similarly, the bottom-right corner of the console is > +.RI ( columns \~\-\~1,\~ lines \~\-\~1): > +.RS 2 > .PP > -.in +4n > .EX > -for x in 0 1 2 3 4 5 6 7 8; do > - mknod \-m 644 /dev/vcs$x c 7 $x; > - mknod \-m 644 /dev/vcsa$x c 7 $[$x+128]; > -done > -chown root:tty /dev/vcs* > + \f[B]| > +___|__________________________________________\[rs] column > + |\f[]. ., \f[B]/\f[] (\f[B]x\f[]) > + \f[B]|\f[] . . , > + \f[B]|\f[] . . , > + \f[B]|\f[] (0, 0) (\f[I]columns\f[]-1, 0) , > + \f[B]|\f[] , > + \f[B]|\f[] , > + \f[B]|\f[] (0, \f[I]lines\f[]-1) (\f[I]columns\f[]-1, \f[I]lines\f[]-1) , > + \f[B]|\f[] . . , > + \f[B]|\f[] . . , > + \f[B]|\f[]. ., > + \f[B]|\f[]---------------------------------------- > + \f[B]| > + V > + line > + \f[](-\f[B]y\f[]) > .EE > -.in > +.RE > +.RE > +.IP \[bu] > +A textual value and one byte of its associated attributes form a > +.I cell\~pair > +that is accessed as 2 consecutive bytes in the file; > +after the header, every consecutive (contiguous, non-overlapping) > +set of 2\~bytes forms such a pair: > +.RS > +.RS 2 > +.PP > +.EX > + \f[B]header\f[] \f[B]pair 0\f[] \f[B]pair 1\f[] > + _________________________________||_____________|______________ > +| | | | || | | | /... > +| lines | columns | column | line || byte | byte | byte | byte \[rs] > +|_______|_________|________|______||______|______|______|______/ > + || | > + 0 1 2 3 4 5 6 7 > +.EE > +.RE > +.PP > +The kernel stores each pair as a single, 2-byte value in > +which the low-order byte comprises the textual value, and the > +high-order byte comprises the attributes. > +The main consequence of this is that the relative order of these > +bytes is determined by the > +.I endianness > +of the kernel. > +.PP > +In > +.IR C , > +there are 2 main ways of dealing with this byte order: > +.RS 2 > +.TP 2 > +.I Customizing code > +tailoring a program to the proper endianness. > +.TP > +.I Avoiding bytes > +accessing the bits instead. > +.RI "(Do " not " use " bit-fields .) > +.RE > +.PP > +More concretely: > +.IP \[bu] 2 > +Determine the endianness of the kernel, and thereby determine > +the byte order explicitly. > +For the sake of simplicity, it may be assumed that the endianness > +of both the kernel and user space are always the same; > +to determine the endianness of the kernel, merely determine the > +endianness of user space, which can be achieved through the > +usual methods. > +This results in the most efficient code if the proper > +determination is made at compile time: > +.RS > +.RS 2 > +.PP > +.EX > +#include <limits.h> // CHAR_BIT > +#include <fcntl.h> // open, O_RDWR > +#include <stdlib.h> // exit, EXIT_FAILURE > +#include <unistd.h> // read, lseek, SEEK_SET, SEEK_CUR, write > +#include <stdio.h> // perror, printf > + > +#if CHAR_BIT > 8 > + #error "Your char size is not supported." > +#endif > + > +#define LITTLE_ENDIAN 1 > +#define BIG_ENDIAN 2 > + > +#ifndef ENDIANNESS > + #error "Please define ENDIANNNESS." > +#endif > + > +typedef struct Pair { > + #if ENDIANNESS == LITTLE_ENDIAN > + char text; > + unsigned char attributes; > + #elif ENDIANNESS == BIG_ENDIAN > + unsigned char attributes; > + char text; > + #else > + #error "Your endianness is not supported." > + #endif > +} Pair; > + > +#define VCSA_PATH "/dev/vcsa2" > + > +int main() > +{ > + int fd = open(VCSA_PATH, O_RDWR); > + if (fd == -1) { > + perror("Could not open " VCSA_PATH); > + exit(EXIT_FAILURE); > + } > + > + Pair pair; > + (void)lseek(fd, 4, SEEK_SET); // Skip the 4-byte header. > + (void)read(fd, &pair, 2); // Read a 2-byte pair. > + > + printf( > + "text=\[aq]%c\[aq] attributes=0x%02x\[rs]n", > + pair.text, pair.attributes > + ); > + > + pair.text = \[aq]$\[aq]; // Set a textual value. > + pair.attributes ^= 0x10; // Toggle an attribute bit. > + > + (void)lseek(fd, -2, SEEK_CUR); // Seek to the old pair. > + (void)write(fd, &pair, 2); // Replace the old pair. > +} > +.EE > +.RE > +.RE > +.IP \[bu] > +Alternatively, access the pair as one multibyte value, thereby > +allowing the machine to handle endianness implicitly; > +access the > +.I bits > +rather than the > +.IR bytes : > +.RS > +.RS 2 > +.PP > +.EX > +#include <fcntl.h> // open, O_RDWR > +#include <stdlib.h> // exit, EXIT_FAILURE > +#include <unistd.h> // read, lseek, SEEK_SET, SEEK_CUR, write > +#include <stdio.h> // perror, printf > +#include <stdint.h> // uint16_t > + > +#define VCSA_PATH "/dev/vcsa2" > + > +int main() > +{ > + int fd = open(VCSA_PATH, O_RDWR); > + if (fd == -1) { > + perror("Could not open " VCSA_PATH); > + exit(EXIT_FAILURE); > + } > + > + uint16_t pair; > + (void)lseek(fd, 4, SEEK_SET); // Skip the 4-byte header. > + (void)read(fd, &pair, 2); // Read a 2-byte pair. > + > + char text = pair & 0xff; // low-order byte. > + unsigned char attributes = (pair >> 8); // high-order byte. > + > + printf( > + "text=\[aq]%c\[aq] attributes=0x%02x\[rs]n", > + text, attributes > + ); > + > + text = \[aq]$\[aq]; // Set a textual value. > + attributes ^= 0x10; // Toggle an attribute bit. > + pair = (attributes << 8) | text; // Construct the new pair. > + > + (void)lseek(fd, -2, SEEK_CUR); // Seek to the old pair. > + (void)write(fd, &pair, 2); // Replace the old pair. > +} > +.EE > +.RE > +.PP > +Note that while it is tempting to use the > +.I bit-field > +construct of the > +.I C > +programming language, such a construct is highly dependent on the > +particular implementation of > +.IR C . > +Avoid using bit-fields. > +.RE > +.RE > +.PP > +.SS Attributes > +The set of attributes (or their structure within an attributes byte) > +depends on the console driver and ultimately on the console hardware; > +a widespread standard is > +.IR "VGA text mode " ( "Video Graphics Array" ), > +for which the bits usually take on the following meanings: > +.RS 2 > +.PP > +.EX > + blink background color foreground color > +|_______|_______________________|_______________________________| > +| | | | | | | | | > +| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | > +|_______|_______|_______|_______|_______|_______|_______|_______| > +| | | | > +.EE > +.RE > +.PP > +Specifically: > +.IP \[bu] 2 > +Bit 7 (the highest-order bit) specifies whether the cell in question > +should blink in order to capture the user's attention. > +.IP \[bu] > +Bits 6, 5, and 4 are used to specify one of 8 possible background colors. > +.IP \[bu] > +Bits 3, 2, 1, and 0 (the lowest-order bits) are used to specify > +one of 16 possible foreground colors; > +the possible colors are determined by the console's color > +\[lq]palette\[rq], which is often customizable (see the > +.BR od (1) > +discussion in the > +.B EXAMPLE > +section). > +.PP > +Naturally, there are many other ways for these bits to be interpreted, > +even under this same VGA text mode standard; > +for instance: > +.IP \[bu] 2 > +Bit 7 can be repurposed as an additional bit for the > +background color, thereby allowing for the full range > +of 16 colors. > +.IP \[bu] > +Bit 3 is normally used as a way to select the foreground intensity > +either by setting up the color palette appropriately, or by using > +a mode of operation that is compatible with the older > +.I MDA > +standard > +.RI ( "Monochrome Display Adapter" ); > +however, it can also be used to switch between 2 different fonts. > +.IP \[bu] > +Bit 0 can be used to specify underlining when operating in a mode > +compatible with the MDA standard. > +.PP > +.SS IOCTL > +There is no special > +.BR ioctl (2) > +request for a console capture device. > +.PP > +These devices replace the > +.BR ioctl_console (2) > +\[lq]screendump\[rq] requests; > +the system administrator should control access to these devices > +by using filesystem permissions. > +.PP > +.SH NOTES > +Though a virtual console capture device provides access to much > +useful information about its corresponding virtual console, it > +may sometimes be necessary to issue an > +.BR ioctl (2) > +request on the corresponding virtual console instead. > +.PP > +.SS High-font mask > +When there is loaded a font with more than 256 code points > +(such as a 512-\[lq]character\[rq] font), then a single 8-bit > +textual value is not capable of referencing every code point; > +at least one more bit (a\~9th bit) is necessary. > +.PP > +This extra bit is stored as one of the attributes of the console > +cell in question. > +Unfortunately, it is not standardized which bit is used for this > +purpose, and so that information must be fetched explicitly from > +the console driver. > .PP > -No > +Since Linux kernel 2.6.18, this has been possible by applying on > +.IR /dev/ttyN " (not " /dev/vcs* ) > +the > .BR ioctl (2) > -requests are supported. > +request > +.BR VT_GETHIFONTMASK , > +which stores a 2-byte bit mask in the object referenced > +by the user-provided pointer argument; > +this mask applies to the entire value of a pair, and may be > +used to determine whether the 9th bit is set, after which the > +code point may be calculated (this implies the code point must > +be stored in an object of integer type that provides at least > +9\~bits of storage). > +For example: > +.RS 2 > +.PP > +.EX > +#include <fcntl.h> // open, O_RDONLY > +#include <stdlib.h> // exit, EXIT_FAILURE > +#include <unistd.h> // lseek, SEEK_SET, read, close > +#include <sys/ioctl.h> // ioctl > +#include <linux/vt.h> // VT_GETHIFONTMASK > +#include <stdint.h> // uint16_t > +#include <stdio.h> // printf > + > +int main() > +{ > + int fd; > + > + fd = open("/dev/tty2", O_RDONLY); // Note: "tty2", not "vcs2". > + if (fd == -1) > + exit(EXIT_FAILURE); > + uint16_t mask; > + (void)ioctl(fd, VT_GETHIFONTMASK, &mask); > + (void)close(fd); > + > + fd = open("/dev/vcsa2", O_RDONLY); > + if (fd == -1) > + exit(EXIT_FAILURE); > + uint16_t pair; > + (void)lseek(fd, 4, SEEK_SET); // Skip the 4-byte header. > + (void)read(fd, &pair, 2); // Read a 2-byte pair. > + (void)close(fd); > + > + int text = pair & 0xff; // The initial 8 bits of the code point. > + if (pair & mask) // if (9th bit of the code point is set) > + text |= 0x100; // set the 9th bit of "text", too. > + > + printf("text=0x%03x mask=0x%04x\[rs]n", text, mask); > +} > +.EE > +.RE > .SH FILES > -.I /dev/vcs[0\-63] > +.I /dev/vcs > .br > -.I /dev/vcsa[0\-63] > +.I /dev/vcsN > +.PP > +.I /dev/vcsa > +.br > +.I /dev/vcsaN > +.PP > +.I N > +is an integer in the range [1,63]; > +it is represented as a single character when in the range [1,9], > +and as two characters when in the range [10,63] (e.g., > +.IR /dev/vcs2 " or " /dev/vcsa33 ). > .\" .SH AUTHOR > .\" Andries Brouwer <aeb@xxxxxx> > +.\" Michael Witten <mfwitten@xxxxxxxxx> > .SH VERSIONS > -Introduced with version 1.1.92 of the Linux kernel. > +The console capture devices were introduced with version 1.1.92 of the > +Linux kernel. > +.PP > +The > +.BR ioctl (2) > +request > +.B VT_GETHIFONTMASK > +was introduced with version 2.6.18 of the Linux kernel. > .SH EXAMPLE > -You may do a screendump on vt3 by switching to vt1 and typing > +.IP \[bu] 2 > +The > +.I /dev/vcsa* > +devices can be used for Braille support. > +.IP \[bu] > +The devices for the initial 8 virtual consoles may be created > +thusly: > +.RS > +.RS 2 > .PP > - cat /dev/vcs3 >foo > +.EX > +#!/bin/sh > + > +mknod -m 644 /dev/vcs c 7 0 > +mknod -m 644 /dev/vcsa c 7 128 > + > +for x in 1 2 3 4 5 6 7 8; do > + mknod -m 644 /dev/vcs"$x" c 7 "$x" > + mknod -m 644 /dev/vcsa"$x" c 7 $((x+128)) > +done > + > +chown root:tty /dev/vcs* > +.EE > +.RE > .PP > -Note that the output does not contain > -newline characters, so some processing may be required, like > -in > +During the boot process, a modern Linux kernel creates at > +least some of these device files, namely > +.IR /dev/vcs ", " /dev/vcs1 ", " /dev/vcsa ", and " /dev/vcsa1 ; > +however, setting the right permissions and ownership might still > +be necessary thereafter. > +.RE > +.IP \[bu] > +You may do a screendump of > +.I /dev/tty2 > +by switching to > +.I /dev/tty3 > +and then running this command: > +.RS > +.RS 2 > .PP > - fold \-w 81 /dev/vcs3 | lpr > +.EX > +.RB "$ " "cat /dev/vcs2 >foo" > +.EE > +.RE > .PP > -or (horrors) > +Note that the output does not separate console lines > +with the newline character, so some processing may be > +required in order to achieve that effect: > +.RS 2 > .PP > - xetterm \-dump 3 \-file /proc/self/fd/1 > +.EX > +.RB "$ " "fold /dev/vcs2 >foo" > +.EE > +.RE > +.RE > +.IP \[bu] > +The dimensions, cursor location, textual values, and attributes of > +.I /dev/tty2 > +may > +be accessed via > +.IR /dev/vcsa2 : > +.RS > +.RS 2 > .PP > -The > -.I /dev/vcsa0 > -device is used for Braille support. > +.EX > +$ \f[B]od -t x2 -An -N16 -w16 /dev/vcsa2\f[] > + 5019 1800 0720 0720 0720 0720 0720 0720 > +$ \f[B]od -t x1 -An -N16 -w16 /dev/vcsa2\f[] > + 19 50 00 18 20 07 20 07 20 07 20 07 20 07 20 07 > +$ \f[B]od -t u1 -An -N16 -w16 /dev/vcsa2\f[] > + 25 80 0 24 32 7 32 7 32 7 32 7 32 7 32 7 > +.EE > +.RE > .PP > -This program displays the character and screen attributes under the > -cursor of the second virtual console, then changes the background color > -there: > +In this case, > +.I /dev/tty2 > +has the following properties: > +.IP - 2 > +25 lines, each of which has 80 columns. > +.IP - > +The cursor is at location > +.RI ( x ,\ \- y ) > += (0,\ 24); > +it is in column 0 of line 24, which is the bottom-left > +corner of the console. > +.IP - > +In this > +.I particular > +case, each cell pair is stored in little-endian byte > +order; > +each textual byte precedes its associated attributes: > +.RS > +.IP o 2 > +Each of the virtual console cells that were accessed has > +as its textual value the hexadecimal number > +.I 0x20 > +(decimal > +.RI number\~ 32 ), > +which is the > +.BR ascii (7) > +code point for the space character (\[aq]\ \[aq]); > +in other words, those cells appear to be blank. > +.IP o > +Each of those cells has an attributes byte represented by the > +.RI number\~ 7 > +(binary number > +.IR 0b111 ); > +the attributes corresponding to the lower\~3 bits are set, and > +those corresponding to the higher\~5 bits are unset. > +.RS > +.PP > +On this system, the virtual console is operating in VGA text mode > +and has the following 16 colors loaded into its color palette: > +.RS 2 > .PP > .EX > -#include <unistd.h> > -#include <stdlib.h> > -#include <stdio.h> > -#include <fcntl.h> > -#include <sys/ioctl.h> > -#include <linux/vt.h> > - > -int > -main(void) > -{ > - int fd; > - char *device = "/dev/vcsa2"; > - char *console = "/dev/tty2"; > - struct {unsigned char lines, cols, x, y;} scrn; > - unsigned short s; > - unsigned short mask; > - unsigned char attrib; > - int ch; > - > - fd = open(console, O_RDWR); > - if (fd < 0) { > - perror(console); > - exit(EXIT_FAILURE); > - } > - if (ioctl(fd, VT_GETHIFONTMASK, &mask) < 0) { > - perror("VT_GETHIFONTMASK"); > - exit(EXIT_FAILURE); > - } > - (void) close(fd); > - fd = open(device, O_RDWR); > - if (fd < 0) { > - perror(device); > - exit(EXIT_FAILURE); > +$ \f[B]component() { cat /sys/module/vt/parameters/default_"$1"; }\f[] > +$ \f[B]palette() { component red; component grn; component blu; }\f[] > +$ \f[B]palette\f[] > +0,170,0,170,0,170,0,170,85,255,85,255,85,255,85,255 > +0,0,170,85,0,0,170,170,85,85,255,255,85,85,255,255 > +0,0,0,0,170,170,170,170,85,85,85,85,255,255,255,255 > +.EE > +.RE > +.PP > +For easier reading, each color can be listed as follows: > +.RS 2 > +.PP > +.EX > +$ \f[B]color() > + { > + palette | awk -F, -v i="$1" \[aq]{print $(i+1)}\[aq] | { > + read red; read green; read blue > + printf \[aq]color %2s = (r,g,b) = (%s,%s,%s)\[rs]n\[aq] \[rs] > + "$1" "$red" "$green" "$blue" > } > - (void) read(fd, &scrn, 4); > - (void) lseek(fd, 4 + 2*(scrn.y*scrn.cols + scrn.x), SEEK_SET); > - (void) read(fd, &s, 2); > - ch = s & 0xff; > - if (s & mask) > - ch |= 0x100; > - attrib = ((s & ~mask) >> 8); > - printf("ch=0x%03x attrib=0x%02x\en", ch, attrib); > - s ^= 0x1000; > - (void) lseek(fd, \-2, SEEK_CUR); > - (void) write(fd, &s, 2); > - exit(EXIT_SUCCESS); > + }\f[] > +$ \f[B]for ((i=0; i<16; ++i)); do color "$i"; done\f[] > +\f[I]color 0\f[] = (r,g,b) = \f[I](0,0,0)\f[] > +color 1 = (r,g,b) = (170,0,0) > +color 2 = (r,g,b) = (0,170,0) > +color 3 = (r,g,b) = (170,85,0) > +color 4 = (r,g,b) = (0,0,170) > +color 5 = (r,g,b) = (170,0,170) > +color 6 = (r,g,b) = (0,170,170) > +\f[I]color 7\f[] = (r,g,b) = \f[I](170,170,170)\f[] > +color 8 = (r,g,b) = (85,85,85) > +color 9 = (r,g,b) = (255,85,85) > +color 10 = (r,g,b) = (85,255,85) > +color 11 = (r,g,b) = (255,255,85) > +color 12 = (r,g,b) = (85,85,255) > +color 13 = (r,g,b) = (255,85,255) > +color 14 = (r,g,b) = (85,255,255) > +color 15 = (r,g,b) = (255,255,255) > +.EE > +.RE > +.PP > +Thus, the attributes specify foreground color 7 (light gray), > +background color 0 (black), and no blinking. > +.RE > +.RE > +.RE > +.IP \[bu] > +The following program displays the textual value and > +attributes byte under the cursor of > +.IR /dev/tty2 , > +and then changes the background color at that location > +(assuming the background color is influenced by the > +attribute bit in question). > +.RS > +.PP > +When run twice in succession on the same system used in > +the above > +.BR od (1) > +example, the program produces this output: > +.RS 2 > +.PP > +.EX > +text=\[aq] \[aq] attributes=0x07 mask=0x0000 > +text=\[aq] \[aq] attributes=0x17 mask=0x0000 > +.EE > +.RE > +.PP > +Here is the program: > +.RS 2 > +.PP > +.EX > +#include <fcntl.h> // open, O_RDONLY, O_RDWR > +#include <stdio.h> // perror, printf > +#include <stdlib.h> // exit, EXIT_FAILURE > +#include <sys/ioctl.h> // ioctl > +#include <linux/vt.h> // VT_GETHIFONTMASK > +#include <stdint.h> // uint8_t, uint16_t > +#include <sys/types.h> // off_t > +#include <unistd.h> // read, lseek, SEEK_CUR, write, close > + > +#define MINOR_NUM "2" > +#define TTY_PATH "/dev/tty" MINOR_NUM > +#define VCSA_PATH "/dev/vcsa" MINOR_NUM > + > +#define PAIR_SIZE 2 > + > +int main() > +{ > + int fd; > + > + fd = open(TTY_PATH, O_RDONLY); > + if (fd == -1) { > + perror("Could not open " TTY_PATH); > + exit(EXIT_FAILURE); > + } > + > + uint16_t mask; > + if (ioctl(fd, VT_GETHIFONTMASK, &mask) == -1) { > + perror("Could not fetch mask for high-font bit"); > + exit(EXIT_FAILURE); > + } > + > + (void)close(fd); > + > + fd = open(VCSA_PATH, O_RDWR); > + if (fd == -1) { > + perror("Could not open " VCSA_PATH); > + exit(EXIT_FAILURE); > + } > + > + struct {uint8_t lines, cols, x, y;} header; > + (void)read(fd, &header, 4); > + > + // Note that the header bytes have already been skipped. > + const off_t cursor_location = header.y*header.cols + header.x; > + const off_t pair_location = cursor_location * PAIR_SIZE; > + (void)lseek(fd, pair_location, SEEK_CUR); > + > + uint16_t pair; > + (void)read(fd, &pair, PAIR_SIZE); > + > + const char *format; > + > + int text = pair & 0xff; // The first 8 bits of the code point. > + if (pair & mask) { // if (9th bit of code point is set) > + text |= 0x100; // set 9th bit of variable, too. > + format = "text=0x%03x attributes=0x%02x mask=0x%04x\[rs]n"; > + } else { > + format = "text=\[aq]%c\[aq] attributes=0x%02x mask=0x%04x\[rs]n"; > + } > + > + // Now, get the attribute bits, but apply the mask such that > + // the attibute bit which represents the code point\[aq]s 9th bit > + // is always treated as 0. > + int attributes = ((pair & ~mask) >> 8); > + > + printf(format, text, attributes, mask); > + > + pair ^= (0x10 << 8); // Toggle an attribute. > + > + (void)lseek(fd, -PAIR_SIZE, SEEK_CUR); // Seek to the old pair. > + (void)write(fd, &pair, PAIR_SIZE); // Replace the old pair. > + (void)close(fd); > } > .EE > +.RE > +.RE > .SH SEE ALSO > +.BR chmod (1), > +.BR chown (1), > +.BR fold (1), > +.BR mknod (1), > +.BR od (1), > .BR ioctl_console (2), > .BR tty (4), > .BR ttyS (4), > +.BR pty (7), > .BR gpm (8) > -- Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/