One of the oldest and, perhaps, most used X utilities is the ubiquitous
xterm
program. An indispensible part of the X Window
System
, it is nonetheless showing its age. Due largely to creeping
featurism and the somewhat strenuous process of being ported to a number
of varied and idiosyncratic platforms, the xterm
source has also
reached a state where further extension and maintainance has become
a non-trivial exercise.
In 1990, we faced the choice of either further extending
xterm
or starting over from scratch. After a long evaluation,
we came to the conclusion that further attempts to modify xterm
would benefit neither us or the X community at large.
Combining our altruistic impulses with a genuine need for a VT220
compliant xterm
, we then set out to completely rewrite
xterm
from scratch. Using some of the lessons learned from the
original xterm
(design vs creeping mutation) and taking full
advantage of having a nice toolkit to work with (something, to be fair,
the original designers did not have), we managed to add many new
features without significantly exceeding the size of the old
xterm
program. We were also able to use the toolkit's widget
mechanism almost exclusively as a framework, a major win in and of
itself.
A secondary, but by no means trivial, design goal was to release not so
much "an xterm
done right" but also a set of tools with which
users could go well beyond what is initially offered here. Given that
the needs of users are many and varied, we focused more on providing a
logical and portable framework than on providing a single-purpose canned
solution. Using this system, users can easily add custom terminal
emulators, display canvases, menus and other 'decorations' using
either an Athena
or Motif
look and feel as desired.
Application writers will also now find it quite simple to use a
slave xterm
from a larger application; one simply creates a
couple of widgets and takes advantage of the hooks provided for getting
at the incoming and outgoing data streams. A number of programmers
accustomed to fighting with the old xterm
's slave mode
should now find life much easier. Like the X group
itself, we
have attempted to provide mechanism, not policy, wherever possible.
The end result, after almost 2 man years of work, is something both
simpler and more complicated than the original xterm
. As we were
stuck for a really clever name, we decided to call it emu, which
is short for emu-lator. Whether or not the code makes one think of
large, hairy, flightless birds is purely a subjective issue and we
refuse to touch it.
Being the kind souls that we are, we present this code free of charge to the X user community for their collective enjoyment and/or amusement. Caveat Emptor.
In this manual, a number of special terms will be used that should be understood before going any further. Some of them may not make much sense initially, but it will at least be clear what the various buzzwords and abbreviations mean (each abbreviation will also be expanded the first time it's used, so it's not necessary to memorize everything at this juncture).
auxTrans Entries
.
canvas
widget being used for character or graphics I/O by the
term widget. See section The Canvas Widget.
term
widget and its canvas
to pass requests and ancillary
information back and forth. See section The ComBlock Interface.
Emu
Client.
auxTrans
entries, ROPs and
input sequences that describe a complete terminal emulation.
termCanvas
widget to map ASCII
characters to actual font glyphs. @xref{OP_CHANGE_FLUT}.
canvas
and term
widgets as well as the menu package. IOP
's are the workhorses of
the emu
package and, aggregated, form a small but functional
programming language. See section The IOP Language.
hard
parser is one compiled directly into the
term
widget for a specific terminal type. A soft
parser is
defined wholly in the resource database and can be modified without
having to recompile anything.
canvas
action to be
performed. A request may or may not take parameters, which are then
found in the comblock
.
requests
. Sequence
Lists are only encountered as parts of See section Input Sequences and
ROPs
.
request list
followed by a bracket delimited string of tokens.
Each token describes either a character to be expected as input or an
IOP
to be performed along the way. Refer to See section Input Sequences for
more information, as this is somewhat complicated.
input sequences
that, as a unit, form part of an
emulation.
term
widget which wraps around the canvas
and any ancillary widgets.
Copyright (C) 1990 PCS Computer Systeme, GmbH Pfaelzer-Wald-Str 36, D-8000 Munich 90, West Germany. Copyright (C) 1994 Jordan K. Hubbard and Michael W. Elbel Everyone is permitted to copy, modify and distribute copies of this license document, provided that the section "Copyright Information" is included exactly as in the original. All Rights Reserved Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. We would also like to request that any enhancements to this software, or its documentation, be sent back to one of the authors for inclusion in subsequent releases. While you are certainly not in any way legally required to do so, such cooperation will result in a better emu for all concerned and is thus kindly requested. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL PCS, THE AUTHORS, OR THEIR HOUSEPETS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Emu
System.
What has been referred to so far as The Emu System is really
nothing more than two widgets and a sample client (see section Using The Emu
Client).
What makes it a system is more the way it all fits together and
provides a framework for future expansion.
To better understand the system, it is probably a good idea at this point to break it down into its component parts and attempt to describe, briefly, what each part does. What follows should only be considered a brief overview since each topic is described in much more detail in its own section of this manual.
The first item of interest is the term
widget, which basically
manages all aspects of the operating system dependent interface: PTY
allocation, process management, and terminal mode manipulation. To put
it another way, the term
widget can be thought of as analgous to
the communications hardware of an actual terminal, minus the screen and
keyboard.
The term
widget also handles all parsing of special input
sequences (more commonly referred to as 'escape sequences) from the
process, turning them into operation codes (see section Input Sequences)
that the canvas
understands.
Finally, the term
widget provides a number of features and hooks
for applications that want to do fancier I/O control and/or additional
widget management. While the term
widget typically manages just
one canvas
child, it can also manage other children (even
additional canvases) as necessary (see section The Term Widget). This is
typically used to implement scrollbars and menubars.
The second (and complimentary) widget of interest is the canvas
.
The canvas
is in charge of all 'screen' output, as well as the
translation of keycode
sequences into ascii characters and mouse
controlled cut and paste operations. Using the analogy of a physical
terminal again, the canvas
widget would be the CRT and keyboard
part.
The canvas
compliments the term
widget by supporting a set
of generic operations, such as moving the cursor, inserting or deleting
text, etc, which the term
widget then 'requests' it to perform as
its recognises various input sequences in its parser
description. Likewise, the canvas
relies on the term
widget to communicate with the process when dispatching of I/O from the
keyboard or cut buffer/selection mechanism is required.
As stated earlier, the term 'canvas' actually refers to any widget that
can speak the ComBlock protocol with the term
widget, not just
the particular widget we supply. In theory, any number of canvas
widgets can, and may, exist for doing various types of display I/O. At
the time of this writing the one canvas
, called the
termCanvas
(see section The Canvas Widget), exists for doing
reasonably sophisticated text display (basic text + VT220
attributes). In time, there will probably be a number of additional
canvases that support generic graphics operations, auxilliary input
devices, etc. A client application will then have the option of creating
any number of term
/canvas
pairs (or one term
with
multiple canvases
) to suit a particular need.
The final part of the system is the emu
sample client, which will
typically be all the average user sees or cares about. Most of the
client is rather simple. It first creates a shell widget to interact
with the window manager, as is normal, and provides a number of command
line options to get directly at various term
and
termCanvas
resources. It then creates a term
widget as its
child and, in turn, a termCanvas
widget and several optional
control widgets under it. As previously stated, the term
widget
does not really care about any widgets other than its canvas
,
though it will readily manage them. This feature is used by the
emu
client to optionally create a scrollbar and menu bar. Since
the term
widget does not know or care about either of these, the
management of their size and position is left to the emu
client
itself. This is handled through a callback provided by the
term
widget which is called whenever a Resize,
GeometryRequest
or Realize
occurs. The emu
client
then adjusts things accordingly. The scrollbar callbacks are likewise
handled by the client with the scrollbar motion being translated into
canvas
scroll requests.
While this approach may seem to put an undue burden on the client
program, it has helped to keep the term
and termCanvas
widgets free of application specific code, and makes the decoupling
of GUI elements and terminal emulation possible.
Also part of the emu
client is the configurable menu
package. Through the toolkit translation mechanism, any number of menus
can be automatically attached to the term
widget without it
having to know or care. Unlike the menu code in the old xterm
program, a menu's actions (as well as appearance) are completely user
configurable. See section Menu Configuration.
Summary:
The general philosophy of the emu
system was to provide two, very
general purpose, communication and text manipulation widgets with ample
room for future expansion. All 'decoratations' like scrollbars, menu
bars and menus are implemented on the client side through hooks made
available by the widgets for that purpose. This approach also allowed
the emu
client to provide either Athena or Motif
widget look-and-feel at very little added cost.
Emu
Client
The emu
client is used in much the same way as the old
xterm
program was, though there are a few new command line
options. Since the emu
system also relies much more heavily on
resources than the old xterm
, installation of the application
defaults is almost mandatory before emu
will work properly.
Without its resources, the typical emu client does not even know how to
emulate a terminal!
Virtually all aspects of the emu
system are user configurable
with almost nothing being assumed about specific terminal types,
keyboards, output fonts or menus. Of course, should the standard
VT220 Emulation
included with emu
be sufficient for your
needs, you won't need to worry about any of this, but should you need
it, the ability to customize just about everything is there.
Writing a new (or modifying an existing) Emulation
generally consists
of 3 basic steps:
canvas
supports and begin mapping out the correspondence between terminal features and
canvas
request types.
IOP
code. Start with a very basic subset
of the terminal's escape sequences and work your way up, testing your
emulation carefully as you go along. It's easy to introduce grammer
ambiguities by injudicious use of %s
and %d
sequences
(see section The IOP Language), so new sequences should be added with care.
Emu's menus are very straight-forward, the menu module (as with the rest of emu) providing a simple mechanism without trying to determine usage policy. The module provides facilities for creating, managing, popping-up, and attaching policy (functionality) to menus. It does not dictate how many menus or menu items exist, what is in each menu, what functions it performs, or how it is popped up. This is all user configurable, and is specified via the resource manager. Menus are, therefore, fully extensible and configurable.
The emu menus are configurable through the resource file. All Athena Form, MenuButton (both for menu bar), SimpleMenu, Sme, SmeBSB, and SmeLine, or Motif XmRowColumn, XmCascadeButton (both for menu bar), XmPopupMenu, XmPushButtonGadget, XmLabelGadget, and XmSeparatorGadget resources are available for customization. Please see the appropriate Intrinsics, Athena or Motif documentaion for further clarification of these resources.
In addition to these standard widget resources, the number of menus, the number of menu items, and the position of menu item separators are settable via resources. For each menu item it is also possible to specify what functionality (action) lies behind it.
Menus are configured via the screen translations by specifying a menu name to pop-up and a key/button sequence indicating how to activate it. For example, the default configuration could look like:
*term.screen.Translations: #override\n\ Ctrl ~Shift<Btn1Down>: XpEmuPopupMenu(menu1) \n\ Ctrl ~Shift<Btn2Down>: XpEmuPopupMenu(menu2, 302) \n\ Ctrl ~Shift<Btn3Down>: XpEmuPopupMenu(menu3, 303) \n\ Ctrl Shift<Btn3Down>: XpEmuPopupMenu(menu4) \n
These translations show that pressing 'Ctrl', but not 'Shift', Pops-Up A Menu Called 'Menu1'. The name 'menu1' actually becomes the name of the menu widget when it is created. It is necessary to have this name since it is used further for qualifying resources. The second and third menus are specified in a similar fashion but by pressing (mouse) button2 and button3, respectively. The fourth menu is also activated by pressing button3, but this time the 'Ctrl' And 'Shift' keys must be pressed.
'XpEmuPopupMenu' expects at least a menu name as a parameter. If other actions (ROPs) are desired on a per menu, not menu item, basis then additional parameters (ROP numbers) may be declared after the menu name. These actions are performed each time just before the menu is popped-up. The second and third menu declarations illustrates such a situation.
The following resources control the behavior of individual menu items.
*menu1.numberItems: 8
This resource must be given.
It is not necessary to specify menu item labels; the toolkit default is to use the menu item widget name, which is, e.g., 'item1'. The resource file, however, does specify item label defaults for the declared menus. The 'menu1' defaults could be:
*menu1*item1.label: Redraw *menu1*item2.label: Reset all *menu1*item3.label: Set TTY sane *menu1*item4.label: Set TTY origin *menu1*item5.label: Send INT signal *menu1*item6.label: Send HUP signal *menu1*item7.label: Send TERM signal *menu1*item8.label: Send KILL signal
In order that menu items within a menu may be functionally (or otherwise) grouped, line separators may be specified in the resource file. Items after which to put line separators are indicated as such:
*menu1.itemLines: item1 item4
In most cases, it is necessary that for each menu item an action is specified. The action resource is, in effect, the function that is invoked when the menu item is selected. This resource may either be a number (ROP action) or a string (emulation action). For example, 'menu1"s items could have ROP action numbers as given below:
*menu1*item1.action: 310 *menu1*item2.action: 311 *menu1*item3.action: 312 *menu1*item4.action: 313 *menu1*item5.action: 314 *menu1*item6.action: 315 *menu1*item8.action: 316 *menu1*item9.action: 317 *menu1*item10.action: 318 *menu1*item11.action: 319
An example of emulation action specification is shown by the possible default 'menu4' action resources:
*menu4*item1.action: tty *menu4*item2.action: emu
Code
Menus are created at that moment the user first clicks a particular button.
The menu package has three explicit contact points to the rest of the emu program. The first is for initialization, which simply registers the exportable actions (XpEmuPopupMenu) with the translation manager. This initialization routine (XpEmuInitializeMenus) is the only routine that must be called by the emu client program.
The second point of contact is for the dispatching of actions (XpEmuMenuDispatch) when menu items are clicked. This is done indirectly via the single menu item callback (DoInteract). The callback passes the respective menu item ROP action number or emulation action string, specified in the resource file, to the emu program. If no ROP number or emulation name exists for the selected menu item, the widget label, or lastly, the widget name are used. This indicates the selection is an emulation.
The third contact point is the complement of the second. It is for the emu program to tell the menu module to perform some action (XpEmuMenuAction). The actions the menu package can perform are limited to activating/deactivating (sensitizing) and marking/unmarking menu items.
The making of a menu consists of detecting the number of items and possible separator lines via the resource manager, and then creating the menu with the single menu callback. No local menu call data exists; all necessary menu information is stored in and derived from the resource manager or the toolkit. In this way, no local data structures are necessary, no local memory is allocated, and therefore, the number of menus and the number of menu items are dynamic and may be of any size. Also, by using a single callback with user specifiable ROP actions, functionality is attached at menu realization time by the resource manager.
The term
Widget is used to encapsulate three basic things in
the general task of emulating a terminal:
PTY
s and muck around with different types of terminal
information structures on the many and varied incarnations of
UNIX
(tm).
terminal emulator
from the
user's perspective as it is a composite
widget and can manage
multiple children. Typically, this will be a canvas
and perhaps
a scrollbar and some menus.
term
widget also provides something fairly unique
in this regard in that it supports the specification of both the typical
hard wired parsers (as in the current xterm
) and a more
dynamic specification process through a small, fast interpreted language
called IOP
(see section The IOP Language). This allows you to
configure emu
to emulate entirely arbirary terminals (within the
basic capabilities of the display canvas
) from simple resource
manager descriptions, and to change emulations on the fly.
The following X-Toolkit resources are provided by the Term widget.
command
commandArgs
command
.
termType
xterm
.
canvas
canvas
widget with the terminal emulator.
Usually done at startup time.
iopRequestProc
IOP
call C
is invoked. See IOP Glossary
for more details.
layoutProc
canvas
s that want to toggle between 80 and 132 columns, for
example.
processDeath
readSize
inParser
outParser
utmpInhibit
loginShell
rops
The task of allocation ptys, getting and setting tty values and dealing
with things like utmp
entries are all encapsulated by a set of
standardized routines for each OS type in the `term/sysdep'
directory. The routines are also layered in such a way as to support
the specification of new operating system definitions in terms of
existing ones, and for this no documentation will suffice so well as
simply looking at the existing examples. A fairly rich set exists for
System V
, BSD
and Linux
architectures, and in all
likelyhood it should require little more that adapting an existing set
of descriptions when porting to a new machine at this point.
By convention, the system dependent routines should be implemented in two files under the `term/sysdep' directory:
These two files are then selectively pulled in with #ifdef
s in
the files `term/process.c' and `term/tty.c', respectively.
Look in these two files to see how existing systems are configured
before doing anything else.
The following routines comprise the system encapsulation API as implented in proc_*.i and tty_*.i:
These should be implemented in proc_<new_os_type>.i:
void process_init(TermWidget w)
int process_wait(int *failcode)
And these should be implemented in tty_<new_os_type>.i:
NOTE: This section currently unimplemented due to lack of time. Please
see the sample client code that deals with scrollbars for a pretty
reasonable example of how to do some of this. The canvas creation code
also deals with the term
widgets' handling of children.
The IOP
is the low-level workhorse of the emu
package,
doing everything from on-the-fly coordinate transformation in
Input Sequences
to setting flags, sending signals or invoking
processes from menus! It is used in the consruction of so-called
soft parsers, and is described in the overview.
IOP
code looks something like the Forth
language, on which
it is in fact loosely based. Like Forth
, IOPs have a stack on
which they can store and manipulate data. Also provided are 128 typed
registers for general storage of data and inter-widget communication
(see section The ComBlock Interface).
While it may appear at first glance that IOP
represents serious
engineering over-kill, let us assure you that each and every feature
of IOP
was added only after identifying a clear need for it.
The examples given below only scratch the surface of what a more
advanced, real-life terminal might desire in the way of emulation and,
if you look at the `tdesc/*' examples, you'll soon see that
emulating terminals such as the vt220
requires a fairly
sophisticated array of tools!
IOPs
can be (and usually are) imbeded in ROP
or Input
sequences by using the special Meta
characters % and/or {}
characters.
For example, take the following IOP sequence:
%ga%1%+%2%*%pa
While this may look like a line of tty noise to the uninitiated user,
it's actually quite simple. The first IOP
is %ga, which
means `Get Register A And Push It On The Stack.' The next
IOP
, %1 simply pushes a literal integer `1' onto the stack.
The %+ IOP
pops off two stack items (in this case, the
contents of register `a' and 1) and adds them together, pushing the
result onto the stack. Now %2 is pushed and %* pops
it, and the earlier result from %+, multiplying them together and
pushing the result. Finally, %pa 'Pops the stack into
register a' and we're done.
Another way of specifying IOP
sequences is to use the `%{}'
delimiters, which mean 'treat all characters in the following sequence
as IOP
characters. The above sequence could thus also be more
easily expressed as:
%{ga 1 + 2 * pa}
The whitespace between IOP
characters is not generally required
(thus you could also say: %{ga1+2*pa}), but serves to make the code more
readable and is encouraged.
String, character and numeric constants be pushed on the stack as `pseudo IOPS' in the following manner:
nnn
nnn
is one or more decimal digits.
'n'
n
is a single character or character specifier (e.g. \nnn,
^n, etc).
'n..n'
n..n
is one or more ascii characters. As with character
constants, the standard `C style' character escapes are supported.
The following IOP
characters are supported by emu
:
+
-
*
/
&
^
|
~
p`a'
g`a'
G
P
C
canvas
.
c`a'
d`a'
s`a'
ba
bs
@
X
D
S
?
:
;
=
>
<
A
!
O
f
j`a'
L
), where `a' is the single character
label in question.
L`a'
j
).
R
I
l
t
i
$
Tg
Ti
emu
started up.
Td
Ts
U
XpNiopRequestProc
in the
term
widget.
The basic premise behind parsing escape sequences (or input
sequences
) in the term
widget is that for any given terminal
escape sequence you want to essentially perform one or more operations
on the canvas
, and that it should be fairly easy to express a
complete emulation as the set of all actions to be performed when a any
conceivable sequence comes in.
For example, let's say we want to write a very simple little terminal emulation for a terminal that only has escape sequences for moving the cursor up, down, left right and to any arbitrary position on the screen. Let's also assume that such a terminal can ring its bell and handles newlines and carriage returns correctly. So, what might the input sequences for such a terminal look like?
First, let's define our hypothetical terminal's escape sequence set (as would typically be found in its documentation) so we can know just what it is we're trying to emulate:
<ESC>J - move cursor down <ESC>K - move cursor up <ESC>H - move cursor left <ESC>L - move cursor right <ESC>=xy - where x and y are ascii characters depicting - a 255x255 screen space. ^G - ring the bell ^M - move to beginning of line ^J - move to next line
Ok, so that's our goal. Now we need to describe this set of sequences
to emu
in a way it will understand, and in terms of the basic
operations supported by its canvas. Emu
expects such information
in the form of an X-Toolkit
resource in the following format:
Emu.term.<term name>-ops: SEQUENCE [.. SEQUENCE ] Where a SEQUENCE consists of: OP-CODE[,OP-CODE,..] <input-sequence>
A complete emulation description is then essentially nothing more than a
list of SEQUENCE
s, each consiting of one or more OP-CODE
s
and an input
description.
Whenever a given input-sequence
in the emulation description is
seen, each OP-CODE
, or Operation Code, in the list is sent to the
canvas
following the parsing and execution of any IOP
instructions imbeded in the input-sequence
itself (see below).
The OP-CODES
are generally specified using macros which represent
the unique operation code numbers in the canvas
, which if you
look in `include/xt_ops.h' you can see the complete set of. Due to
the way that xrdb
works, it is possible to push the emulation
descriptions through the C pre-processor first for substitution and thus
gain this level of canvas
opcode/description independance.
Sound confusing? It is, at first, so let's just cut to the chase and show you what the description would look like (taken in the same order) in the resource database and describe it in more detail afterwards:
*term.funkyTerm-ops: \ OP_MOVE_REL_ROW <\EJ%1%py> \ OP_MOVE_REL_ROW <\EK%-1%py> \ OP_MOVE_REL_COLUMN <\EH%-1%px> \ OP_MOVE_REL_COLUMN <\EL%1%px> \ OP_MOVE_ABS <\E=%c%px%c%py> \ OP_RING_BELL <^G> \ OP_MOVE_ABS_COLUMN <%0%px\r> \ OP_MOVE_REL_ROW <%1%py\n>
To truly understand this you should also read the See section The IOP Language chapter, but we can (hopefully) describe this simple example in enough detail to make it obvious enough on its own here.
Starting with the first line for handling the `<ESC>J' sequence, we
see the opcode OP_MOVE_REL_ROW, which means, not surprisingly, "move the
cursor to a relative position in the current row". We also see the
sequence \EJ (ESCAPE-J) followed by the IOP
commands
`%1', which pushes the literal 1 onto stack, and `%py', which
pops the stack and stores it into register y
.
Looking briefly into the See section Canvas Functions section for a moment,
we see that OP_MOVE_REL_ROW takes its argument in register y
and
so this all makes some sense - we're asking the canvas
to move
the cursor down one relative column position. In general, the
convention is that negative arguments move the cursor left or up
(depending on the operation) and positive arguments move the cursor
right or down. In this context, the second sequence for ESCAPE-K makes
sense, as do the next two (though we notice that the OP_MOVE_REL_COLUMN
functions take their arguments in register x
instead of y
,
this is just convention).
The OP_MOVE_ABS_COLUMN is slightly more complex in that we see the
unfamiliar IOP
command `%c' invoked, but this is pretty
simple as well - it just matches the next character in the input stream,
which is then pushed unchanged as an integer value into register
x
(the second character going into y
).
The final 3 calls are simple indeed - ring the bell on a ^G (Control-G), move the cursor to the beginning of the current line on ^M (carriage return) and move down one line on ^J (newline). Voila! We've just created a terminal emulation!
Needless to say, actual terminals, especially fairly intelligent ones, rarely make things this easy and you've considerably more reading ahead of you if you truly wish to create one from scratch! It is suggested that you start with See section The IOP Language and then See section Canvas Functions to gain a good understanding of the underlying mechanisms.
Then take a look at the vt220
emulation in the `tdesc/'
directory for real and fairly complex examples. It is possible to have
an amazing degree of `execution' take place in the input-sequence
alone due to the ability to imbed IOP
code in it, and only a real
example will suffice in showing just how far you can go with it. It
helps to print the See section IOP Glossary. out and stick it up next to your
terminal as well!
ROP
s, or Reverse OPerations, are simply special instances of
IOP
code that don't correspond to any actual Input
Sequence
. Rather, they're used by menus, the canvas
at
initialization time (the reserved ROP
ROP_INIT_CANVAS
is
always called when emu
first starts up and is used to put the
canvas
into a known state) and by emulations that wish to
output data to the tty, such as for terminal ID or "report
current position" sequences.
Because ROP
s are more intended for output, rather that input
operations, they can in fact cause certain primitives in the IOP
language to behave somewhat differently. Such differences, when they
occur, are noted in the relevant portions of the See section IOP Glossary..
Rather than give many examples of ROP
usage here, it will be left
to the user to examine some of the existing examples since, with the
possible exception of ROP_INIT_CANVAS
, most uses of ROP
calls are only used in special-case situations where the user is already
likely to know exactly what they're doing. Suffice to say that
ROP
s are designed to enable the user to create arbirary
IOP
"routines" that can be called at any time and are not tied to
the tty input state machine that governs when See section Input Sequences are
invoked.
The TermCanvas widget
(referred to hereafter as the
canvas
) is the portion of emu that provides the actual screen
output and keyboard handling for emu.
The canvas
manages a virtual screen
(referred to hereafter
as the screen
) of a configurable size, plus an area where text
which scrolls off the top of the screen is stored (the save area
).
The canvas
also supplies a text cursor (the cursor
) of
configurable size and blink rate.
Text can be exchanged with other clients via the Toolkit's Selection
Mechanism
.
The following X-Toolkit resources are provided by the canvas
lines
columns
font
XtDefaultFont
(fixed
).
boldFont
font
.
dWideFont
font
.
dWHighFont
font
.
dWideBFont
font
.
dWHighBFont
font
.
underlineWidth
foreground
XtDefaultForeground
(black
).
background
XtDefaultBackground
(white
).
cursorFg
XtDefaultForeground
.
cursorBg
XtDefaultBackground
.
cursorHeight
cursorWidth
cursorBlinking
True
.
blinkInterval
blinkWOFocus
False
.
textBlinkInterval
wrapAround
True
.
insertMode
False
.
bellVolume
defTabWidth
termType
setSize
output
notifyFirstMap
pointerShape
xterm
saveLines
save area
in lines. The default is 64 or lines
,
whichever is larger.
multiClickTime
jumpScrollLines
The canvas
supports the following toolkit actions:
focus-in()
focus-out()
key-input([<string>])
reverse parser
. With an argument key-input
uses
the given string instead.
select-start()
select-extend()
select-end()
insert-selection()
reverse parser
call-parser(<opcode>, <buffer>, [<register>, <value>], ...)
reverse parser
with the specified opcode and buffer
(use ""
if the buffer is not needed).
If register/value pairs are provided it sets the registers accordingly.
call-canvas(<opcode>, <buffer>, [<register>, <value>], ...)
opcode
in the canvas. The buffer and any
supplied registers are set accordingly.
canvas
communicates with the outside world (that is to say,
the term
widget (See section The Term Widget)) using a mechanism
called a comblock
. The comblock
provides a general
purpose communications buffer of sorts, containing space not only for
characters to be displayed (the buffer
) but also a set of
registers for placing the parameters for various higher level requests
into. See the See section Canvas Functions section for more details on
which request types, and the registers they use, are supported (see also
See section The ComBlock Interface).
Any number of canvas instances can be created by an application, and it
is theoretically possible for a term
Widget to multiplex
communications between multiple instances, all the necessary frame-work
is in place for such things though the default emu
client assumes
only one canvas and always communicates with the first one created.
The following character attributes are defined in `emu/include/xt_ops.h'
and can be or
ed together in any combination to change the way
characters are displayed.
ATT_NONE
screen.font
ATT_BOLD
screen.boldFont
ATT_UNDERL
screen.underlineWidth
ATT_BLINK
textBlinkInterval
ATT_REVERSE
If the canvas is compiled with support for double sized fonts, the following line attributes are available and can be used to change the way whole lines are displayed. Only one of the attributes may be specified at a time.
LINE_D_NORMAL
LINE_D_WIDE
screen.DWideFont
or screen.DWideBFont
.
LINE_D_UPPER
screen.DWHighFont
or
screen.DWHighBFont
. Characters will be drawn and clipped so
that the upper half of a double wide, double high font is visible.
LINE_D_LOWER
screen.DWHighFont
or
screen.DWHighBFont
. Characters will be drawn and clipped so
that the lower half of a double wide, double high font is visible.
To allow for text color attributes, the canvas supports one of 16
foreground and background colors for each character. The mapping between
logical character colors and actual X colors is done via a color
index table (CIT)
, the entries in which can be set through the canvas
function @xref{OP_SET_CIT_CELL}. The selection of logical foreground
and background colors on the canvas is done through the functions
@xref{OP_CHANGE_FG_COLOR} and @xref{OP_CHANGE_BG_COLOR}.
The following functions are recognized by the canvas
, the
constants for which are defined in `emu/include/xt_ops.h'. Data is
exchanged via the given comblock
(see section The ComBlock Interface).
OP_INSERT buffer = String to insert
The text in the comblock
's buffer is inserted at the current
cursor position with the current attributes. If insert mode
is
on, characters to the right, including the cursor position are shifted
to the right. If line-wrap
mode is on, characters beyond the
right border of the screen are wrapped to the next line.
OP_MOVE_ABS x = column, y = line
Move the cursor to the absolute row and column position given in register
x and y. If the relative-positioning
flag is set, the
start line of the scroll-region is added to y and the cursor is restricted
to the scroll region. With the flag off the cursor is able to leave the
scroll region.
OP_MOVE_REL x = delta column, y = delta line
Move the cursor relative to the current position by the amount of rows and columns given in register x and y. The cursor is restricted to the scroll region.
OP_MOVE_ABS_COLUMN x = column
Move the cursor to the absolute column given in register x on the current line. OP_MOVE_ABS_ROW y = line
Move the cursor to the absolute row given in register y in the current
column. Rules for positioning relative to the scroll region and leaving the
region apply as in OP_MOVE_ABS
.
OP_MOVE_REL_COLUMN x = delta column
Move the cursor relative to the current position in the current row. The delta is given in register x.
OP_MOVE_REL_ROW y = delta line
Move the cursor relative to the current position in the current column. The delta is given in register y. The cursor is restricted to the scroll region.
OP_MOVE_REL_ROW_SCROLLED y = delta line
Move the cursor relative to the current postion in the current column. The delta is given in register y. If the cursor would leave the scroll region, the text inside the region is scrolled accordingly.
OP_INSERT_MODE (no args)
Turn insert mode on.
OP_OVERWRITE_MODE (no args)
Turn overwrite mode on - insert mode off.
OP_DELETE_CHARS a = number of characters to delete
Delete characters in the current line starting at the cursor position. The number of characters to be deleted is specified in register a. Characters to the right of the deleted ones are shifted to the left.
OP_DELETE_TO_EOL (no args)
Delete characters in the current line from the cursor position up to the end of the line.
OP_DELETE_LINES a = number of lines to delete
Deletes the number of lines specified in register a, and scrolls the lines under the deleted area up. The cursor is set to the left border of the screen. The deleted area starts at the current line. This action is restricted to the scrolling region.
OP_DELETE_TO_EOSCR (no args)
Deletes all characters after the cursor till the end of the screen.
OP_ERASE_CHARS a = number of characters to erase
Erase characters in the current line starting at the cursor position. The number of characters to be erased is specified in register a.
OP_ERASE_LINE_LEFT (no args)
Erases characters from the beginning of the current line to and including the cursor position. OP_ERASE_LINES a = number of lines to erase
Erases the number of lines specified in register a.
OP_ERASE_FROM_TOSCR (no args)
Erases characters from the top of the screen up to and including the cursor position.
OP_CLEAR_SCREEN (no args)
Clears the whole screen.
OP_INSERT_LINES a = number of lines to insert
Inserts the number of lines specified in register a at the current line. Existing lines are scrolled down accordingly. The cursor is set to the left border of the screen. This operation is restricted to the scroll region.
OP_SET_SCROLL_REGION a = start line, b = end line
Sets the scoll region to start at the line specified in register a, and end at the line specified in register b.
OP_RING_BELL (no args)
Calls 'XBell' with the volume specified in the resource bellVolume (default is 100%).
OP_HOR_TAB (no args)
Move the cursor to the next right tab in the current line. If there is no tab to the right of the cursor, the cursor is set to the right border of the screen.
OP_SET_TAB_CUR_COL (no args)
Sets a tab stop at the current column.
OP_SET_TAB_COL x = column
Sets a tab stop at the column specified in register x.
OP_SET_TABS_EQ_WIDTH a = distance
Sets up tabs with a distance specified in register a.
OP_CLEAR_TAB_CUR_COL (no args)
Clears a tab stop at the current column.
OP_CLEAR_TAB_COL x = column
Clears a tab stop at the column specified in register x.
OP_CLEAR_ALL_TABS (no args)
Clears all tabs.
OP_SET_ATTRIBUTE a = attribute(s)
Sets the attributes given in register a (more than one can be
or
ed together).
OP_CLEAR_ATTRIBUTE b = attribute(s)
Clears the attributes given in register b (more than one can be
or
ed together).
OP_OVERRIDE_TRANSLATIONS a = name of the translation
Retrieves the resource <term-type>-auxTrans-<name>
and overrides the
current translations with the resource. <name> is given in register a.
OP_CHANGE_FLUT a = name of the resource, b = offset to use
Retrieves the resource <term-type>-flut-<name>
and changes the flut
according to the info found there. <name> is given in register a,
register b specifies the integer offset in the flut to use.
OP_CANVAS_SIZE (return) x = columns, (return) y = lines
The canvas returns its size in registers x and y.
OP_CANVAS_CURSOR_POS (return) x = column, (return) y = line
The canvas returns the current cursor position in registers x and y.
OP_CANVAS_ATTRIBS (return) a = attributes
The canvas returns the current attribues in register a (all or
ed
together).
OP_CANVAS_SCROLL_REGION return a = start line, b = end line
Return start and end line of the scroll region in registers a and b.
OP_CANVAS_WRAP_MODE return a = boolean flag
Report in register a whether line wrap mode is on.
OP_CANVAS_REVERSE_MODE return a = boolean flag
Report in register a whether the canvas screen is in reverse video.
OP_CANVAS_CURSOR_ON return a = boolean flag
Report in register a whether the cursor is visible.
OP_CANVAS_CURSOR_BLINKING return a = boolean flag
Report in register a whether the cursor is blinking.
OP_CANVAS_CURSOR_SIZE return x = width, y = height in pixels
Return the current cursor size in registers x and y.
OP_REDRAW_SCREEN no args
Clears and redraws the entire screen.
OP_CHANGE_FONTS a = normal font, b = bold font or empty string
Changes the current fonts to the names given in registers a (normal font), and b (bold font). The fonts only get installed when they are both of the same size. If the bold font is an empty string, the canvas tries to derive a bold font from the normal one. If double sized font support is compiled into the canvas, it automatically generates names for the double sized fonts as well.
If an empty string is passed for the normal font, the canvas uses the initial one (e.g. specified in the resource database).
OP_SCROLL_SCREEN_ABSOLUTE a = save line to scroll to
Viewing the save area and the actual screen as continuous lines with line 0 being the beginning of the actual screen, numbering the save area negative upwards, this function places line -a at the top of the screen (0 thus re- presenting the normal case with only the actual screen showing).
OP_SCROLL_SCREEN_RELATIVE a = number of lines to scroll
Scrolls save area and actual screen a lines down (negative a results in scrolling upwards).
OP_CURSOR_OFF (no args)
Switches cursor off.
OP_CURSOR_ON (no args)
Switches cursor on.
OP_SET_SCREEN_SIZE (no args)
Sets the screen size to the values given in registers x and y.
OP_WRAP_AROUND (no args)
Turns line wrap around on.
OP_DONT_WRAP (no args)
Turns line wrap around off.
OP_CURSOR_POS_REL_TO_SCR_REG (no args)
Makes cursor positioning relative to the scroll region.
OP_CURSOR_POS_ABSOLUTE (no args)
Makes cursor positioning absolute.
OP_REVERSE_VIDEO (no args)
Puts the canvas in reverse video.
OP_NORMAL_VIDEO (no args)
Puts the canvas in normal video.
OP_SAVE_FLUT (no args)
Saves the current flut. Only one flut can be saved at a time, subsequent calls will overwrite the previous saved flut.
OP_RESTORE_FLUT (no args)
Restores flut to the last saved value.
OP_SET_CURSOR_BLINK a = boolean flag
Depending on the value in register a, cursor blinking gets turned on or off.
OP_SET_CURSOR_SIZE x = width, y = height in pixels
Sets the cursor size to the values in registers x and y.
OP_CHANGE_FG_COLOR a = number of the color to use
Changes the foreground color in which text is being drawn to the one specified in register a.
OP_CHANGE_BG_COLOR b = number of the color to use
Changes the background color with which text is being drawn to the one specified in register b.
OP_SET_CIT_CELL a = cell number, b = fg color, c = bg color
Sets the fore- and background colors of the CIT cell specified in register a to the values specified in registers b and c.
OP_CANVAS_DISPLAY_CELLS return a = number of color cells
Returns the number of color cells that the screen on which the canvas is running supports. This can be used to determine whether a emulation should use color or not (B/W screen vs. color screen).
OP_SET_JUMP_SCROLL a = number of lines to jump scroll
Sets the maximum number of lines the canvas should scroll at a time to the value in register a.
OP_GET_JUMP_SCROLL return a = current setting of js lines
Returns the number of lines the canvas will maximally scroll at a time in register b.
OP_SET_LINE_ATTRIBUTES a = attribute
Sets the attributes of the current line to the one given in register a. Right now it is not allowed to set more than one attribute at a time.
The various parts of emu
use the comblock
interface to
exchange information and as such it's important for at least the emu
programmer (or widget user) to understand something of how it works.
A comblock is a structure containing some buffer storage and a set of
registers
in which operands for canvas actions
are passed
(see section Canvas Functions). Though the comblock
should really
be regarded as an opaque structure and only manipulated through its
accessor functions and macros (see `include/common.h'), it helps for the
purpose of this document to describe its internals:
typedef struct _comblock { int opcode; /* operation code */ unsigned char buffer[BUF_SZ]; /* data buffer */ int nbytes; /* data buffer length */ Register regs[CB_NREGS]; /* register array */ } ComBlock, *ComBlockPtr;
Registers are defined as:
typedef struct _reg { int type; /* data type */ Generic data; /* data ptr */ } Register;
`Register.type' tells what type of data (int, string, float, etc) is contained in the register.
`Register.data' is a pointer to the data, or the data itself if a it can be contained in the storage provided by a pointer cell (usually 32 bits).
`ComBlock.opcode' identifies a specific operation to be performed.
`ComBlock.buffer' is used to transmit large amounts of data,
generally I/O between the term
and canvas
widgets.
`ComBlock.nbytes' tells how many bytes are in the `buffer'.
`ComBlock.regs' contains a set of general purpose registers.
Each register can hold several types of data:
The comblock is allocated in the term
widget and uses it to
transfer data between the parser
, reverse parser
and
canvas
sections. It is also used by the parser
and
reverse parser
for private storage.
The current policy is to use registers `a' to `d' and `x'
to `z' to pass arguments and return values. Registers with lower
case letter or numberic names should only be used for private data of
the parser
, upper case letters should only be used by the
reverse parser
. These conventions help to keep things straight.
Finally, when using the comblock
, the following accessor
functions/macros should be used:
reg_size
reg_type(reg)
reg
argument is a character denoting the register name.
reg_data(reg)
reg
argument is a character denoting the register name.
cb_size
cb_opcode(cb)
cb
argument is the
ComBlock structure to be examined.
cb_buffer(cb)
cb
argument is the ComBlock structure to be examined.
cb_nbytes(cb)
cb
argument is the ComBlock structure to be examined.
cb_regs(cb)
cb
argument is the ComBlock structure
to be examined.
cb_reg(cb, reg)
cb
argument
is the ComBlock structure to be examined and the reg
argument is the
register name.
cb_reg_type(cb, reg)
cb_reg
and reg_type
. The cb
argument is the
ComBlock structure to be examined and the reg
argument is the
register name.
cb_reg_data(cb, reg)
cb_reg
and reg_type
. The cb
argument is the
ComBlock structure to be examined and the reg
argument is the
register name.
[1] McCormack, Joel. Asente, Paul and Swick, Ralph R. X Toolkit Intrinsics - C Language Interface Massachusetts Institute of Technology, 1985, 1986, 1987, 1988
[2] Swick, Ralph R. and Weissman, Terry. X Toolkit Athena Widgets - C Language Interface Massachusetts Institute of Technology, 1985, 1986, 1987, 1988