The image was viewed as a reflection in a mirrorlike surface, and this gave rise to a need to left-right reverse ("fliplr") the image, or to up-down ("flipud") the image.
The reflectional aspect is visible in this figure, where we can see that the "eye is a camera" (a reflection thereof), and of course the eye also sees a reflection of the aremac (the aremac is the device that synthesizes rays of light from the camera after processing by the computer).
An EyeTap system is a system that causes the eye itself to function, in effect, as if it were both a camera and a display. Rays of eyeward bound light are diverted to a sensor array, by the diverter (two-sided mirror or beamsplitter) and then re-constructed on the backside of the diverter, for being directed into the eye.
The functionality of EyeTap is illustrated in the figure, showing how
the focus linking creates the illusion of transparancy even if the diverter
is 100% silvered and passes no light:
Of course the diverter can be partially silvered, and often is, in many EyeTaps.
Over the past few years, we have been working on a number of programming environments for EyeTap systems. A common feature of EyeTap systems is the diverter, which renders image contant as left-right reversed, or top-bottom reversed.
Accordingly, we have been developing a number of options and methodologies for dealing with this problem.
Although early cathode-ray-tube EyeTap systems relied on swapping deflection yoke wires (in color eyetaps one must be careful to swap only the horizontal yoke wiring, otherwise images are chopped off because of the critical timing of the filter wheel as synchronized with the vertical deflection), there arise certain conditions where it is inconventient to reverse the hardware.
Moreover, since the sensor apparatus (whether it be a camera or similar sensory system) also sees the world in reverse, it is often desired that the display actually display a backwards image to properly compensate, and thus the two will reverse each other and cancel out.
Although it is possible to reverse the computer screen in a variety of different ways, the focus of this article will be on X windows reversal. SVGATextMode can also be reversed, but then in addition to reversing the order of the data read out, we also need to define reversed fonts. The situation is greatly simplified using what we call ReverseX.
Without reversing X, images would appear to the viewer in the following manner:
One early attempt was a program called caplive, developed by Mann, and extended by Mann, Fung, and several others, into something called xcaplive, along with a set of utilities called glinaccess, meant for use with EyeTap systems.
Another attempt (a collaboration between Mann and Waites) involved a free running reversal of X that did not take advantage of individual X events to update only when necessary.
More recently, a thesis project proposed by Mann, and carried out by Manders, under Mann's supervision, included, among other things, the reversal of XF86, to facilitate use of XF86 with an EyeTap system.
With the restructuring of the XF86 code of late, XFree86 4.0.x has provided
an easy means of left-right reversing or up-down reversing
the Xserver. Specifically, new
installations of XFree86 version 4.0.x have an option to make use of
a "Shadow Buffer". In a regular install of XF 4.0, most chipsets will
offer the option of rotating the xserver through an angle of pi/4 (90
degrees). This capability is due
to the efforts of Mark Vojkovich
The inital code provided in the XF4.0 distribution performs a harder task
than what is needed for the left-right reverse or up-down reverse
xserver. Any one of the
drivers provided in XF 4.0 will have essentially the same structure as
what will be discussed here on, but for the purpose of example, the
Nvidia driver (nv) has been chosen. Note that this manipulation has been
successfully preformed on almost all of the XF 4.0 drivers.
First, it is advantageous to examine the code to which the necessary
modifications shall occur. After
expanding the XF 4.0 source code (available at ftp://ftp.xfree86.org),
we get the file .../xc/programs/Xserver/hw/xfree86/drivers/nv/nv_shadow.c.
The following code comes from the above file:
Now, this code is active when the option "Shadow Buffer" is selected
in the XF86Config file. All it is doing is writing from allocated memory
to the graphics card. Understanding this code is not all that difficult
if we consider what some of the variables are.
The sections of the hardware memory-mapped regions (pointed to by pNv->FbStart)
which need to be redrawn because of changes are outlined by pbox's.
A pbox is just a set
of 4 offsets, 2 x offsets and 2 y offsets.
The variable "num" is the number of pboxes which need
to be redrawn. The width and height of the pboxs may be calclated from
these offsets.
The code above redraws one pbox at a time upon each iteration of the outer
while loop.
The variables "src" and "dst" are the memory addresses of the source and
destination. The real changes which need to be made are that we must
write the rgb information to the destination maintaining the order of the rgb
bits, but one level of abstraction higher, these bit groups must be written in
reverse order. This means we must write the pixels out in the destination
starting from the opposite pbox x co-ordinate than the code above. This may be
accomplished by changing
At this point, the code is beginning it's read of the source address from the
correct location. To proceed in the correct manner,
for each horizontal line, the actual groups of rgb bits
must be written out in reverse. This implies that the inner while must change
as well. Unfortunately, we can't write the bits out one "width" at a time
(which is what the memcpy is doing in the original code). We must traverse the
sections in finer increments, which leads use to change
To further highlight all the changes which have been made to this point,
a completely modified example of nv_shadow.c is available
HERE
So, the changes so far will left-right reverse the Xserver, now all that
is needed is to get the driver to call it. Certainly the easiest way
is to simply add an option to the XF86Config file such that when called,
the Xserver would be left-right reversed.
We need to get our new option (which we will call FlipLR) to get parsed by
the driver. Specifically, when we add Option "FlipLR" to the device section
of the XF86Config file we want to have out Xserver reversed.
Taking a careful look at nv_driver.c around line 299, we find the following
structure:
Aside from the variable pNv->Flip not existing yet, if it did in fact
exist, we have now properly parsed the XF86Config file. Now what is needed
is to have the function NVRefreshAreaFlip be used when needed. This is
done in NVScreenInit. Within NVScreenInit,
there is a section dealing with the "Shadow Buffer". Using a
reasonable editor to make the changes, you can search for the string
if(pNv->ShadowFB)
in the Nvidia driver, this occurs around line 1779 of nv_driver.c. The original
piece of code is the following:
The pNv->Flip field must be defined. There is a struct which will be similar to:
We believe that vandalism, tagging, or other attempts to deface our
property is wrong, so we wrote a
letter to the organization we suspected
of tagging our computer.
void
NVRefreshArea(ScrnInfoPtr pScrn, int num, BoxPtr pbox)
{
NVPtr pNv = NVPTR(pScrn);
int width, height, Bpp, FBPitch;
unsigned char *src, *dst;
Bpp = pScrn->bitsPerPixel >> 3;
FBPitch = BitmapBytePad(pScrn->displayWidth * pScrn->bitsPerPixel);
while(num--) {
width = (pbox->x2 - pbox->x1) * Bpp;
height = pbox->y2 - pbox->y1;
src = pNv->ShadowPtr +
(pbox->y1 * pNv->ShadowPitch) +
(pbox->x1 * Bpp);
dst = pNv->FbStart + (pbox->y1 * FBPitch) + (pbox->x1 * Bpp);
while(height--) {
memcpy(dst, src, width);
dst += FBPitch;
src += pNv->ShadowPitch;
}
pbox++;
}
}
This will get us to the other end of the pbox in the representation in memory.
dst = pNv->FbStart + (pbox->y1 * FBPitch) + (pbox->x1 * Bpp);
to
dst = pNv->FbStart + (pbox->y1 * FBPitch) + ((pScrn->virtualX - pbox->x1) * Bpp)
to
while(height--) {
memcpy(dst,src,width);
dst += FBPitch;
src += pNv->ShadowPitch;
}
The code above takes care of the painting of the pixels. What is
left to do is to correct the positioning of the mouse (which is
not yet reversed).
A subrountine at the start of nv_shadow.c
name NVPointerMoved allows us to do this.
If we look at the NVPointerMoved subroutine, the code is used to change
x co-ordinates to y co-ordinates and similarly y to x. This is of
course not needed to reverse the X server, but the code may be modified
to perform the function needed. The next section of code has been commented
to outline the necessary modifications:
while(height--) {
for (i= 0; i
What has been done in the new section of code is to take the original
y co-ordinate and leave it unchanged and subtract the original x co-ordinate
from width of the screen. This is all that is needed for left-right reversing
the mouse. The variable pNv->Flip is important and will be explained
in what follows.
void
NVPointerMoved(int index, int x, int y)
{
ScrnInfoPtr pScrn = xf86Screens[index];
NVPtr pNv = NVPTR(pScrn);
int newX, newY;
/* new section */
if(pNv->Flip == 1) {
newX = pScrn->pScreen-width - x - 1;
newY = y;
}
else {
/*
* old section
* this section just does the x-y transform
* needed for rotating the X server
*/
if(pNv->Rotate == 1) {
newX = pScrn->pScreen->height - y - 1;
newY = x;
}
else {
newX = y;
newY = pScrn->pScreen->width - x - 1;
}
}
(*pNv->PointerMoved)(index, newX, newY);
}
These are the usual options available to the driver. These options will differ
slightly from driver to driver, but some similar structure exists in each of the
drivers. What is needed in the structure is a line which should appear as:
static OptionInfoRec NVOptions[] = {
{ OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_SHOWCACHE, "ShowCache", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_FBDEV, "UseFBDev", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_ROTATE, "Rotate", OPTV_ANYSTR, {0}, FALSE },
{ -1, NULL, OPTV_NONE, {0}, FALSE }
};
The subroutine
NVPreInit is used to parse the XF86Config file and set various structures
parameters to the correct values. The lines which need to be added can occur
within a wide range of the subroutine. In keeping with the overall design,
with the Nvidia driver, it makes sense to add some code at line 1061 (right
after the Rotate option is parsed. Adding:
/* our new cyborg enhanced option */
{ OPTION_FLIP, "FlipLR", OPTV_BOOLEAN, {0}, FALSE },
will set the required variables in the pNv structure to the values
they need to be for left-right reversing the Xserver. At this point,
the field pNv->Flip doesn't exist. This needs to be added to the header
file and will be dealt with later.
if ((s = xf86GetOptValString(NVOptions, OPTION_FLIP))){
pNv->ShadowFB = TRUE;
pNv->NoAccel = TRUE;
pNv->HWCursor = FALSE;
pNv->Flip = TRUE;
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
"Left Right Reversing the screen - acceleration disabled\n");
}
Since we want to add in the left-right reverse functionality,
we can modify the above
section of code to consider our reverse X cyborg option by changing the above
code to :
if(pNv->ShadowFB) {
RefreshAreaFuncPtr refreshArea = NVRefreshArea;
if(pNv->Rotate) {
pNv->PointerMoved = pScrn->PointerMoved;
pScrn->PointerMoved = NVPointerMoved;
switch(pScrn->bitsPerPixel) {
case 8: refreshArea = NVRefreshArea8; break;
case 16: refreshArea = NVRefreshArea16; break;
case 32: refreshArea = NVRefreshArea32; break;
}
}
ShadowFBInit(pScreen, refreshArea);
}
All that is left to do is add the appropriate entries
to the header file. The name of this file varies depending on the driver. Of
course it is always a .h file which narrows the field greatly. All drivers will
have a similar header, but for the Nvidia driver, the file is nv_type.h. If the
modifications are on a different driver, it's probably best to look at the Nvidia
.h file and grep for some similar entries in the directory of the driver which
is being modified.
if(pNv->ShadowFB) {
RefreshAreaFuncPtr refreshArea = NVRefreshArea;
if(pNv->Flip){
pNv->PointerMoved = pScrn->PointerMoved;
pScrn->PointerMoved = NVPointerMoved;
refreshArea = NVRefreshAreaFlip;
}
else if(pNv->Rotate) {
pNv->PointerMoved = pScrn->PointerMoved;
pScrn->PointerMoved = NVPointerMoved;
switch(pScrn->bitsPerPixel) {
case 8: refreshArea = NVRefreshArea8; break;
case 16: refreshArea = NVRefreshArea16; break;
case 32: refreshArea = NVRefreshArea32; break;
}
}
ShadowFBInit(pScreen, refreshArea);
}
To which we simply want to add the entry
typedef struct {
RIVA_HW_INST riva;
RIVA_HW_STATE SavedReg;
RIVA_HW_STATE ModeReg;
EntityInfoPtr pEnt;
pciVideoPtr PciInfo;
PCITAG PciTag;
.
.
.
unsigned int (*ddc1Read)(ScrnInfoPtr);
void (*DDC1SetSpeed)(ScrnInfoPtr, xf86ddcSpeed);
Bool (*i2cInit)(ScrnInfoPtr);
I2CBusPtr I2C;
xf86Int10InfoPtr pInt;
} NVRec, *NVPtr;
Lastly, we want to add our newly created function for left right reversing to
the header. This probably belongs around
Bool Flip;
and should be declared as
void NVRefreshArea(ScrnInfoPtr pScrn, int num, BoxPtr pbox);
Now, we may safely reverse the X server by adding the line
void NVRefreshAreaFlip(ScrnInfoPtr pScrn, int num, BoxPtr pbox);
to the XFree4.0 XF86Config file, which should be located in the directory
/etc/X11.
Just in case any of the modifications above were not clear, a modified
version of the nv_driver.c is available HERE
and the header file nv_type.h is available HERE
Option "FlipLR"
Unfortunately the computers we're using to do these experiments with
were vandalized both inside and out.