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
|
LBX design notes
----------------
Much of LBX is implemented as an extension. Some modifications have
been made to the Xserver OS layer to support its requirements, but
the only other impact LBX has on the core server are some hooks for
supporting tags.
Flow control
LBX multiplexes the data streams of all its clients into one, and then
splits them apart again when they are received. The X_LbxSwitch message
is used to tell each end which client is using the wire at the time.
Swapping
Swapping is handled as with any X extension, with one caveat.
Since a proxy can be supporting clients with different byte orders,
and they all share the same wire, all length fields are converted
to be sent in the proxy byte order. This prevents any problems with
length computation that may occur when clients are switched.
Tags
Tags are used to support large data items that are expected to be
queried multiple times. Such things as the keyboard map and font
metrics are often requested by multiple clients. Rather than send the
data each time, the first time the data is sent it includes a tag.
The proxy saves this data, so that subsequent requests can send
only the tag. The proxy then pulls up its local copy of the data
and sends it on to its clients.
To support this, the Xserver keeps track of what tags are known to
the proxy. The proxy can send InvalidateTag messages if it doesn't
store the tagged data. The server also sends InvalidateTag messages
when the data changes, to allow the proxy to clean out obsolete data.
If the server & proxy get out of sync, and the proxy receives a
tag which is cannot resolve, it can send a QueryTag message and the
server will respond with the requested data.
Property data makes special use of tags. A common use of properties
is for inter-client communication. If both clients use the proxy,
its wasteful to send the data to the server and then back, when
the server may never need it. X_LbxChangeProperty does the
same work as X_ChangeProperty, but it does not send the data.
X_LbxChangeProperty replies with a tag which points to the data.
If the property information is used locally, the server responds to
X_LbxGetProperty with a tag, and the property data need never be
sent to the server. If the server does require the data, it can
issue a QueryTag message. The proxy can also send the data on at
any time if it thinks its appropriate (ie, wire goes idle).
The heuristics of property handling can be complex. Because
X_LbxChangeProperty is a round-trip, it can take longer to use it
than X_ChangeProperty for some wires, especially if the amount of
property data is small. Using X_LbxChangeProperty can also be
a mistake for ICCCM properties, if the window manager is not a
proxy client.
Tag caching
The proxy contains a tag caching system that allows it to store a
controlled amount of tag data. Limited proxy hosts may wish to use
small caches or none at all. When the cache becomes full, it will
throw out the oldest data (and send the appropriate InvalidateTag
message to the Xserver).
Currently two tag caches are used, one for properties and another
for other data types. This may want to be modified to separate
out font metrics.
All tagged data is stored in the proxy byte order.
Short-circuiting
Short-circuiting is used to handle 'constant' data. This includes
atoms, colorname/RGB mappings, and AllocColor calls. Atoms and
colorname/RGB mappings stay constant for the life of the server.
AllocColor replies are constant for each colormap. Short-circuiting
replaces round-trip requests with one-way requests, and can sometimes
use one in place of many.
Atoms are used heavily for ICCCM communication. Once the proxy knows
the string<->atom mapping, it has no need to send the request on to
the server.
Colorname/RGB mappings are constant, so once the proxy sees the
response from X_LookupColor, it need not forward any subsequent
requests.
Clients often use the same color cells, so once a read-only color
allocation has occurred, the proxy knows what RGB values should
be returned to the client. The proxy doesn't need to forward any
AllocColor requests it can resolve, but it must tell the server to
modify the color cell's reference count. X_LbxIncrementPixel is
used to support this.
For all three classes of short-circuiting, the server must still
tell the server a request has occured, so that the request sequence
numbers stay in sync. This is done with X_LbxModifySequence.
Sequence numbers cause the major complication with short-circuiting.
X guarantees that any replies, events or errors generated by a
previous request will be sent before those of a later request.
This means that any requests that can be handled by the proxy must
have their reply sent after any previous events or errors.
There are 3 possible ways to support short-circuiting:
- fully correct protocol, which ensures that nothing can be out
of order
- mostly correct protocol, where only errors can be out of order
- poor protocol, where events & errors can be out of order.
A smart client or test suite could send a request it knows will
generate an event or error, followed by an InternAtom request,
and get the InternAtom reply before it gets the event.
Xlib hides this problem from most applications, so the 'poor'
protocol can be sufficient. For a fully safe environment, the proxy
can be compiled to use any of the three forms (or no short-circuiting
at all). In no case do we allow replies to come back out of order.
The proxy knows what can come back from all the core requests --
for any extensions it assumes the worst case and expects a reply.
Reply matching
LBX needs to store information about certain requests to support both
tags and short-circuiting. To do this, it creates a Reply record for
each request that can return a reply. Most of these are only used
as place holders, but for special requests data is stashed in them
(eg, InternAtom needs to save the atom name, so it can store it with
the returned Atom.)
Using the core protocol and Xlib, there is usually only one
pending Reply record per client. One common exception is caused by
XGetWIndowAttributes(), which sends two roundtrip requests and then
collects the results from both.
Test suites and interfaces other than Xlib may not follow this
convention, and could result in a number of pending Reply records.
The worst case are extensions. If the proxy doesn't know about
them, it must assume the worst case, and create a Reply record for
each extension request. These cannot be cleaned out until data
comes back from the server (event, error or reply), which allows
the proxy to flush any Reply records with older sequence numbers.
This has the potential to eat a huge amount of proxy memory, if an
extension issues a huge number of one-way requests.
Motion events
To prevent clogging the wire with MotionNotify events, the server and
proxy work together to minimize the number of events on the wire.
This is done with X_LbxAllowMotion. The proxy determines how many
events 'fill' the wire (currently hardcoded -- should be computed) and
'allows' that many events. When the server generates a MotionEvent
for a proxy client, it decrements the allowed number, throwing away
any after the wire is full. When the proxy receives a MotionNotify,
it sends an X_LbxAllowMotion to the server.
Delta cache
LBX takes advantage of the fact that an X message may be very similar
to one that has been previously sent. For example, a KeyPress event
may differ from a previous KeyPress event in just a few bytes. By
sending just the bytes that differ (or "deltas"), the number of bytes
sent over the wire can be substantially reduced. Delta compaction is
used on requests being sent by the proxy as well as on replies and
events being sent by the server.
Both the server and the proxy keep a cache of the N (currently
defaulted to 16) X messages sent and received. Only messages
smaller than a fixed maximum (currently defaulted to 64) are
saved in the delta cache.
Whenever the server has a message to send, and the message is of
appropriate length, the message is compared to any same-length messages
in its send cache. The message with the fewest number of differing
bytes is selected. If the number of differences is small enough and
the resulting X_LbxDelta message would not be longer than the original
message, the X_LbxDelta message is sent in place of the original.
The original message must also be place in the send cache. The proxy
uses the same algorithm when it has a message to send to the server.
Compression
Before being passed down to the transport layer, all messages are
passed through a general purpose data compressor (currently only LZW is
supported). The LZW compressor is presented with a simple byte stream -
the X and LBX message boundaries are not apparent. The data is
broken up into fixed sized blocks. Each block is compressed, then a two
byte header is prepended, and then the entire packet is transmitted.
(NOTE: LBX is designed to allow other compression algorithms to be used
instead of LZW. However, there is no requirement that the packet format
used for LZW be used for implementations involving other compression
algorithms.) The LZW compressor also provides for the ability to transmit
data uncompressed. This is useful when the data has already been
compressed by some other means (eg. a bitmap may be compressed using a
FAX G4 encoding) and further compression would not be effective.
The LZW compressor attempts to buffer up enough raw data to fill out a
complete block before actually compressing the data. This improves
compression efficiency. However, the LZW buffers are always flushed
before the server/proxy goes to sleep to await more data.
Master Client
When the initial X connection between the proxy and the server is
converted to LBX mode, the proxy itself becomes the "master" client.
New client requests and some tags related messages are sent in the
context of the master client.
Server Grabs
The master client must be grab-proof because the server may need to
retrieve tagged data from the proxy at any time. Since the master client
is multiplexed onto the same connection as other clients, the other
clients effectively become grab-proof as well. While the server is
grabbed, messages for non-master clients can be buffered. However, it's
possible for some client to eat up a large amount of buffer space before
the server is ungrabbed. In order to counteract this, when the server
is grabbed, an X_LbxListenToOne message will be sent to the proxy. If
the client grabbing the server belongs to the proxy, then only master
client and grabbing client messages will be transmitted to the server.
If the grabbing client does not belong to the proxy, then only master
client messages will be transmitted. The server will transmit an
X_LbxListenToAll to the proxy when the server is ungrabbed.
Graphics Re-encoding
The LBX proxy attempts to reencode X_PolyPoint, X_PolyLine, X_PolySegment,
X_PolyRectangle, X_PolyArc, X_FillPoly, X_PolyFillRectangle, and
X_PolyFillArc requests. If the request can be reencoded, it is
replaced by an equivalent LBX form of the request. The requests
are reencoded by attempting to reduce all 2-byte coordinate, length,
width and angle fields to 1 byte. Where applicable, the coordinate mode
is also converted to "previous" to improve the compressibility of the
resulting data.
Data Flow
The LBX data stream goes through a number of layers, all of which
should be negotiable:
0. client requests
1. read by LBX proxy
2. potential byte-swapping
3. requests-specific processing and reencoding
4. potential byte swapping
5. delta replacement
6. stream (LZW) compression
transport
5. stream decompression
4. delta substitution
3. potential byte swapping
2. re-encoding
1. request processing
The reverse process occurs with X server replies/events/errors.
--------
$NCDXorg: @(#)design,v 1.4 1994/04/11 18:17:03 lemke Exp $
$Xorg: design,v 1.3 2000/08/17 19:53:53 cpqbld Exp $
|