X.Org Manual pages: Section 4
- aiptek - Aiptek USB Digital Tablet Input Driver for Linux
- apm - Alliance ProMotion video driver
- chips - Chips and Technologies video driver
- cirrus - Cirrus Logic video driver
- citron - Citron Infrared Touch Driver (CiTouch)
- cyrix - Cyrix video driver
- dmc - DMC input driver
- - specific DGA extension. For our purposes, the
MI event queue handling should be suitable. It is initialized by
calling mieqInit():
- mieqInit()
- This MI function initializes the MI event queue for the
core devices, and is passed the public component of the input handles
for the two core devices.
If a wakeup handler is required to deliver synchronous input
events, it can be registered here by calling the DIX function
RegisterBlockAndWakeupHandlers(). (See the devReadInput() description
below.)
4.1.2. InitAndStartDevices()
InitAndStartDevices() is a DIX function that is called immediately
after InitInput() from the X server's main() function. Its purpose is
to initialize each input device that was registered with
AddInputDevice(), enable each input device that was successfully
initialized, and create the list of enabled input devices. Once each
registered device is processed in this way, the list of enabled input
devices is checked to make sure that both a core keyboard device and
core pointer device were registered and successfully enabled. If not,
InitAndStartDevices() returns failure, and results in the the X server
exiting with a fatal error.
Each registered device is initialized by calling its callback
(dev->deviceProc) with the DEVICE_INIT argument:
- (*dev->deviceProc)(dev, DEVICE_INIT)
- This function initializes the
device structs with core information relevant to the device.
For pointer devices, this means specifying the number of buttons,
default button mapping, the function used to get motion events (usually
miPointerGetMotionEvents()), the function used to change/control the
core pointer motion parameters (acceleration and threshold), and the
motion buffer size.
For keyboard devices, this means specifying the keycode range,
default keycode to keysym mapping, default modifier mapping, and the
functions used to sound the keyboard bell and modify/control the
keyboard parameters (LEDs, bell pitch and duration, key click, which
keys are auto-repeating, etc).
Each initialized device is enabled by calling EnableDevice():
- EnableDevice()
- EnableDevice() calls the device callback with
DEVICE_ON:
- (*dev->deviceProc)(dev, DEVICE_ON)
- This typically opens and
initializes the relevant physical device, and when appropriate,
registers the device's file descriptor (or equivalent) as a valid
input source.
EnableDevice() then adds the device handle to the X server's
global list of enabled devices.
InitAndStartDevices() then verifies that a valid core keyboard and
pointer has been initialized and enabled. It returns failure if either
are missing.
4.1.3. devReadInput()
Each device will have some function that gets called to read its
physical input. These may be called in a number of different ways. In
the case of synchronous I/O, they will be called from a DDX
wakeup-handler that gets called after the server detects that new input is
available. In the case of asynchronous I/O, they will be called from a
(SIGIO) signal handler triggered when new input is available. This
function should do at least two things: make sure that input events get
enqueued, and make sure that the cursor gets moved for motion events
(except if these are handled later by the driver's own event queue
processing function, which cannot be done when using the MI event queue
handling).
Events are queued by calling mieqEnqueue():
- mieqEnqueue()
- This MI function is used to add input events to the
event queue. It is simply passed the event to be queued.
The cursor position should be updated when motion events are
enqueued, by calling either miPointerAbsoluteCursor() or
miPointerDeltaCursor():
- miPointerAbsoluteCursor()
- This MI function is used to move the
cursor to the absolute coordinates provided.
- miPointerDeltaCursor()
- This MI function is used to move the cursor
relative to its current position.
4.1.4. ProcessInputEvents()
ProcessInputEvents() is a DDX function that is called from the X
server's main dispatch loop when new events are available in the input
event queue. It typically processes the enqueued events, and updates
the cursor/pointer position. It may also do other DDX-specific event
processing.
Enqueued events are processed by mieqProcessInputEvents() and passed
to the DIX layer for transmission to clients:
- mieqProcessInputEvents()
- This function processes each event in the
event queue, and passes it to the device's input processing function.
The DIX layer provides default functions to do this processing, and they
handle the task of getting the events passed back to the relevant
clients.
- miPointerUpdate()
- This function resynchronized the cursor position
with the new pointer position. It also takes care of moving the cursor
between screens when needed in multi-head configurations.
4.1.5. DisableDevice()
DisableDevice is a DIX function that removes an input device from the
list of enabled devices. The result of this is that the device no
longer generates input events. The device's data structures are kept in
place, and disabling a device like this can be reversed by calling
EnableDevice(). DisableDevice() may be called from the DDX when it is
desirable to do so (e.g., the XFree86 server does this when VT
switching). Except for special cases, this is not normally called for
core input devices.
DisableDevice() calls the device's callback function with
DEVICE_OFF
:
- (*dev->deviceProc)(dev, DEVICE_OFF)
- This typically closes the
relevant physical device, and when appropriate, unregisters the device's
file descriptor (or equivalent) as a valid input source.
DisableDevice() then removes the device handle from the X server's
global list of enabled devices.
4.1.6. CloseDevice()
CloseDevice is a DIX function that removes an input device from the
list of available devices. It disables input from the device and frees
all data structures associated with the device. This function is
usually called from CloseDownDevices(), which is called from main() at
the end of each server generation to close all input devices.
CloseDevice() calls the device's callback function with
DEVICE_CLOSE
:
- (*dev->deviceProc)(dev, DEVICE_CLOSE)
- This typically closes the
relevant physical device, and when appropriate, unregisters the device's
file descriptor (or equivalent) as a valid input source. If any device
specific data structures were allocated when the device was initialized,
they are freed here.
CloseDevice() then frees the data structures that were allocated
for the device when it was registered/initialized.
4.1.7. LegalModifier()
LegalModifier() is a required DDX function that can be used to
restrict which keys may be modifier keys. This seems to be present for
historical reasons, so this function should simply return TRUE
unconditionally.
4.2. Output handling
The following sections describe the main functions required to
initialize, use and close the output device(s) for each screen in the X
server.
4.2.1. InitOutput()
This DDX function is called near the start of each server generation
from the X server's main() function. InitOutput()'s main purpose is to
initialize each screen and fill in the global screenInfo structure for
each screen. It is passed three arguments: a pointer to the screenInfo
struct, which it is to initialize, and argc and argv from main(), which
can be used to determine additional configuration information.
The primary tasks for this function are outlined below:
- Parse configuration info: The first task of InitOutput()
is to parses any configuration information from the configuration
file. In addition to the XF86Config file, other configuration
information can be taken from the command line. The command line
options can be gathered either in InitOutput() or earlier in the
ddxProcessArgument() function, which is called by
ProcessCommandLine(). The configuration information determines the
characteristics of the screen(s). For example, in the XFree86 X
server, the XF86Config file specifies the monitor information, the
screen resolution, the graphics devices and slots in which they are
located, and, for Xinerama, the screens' layout.
- Initialize screen info: The next task is to initialize
the screen-dependent internal data structures. For example, part of
what the XFree86 X server does is to allocate its screen and pixmap
private indices, probe for graphics devices, compare the probed
devices to the ones listed in the XF86Config file, and add the ones that
match to the internal xf86Screens[] structure.
- Set pixmap formats: The next task is to initialize the
screenInfo's image byte order, bitmap bit order and bitmap scanline
unit/pad. The screenInfo's pixmap format's depth, bits per pixel
and scanline padding is also initialized at this stage.
- Unify screen info: An optional task that might be done at
this stage is to compare all of the information from the various
screens and determines if they are compatible (i.e., if the set of
screens can be unified into a single desktop). This task has
potential to be useful to the DMX front-end server, if Xinerama's
PanoramiXConsolidate() function is not sufficient.
Once these tasks are complete, the valid screens are known and each
of these screens can be initialized by calling AddScreen().
4.2.2. AddScreen()
This DIX function is called from InitOutput(), in the DDX layer, to
add each new screen to the screenInfo structure. The DDX screen
initialization function and command line arguments (i.e., argc and argv)
are passed to it as arguments.
This function first allocates a new Screen structure and any privates
that are required. It then initializes some of the fields in the Screen
struct and sets up the pixmap padding information. Finally, it calls
the DDX screen initialization function ScreenInit(), which is described
below. It returns the number of the screen that were just added, or -1
if there is insufficient memory to add the screen or if the DDX screen
initialization fails.
4.2.3. ScreenInit()
This DDX function initializes the rest of the Screen structure with
either generic or screen-specific functions (as necessary). It also
fills in various screen attributes (e.g., width and height in
millimeters, black and white pixel values).
The screen init function usually calls several functions to perform
certain screen initialization functions. They are described below:
- {mi,*fb}ScreenInit()
- The DDX layer's ScreenInit() function usually
calls another layer's ScreenInit() function (e.g., miScreenInit() or
fbScreenInit()) to initialize the fallbacks that the DDX driver does not
specifically handle.
After calling another layer's ScreenInit() function, any
screen-specific functions either wrap or replace the other layer's
function pointers. If a function is to be wrapped, each of the old
function pointers from the other layer are stored in a screen private
area. Common functions to wrap are CloseScreen() and SaveScreen().
- miInitializeBackingStore()
- This MI function initializes the
screen's backing storage functions, which are used to save areas of
windows that are currently covered by other windows.
- miDCInitialize()
- This MI function initializes the MI cursor
display structures and function pointers. If a hardware cursor is used,
the DDX layer's ScreenInit() function will wrap additional screen and
the MI cursor display function pointers.
Another common task for ScreenInit() function is to initialize the
output device state. For example, in the XFree86 X server, the
ScreenInit() function saves the original state of the video card and
then initializes the video mode of the graphics device.
4.2.4. CloseScreen()
This function restores any wrapped screen functions (and in
particular the wrapped CloseScreen() function) and restores the state of
the output device to its original state. It should also free any
private data it created during the screen initialization.
4.2.5. GC operations
When the X server is requested to render drawing primitives, it does
so by calling drawing functions through the graphics context's operation
function pointer table (i.e., the GCOps functions). These functions
render the basic graphics operations such as drawing rectangles, lines,
text or copying pixmaps. Default routines are provided either by the MI
layer, which draws indirectly through a simple span interface, or by the
framebuffer layers (e.g., CFB, MFB, FB), which draw directly to a
linearly mapped frame buffer.
To take advantage of special hardware on the graphics device,
specific GCOps functions can be replaced by device specific code.
However, many times the graphics devices can handle only a subset of the
possible states of the GC, so during graphics context validation,
appropriate routines are selected based on the state and capabilities of
the hardware. For example, some graphics hardware can accelerate single
pixel width lines with certain dash patterns. Thus, for dash patterns
that are not supported by hardware or for width 2 or greater lines, the
default routine is chosen during GC validation.
Note that some pointers to functions that draw to the screen are
stored in the Screen structure. They include GetImage(), GetSpans(),
PaintWindowBackground(), PaintWindowBorder(), CopyWindow() and
RestoreAreas().
4.2.6. Xnest
The Xnest X server is a special proxy X server that relays the X
protocol requests that it receives to a ``real'' X server that then
processes the requests and displays the results, if applicable. To the X
applications, Xnest appears as if it is a regular X server. However,
Xnest is both server to the X application and client of the real X
server, which will actually handle the requests.
The Xnest server implements all of the standard input and output
initialization steps outlined above.
- InitOutput()
- Xnest takes its configuration information from
command line arguments via ddxProcessArguments(). This information
includes the real X server display to connect to, its default visual
class, the screen depth, the Xnest window's geometry, etc. Xnest then
connects to the real X server and gathers visual, colormap, depth and
pixmap information about that server's display, creates a window on that
server, which will be used as the root window for Xnest.
Next, Xnest initializes its internal data structures and uses the
data from the real X server's pixmaps to initialize its own pixmap
formats. Finally, it calls AddScreen(xnestOpenScreen, argc, argv) to
initialize each of its screens.
- ScreenInit()
- Xnest's ScreenInit() function is called
xnestOpenScreen(). This function initializes its screen's depth and
visual information, and then calls miScreenInit() to set up the default
screen functions. It then calls miInitializeBackingStore() and
miDCInitialize() to initialize backing store and the software cursor.
Finally, it replaces many of the screen functions with its own
functions that repackage and send the requests to the real X server to
which Xnest is attached.
- CloseScreen()
- This function frees its internal data structure
allocations. Since it replaces instead of wrapping screen functions,
there are no function pointers to unwrap. This can potentially lead to
problems during server regeneration.
- GC operations
- The GC operations in Xnest are very simple since
they leave all of the drawing to the real X server to which Xnest is
attached. Each of the GCOps takes the request and sends it to the
real X server using standard Xlib calls. For example, the X
application issues a XDrawLines() call. This function turns into a
protocol request to Xnest, which calls the xnestPolylines() function
through Xnest's GCOps function pointer table. The xnestPolylines()
function is only a single line, which calls XDrawLines() using the same
arguments that were passed into it. Other GCOps functions are very
similar. Two exceptions to the simple GCOps functions described above
are the image functions and the BLT operations.
The image functions, GetImage() and PutImage(), must use a temporary
image to hold the image to be put of the image that was just grabbed
from the screen while it is in transit to the real X server or the
client. When the image has been transmitted, the temporary image is
destroyed.
The BLT operations, CopyArea() and CopyPlane(), handle not only the
copy function, which is the same as the simple cases described above,
but also the graphics exposures that result when the GC's graphics
exposure bit is set to True. Graphics exposures are handled in a helper
function, xnestBitBlitHelper(). This function collects the exposure
events from the real X server and, if any resulting in regions being
exposed, then those regions are passed back to the MI layer so that it
can generate exposure events for the X application.
The Xnest server takes its input from the X server to which it is
connected. When the mouse is in the Xnest server's window, keyboard and
mouse events are received by the Xnest server, repackaged and sent back
to any client that requests those events.
4.2.7. Shadow framebuffer
The most common type of framebuffer is a linear array memory that
maps to the video memory on the graphics device. However, accessing
that video memory over an I/O bus (e.g., ISA or PCI) can be slow. The
shadow framebuffer layer allows the developer to keep the entire
framebuffer in main memory and copy it back to video memory at regular
intervals. It also has been extended to handle planar video memory and
rotated framebuffers.
There are two main entry points to the shadow framebuffer code:
- shadowAlloc(width, height, bpp)
- This function allocates the in
memory copy of the framebuffer of size width*height*bpp. It returns a
pointer to that memory, which will be used by the framebuffer
ScreenInit() code during the screen's initialization.
- shadowInit(pScreen, updateProc, windowProc)
- This function
initializes the shadow framebuffer layer. It wraps several screen
drawing functions, and registers a block handler that will update the
screen. The updateProc is a function that will copy the damaged regions
to the screen, and the windowProc is a function that is used when the
entire linear video memory range cannot be accessed simultaneously so
that only a window into that memory is available (e.g., when using the
VGA aperture).
The shadow framebuffer code keeps track of the damaged area of each
screen by calculating the bounding box of all drawing operations that
have occurred since the last screen update. Then, when the block handler
is next called, only the damaged portion of the screen is updated.
Note that since the shadow framebuffer is kept in main memory, all
drawing operations are performed by the CPU and, thus, no accelerated
hardware drawing operations are possible.
4.3. Xinerama
Xinerama is an X extension that allows multiple physical screens
controlled by a single X server to appear as a single screen. Although
the extension allows clients to find the physical screen layout via
extension requests, it is completely transparent to clients at the core
X11 protocol level. The original public implementation of Xinerama came
from Digital/Compaq. XFree86 rewrote it, filling in some missing pieces
and improving both X11 core protocol compliance and performance. The
Xinerama extension will be passing through X.Org's standardization
process in the near future, and the sample implementation will be based
on this rewritten version.
The current implementation of Xinerama is based primarily in the DIX
(device independent) and MI (machine independent) layers of the X
server. With few exceptions the DDX layers do not need any changes to
support Xinerama. X server extensions often do need modifications to
provide full Xinerama functionality.
The following is a code-level description of how Xinerama functions.
Note: Because the Xinerama extension was originally called the
PanoramiX extension, many of the Xinerama functions still have the
PanoramiX prefix.
- PanoramiXExtensionInit()
- PanoramiXExtensionInit() is a
device-independent extension function that is called at the start of
each server generation from InitExtensions(), which is called from
the X server's main() function after all output devices have been
initialized, but before any input devices have been initialized.
PanoramiXNumScreens is set to the number of physical screens. If
only one physical screen is present, the extension is disabled, and
PanoramiXExtensionInit() returns without doing anything else.
The Xinerama extension is registered by calling AddExtension().
A local per-screen array of data structures
(panoramiXdataPtr[])
is allocated for each physical screen, and GC and Screen private
indexes are allocated, and both GC and Screen private areas are
allocated for each physical screen. These hold Xinerama-specific
per-GC and per-Screen data. Each screen's CreateGC and CloseScreen
functions are wrapped by XineramaCreateGC() and
XineramaCloseScreen() respectively. Some new resource classes are
created for Xinerama drawables and GCs, and resource types for
Xinerama windows, pixmaps and colormaps.
A region (XineramaScreenRegions[i]) is initialized for each
physical screen, and single region (PanoramiXScreenRegion) is
initialized to be the union of the screen regions. The
panoramiXdataPtr[] array is also initialized with the size and
origin of each screen. The relative positioning information for the
physical screens is taken from the array
dixScreenOrigins[], which
the DDX layer must initialize in InitOutput(). The bounds of the
combined screen is also calculated (PanoramiXPixWidth and
PanoramiXPixHeight).
The DIX layer has a list of function pointers
(ProcVector[]) that
holds the entry points for the functions that process core protocol
requests. The requests that Xinerama must intercept and break up
into physical screen-specific requests are wrapped. The original
set is copied to SavedProcVector[]. The types of requests
intercepted are Window requests, GC requests, colormap requests,
drawing requests, and some geometry-related requests. This wrapping
allows the bulk of the protocol request processing to be handled
transparently to the DIX layer. Some operations cannot be dealt with
in this way and are handled with Xinerama-specific code within the
DIX layer.
- PanoramiXConsolidate()
- PanoramiXConsolidate() is a
device-independent extension function that is called directly from
the X server's main() function after extensions and input/output
devices have been initialized, and before the root windows are
defined and initialized.
This function finds the set of depths (PanoramiXDepths[]) and
visuals (PanoramiXVisuals[])
common to all of the physical screens.
PanoramiXNumDepths is set to the number of common depths, and
PanoramiXNumVisuals is set to the number of common visuals.
Resources are created for the single root window and the default
colormap. Each of these resources has per-physical screen entries.
- PanoramiXCreateConnectionBlock()
- PanoramiXConsolidate() is a
device-independent extension function that is called directly from
the X server's main() function after the per-physical screen root
windows are created. It is called instead of the standard DIX
CreateConnectionBlock() function. If this function returns FALSE,
the X server exits with a fatal error. This function will return
FALSE if no common depths were found in PanoramiXConsolidate().
With no common depths, Xinerama mode is not possible.
The connection block holds the information that clients get when
they open a connection to the X server. It includes information
such as the supported pixmap formats, number of screens and the
sizes, depths, visuals, default colormap information, etc, for each
of the screens (much of information that
xdpyinfo
shows). The
connection block is initialized with the combined single screen
values that were calculated in the above two functions.
The Xinerama extension allows the registration of connection
block callback functions. The purpose of these is to allow other
extensions to do processing at this point. These callbacks can be
registered by calling XineramaRegisterConnectionBlockCallback() from
the other extension's ExtensionInit() function. Each registered
connection block callback is called at the end of
PanoramiXCreateConnectionBlock().
4.3.1. Xinerama-specific changes to the DIX code
There are a few types of Xinerama-specific changes within the DIX
code. The main ones are described here.
Functions that deal with colormap or GC -related operations outside of
the intercepted protocol requests have a test added to only do the
processing for screen numbers > 0. This is because they are handled for
the single Xinerama screen and the processing is done once for screen 0.
The handling of motion events does some coordinate translation between
the physical screen's origin and screen zero's origin. Also, motion
events must be reported relative to the composite screen origin rather
than the physical screen origins.
There is some special handling for cursor, window and event processing
that cannot (either not at all or not conveniently) be done via the
intercepted protocol requests. A particular case is the handling of
pointers moving between physical screens.
4.3.2. Xinerama-specific changes to the MI code
The only Xinerama-specific change to the MI code is in miSendExposures()
to handle the coordinate (and window ID) translation for expose events.
4.3.3. Intercepted DIX core requests
Xinerama breaks up drawing requests for dispatch to each physical
screen. It also breaks up windows into pieces for each physical screen.
GCs are translated into per-screen GCs. Colormaps are replicated on
each physical screen. The functions handling the intercepted requests
take care of breaking the requests and repackaging them so that they can
be passed to the standard request handling functions for each screen in
turn. In addition, and to aid the repackaging, the information from
many of the intercepted requests is used to keep up to date the
necessary state information for the single composite screen. Requests
(usually those with replies) that can be satisfied completely from this
stored state information do not call the standard request handling
functions.
Distributed Multihead X design
: Background
Previous: Current issues
Next: Development Results