[prev in list] [next in list] [prev in thread] [next in thread] 

List:       linux-man
Subject:    Re: [PATCH] vcs.4: Re-write and expound
From:       "Michael Kerrisk (man-pages)" <mtk.manpages () gmail ! com>
Date:       2019-03-08 17:00:14
Message-ID: e74b0cb6-539f-c3f1-476d-3d9287355dda () gmail ! com
[Download RAW message or body]

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@gmail.com>
> ---
>  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@vanzandt.mv.com>
>  .\" 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@cs.unc.edu
>  .\" 2007-12-17, Samuel Thibault <samuel.thibault@ens-lyon.org>:
>  .\"     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@cwi.nl>
> +.\" Michael Witten <mfwitten@gmail.com>
>  .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/
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic