On Fri, 8 Mar 2019 18:00:14 +0100, Michael Kerrisk wrote: > 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? The following version of the patch incorporates the requested citations. It also involves a minor fix: diff --git a/man4/vcs.4 b/man4/vcs.4 index 3eca9886f..85d124bd2 100644 --- a/man4/vcs.4 +++ b/man4/vcs.4 @@ -359,7 +359,7 @@ determination is made at compile time: #define BIG_ENDIAN 2 #ifndef ENDIANNESS - #error "Please define ENDIANNNESS." + #error "Please define ENDIANNESS." #endif typedef struct Pair { Save this email to: /path/to/patch and then apply it with: git am --scissors /path/to/patch Sincerely, Michael Witten --8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<-- 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 ---------------------------------------------------------- * The naming conventions and uses of various terminal device types can be derived from these sources: $ cd /path/to/linux/repo $ git show v5.0:Documentation/admin-guide/devices.rst $ git show v5.0:Documentation/admin-guide/devices.txt $ git show v5.0:include/uapi/linux/vt.h | sed -n '/MIN_NR_CONSOLES/{p;q}'; \ git show v5.0:drivers/tty/vt/vc_screen.c | sed -n '/^void vcs_make_sysfs/,/^}/p;/__init vcs_init/,$p' * The meanings and structure of attribute bits are a reflection of the following: * Table 2-8 on PDF-page 34 of http://bitsavers.trailing-edge.com/pdf/ibm/pc/cards/IBM_VGA_XGA_Technical_Reference_Manual_May92.pdf * http://www.osdever.net/FreeVGA/vga/vgatext.htm * https://en.wikipedia.org/w/index.php?title=VGA-compatible_text_mode&oldid=874285916 * $ cd /path/to/linux/repo $ git show v5.0:drivers/tty/vt/vt.c | sed -n '/u8 build_attr/,/^}/p' $ git show v5.0:drivers/video/console/vgacon.c | sed -n '/u8 vgacon_build_attr/,/^}/p' $ git show v5.0:drivers/video/console/mdacon.c | sed -n '/u8 mdacon_build_attr/,/^}/p' * The color palette example has a basis in these `sysfs' objects: $ git show v5.0:drivers/tty/vt/vt.c | sed -n '/module_param_array/p' module_param_array(default_red, byte, NULL, S_IRUGO | S_IWUSR); module_param_array(default_grn, byte, NULL, S_IRUGO | S_IWUSR); module_param_array(default_blu, byte, NULL, S_IRUGO | S_IWUSR); * The admonition against using C `bit-fields' is apparent from reading pages 112-114 of this [draft] C standard: C11 Draft Standard. WG14 paper N1570 "ISO/IEC 9899:201x, Programming languages -- C". 2011-04-12. JTC1/SC22/WG14. Publically available working-draft of the C11 standard (ISO/IEC 9899:2011). http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf MD5: 658f5f4490464255b11e1d5502474deb Signed-off-by: Michael Witten <mfwitten@xxxxxxxxx> --- man4/vcs.4 | 959 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 834 insertions(+), 125 deletions(-) diff --git a/man4/vcs.4 b/man4/vcs.4 index bc481243d..85d124bd2 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,167 +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 -.\" " -.TH VCS 4 2019-03-06 "Linux" "Linux Programmer's Manual" +.\" +.\" 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 -The devices for the first eight virtual consoles may be created by: +Such a file usually has mode +.I 0644 +and ownership +.IR root:tty ; +for details, see +.BR chmod (1) +and +.BR chown (1). +.PP +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 +.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 ENDIANNESS." +#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 -.in +.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 -No +.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) -requests are supported. +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 +Since Linux kernel 2.6.18, this has been possible by applying on +.IR /dev/ttyN " (not " /dev/vcs* ) +the +.BR ioctl (2) +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 -.in +4n .EX -cat /dev/vcs3 >foo +#!/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 -.in +.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 -.in +4n .EX -fold \-w 81 /dev/vcs3 | lpr +.RB "$ " "cat /dev/vcs2 >foo" .EE -.in +.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 -.in +4n .EX -setterm \-dump 3 \-file /proc/self/fd/1 +.RB "$ " "fold /dev/vcs2 >foo" .EE -.in +.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 +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 -This program displays the character and screen attributes under the -cursor of the second virtual console, then changes the background color -there: +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) -- 2.19.0