RFC 746

SUPDUP graphics extension

πŸ“… published March 1978, from ietf

The SUPDUP Graphics Extension πŸŽ€

Okay bestie, this is RFC 746, and it's literally the SUPDUP graphics extension πŸ–ΌοΈβœ¨ Here's the tea on the title block so you know exactly what you're looking at:

  • Number: RFC 746 (NIC 43976), filed under the Network Working Group 🌸
  • Author: Richard Stallman, from MIT-AI 🧚
  • Date: 17 March 1978 πŸ’…
  • Category or status: not listed in the title block
  • Obsoletes: nothing listed
  • Updates: nothing listed

Okay so the intro is giving "I built a cool thing, please help me test it." It extends SUPDUP so you can show pictures on your terminal screen, not just text πŸ¦‹ It points you to RFC 734 "SUPDUP Protocol" by Crispin for the basics, so go read that first if you haven't, no cap.

Real talk though: this extension has never been implemented, so it's presumably got some problems πŸ™ˆ The whole point of publishing it is to ask for suggestions and to find someone who'll try to bring it up. Main character energy, but honest about it πŸ’œ

The major accomplishments are the stuff that makes this lowkey iconic:

  • It is easy to do simple things. πŸ“
  • Any program on the server host can at any time begin outputting pictures. No special preparations are needed. πŸ”₯
  • No additional network connections are needed. Graphics commands go through the normal text output connection. ✨
  • It has nothing really to do with the network. It's suitable for locally connected intelligent display terminals too, and programs don't need to know whether they're running locally or remotely. It can be the universal way of expressing graphics output, for whatever destination. Programs written for non-network terminals lose little convenience, and they automatically become usable over the ARPA network. Universal graphics energy 🌈
  • Loss of output (maybe from a "silence" command the user typed) does not leave the user host confused. 🫢
  • The terminal doesn't need to remember the internal "semantic" structure of the picture being displayed, just the lines and points, or even just bits in a bit matrix. πŸ’…
  • The server host doesn't need to invoke arbitrary terminal-dependent software to turn a standard language into one a terminal can use. Instead, a standard language is defined that all programmable terminals can interpret easily. Differences between terminals are handled by conventions that include enough redundant information in the output stream that every type of terminal has what it needs when it needs it, even if it can't remember that info in usable form from one command to the next. πŸ‘‘

Those who care about network graphics should also read about the Multics Graphics System. It has the same basic purpose, but its assumptions are very different, although it did inspire a few features of this proposal 🧚

-2-


SUPDUP Initial Negotiation:

Okay, so one new optional variable gets added: the SMARTS variable. ✨ It should come after the other variables that the SUPDUP user process sends to the SUPDUP server process. Bits and fields in the left half-word get names starting with "%TQ", and bits and fields in the right half get names starting with "%TR". Not all of the SMARTS variable is about graphics, but most of it is, bestie πŸŽ€ The %TQGRF bit should be 1 if the terminal supports graphics output at all. πŸ’–


Invoking the Graphics Protocol:

Graphics mode gets entered with a %TDGRF (octal 231) code in the output stream. The characters after it in the range 0 - 177 get interpreted according to the graphics protocol. Any character 200 or larger (a %TD code) leaves graphics mode and then gets its normal interpretation. This is the safety net 🧚 If the server forgets the terminal is in graphics mode, the terminal won't stay confused for long.

Once you're in graphics mode, the output stream should contain a sequence of graphics protocol commands, each followed by its arguments. A zero as a command is a no-op. πŸ¦‹ To leave graphics mode on purpose, it's best to use a %TDNOP. 🫢

-3-


Co-ordinates:

Graphics mode uses a cursor position that gets remembered from one graphics command to the next while you're in graphics mode. This graphics cursor is not the same one used by normal type-out. Graphics protocol commands have no effect on the normal type-out cursor, and normal type-out has no effect on the graphics mode cursor. πŸ™…β€β™€οΈ Also, the graphics cursor's position is measured in dots instead of characters. ✨

The two units (dots and characters) are related through the %TQHGT and %TQWID fields of the terminal's SMARTS variable. Those fields hold the height and width, in dots, of the box one character occupies. The screen size in either dimension is assumed to be the length of one character box times the number of characters in that direction on the screen. If the screen is actually bigger than that, the excess may or may not be part of the visible area. Either way, the program won't know it exists πŸ’€

Each co-ordinate of the cursor position is a 14-bit signed number, and zero sits at the center of the screen. If the screen dimension is an even number of dots, the visible negative points extend one unit farther than the positive ones, in proper two's complement fashion πŸ’… Excessively large co-ordinate values end up off the screen, but they're still meaningful. 🌸

There's also an alternate mode that some terminals may support, using virtual co-ordinates. The specified co-ordinates are still 14-bit signed numbers. But instead of being in units of physical dots, +4000 octal is treated as the top of the screen or the right edge, and -4000 octal is the bottom of the screen or the left edge. The terminal is responsible for scaling these virtual co-ordinates into screen dots. Not every terminal needs this capability. The %TQVIR bit in the SMARTS variable says whether it exists. 🧚

To use virtual co-ordinates, the server should send a %GOVIR. To go back to physical co-ordinates, it should send a %GOPHY. These should be repeated at intervals, such as when graphics mode is entered, even though the terminal is supposed to remember the state of the switch anyway. 🫢 The repetition is so that losing some output won't cause unbounded confusion. Bestie, that's a whole safety strategy, and it's so smart 😭

The virtual co-ordinates are based on a square. If the visible area on the terminal isn't a square, the standard virtual range should correspond to a square around the center of the screen, and the rest of the visible area should correspond to virtual co-ordinates just beyond the normally visible range. 🌈

Graphics protocol commands take two kinds of cursor position arguments: absolute ones and relative ones. Commands that take address arguments generally have two forms, one for each type of address. πŸŽ€

A relative address is two offsets, delta-X and delta-Y, from the old cursor position. Each offset is a 7-bit two's complement number that occupies one character. An absolute address is two co-ordinates, each 14 bits long, occupying two characters, and each of those characters carries 7 bits. The X co-ordinate or offset comes before the Y. Both types of address set the running cursor position, which the next address will use if it's relative. πŸ’–

-4-

It's perfectly legit for parts of objects to go off the screen. What happens to them isn't terribly important, as long as it isn't disastrous. It also shouldn't interfere with the cursor position reckoning, and it shouldn't cause later objects, drawn after the cursor moves back onto the screen, to get misdrawn. πŸ™ˆ

Whether a particular spot on the screen is specified with an absolute or a relative address is of no consequence. The order in which objects get drawn is of no consequence either. Each object is independent of all the others, and it exists at the place that was specified, one way or another, by the command that created it. ✨ Relative addresses are there for data compression. They're not an attempt to spare programs from the meagre intelligence needed to convert between absolute and relative addresses, since more intelligence than that will surely be needed for other parts of the graphics protocol. 🧚 Relative addresses also aren't meant to make several objects relocate together when one gets "moved" or erased. Terminals aren't expected to remember any relation between objects once they're drawn. Most won't be able to anyway πŸ¦‹

Even though the cursor position on entry to graphics mode stays where it was at the last exit, it's wise to reinitialize it with a %GOMVA command before any long transfer. That limits the effects of lost output. 🫢

-5-

Graphics Input ✨

okay so the %TRGIN bit in the right half of the SMARTS variable tells you the terminal can hand over a graphic input in the form of a cursor position whenever you ask for one πŸŽ€ sending a %GOGIN command asks it to read the cursor position. You should follow it with an argument character, which gets included in the reply and links that reply to the specific request for input that made it happen πŸ’…

the reply should look like a Top-Y character (code 4131), then the reply code character we just talked about, then an absolute cursor position. Top-Y isn't normally meaningful as input, so %GOGIN replies can be told apart from keyboard input reliably πŸ™ˆ unsolicited graphic input should be sent with a Top-X instead of a Top-Y so the program can tell them apart too. And instead of a reply code (which isn't needed here), the terminal should send an encoding of the buttons the user pressed on their input device, if it has more than one button 🫢


Sets 🧚

terminals may define the concept of a "set" of objects πŸ’œ there are up to 200 different sets, and each one can hold arbitrarily many objects. at any given time exactly one set is selected: objects you draw become part of that set, and objects you erase get removed from it ✨ objects in a set that isn't the selected one can't be erased unless you switch to the set(s) they live in, no cap πŸ™…β€β™€οΈ

a set can be made temporarily invisible as a whole, without being erased or having its contents forgotten, and then made instantly visible again πŸ¦‹ a whole set can also be moved. every set has a point called its "center," and all the objects in it are remembered relative to that center. the center can be moved anywhere, which moves every object in the set at once 🌈 so before you start using a set, you should "move" its center to some absolute location first, bestie πŸŽ€

center motion can easily push objects off the screen 😭 when that happens, it doesn't matter what happens to those objects temporarily, BUT their "positions" must not be forgotten, so that undoing the center motion brings them back to visibility in their previous positions πŸ” sets aren't easy to implement on bit matrix terminals, so those should ignore all set operations (except for a degenerate interpretation with blinking, if that's implemented) πŸ’€ the %TQSET bit in the SMARTS variable tells you the terminal implements multiple sets of objects.

on a terminal that supports multiple sets, the %GOCLR command should empty all sets and mark all of them "visible" (by performing a %GOVIS on each one). a %TDCLR SUPDUP command should do the same thing πŸ“ which means any program that starts by clearing the screen won't have to stress about initializing the states of all the sets. main character energy πŸ‘‘


Blinking 🌸

some terminals can make objects blink on the screen ✨ the %GOBNK command means "make the current set blink." all the objects already in it start blinking, and any new objects blink too πŸ’– %GOVIS or %TOINV cancels the effect of a %GOBNK, making the objects of the set permanently visible or invisible. %TQBNK indicates that the terminal supports blinking on the screen 🌈

BUT there's a problem 😭 some intelligent bit matrix terminals may be able to blink a few objects, as long as they're told in advance, before the objects get drawn. they won't be able to support arbitrary use of %GOBNK, though πŸ’€

the solution is a convention for the use of %TOBNK 🧚 which, together with degenerate definitions for set operations, makes it possible to give commands that reliably work on any terminal that supports blinking 🫢 on a terminal that sets %TQBNK but not %TQSET, %GOBNK is defined to make objects drawn after it get drawn blinking. %GOSET cancels this, so the objects that follow get drawn unblinking, and this holds no matter what argument you give to the %GOSET πŸ”₯

so the way for a program to work on all terminals with %TQBNK, whether they know about sets or not, goes like this: write a bliniking picture, select some set other than your normal one (set 1 will do), do %GOBNK, output the picture, and then reselect set 0 πŸŽ€ the picture blinks while you draw things in set 0. to draw more blinking objects, you have to reselect set 1 and do another %GOBNK. just reselecting set 1 won't work on terminals that don't really support sets, since they don't remember that the blinking objects are "in set 1" and not "in set 0" πŸ™ˆ

erasing a blinking object should make it disappear on any terminal that implements blinking ✨ on bit matrix terminals, blinking MUST always be done by XORing, so the non-blinking background doesn't get destroyed πŸ›‘οΈ

%GOCLS, on a terminal that supports blinking but not sets, should delete all the blinking objects πŸ’… so the convention for deleting all blinking objects is: select set 1, do a %GOCLS, and reselect set 0. this has the desired effect on all terminals πŸ¦‹ this definition of %GOCLS causes no trouble on non-set terminals, since %GOCLS would otherwise be meaningless to them 🌸

if you want blinking objects to stop blinking but stay visible, a %GOVIS does it on a terminal that supports sets. but in general, the only way to do it is to delete them and redraw them as permanent 😭


Rectangles and XOR πŸ’œ

bit matrix terminals have their own operations that display list terminals can't duplicate 🌈 first of all, they have XOR mode, where objects drawn cancel existing objects when they overlap ✨ in this mode, drawing an object and erasing it are the same operation. all the %GOD.. commands act IDENTICALLY to the corresponding %GOE..'s πŸ™Œ XOR mode gets entered with a %GOXOR and left with a %GOIOR. display list terminals ignore both commands, so the program should keep distinguishing draw commands from erase commands even in XOR mode πŸŽ€ the %TQXOR bit indicates a terminal that implements XOR mode. XOR mode, once set, stays set even if you leave graphics mode and re-enter it πŸ’– still, it's wise to re-specify it from time to time, in case output gets lost πŸ˜…

bit matrix terminals can also draw solid rectangles πŸ“ so they can implement the commands %GODRR, %GODRA, %GOERR, and %GOERA. a rectangle is specified by taking the current cursor position as one corner, and giving the address of the opposite corner. that can be done with either a relative address or an absolute one πŸ“ the %TQREC bit indicates that the terminal implements the rectangle commands.

of course, a sufficiently intelligent bit matrix terminal can provide all the features of a display list terminal by remembering display lists that are redundant with the bit matrix, and using them to update the matrix when a %GOMSR or %GOVIS is done 🧚 but honestly, most bit matrix terminals aren't expected to go to such lengths πŸ’€


How Several Processes Can Draw On One Terminal Without Interfering With Each Other 🫧

okay so if we define "input-stream state" information as whatever information can affect the action of any command, other than what's contained in the command itself, then each of the several processes has to have its own set of input-stream state variables πŸŽ€

this is handled by the %GOPSH command πŸ’… the %GOPSH command saves all that input-stream information, to be restored when graphics mode is exited. if the processes can arrange to output blocks of characters uninterruptibly, they can start each block with a %GOPSH followed by commands that set up the input-stream state information however they like ✨ each block of graphics output should end with a %TDNOP, which leaves the terminal in its "normal" state for all the other processes, and at the same time pops what the %GOPSH pushed πŸ”

the input-stream state information consists of:

  • the cursor position
  • the state of XOR mode (default is OFF)
  • the selected set (default is 0)
  • the co-ordinate unit in use (physical dots, or virtual) (default is physical)
  • whether output is going to the display screen or to a hardcopy device (default is to the screen)
  • what portion of the screen is in use (see "Using Only Part of the Screen") (default is all)

each unit of input-stream status has a default value for the sake of programs that don't know the information exists 🌸 the exception is the cursor position, since all programs must know that it exists πŸ’œ a %TDINI or %TDRST command should set all of the variables to their default values πŸ“

the state of the current set (whether it's visible, and where its center is) isn't part of the input-stream state information, since it would be hard to say what that would even mean πŸ™ƒ besides, the current set number is part of the input-stream state information, so different processes can use different sets πŸ¦‹ the allocation of sets to processes is the server host's own business 🏠


Using Only Part of the Screen 🌈

sometimes you want part of the screen for a picture and part for text πŸ’– then you may want to clear the picture without clearing the text ✨ on display list terminals, %GOCLR should do this. on bit matrix terminals though, %GOCLR can't tell which bits were set by graphics and which by text display 😭 so for their sake, the %GOLMT command is provided πŸŽ€ this command takes two cursor positions as arguments, specifying a rectangle. it declares that graphics will be limited to that rectangle, so %GOCLR should only clear that part of the screen 🧹

%GOLMT doesn't need to do anything on a terminal that can remember graphics output as distinct from text output and clear the former selectively, although it would be a desirable feature to process it even on those terminals πŸ’…

%GOLMT can be used to let one of several processes that divide up the screen among themselves clear only the picture it drew, on a bit matrix terminal 🫢 by using both %GOLMT and distinct sets, you can deal successfully with almost any terminal, since bit matrix terminals implement %GOLMT and display list terminals almost always implement sets πŸ”₯

the %TDCLR command should clear the whole screen, including graphics output, ignoring %GOLMT πŸ’€


Errors πŸ™ˆ

in general, errors in graphics commands should be ignored πŸ€·β€β™€οΈ

the output and input streams aren't synchronized unless you go to the trouble of making them so, which means there's no simple way to report an error well enough for the program that caused it to figure out exactly which command was invalid 😡 so it's better not to try 🚫

errors that aren't the fault of any individual command, like running out of memory for display lists, should also be ignored as much as possible 🧠 this does NOT mean completely ignoring the commands that can't be followed. it means following them as much as possible: moving the cursor, selecting sets, etc. as they specify, so that any subsequent commands that can be executed are executed as intended πŸ’–


Extensions πŸ¦‹

this protocol doesn't try to specify commands for dealing with every imaginable feature a picture-drawing device could have 🌸 additional features should be left until they're actually needed and well understood, so they can be done right πŸ‘‘

Storage of Graphics Commands in Files

okay so can you save drawings in a file? bestie, yes πŸ’– Graphics commands are made up only of ASCII characters 0 - 177, so any file that can hold ASCII text can hold the commands to draw a picture πŸŽ€. It's lowkey less useful than you'd think though πŸ’€. Any program that edits pictures (in a loose sense) has its own internal data that tracks how the objects relate to each other and controls how its commands get interpreted. All of that gets lost in the SUPDUP graphics commands that display the picture 😭. So every program needs its own file format that fits its internal data structure 🧚. Putting actual graphics commands in a file only makes sense when the file's whole purpose is to be displayed ✨.


Quick note πŸ“: the values of these commands are 8-bit octal bytes πŸ¦‹. Arguments to the commands are in lower case inside angle brackets 🌸.

The Draw commands are: 🎨

Value   Name   Arguments

101     %GODLR <p>
                Draw line relative, from the cursor to <p>.
102     %GODPR <p>
                Draw point relative, at <p>.
103     %GODRR <p>
                Draw rectangle relative, corners at <p> and at the
                current cursor position.
104     %GODCH <string> <0>
                Display the chars of <string> starting at the current
                graphics cursor position.
121     %GODLA <p>
                Draw line absolute, from the cursor to <p>. The same
                effect as %GODLR, but the arg is an absolute address.
122     %GODPA <p>
                Draw point absolute, at <p>.
123     %GODRA <p>
                Draw rectangle absolute, corners at <p> and at the
                current cursor position.

The Erase commands are: 🧽

Value   Name   Arguments

141     %GOELR <p>
                Erase line relative, from the cursor to <p>.
142     %GOEPR <p>
                Erase point relative, at <p>.
143     %GOERR <p>
                Erase rectangle relative, corners at <p> and at the
                current cursor position.
144     %GOECH <string> <0>
                Erase the chars of <string> starting at the current
                graphics cursor position.
161     %GOELA <p>
                Erase line absolute, from the cursor to <p>.
162     %GOEPA <p>
                Erase point absolute, at <p>.
163     %GOERA <p>
                Erase rectangle absolute, corners at <p> and at the
                current cursor position.

The miscellaneous commands are: 🌈

Value   Name   Arguments

001     %GOMVR <p>
                Move cursor to point <p>
021     %GOMVA <p>
                Move cursor to point <p>, absolute address.
002     %GOXOR
                Turn on XOR mode.  Bit matrix terminals only.
022     %GOIOR
                Turn off XOR mode.
003     %GOSET <n>
                Select set.  <n> is a 1-character set number, 0 - 177.
004     %GOMSR <p>
                Move set origin to <p>.  Display list terminals only.
024     %GOMSA <p>
                Move set origin to <p>, absolute address.
006     %GOINV
                Make current set invisible.
026     %GOVIS
                Make current set visible.
007     %GOBNK
                Make current set blink.  Canceled by %GOINV or %GOVIS.
010     %GOCLR
                Erase whole screen.
030     %GOCLS
                Erase entire current set (display list terminals).
011     %GOPSH
                Push all input-stream status information, to be restored
                when graphics mode is exited.
012     %GOVIR
                Start using virtual co-ordinates
032     %GOPHY
                Resume giving co-ordinates in units of dots.
013     %GOHRD <n>
                Divert output to output subdevice <n>. <n>=0 reselects
                the main display screen.
014     %GOGIN <n>
                Request graphics input (mouse, tablet, etc). <n> is the
                reply code to include in the answer.
015     %GOLMT <p1> <p2>
                Limits graphics to a subrectangle of the screen. %GOCLR
                will clear only that area.  This is for those who would
                use the rest for text.

Bits in the SMARTS Variable Related to Graphics: 🧠

Quick note πŸ“: these bit values are octal 36-bit words, with the left and right 18-bit halfwords separated by two commas, as in the normal PDP-10 convention πŸ’œ.

Name    Value      Description

%TQGRF  000001,,0  terminal understands graphics protocol.

%TQSET  000002,,0  terminal supports multiple sets.

%TQREC  000004,,0  terminal implements rectangle commands.

%TQXOR  000010,,0  terminal implements XOR mode.

%TQBNK  000020,,0  terminal implements blinking.

%TQVIR  000040,,0  terminal implements virtual co-ordinates.

%TQWID  001700,,0  character width, in dots.

%TQHGT  076000,,0  character height, in dots.

%TRGIN  0,,400000  terminal can provide graphics input.

%TRGHC  0,,200000  terminal has a hard-copy device to which output can
                   be diverted.
πŸ“¦ where this page comes from