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

List:       linux-man
Subject:    [PATCH v1] vcs.4: Re-write and expound
From:       Michael Witten <mfwitten () gmail ! com>
Date:       2019-03-12 23:25:33
Message-ID: 1b2ebba3d0484c8f864ea9e909121bc6-mfwitten () gmail ! com
[Download RAW message or body]

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@gmail.com>
---
 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@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,167 +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
-.\" "
-.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@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
-.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


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

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