summaryrefslogtreecommitdiff
path: root/xc/programs/Xserver/hw/xfree86/doc/sgml/dmx.sgml
blob: dcd62756f806d29a07ee1198d1b92c5782b8e603 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
<!DOCTYPE linuxdoc PUBLIC "-//XFree86//DTD linuxdoc//EN">
  <article>

    <!-- Title information -->
      <title>Distributed multihead X design
      <author>Kevin E. Martin and David H. Dawes
      <date>28 November 2001 (created 25 July 2001)
      <abstract>
        This document covers the motivation, background, design and
        implementation of the distributed multihead X (DMX) system.  It
        is a living document and describes the current design and
        implementation details of the DMX system.  As the project
        progresses, this document will be continually updated to reflect
        the changes in the code and/or design.  <it>Copyright 2001 by VA
        Linux Systems, Inc., Fremont, California.  Copyright 2001 by Red
        Hat, Inc., Durham, North Carolina</it>
      </abstract>

      <!-- Table of contents -->
    <toc>

    <!-- Begin the document -->
    <sect>Introduction

<p>Current Open Source multihead solutions are limited to a single
physical machine.  A single X server controls multiple display devices,
which can be arranged as independent heads or unified into a single
desktop.  These solutions are limited to the number of physical devices
that can co-exist in a single machine (e.g., due to the number of
AGP/PCI slots available for graphics cards).  Thus, large tiled displays
are not currently possible.  This limitation will be solved by
eliminating the requirement that the display devices reside in the same
physical machine.  This will be accomplished by developing a front-end
proxy X server that will control multiple back-end X servers that make
up the large display.  These X servers can either run on the same or
separate machines.

<p>The overall structure of the distributed multihead X (DMX) project is
as follows: A single front-end X server will act as a proxy to a set of
back-end X servers, which handle all of the visible rendering.  X
clients will connect to the front-end server just as they normally would
to a regular X server.  To the client, everything appears as if they
have a single large display; however, using an extension they can
request the arrangement of the back-end server screens as well as other
information that might be useful to them (e.g., for placement of pop-up
windows, window alignments by the window manager, etc.).  A
configuration tool will be developed that will use this extension and
will aid setup and arrangement of the DMX system.

<p>DMX can be broken down into two main parts: input devices and
rendering/windowing requests.  Each of these are describe briefly below,
and the rest of this design document will describe them in greater
detail.  When the project is complete, this design doc will be delivered
with the final product documentation.

<p>Input devices can be handled either in the front-end server or can be
funneled through the front-end server from one (or more) or the back-end
servers.  If they are served by the front-end server, then they will be
need to be connected directly to the system that runs the front-end
server and cannot be in use by another X server running on that system.
Basic devices, such as the mouse and keyboard, will be handled directly.
Other devices will be processed by the XInput extension.  Events will be
passed back to the requesting client.  For devices that control a
cursor, the software or hardware cursor on the appropriate back-end
server(s) will be enabled and positioned accordingly, while the cursors
on the other back-end server(s) will be disabled.

<p>Rendering requests will be accepted by the front-end server; however,
rendering to visible windows will be broken down as needed and sent to
the appropriate back-end server(s) via X11 library calls for actual
rendering.  The basic framework will follow a Xnest-style approach.  GC
state will be managed in the front-end server and sent to the
appropriate back-end server(s) as required.  Pixmap rendering will (at
least initially) be handled by the front-end X server.  Windowing
requests (e.g., ordering, mapping, moving, etc.) will handled in the
front-end server.  If the request requires a visible change, the
windowing operation will be translated into requests for the appropriate
back-end server(s).  Window state will be mirrored in the back-end
server(s) as needed.


<!-- ============================================================ -->
<sect>Background

<p>This section describes the existing Open Source architectures that
can be used to handle multiple screens and upon which this development
project is based.

<sect1>Core input device handling

<p>The following is a description of how core input devices are handled
by an X server.

<sect2>InitInput()

<p>InitInput() is a DDX function that is called at the start of each
server generation from the X server's main() function.  Its purpose is
to determine what input devices are connected to the X server, register
them with the DIX and MI layers, and initialize the input event queue.
InitInput() does not have a return value, but the X server will abort if
either a core keyboard device or a core pointer device are not
registered.  Extended input (XInput) devices can also be registered in
InitInput().

<p>InitInput() usually has implementation specific code to determine
which input devices are available.  For each input device it will be
using, it calls AddInputDevice():

<descrip>
<tag/AddInputDevice()/ This DIX function allocates the device structure,
registers a callback function (which handles device init, close, on and
off), and returns the input handle, which can be treated as opaque.  It
is called once for each input device.
</descrip>

<p>Once input handles for core keyboard and core pointer devices have
been obtained from AddInputDevice(), they are registered as core devices
by calling RegisterPointerDevice() and RegisterKeyboardDevice().  Each
of these should be called once.  If both core devices are not
registered, then the X server will exit with a fatal error when it
attempts to start the input devices in InitAndStartDevices(), which is
called directly after InitInput() (see below).

<descrip>
<tag/Register{Pointer,Keyboard}Device()/ These DIX functions take a
handle returned from AddInputDevice() and initialize the core input
device fields in inputInfo, and initialize the input processing and grab
functions for each core input device.
</descrip>

<p>The core pointer device is then registered with the miPointer code
(which does the high level cursor handling).  While this registration
is not necessary for correct miPointer operation in the current XFree86
code, it is still done mostly for compatibility reasons.

<descrip>
<tag/miRegisterPointerDevice()/ This MI function registers the core
pointer's input handle with with the miPointer code.
</descrip>

<p>The final part of InitInput() is the initialization of the input
event queue handling.  In most cases, the event queue handling provided
in the MI layer is used.  The primary XFree86 X server uses its own
event queue handling to support some special cases related to the XInput
extension and the XFree86-specific DGA extension.  For our purposes, the
MI event queue handling should be suitable.  It is initialized by
calling mieqInit():

<descrip>
<tag/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.
</descrip>

<p>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.)

<sect2>InitAndStartDevices()

<p>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.

<p>Each registered device is initialized by calling its callback
(dev-&gt;deviceProc) with the DEVICE_INIT argument:

<descrip>
<tag/(*dev-&gt;deviceProc)(dev, DEVICE_INIT)/ This function initializes the
device structs with core information relevant to the device.

<p>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.

<p>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).
</descrip>

<p>Each initialized device is enabled by calling EnableDevice():

<descrip>
<tag/EnableDevice()/ EnableDevice() calls the device callback with
DEVICE_ON:
    <descrip>
    <tag/(*dev-&gt;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.
    </descrip>

    <p>EnableDevice() then adds the device handle to the X server's
    global list of enabled devices.
</descrip>

<p>InitAndStartDevices() then verifies that a valid core keyboard and
pointer has been initialized and enabled.  It returns failure if either
are missing.

<sect2>devReadInput()

<p>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).

<p>Events are queued by calling mieqEnqueue():

<descrip>
<tag/mieqEnqueue()/ This MI function is used to add input events to the
event queue.  It is simply passed the event to be queued.
</descrip>

<p>The cursor position should be updated when motion events are
enqueued, by calling either miPointerAbsoluteCursor() or
miPointerDeltaCursor():

<descrip>
<tag/miPointerAbsoluteCursor()/ This MI function is used to move the
cursor to the absolute coordinates provided.
<tag/miPointerDeltaCursor()/ This MI function is used to move the cursor
relative to its current position.
</descrip>

<sect2>ProcessInputEvents()

<p>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.

<p>Enqueued events are processed by mieqProcessInputEvents() and passed
to the DIX layer for transmission to clients:

<descrip>
<tag/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.
<tag/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.
</descrip>


<sect2>DisableDevice()

<p>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.

<p>DisableDevice() calls the device's callback function with
<tt/DEVICE_OFF/:

<descrip>
<tag/(*dev-&gt;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.
</descrip>

<p>DisableDevice() then removes the device handle from the X server's
global list of enabled devices.


<sect2>CloseDevice()

<p>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.

<p>CloseDevice() calls the device's callback function with
<tt/DEVICE_CLOSE/:

<descrip>
<tag/(*dev-&gt;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.
</descrip>

<p>CloseDevice() then frees the data structures that were allocated
for the device when it was registered/initialized.


<sect2>LegalModifier()
<!-- dmx/dmxinput.c - currently returns TRUE -->           
<p>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.


<sect1>Output handling

<p>The following sections describe the main functions required to
initialize, use and close the output device(s) for each screen in the X
server.

<sect2>InitOutput()

<p>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.

<p>The primary tasks for this function are outlined below:

<enum>
    <item><bf/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.

    <item><bf/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&lsqb;&rsqb; structure.

    <item><bf/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.

    <item><bf/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.
</enum>

<p>Once these tasks are complete, the valid screens are known and each
of these screens can be initialized by calling AddScreen().

<sect2>AddScreen()

<p>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.

<p>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.

<sect2>ScreenInit()

<p>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).

<p>The screen init function usually calls several functions to perform
certain screen initialization functions.  They are described below:

<descrip>
<tag/{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.

<p>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().

<tag/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.

<tag/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.
</descrip>

<p>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.

<sect2>CloseScreen()

<p>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.

<sect2>GC operations

<p>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.

<p>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.

<p>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().

<sect2>Xnest

<p>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.

<p>The Xnest server implements all of the standard input and output
initialization steps outlined above.

<descrip>
<tag/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.

<p>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.

<tag/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.

<tag/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.

<tag/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.

<p>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.

<p>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.
</descrip>

<p>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.

<sect2>Shadow framebuffer

<p>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.

<p>There are two main entry points to the shadow framebuffer code:

<descrip>
<tag/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.

<tag/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).
</descrip>

<p>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.

<p>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.


<sect1>Xinerama

<p>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.

<p>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.

<p>The following is a code-level description of how Xinerama functions.

<p>Note: Because the Xinerama extension was originally called the
PanoramiX extension, many of the Xinerama functions still have the
PanoramiX prefix.

<descrip>
    <tag/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.

    <p>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.

    <p>The Xinerama extension is registered by calling AddExtension().
    
    <p>A local per-screen array of data structures
    (panoramiXdataPtr&lsqb;&rsqb;)
    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.

    <p>A region (XineramaScreenRegions&lsqb;i&rsqb;) is initialized for each
    physical screen, and single region (PanoramiXScreenRegion) is
    initialized to be the union of the screen regions.  The
    panoramiXdataPtr&lsqb;&rsqb; 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&lsqb;&rsqb;, which
    the DDX layer must initialize in InitOutput().  The bounds of the
    combined screen is also calculated (PanoramiXPixWidth and
    PanoramiXPixHeight).

    <p>The DIX layer has a list of function pointers
    (ProcVector&lsqb;&rsqb;) 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&lsqb;&rsqb;.  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.

    <tag/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.

    <p>This function finds the set of depths (PanoramiXDepths&lsqb;&rsqb;) and
    visuals (PanoramiXVisuals&lsqb;&rsqb;)
    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.

    <tag/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.

    <p>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 <tt/xdpyinfo/ shows).  The
    connection block is initialized with the combined single screen
    values that were calculated in the above two functions.

    <p>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().
</descrip>

<sect2>Xinerama-specific changes to the DIX code

<p>There are a few types of Xinerama-specific changes within the DIX
code.  The main ones are described here.

<p>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.

<p>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.

<p>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.

<sect2>Xinerama-specific changes to the MI code

<p>The only Xinerama-specific change to the MI code is in miSendExposures()
to handle the coordinate (and window ID) translation for expose events.

<sect2>Intercepted DIX core requests

<p>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.


<!-- ============================================================ -->
<sect>Development plan

<p>This section describes the current development plan.

<sect1>Bootstrap code

<p>To allow for rapid development of the DMX server by multiple
developers during the first development stage, the problem will be
broken down into three tasks: the overall DMX framework, back-end
rendering services and input device handling services.  However, before
the work begins on these tasks, a simple framework that each developer
could use was implemented to bootstrap the development effort.  This
framework renders to a single back-end server and provides dummy input
devices (i.e., the keyboard and mouse).  The simple back-end rendering
service was implemented using the shadow framebuffer support currently
available in the XFree86 environment.

<p>Using this bootstrapping framework, each developer has been able to
work on each of the tasks listed above independently as follows: the
framework will be extended to handle arbitrary back-end server
configurations; the back-end rendering services will be transitioned to
the more efficient Xnest-style implementation; and, an input device
framework to handle various input devices via the input extension will
be developed.

<p>Status: The boot strap code is complete.  <!-- August 2001 -->


<sect1>Input device handling

<p>An X server (including the front-end X server) requires two core
input devices -- a keyboard and a pointer (mouse).  These core devices
are handled and required by the core X11 protocol.  Additional types of
input devices may be attached and utilized via the XInput extension.
These are usually referred to as ``extended input devices''.

<p>There are some options as to how the front end X server gets its core
input devices:

<enum>
    <item>The physical input devices (e.g., keyboard and mouse) can be
    attached directly to the front end X server.  In this case, the
    keyboard and mouse on the machine running the front end X server
    will be used.  The front end will have drivers to read the raw input
    from those devices and convert it into the required X input events
    (key press/release, pointer button press/release, pointer motion).
    The front end keyboard driver will keep track of keyboard properties
    such as key and modifier mappings, autorepeat state, keyboard sound
    and led state.  Similarly the front end pointer driver will keep
    track if pointer properties such as the button mapping and movement
    acceleration parameters.  With this option, input is handled fully
    in the front end X server, and the back end X servers are used in a
    display-only mode.

    <p>There are some options for how the input device support could be
    implemented:

    <enum>
        <item>The XFree86 X server has modular input drivers that could
        be adapted for this purpose.  The mouse driver supports a wide
        range of mouse types and interfaces, as well as a range of
        Operating System platforms.  The keyboard driver in XFree86 is
        not currently as modular as the mouse driver, but could be made
        so.  The XFree86 X server also has a range of other input
        drivers for extended input devices such as tablets and touch
        screens.

        <item>The <tt/kdrive/ X server in XFree86 has built-in drivers that
        support PS/2 mice and keyboard under Linux.  The mouse driver
        can indirectly handle other mouse types if the Linux utility
        <tt/gpm/ is used as to translate the native mouse protocol into
        PS/2 mouse format.  These drivers could be adapted and built in
        to the front end X server if this range of hardware and OS
        support is sufficient.
    </enum>

    <item>The front end can make use of the core input devices attached
    to a single back end X server.  In this case, the front end will get
    the core input events from that back end, and the keyboard and mouse
    used to control the distributed X array will be physically connected
    to that back end.  The keyboard and pointer state will also be
    handled in that back end, with the front end simply acting as a
    proxy for input-device related X requests.

    <item>The front end can make use of the core input devices attached
    to one or more of the back end X servers.  This is like option 2,
    except that the core input events from multiple back ends are
    merged into a single input event stream.  This can work sanely
    when only a single set of input devices is used at any given time.
    The keyboard and pointer state will be handled in the front end,
    with changes propagated to the back end servers as needed.  This
    option can work well when the keyboards and pointers on each back
    end are the same.  When they aree different (different numbers of
    buttons, different keys, etc) there are issues to resolve in how the
    front end exports a single set of consistent core input devices.

    <item>A variation on option 3 is to nominate one back end X server
    (or even the front end X server) as providing the core input
    devices, and allowing access to the core input devices on the other
    back ends as extended input devices.  Among other things, this
    would allow the current core devices to be switched at run time,
    and it would allow XInput-aware applications to access the full
    capabilities of each back-end's core input devices simultaneously.
    The back end X servers would not need to support the XInput
    extension for this to work.  The XFree86 implementation of the
    XInput extension allows multiple extended input devices to generate
    core input events, and leveraging this would be an option for
    implementing option 3.

    <item>The front end server could create a console window that is
    displayed on an X server independent of the back end X servers.
    This console window could display things like the physical screen
    layout, and the front end could get its core input events from events
    delivered to the console window.

</enum>

<p>Although extended input devices are not specifically mentioned in the
Distributed X requirements, option 4 above could be further extended to
make extended input devices attached to back end X servers visible,
and/or to allow extended input devices to be attached to the front end X
server.

<p>The bootstrap code (Xdmx) currently has dummy input devices.  These
do the necessary initialization to satisfy the X server's requirements
for core pointer and keyboard devices, but no input events are ever
generated.

<p>The input device support in Xnest could be used to implement the
input-from-back-end approach in the bootstrap code.  The mouse and
keyboard handling code from the <tt/kdrive/ X server in XFree86 would be a
good starting point for implementing the input-from-front-end approach
in the bootstrap code.

<p>Input-related work required with the bootstrap code:

<itemize>
    <item>Evaluate using back end input devices vs attaching the input
    devices directly to the front end:

    <itemize>
        <item>Implement back end input in the bootstrap code.
        <item>Implement front end input in the bootstrap code.
        <item>Evaluate both options with the bootstrap code extended to
        drive more than one back end.
    </itemize>
</itemize>

<p>Status: Not yet started.


<sect1>Output device handling

<p>The output of the DMX system displays rendering and windowing
requests across multiple screens.  The screens are typically arranged in
a grid such that together they represent a single large display.

<p>The output section of the DMX code consists of two parts.  The first
is in the front-end proxy X server, which accept client connections,
manages the windows and potentially renders primitives but does not
actually display any of the drawing primitives.  The second part is the
back-end X server(s), which accept commands from the front-end server
and display the results on their screens.

<sect2>Initialization

<p>The DMX front-end must first initialize its screens by connecting to
each of the back-end X servers and collecting information about each of
these screens.  However, the information collected from the back-end X
servers might be inconsistent.  Handling these cases can be difficult
and/or inefficient.  For example, a two screen system has one back-end X
server running at 16bpp while the second is running at 32bpp.
Converting rendering requests (e.g., XPutImage() or XGetImage()
requests) to the appropriate bit depth can be very time consuming.
Analyzing these cases to determine how or even if it is possible to
handle them is required.  The current Xinerama code handles many of
these cases (e.g., in PanoramiXConsolidate()) and will be used as a
starting point.  In general, the best solution is to use homogeneous X
servers and display devices.

<p>Once this screen consolidation is finished, the relative position of
each back-end X server's screen in the unified screen is initialized.  A
full-screen window is opened on each of the back-end X servers, and the
cursor on each screen is turned off.

<sect2>Handling rendering requests

<p>After initialization, X applications connect to the front-end server.
There are two possible implementations of how rendering and windowing
requests are handled in the DMX system:

<enum>
    <item>A shadow framebuffer is used in the front-end server as the
    render target.  In this option, all protocol requests are completely
    handled in the front-end server.  All state and resources are
    maintained in the front-end including a shadow copy of the entire
    framebuffer.  The framebuffers attached to the back-end servers are
    updated by XPutImage() calls with data taken directly from the
    shadow framebuffer.

    <p>This solution suffers from two main problems.  First, it does not
    take advantage of any accelerated hardware available in the system.
    Second, the size of the XPutImage() calls can be quite large and
    thus will be limited by the bandwidth available.

    <item>Rendering requests are sent to each back-end server for
    handling (as is done in the Xnest server described above).  In this
    option, certain protocol requests are handled in the front-end
    server and certain requests are repackaged and then sent to the
    back-end servers.  The framebuffer is distributed across the
    multiple back-end servers.  Rendering to the framebuffer is handled
    on each back-end and can take advantage of any acceleration
    available on the back-end servers' graphics display device.  State
    is maintained both in the front and back-end servers.

    <p>This solution suffers from two main drawbacks.  First, protocol
    requests are sent to all back-end servers -- even those that will
    completely clip the rendering primitive -- which wastes bandwidth
    and processing time.  Second, state is maintained both in the front
    and back-end servers.  These drawbacks are not as severe as in
    option 1 (above) and can either be overcome through optimizations or
    are acceptable.  Therefore, this option will be used in the final
    implementation.
</enum>

<p>Status: The initial bootstrap code implements option 1 above and
consists of a single back-end server with the front-end server's feeding
it via a shadow framebuffer with XPutImage() calls.  This solution is
temporary and only used as a implementation vehicle that will allow
independent development of the input and output sections of the DMX
system.  The next stage in the output device development will implement
the more efficient Xnest-style option.


<sect1>X Test Suite support

<p>The X Test Suite contains tests that verify Xlib functions operate
correctly.  The test suite is designed to run on a single X server;
however, since X applications will not be able to tell the difference
between the DMX server and a standard X server, the X Test Suite should
run with minimal modifications on the DMX server to test Xlib
correctness.  Additional tests which verify that rendering requests
crossing display boundaries will be developed and added to the test
suite.

<p>An extension which enables remote input testing is required for the X
Test Suite to function.  The XTest extension will be implemented in the
front-end server.

<p>Status: Not yet started.


<sect1>Configuration tool

<p>Configuring a large number of screens can be cumbersome, so a
configuration tool that allows a user to configure the screens to be
used will be developed.  It will support both a text and graphical
interface.

<p>Status: Not yet started.


<sect1>Optimizing DMX

<p>The simple Xnest-style solution described above sends the repackaged
protocol requests to all back-end servers.  Simple clipping tests in the
front-end X server will be used to limit sending the repackaged protocol
requests to only those back-end servers that will actually display the
data.

<p>In addition, each protocol request will be analyzed to determine if
it is possible to break the request into smaller pieces at display
boundaries.  The initial ones to be analyzed are put and get image
requests since they will require the greatest bandwidth to transmit data
between the front and back-end servers.  Other protocol requests will be
analyzed and those that will benefit from breaking them into smaller
requests will be implemented.

<p>In the simple solution, BLT operations (e.g., XCopyArea() and window
moves) that cross display boundaries require moving pixel data from
possibly multiple back-end servers to the front-end server and then
potentially to several back-end servers.  This data movement is very
inefficient and direct communication between back-end X servers could
eliminate several extraneous data copies.  An X server extension will be
implemented and used by the front and back-end servers to allow back-end
servers to directly send pixel data to each other under the direct
control of the front-end server.  This X extension is entirely optional
and the DMX system will function without it; however, the performance of
the system might degrade if the front and back-end servers do not
implemented it.

<p>Status: Not yet started.


<sect1>Common X extension support

<p>The XInput, XKeyboard and Shape X extensions are commonly used and
support for them will be added to the DMX system.  In addition, support
for the Render extension (as well as the XTest extension mentioned
above) will be added.

<p>The XInput extension will be used to allow multiple and non-standard
input devices to be accessed simultaneously.  These input devices might
be connected to either the front-end or back-end servers.  The XKeyboard
extension will allow much better keyboard mappings control.  The Shape
extension allows arbitrarily shaped windows and is commonly used by
various window managers.

<p>Status: Not yet started.


<sect1>OpenGL support

<p>OpenGL support using the Mesa code base exists in XFree86 release 4
and later.  Currently, the direct rendering infrastructure (DRI)
provides accelerated OpenGL support for local clients and unaccelerated
OpenGL support (i.e., software rendering) is provided for non-local
clients.

<p>The single head OpenGL support in XFree86 4.x will be extended to use
the DMX system.  When the front and back-end servers are on the same
physical hardware, it is possible to use the DRI to directly render to
the back-end servers.  First, the existing DRI will be extended to
support multiple display heads, and then to support the DMX system.
OpenGL rendering requests will be direct rendering to each back-end X
server.  The DRI will request the screen layout (either from the
existing Xinerama extension or a DMX-specific extension).  Support for
synchronized swap buffers will also be added (on hardware that supports
it).  Note that a single front-end server with a single back-end server
on the same physical machine can emulate accelerated indirect rendering.

<p>When the front and back-end servers are on different physical
hardware or are using non-XFree86 4.x X servers, a mechanism to render
primitives across the back-end servers will be provided.  There are
several options as to how this can be implemented.

<enum>
    <item>The existing OpenGL support in each back-end server can be
    used by repackaging rendering primitives and sending them to each
    back-end server.  This option is similar to the unoptimized
    Xnest-style approach mentioned above.  Optimization of this solution
    is beyond the scope of this project and is better suited to other
    distributed rendering systems.

    <item>Rendering to a pixmap in the front-end server using the
    current XFree86 4.x code, and then displaying to the back-ends via
    calls to XPutImage() is another option.  This option is similar to
    the shadow frame buffer approach mentioned above.  It is slower and
    bandwidth intensive, but has the advantage that the back-end servers
    are not required to have OpenGL support.
</enum>

<p>These, and other, options will be investigated in this phase of the
work.

<p>Future work (outside the scope of the current project) could further
extend this work to use a distributed rendering system such as
Chromium/WireGL.

<p>Status: Not yet started.


<!-- ============================================================ -->
    <sect>Implementation Details

      <p>
        In this section the current state of the project implementation
        is discussed.

      <sect1>Input

        <p>
          Input can currently be accepted from dummy input devices (for
          debugging), from a back-end server, and from the console.  In
          the future, input will also be accepted from the front-end.

        <sect2>InitInput()

          <sect3>dmxDummyInitInput()
              <!-- dmx/dmxdummy.c -->
            <p>
              Dummy input is selected using the <tt/-inputfrom dummy/
              command-line option.  The dummy input devices are stub
              core input devices that do not generate any input events.
              When dummy input is selected, dmxInputSource is
              initialized to <tt/InputFromDummy/.
            <p>
              When using dummy input devices, no wakeup handler to
              deliver synchronous input events is registered.  The
              processInputEvents fields of dmxInputRec is initialized
              with dmxDummyProcessInputEvents().

          <sect3>dmxBackEndInitInput()
              <!-- dmx/dmxbeinput.c -->
            <p>
              When a back-end server is specified on the command-line
              using the <tt/-inputfrom/ option, the keyboard and mouse
              from that back-end server are handled by input devices
              that retransmit core input events from a single back-end
              server.  This configuration would typically be used for a
              desktop, where one of the back-end servers is running on
              the user's main computer (i.e., the one connected to the
              most convenient keyboard and mouse).  When back-end input
              is selected, dmxInputSource is initialized to
              <tt/InputFromBackEnd/.
            <p>
              A DMX input device structure (dmxInputRec) is initialized
              with pointers and parameters for the back-end server.
              These include the display, window, and initial pointer
              position.  The processInputEvents field is set to
              dmxBackEndProcessInputEvents().
            <p>
              The file descriptor for the connection to the back end X
              server is registered with AddEnabledDevice().  The
              input-related file descriptors are often registered later
              when the devices are turned on.  In this case they are
              registered here because input for both pointer and
              keyboard arrives over the same connection to the back-end
              and it is simple to turn the input devices on/off by
              changing the event mask.  dmxBlockHandler() and
              dmxWakeupHandler() are registered as block and wakeup
              handlers.  The block handler does nothing, but is
              registered here because block and wakeup handlers must be
              registered as pairs, and because it might be needed later.

          <sect3>dmxConsoleInitInput()
              <!-- dmx/dmxconsole.c -->
            <p>
              If a console has been specified with the <tt/-console/
              option (the console window will currently display the
              layout of the back-end displays), then the <tt/-inputfrom
              console/ command-line option can be used to specify that
              mouse and keyboard events occurring over the console
              window (a normal X window) will be retransmitted to the
              back-end servers.  When console input is selected,
              dmxInputSource is initialized to <tt/InputFromConsole/.
            <p>
              A DMX input device structure (dmxInputRec) is initialized
              with pointers and parameters for the console window.  The
              processInputEvents field is set to
              dmxConsoleProcessInputEvents().  No event handlers are
              registered.
              
        <sect2>InitAndStartDevices()
            <!-- dix/devices.c - no changes to code - effect via
            function pointers, as described here -->  

          <sect3>Init and Start for the dummy input source.
            <p>
              MouseOnOff(dev, DEVICE_INIT) calls the DIX function
              InitPointerDeviceStruct() to initialize the core pointer
              parameters and mappings.  The number of buttons is set to
              3, the initial button map is an identity mapping,
              miPointerGetMotionEvents() is registered as the
              get-motion-events-function, a dummy function (NoopDDA())
              is registered as the control-function, and the motion
              buffer size is set to the value returned by
              miPointerGetMotionBufferSize().
            <p>
              KeyboardOnOff(dev, DEVICE_INIT) calls the DIX function
              InitKeyboardDeviceStruct() to initialize the core keyboard
              parameters and mappings.  The keycode range is set to the
              minimal range (8-8), the keycode to keysym table is set to
              the minimum size (1x1) with the single entry initialized
              to <tt/NoSymbol/, the modifier mapping is initialized
              implicitly to empty, and a dummy function (NoopDDA()) is
              registered as the bell and control functions.
            <p>
              MouseOnOff(dev, DEVICE_ON) and KeyboardOnOff(dev,
              DEVICE_ON) both set ((DevicePtr)dev)-&gt;on to TRUE to
              indicate that the devices are on.  No file descriptors are
              registered since these input devices are dummy devices
              that do not generate any events.

          <sect3>Init and Start for the back-end and console input sources.
            <p>
              dmxPointerProc(dev, DEVICE_INIT) calls
              XGetPointerMapping() to find out the number of physical
              buttons that the back-end's core pointer has. It then
              initializes the pointer map with the identity mapping.
              <bf/Note:/ this preserves any changes that may have been
              made to the back-end's pointer map.
            <p>
              In the future, we will consider setting the back-end map
              with XSetPointerMapping() after initializing it, or
              leaving the back-end map unchanged and maintain map
              changes internally. <!-- FIXME -->
            <p>
              It then calls the DIX function InitPointerDeviceStruct()
              to initialize the core pointer parameters and
              mappings. miPointerGetMotionEvents() is registered as the
              get-motionevents-function, dmxChangePointerControl() is
              registered as the control-function, and the motion buffer
              size is set to the value returned by
              miPointerGetMotionBufferSize().
              
              <!-- FIXME - Add a section about the pointer control
              function. -->

            <p>
              dmxKeyboardProc(dev, DEVICE_INIT) calls
              XGetModifierMapping() to get the back-end's modifier map,
              XDisplayKeycodes() to get the range of the back-end's
              keycodes, and XGetKeyboardMapping to get the back-end's
              keymap.  That information is passed to the DIX function
              InitKeyboardDeviceStruct() to initialize them for the
              front-end.
            <p>
              <bf/Note:/ Code is present for handling the XKEYBOARD
              extension, but it is currently disabled, untested, and is
              not described here.
            <p>
              dmxPointerProc(dev, DEVICE_ON) and dmxKeyboardProc(dev,
              DEVICE_ON) both set ((DevicePtr)dev)-&gt;on to TRUE to
              indicate that the devices are on.  No file descriptors are
              registered here because the back-end's connection file
              descriptor was already registered in the InitInput()
              phase.  The back-end's event mask is changed to enable
              pointer and keyboard events respectively to be received.
              dmxPointerProc() also moves the back-end's cursor position
              to the center of the screen.

        <sect2>devReadInput()

          <sect3>Reading input for the dummy input source.
            <p>
              No input events are generated, so there are no
              devReadInput functions.

          <sect3>Reading input for the back-end input source.
            <p>
              dmxWakeupHandler() gets called after input has been
              detected and, in turn, calls dmxCollectEvents() to collect
              and enqueue this back end input events.
            <p>
              dmxCollectEvents() processes all selected events from the
              back-end, and enqueues KeyPress, KeyRelease, ButtonPress,
              ButtonRelease, and MotionNotify events.  The key and
              button events are enqueued directly. The motion events
              need to be converted into the necessary form.  The motion
              events come in as the absolute position of the cursor on
              the back-end screen.  To allow the motion events to extend
              beyond the coordinates of the back-end screen, a technique
              is used to get relative events from the back-end.  This is
              done by setting (and resetting) the back-end cursor
              position back to the middle of the screen.  The movement
              relative to that position is calculated.  The current
              front-end cursor position is retrieved by calling
              miPointerPosition(), and the new front-end cursor position
              is calculated by adding in the movement.  This absolute
              position is enqueued as the motion event, and
              miPointerAbsoluteCursor() is called to set the new
              front-end cursor position.

          <sect3>Reading input for the console input source.
            <p>
              Reading input from the console window is similar to
              reading input from a standard X window and used the same
              functions as described above for reading input from the
              ban-end server.
              
        <sect2>ProcessInputEvents()
            <!-- dmx/dmxinput.c -->
          <p>
            ProcessInputEvents() calls the function that the
            dmxInputRec's processInputEvents field was initialized to
            during the InitInput() phase. If it was not initialized, then
            it returns without doing anything.

          <sect3>dmxDummyProcessInputEvents()
            <p>
              dmxDummyProcessInputEvents() is an empty function that
              returns without doing anything.  This is because there are
              no input events to process.

          <sect3>dmxBackEndProcessInputEvents()
            <p>
              dmxBackEndProcessInputEvents() processes the event queue
              by calling mieqProcessInputEvents(), and synchronizes the
              sprite with the cursor position by calling
              miPointerUpdate().  If the back-end's cursor has moved
              from the middle of its screen, it is moved back to the
              middle here.
            <p>
              Movement between screens is handled by the routines that
              this routine calls.  <!-- FIXME - Add a better description
              of the code that handles moving the pointer between
              screens. -->

          <sect3>dmxConsoelProcessInputEvents()
            <p>
              dmxConcoleProcessInputEvents() processes the event queue
              by calling mieqProcessInputEvents(), and synchronizes the
              sprite with the cursor position by calling
              miPointerUpdate().
              
        <sect2>DisableDevice()

          <sect3>DisableDevice() for dummy input.
            <p>
              MouseOnOff(dev, DEVICE_OFF) and KeyboardOnOff(dev,
              DEVICE_OFF) both set ((DevicePtr)dev)-&gt;on to FALSE to
              indicate that the devices are off.  No file descriptors
              were registered, so none need to be unregistered.

          <sect3>DisableDevice() for back-end and console input.
            <p>
              dmxPointerProc(dev, DEVICE_OFF) and dmxKeyboardProc(dev,
              DEVICE_OFF) both set ((DevicePtr)dev)-&gt;on to FALSE to
              indicate that the devices are off.  No file descriptors
              are unregistered here because the back-end's connection
              file descriptor is always registered. The back-end's event
              mask is changed to disable pointer and keyboard events
              respectively to be received.

        <sect2>CloseDevice()
          <p>
            All of device proc functions do exactly the same for
            DEVICE_CLOSE as for DEVICE_OFF.  No data structures are
            allocated within the drivers, so none need to be freed here.

      <sect1>Output
        <p>
          Currently, the bootstrap code can connect to multiple back-end
          servers.  All primitives are rendering in a shadow framebuffer
          and updates are sent to the back-end X servers via XPutImage()
          calls.  The front-end's screen characteristics are initialized
          to match those of the back-end server to which it is
          connected.  Below is a description of the basic output
          functions and how they are currently handled.

        <sect2>InitOutput()
            <!-- dmx/dmxinit.c -->
          <p>
            In the current DMX bootstrap code, the main configuration
            info required is the back-end server display names.  These
            can be specified from the command line (but not yet via a
            configuration file).  When not specified on the command
            line, a single back-end server is assumed, with its name
            taken from the DISPLAY environment variable.
            dmxDisplayInit() collects the back-end screens' attributes,
            visual info, colormap info and pixmap formats and
            initializes screen-dependent internal data structures.
            <bf/NOTE:/ at this time, the screen depth is hard-coded to
            16 bits, so the front-end server and all of the back-end
            servers must all run at this depth -- this will be changed
            in the future.  <!-- FIXME --> dmxSetPixmapFormat()
            initializes the pixmap settings from the back-end screens'
            pixmap format(s).  If is is possible to open a connection to
            the back-end server(s) and collect the required data from
            them, the screens are valid so AddScreen() can be called for
            each screen.
          <p>
            After all of the screens are initialized, their relative
            positions are set.  For now, the first screen (screen 0) has
            its origin set to (0,&nbsp;0), and each other screen is
            positioned to the right of the preceding screen, with the
            top edges aligned.  Configuration options will be added
            later to allow the screen layout to be customized.
            dmxInitOrigins() is called to calculate the DIX global array
            dixScreenOrigins&lsqb;&rsqb;, and to initialize other
            information used internally to determine how the
            pointer/cursor moves between multiple screens. The
            dixScreenOrigins&lsqb;&rsqb; array is used both in our DDX
            code, and in the DIX Xinerama code.
          <p>
            Currently, no signal handlers are installed to clean up
            after unexpected signals.  However, the DIX/OS layer handles
            SIGINT and SIGTERM.

        <sect2>dmxScreenInit()
            <!-- dmx/dmxscrinit.c -->
          <p>
            dmxScreenInit() performs several functions, which are
            outlined below.  All data about the screens are stored in
            the dmxScreens&lsqb;&rsqb; structure.  A pointer to the
            current screen, dmxScreen, is used locally in
            dmxScreenInit().
          <p>
            First, since the shadow framebuffer layer is used, the main
            memory copy of the framebuffer is allocated by calling
            shadowAlloc().  The pointer to the shadow framebuffer is
            saved in the dmxScreen private data structure.
          <p>
            The visual types for each depth are collected and
            miSetVisualTypesAndMasks() is called to set the MI layer's
            visual information.
          <p>
            The generic framebuffer layer code, FB, is used to render to
            the shadow framebuffer.  To initialize the FB layer,
            fbScreenInit() is called with the pointer to the shadow
            framebuffer among the arguments. After the framebuffer has
            initialized the basic screen functions, shadowInit() is
            called to initialize the shadow framebuffer
            layer. miInitializeBackingStore() and miDCInitialize() are
            called next to initialize backing store and the software
            cursor.  Several screen structure elements are initialized
            including the width and height of the screen, the white and
            black pixel (from the back-end sever).
          <p>
            Next, the CloseScreen() function is wrapped and the
            SaveScreen() function is replaced.  The back-end's screen
            saver is disabled and reset (unblanked).  The
            dmxSaveScreen() function takes care of turning on and off
            the back-end's screen saver as required.
          <p>
            At this point, the root window of the front-end server is
            created, which is simply a window on the back-end server.
            The window is initialized to be the full screen size, with
            the override_redirect bit set.  The back-end cursor is
            disabled, since the software cursor in the shadow
            framebuffer is currently used.  Next, the window is mapped
            and the default colormap is created (via
            fbCreateDefColormap()).
          <p>
            Finally, a GC, which is used to copy data from the shadow
            frame buffer to the back-end server, is created, and an
            Ximage is created from the shadow framebuffer, which is used
            in the XPutImage() calls in dmxShadowUpdateProc() (see
            below).

        <sect2>dmxCloseScreen()
            <!-- dmx/dmxscrinit.c -->
          <p>
            dmxCloseScreen currently restores the back-end server's
            screen saver parameters, and unwraps and calls the
            CloseScreen() that it wraps.
          <p> <!-- FIXME -->
            In the future code will be added to free resources allocated
            during screen initialization and unwrap any additional
            wrapped functions.

        <sect2>Rendering
          <p>
            Since the bootstrap code uses the shadow FB layer, its
            drawing functions are called initially.  They calculate the
            damaged area caused by the drawing function, and then call
            the lower layers that actually perform the rendering.
            Rendering requests are currently handled by the FB and MI
            layers.  The damaged areas of the screen are then refreshed
            by the dmxShadowUpdateProc() function the next time the
            shadow FB layer's BlockHandler is called.

<!-- ============================================================ -->
    <sect>Current issues

      <p>
        In this sections the current issues are outlined that require
        further investigation.

      <sect1>Fonts
        <p>
          The font path and glyphs need to be the same for the front-end
          and each of the back-end servers.  Font glyphs could be sent
          to the back-end servers as necessary but this might consume a
          significant amount of available bandwidth during font
          rendering for clients that use many different fonts (e.g.,
          Netscape).  Initially, the font server (xfs) will be used to
          provide the fonts to both the front-end and back-end servers.
          Other possibilities will be investigated during development.

      <sect1>Pixmaps
        <p>
          Is it more efficient to handle pixmaps in the front-end or the
          back-end?  During our initial development, rendering to
          pixmaps will be handled in the front end and transfer the data
          to the back-end servers as necessary.  Alternatives will be
          investigated during development.

      <sect1>Zero width rendering primitives
        <p>
          To allow pixmap and on-screen rendering to be pixel perfect,
          all back-end servers must render zero width primitives exactly
          the same as the front-end renders the primitives to pixmaps.
          For those back-end servers that do not exactly match, zero
          width primitives will be automatically converted to one width
          primitives.  This can be handled in the front-end server via
          the GC state.

      <sect1>Properties, resources and GC state
        <p>
          Properties, resources and GC state will handled in the
          front-end server and duplicate the state in each back-end
          server.  During the development cycle, other alternatives and
          optimizations will be investigated.

      <sect1>DMX extension
	<p>
	  The DMX extension will provide DMX-aware X clients with the
	  screen information (clipping rectangle for each screen
	  relative to the virtual screen) and window information (window
	  IDs for each back-end window that corresponds to each DMX
	  window).  Allowing clients to access this information will
	  permit them to directly render to those windows.

      <sect1>GLX support
	<p>
	  The utility of the software rendered OpenGL support for
	  non-local back-end servers is questionable.  On modern
	  graphics accelerators, it is likely that the back-end servers
	  will have hardware accelerated OpenGL support.  In general,
	  OpenGL/GLX support is best left to another approach (e.g.,
	  Chromium); however, it might prove useful to support pushing a
	  subset of primitives to the back-end servers, so that they can
	  handle the rendering directly.  We will investigate this
	  possibility.  These will include only those primitives that
	  are output only, as there are a number of issues with the
	  other primitives.  These other primitives will be left
	  unsupported and will either return errors or be turned into
	  no-ops.  If useful, then the software rendered OpenGL in the
	  front-end server will not be implemented.

      <sect1>Output scaling
	<p>
	  With very large tiled displays, it might be difficult to read
	  the information on the standard X desktop.  In particular, the
	  cursor can be easily lost and fonts could be difficult to
	  read.  Automatic primitive scaling might be very useful.  We
	  will investigate the possibility of scaling the cursor and
	  providing a set of alternate pre-scaled fonts to replace the
	  standard fonts that many applications use (e.g., fixed).
	  Other options for automatic scaling will also be investigated.

      <sect1>Per-screen colormaps
	<p>
	  Each screen in the set of back-end X servers should be able to
	  be adjusted via the configuration utility.  This support is
	  required to allow the back-end screens to be calibrated via
	  custom gamma tables.  On 24-bit systems that support a
	  DirectColor visual, this type of correction can be
	  accommodated.  One possible implementation would be to
	  advertise to X client of the DMX server a TrueColor visual
	  while using DirectColor visuals on the back-end servers to
	  implement this type of color correction.  Other options will
	  be investigated.

  </article>
  
  <!-- Local Variables: -->
  <!-- fill-column: 72  -->
  <!-- End:             -->