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
|
LibVNCServer: a library for easy implementation of a RDP/VNC server.
Copyright (C) 2001 Johannes E. Schindelin
What is it?
-----------
VNC is set of programs using the RFB (Remote Frame Buffer) protocol. They
are designed to "export" a frame buffer via net (if you don't know VNC, I
suggest you read "Basics" below). It is already in wide use for
administration, but it is not that easy to program a server yourself.
This has been changed by LibVNCServer.
There are two examples included:
- example, a shared scribble sheet
- pnmshow, a program to show PNMs (pictures) over the net.
The examples are not too well documented, but easy straight forward and a
good starting point.
Try example: it outputs on which port it listens (default: 5900), so it is
display 0. To view, call
vncviewer :0
You should see a sheet with a gradient and "Hello World!" written on it. Try
to paint something. Note that everytime you click, there is some bigger blot.
The size depends on the mouse button you click. Open a second vncviewer with
the same parameters and watch it as you paint in the other window. This also
works over internet. You just have to know either the name or the IP of your
machine. Then it is
vncviewer machine.where.example.runs.com:0
or similar for the remote client. Now you are ready to type something. Be sure
that your mouse sits still, because everytime the mouse moves, the cursor is
reset! If you are done with that demo, just press Escape in the viewer. Note
that the server still runs, even if you closed both windows. When you
reconnect now, everything you painted and wrote is still there. So you press
"Page Up" for a blank page.
The demo pnmshow is much simpler: you either provide a filename as argument
or pipe a file through stdin. Note that the file has to be a raw pnm/ppm file,
i.e. a truecolour graphics. Only the Escape key is implemented. This may be
the best starting point if you want to learn how to use LibVNCServer. You
are confronted with the fact that the bytes per pixel can only be 8, 16 or 32.
How to use
----------
To make a server, you just have to initialise a server structure using the
function rfbDefaultScreenInit, like
rfbScreenInfoPtr rfbScreen =
rfbGetScreen(argc,argv,width,height,8,3,bpp);
where byte per pixel should be 1, 2 or 4. If performance doesn't matter,
you may try bpp=3 (internally one cannot use native data types in this
case; if you want to use this, look at pnmshow24).
You then can set hooks and io functions (see below) or other
options (see below).
And you allocate the frame buffer like this:
rfbScreen->frameBuffer = (char*)malloc(width*height*bpp);
After that, you initialize the server, like
rfbInitServer(rfbScreen);
You can use a blocking event loop, a background (pthread based) event loop,
or implement your own using the rfbProcessEvents function.
Making it interactive
---------------------
Input is handled by IO functions (see below).
Whenever you change something in the frame buffer, call rfbMarkRectAsModified.
You should make sure that the cursor is not drawn before drawing yourself
by calling rfbUndrawCursor. You can also draw the cursor using rfbDrawCursor,
but it hardly seems necessary. For cursor details, see below.
Utility functions
-----------------
Whenever you draw something, you have to call
rfbMarkRectAsModified(screen,x1,y1,x2,y2).
This tells LibVNCServer to send updates to all connected clients.
Before you draw something, be sure to call
rfbUndrawCursor(cl).
This tells LibVNCServer to hide the cursor.
Remark: There are vncviewers out there, which know a cursor encoding, so
that network traffic is low, and also the cursor doesn't need to be
drawn the cursor everytime an update is sent. LibVNCServer handles
all the details. Just set the cursor and don't bother any more.
To set the mouse coordinates (or emulate mouse clicks), call
defaultPtrAddEvent(buttonMask,x,y,cl);
However, this works only if your client doesn't do local cursor drawing. There
is no way (to my knowledge) to set the pointer of a client via RFB protocol.
IMPORTANT: do this at the end of your function, because this actually draws
the cursor if no cursor encoding is active.
What is the difference between rfbScreenInfoPtr and rfbClientPtr?
-----------------------------------------------------------------
The rfbScreenInfoPtr is a pointer to a rfbScreenInfo structure, which
holds information about the server, like pixel format, io functions,
frame buffer etc.
The rfbClientPtr is a pointer to an rfbClientRec structure, which holds
information about a client, like pixel format, socket of the
connection, etc.
A server can have several clients, but needn't have any. So, if you
have a server and three clients are connected, you have one instance
of a rfbScreenInfo and three instances of rfbClientRec's.
The rfbClientRec structure holds a member
rfbScreenInfoPtr screen
which points to the server and a member
rfbClientPtr next
to the next client.
The rfbScreenInfo structure holds a member
rfbClientPtr rfbClientHead
which points to the first client.
So, to access the server from the client structure, you use client->screen.
To access all clients from a server, get screen->rfbClientHead and
iterate using client->next.
If you change client settings, be sure to use the provided iterator
rfbGetClientIterator(rfbScreen)
with
rfbClientIteratorNext(iterator)
and
rfbReleaseClientIterator
to prevent thread clashes.
Other options
-------------
These options have to be set between rfbGetScreen and rfbInitServer.
If you already have a socket to talk to, just set rfbScreen->inetdSock
(originally this is for inetd handling, but why not use it for your purpose?).
To also start an HTTP server (running on port 5800+display_number), you have
to set rfbScreen->httpdDir to a directory containing vncviewer.jar and
index.vnc (like the included "classes" directory).
Hooks and IO functions
----------------------
There exist the following IO functions as members of rfbScreen:
kbdAddEvent, kbdReleaseAllKeys, ptrAddEvent and setXCutText
kbdAddEvent(Bool down,KeySym key,rfbClientPtr cl)
is called when a key is pressed.
kbdReleaseAllKeys(rfbClientPtr cl)
is not called at all (maybe in the future).
ptrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl)
is called when the mouse moves or a button is pressed.
WARNING: if you want to have proper cursor handling, call
defaultPtrAddEvent(buttonMask,x,y,cl)
in your own function. This sets the coordinates of the cursor.
setXCutText(char* str,int len,rfbClientPtr cl)
is called when the selection changes.
There is only one hook:
newClientHook(rfbClientPtr cl)
is called when a new client has connected.
You can also override the following methods:
getCursorPtr(rfbClientPtr cl)
This could be used to make an animated cursor (if you really want ...)
setTranslateFunction(rfbClientPtr cl)
If you insist on colour maps or something more obscure, you have to
implement this. Default is a trueColour mapping.
Cursor handling
---------------
The screen holds a pointer
rfbCursorPtr cursor
to the current cursor. Whenever you set it, remember that any dynamically
created cursor (like return value from rfbMakeXCursor) is not free'd!
The rfbCursor structure consists mainly of a mask and a source. The mask
describes, which pixels are drawn for the cursor (a cursor needn't be
rectangular). The source describes, which colour those pixels should have.
The standard is an XCursor: a cursor with a foreground and a background
colour (stored in backRed,backGreen,backBlue and the same for foreground
in a range from 0-0xffff). Therefore, the arrays "mask" and "source"
contain pixels as single bits stored in bytes in MSB order. The rows are
padded, such that each row begins with a new byte (i.e. a 10x4
cursor's mask has 2x4 bytes, because 2 bytes are needed to hold 10 bits).
It is however very easy to make a cursor like this:
char* cur=" "
" xx "
" x "
" ";
char* mask="xxxx"
"xxxx"
"xxxx"
"xxx ";
rfbCursorPtr c=rfbMakeXCursor(4,4,cur,mask);
You can even set "mask" to NULL in this call and LibVNCServer will calculate
a mask for you (dynamically, so you have to free it yourself).
There is also an array named "richSource" for colourful cursors. They have
the same format as the frameBuffer (i.e. if the server is 32 bit,
a 10x4 cursor has 4x10x4 bytes).
History
-------
LibVNCServer is based on Tridia VNC and OSXvnc, which in turn are based on
the original code from ORL/AT&T.
When I began hacking with computers, my first interest was speed. So, when I
got around assembler, I programmed the floppy to do much of the work, because
it's clock rate was higher than that of my C64. This was my first experience
with client/server techniques.
When I came around Xwindows (much later), I was at once intrigued by the
elegance of such connectedness between the different computers. I used it
a lot - not the least priority lay on games. However, when I tried it over
modem from home, it was no longer that much fun.
When I started working with ASP (Application Service Provider) programs, I
tumbled across Tarantella and Citrix. Being a security fanatic, the idea of
running a server on windows didn't appeal to me, so Citrix went down the
basket. However, Tarantella has it's own problems (security as well as the
high price). But at the same time somebody told me about this "great little
administrator's tool" named VNC. Being used to windows programs' sizes, the
surprise was reciprocal inverse to the size of VNC!
At the same time, the program "rdesktop" (a native Linux client for the
Terminal Services of Windows servers) came to my attention. There where even
works under way to make a protocol converter "rdp2vnc" out of this. However,
my primary goal was a slow connection and rdp2vnc could only speak RRE
encoding, which is not that funny with just 5kB/s. Tim Edmonds, the original
author of rdp2vnc, suggested that I adapt it to Hextile Encoding, which is
better. I first tried that, but had no success at all (crunchy pictures).
Also, I liked the idea of an HTTP server included and possibly other
encodings like the Tight Encodings from Const Kaplinsky. So I started looking
for libraries implementing a VNC server where I could steal what I can't make.
I found some programs based on the demo server from AT&T, which was also the
basis for rdp2vnc (can only speak Raw and RRE encoding). There were some
rumors that GGI has a VNC backend, but I didn't find any code, so probably
there wasn't a working version anyway.
All of a sudden, everything changed: I read on freshmeat that "OSXvnc" was
released. I looked at the code and it was not much of a problem to work out
a simple server - using every functionality there is in Xvnc. It became clear
to me that I *had* to build a library out of it, so everybody can use it.
Every change, every new feature can propagate to every user of it.
It also makes everything easier:
You don't care about the cursor, once set (or use the standard cursor).
You don't care about those sockets. You don't care about encodings.
You just change your frame buffer and inform the library about it. Every once
in a while you call rfbProcessEvents and that's it.
Basics
------
VNC (Virtual network computing) works like this: You set up a server and can
connect to it via vncviewers. The communication uses a protocol named RFB
(Remote Frame Buffer). If the server supports HTTP, you can also connect
using a java enabled browser. In this case, the server sends back a
vncviewer applet with the correct settings.
There exist several encodings for VNC, which are used to compress the regions
which have changed before they are sent to the client. A client need not be
able to understand every encoding, but at least Raw encoding. Which encoding
it understands is negotiated by the RFB protocol.
The following encodings are known to me:
Raw, RRE, CoRRE, Hextile, CopyRect from the original AT&T code and
Tight, ZLib, LastRect, XCursor, RichCursor from Const Kaplinsky et al.
If you are using a modem, you want to try the "new" encodings. Especially
with my 56k modem I like ZLib or Tight with Quality 0. In my tests, it even
beats Tarantella.
There is the possibility to set a password, which is also negotiated by the
RFB protocol, but IT IS NOT SECURE. Anybody sniffing your net can get the
password. You really should tunnel through SSH.
License
-------
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.dfdf
Contact
-------
To contact me, mail me: Johannes dot Schindelin at gmx dot de
|