2
* cursor.c - support for cursor shape updates.
6
* Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
7
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
9
* This is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation; either version 2 of the License, or
12
* (at your option) any later version.
14
* This software is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this software; if not, write to the Free Software
21
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
26
#include <rfb/rfbregion.h>
29
* Send cursor shape either in X-style format or in client pixel format.
33
rfbSendCursorShape(cl)
37
rfbFramebufferUpdateRectHeader rect;
38
rfbXCursorColors colors;
40
int bitmapRowBytes, maskBytes, dataBytes;
45
pCursor = cl->screen->cursor;
46
/*if(!pCursor) return TRUE;*/
48
if (cl->useRichCursorEncoding) {
49
if(pCursor && !pCursor->richSource)
50
MakeRichCursorFromXCursor(cl->screen,pCursor);
51
rect.encoding = Swap32IfLE(rfbEncodingRichCursor);
53
if(pCursor && !pCursor->source)
54
MakeXCursorFromRichCursor(cl->screen,pCursor);
55
rect.encoding = Swap32IfLE(rfbEncodingXCursor);
58
/* If there is no cursor, send update with empty cursor data. */
60
if ( pCursor && pCursor->width == 1 &&
61
pCursor->height == 1 &&
62
pCursor->mask[0] == 0 ) {
66
if (pCursor == NULL) {
67
if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE ) {
68
if (!rfbSendUpdateBuf(cl))
71
rect.r.x = rect.r.y = 0;
72
rect.r.w = rect.r.h = 0;
73
memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
74
sz_rfbFramebufferUpdateRectHeader);
75
cl->ublen += sz_rfbFramebufferUpdateRectHeader;
77
cl->rfbCursorShapeBytesSent += sz_rfbFramebufferUpdateRectHeader;
78
cl->rfbCursorShapeUpdatesSent++;
80
if (!rfbSendUpdateBuf(cl))
86
/* Calculate data sizes. */
88
bitmapRowBytes = (pCursor->width + 7) / 8;
89
maskBytes = bitmapRowBytes * pCursor->height;
90
dataBytes = (cl->useRichCursorEncoding) ?
91
(pCursor->width * pCursor->height *
92
(cl->format.bitsPerPixel / 8)) : maskBytes;
94
/* Send buffer contents if needed. */
96
if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader +
97
sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) {
98
if (!rfbSendUpdateBuf(cl))
102
if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader +
103
sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) {
104
return FALSE; /* FIXME. */
107
saved_ublen = cl->ublen;
109
/* Prepare rectangle header. */
111
rect.r.x = Swap16IfLE(pCursor->xhot);
112
rect.r.y = Swap16IfLE(pCursor->yhot);
113
rect.r.w = Swap16IfLE(pCursor->width);
114
rect.r.h = Swap16IfLE(pCursor->height);
116
memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
117
cl->ublen += sz_rfbFramebufferUpdateRectHeader;
119
/* Prepare actual cursor data (depends on encoding used). */
121
if (!cl->useRichCursorEncoding) {
122
/* XCursor encoding. */
123
colors.foreRed = (char)(pCursor->foreRed >> 8);
124
colors.foreGreen = (char)(pCursor->foreGreen >> 8);
125
colors.foreBlue = (char)(pCursor->foreBlue >> 8);
126
colors.backRed = (char)(pCursor->backRed >> 8);
127
colors.backGreen = (char)(pCursor->backGreen >> 8);
128
colors.backBlue = (char)(pCursor->backBlue >> 8);
130
memcpy(&cl->updateBuf[cl->ublen], (char *)&colors, sz_rfbXCursorColors);
131
cl->ublen += sz_rfbXCursorColors;
133
bitmapData = (uint8_t *)pCursor->source;
135
for (i = 0; i < pCursor->height; i++) {
136
for (j = 0; j < bitmapRowBytes; j++) {
137
bitmapByte = bitmapData[i * bitmapRowBytes + j];
138
cl->updateBuf[cl->ublen++] = (char)bitmapByte;
142
/* RichCursor encoding. */
143
int bpp1=cl->screen->rfbServerFormat.bitsPerPixel/8,
144
bpp2=cl->format.bitsPerPixel/8;
145
(*cl->translateFn)(cl->translateLookupTable,
146
&(cl->screen->rfbServerFormat),
147
&cl->format, (char*)pCursor->richSource,
148
&cl->updateBuf[cl->ublen],
149
pCursor->width*bpp1, pCursor->width, pCursor->height);
151
cl->ublen += pCursor->width*bpp2*pCursor->height;
154
/* Prepare transparency mask. */
156
bitmapData = (uint8_t *)pCursor->mask;
158
for (i = 0; i < pCursor->height; i++) {
159
for (j = 0; j < bitmapRowBytes; j++) {
160
bitmapByte = bitmapData[i * bitmapRowBytes + j];
161
cl->updateBuf[cl->ublen++] = (char)bitmapByte;
165
/* Send everything we have prepared in the cl->updateBuf[]. */
167
cl->rfbCursorShapeBytesSent += (cl->ublen - saved_ublen);
168
cl->rfbCursorShapeUpdatesSent++;
170
if (!rfbSendUpdateBuf(cl))
177
* Send cursor position (PointerPos pseudo-encoding).
181
rfbSendCursorPos(rfbClientPtr cl)
183
rfbFramebufferUpdateRectHeader rect;
185
if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
186
if (!rfbSendUpdateBuf(cl))
190
rect.encoding = Swap32IfLE(rfbEncodingPointerPos);
191
rect.r.x = Swap16IfLE(cl->screen->cursorX);
192
rect.r.y = Swap16IfLE(cl->screen->cursorY);
196
memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
197
sz_rfbFramebufferUpdateRectHeader);
198
cl->ublen += sz_rfbFramebufferUpdateRectHeader;
200
cl->rfbCursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader;
201
cl->rfbCursorPosUpdatesSent++;
203
if (!rfbSendUpdateBuf(cl))
209
/* conversion routine for predefined cursors in LSB order */
210
unsigned char rfbReverseByte[0x100] = {
211
/* copied from Xvnc/lib/font/util/utilbitmap.c */
212
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
213
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
214
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
215
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
216
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
217
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
218
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
219
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
220
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
221
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
222
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
223
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
224
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
225
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
226
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
227
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
228
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
229
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
230
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
231
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
232
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
233
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
234
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
235
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
236
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
237
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
238
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
239
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
240
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
241
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
242
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
243
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
246
void rfbConvertLSBCursorBitmapOrMask(int width,int height,unsigned char* bitmap)
248
int i,t=(width+7)/8*height;
250
bitmap[i]=rfbReverseByte[(int)bitmap[i]];
253
/* Cursor creation. You "paint" a cursor and let these routines do the work */
255
rfbCursorPtr rfbMakeXCursor(int width,int height,const char* cursorString,const char* maskString)
257
int i,j,w=(width+7)/8;
258
rfbCursorPtr cursor = (rfbCursorPtr)calloc(1,sizeof(rfbCursor));
262
cursor->cleanup=TRUE;
264
cursor->height=height;
265
/*cursor->backRed=cursor->backGreen=cursor->backBlue=0xffff;*/
266
cursor->foreRed=cursor->foreGreen=cursor->foreBlue=0xffff;
268
cursor->source = (unsigned char*)calloc(w,height);
269
cursor->cleanupSource = TRUE;
270
for(j=0,cp=cursorString;j<height;j++)
271
for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>1,cp++)
272
if(*cp!=' ') cursor->source[j*w+i/8]|=bit;
275
cursor->mask = (unsigned char*)calloc(w,height);
276
for(j=0,cp=maskString;j<height;j++)
277
for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>1,cp++)
278
if(*cp!=' ') cursor->mask[j*w+i/8]|=bit;
280
cursor->mask = (unsigned char*)rfbMakeMaskForXCursor(width,height,(char*)cursor->source);
281
cursor->cleanupMask = TRUE;
286
char* rfbMakeMaskForXCursor(int width,int height,char* source)
288
int i,j,w=(width+7)/8;
289
char* mask=(char*)calloc(w,height);
292
for(j=0;j<height;j++)
293
for(i=w-1;i>=0;i--) {
295
if(j>0) c|=source[(j-1)*w+i];
296
if(j<height-1) c|=source[(j+1)*w+i];
300
if(i<w-1 && (c&0x01))
303
mask[j*w+i]|=(c<<1)|c|(c>>1);
309
void rfbFreeCursor(rfbCursorPtr cursor)
312
if(cursor->cleanupRichSource && cursor->richSource)
313
free(cursor->richSource);
314
if(cursor->cleanupSource && cursor->source)
315
free(cursor->source);
316
if(cursor->cleanupMask && cursor->mask)
321
cursor->cleanup=cursor->cleanupSource=cursor->cleanupMask
322
=cursor->cleanupRichSource=FALSE;
323
cursor->source=cursor->mask=cursor->richSource=0;
329
/* background and foregroud colour have to be set beforehand */
330
void MakeXCursorFromRichCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor)
332
rfbPixelFormat* format=&rfbScreen->rfbServerFormat;
333
int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8,
334
width=cursor->width*bpp;
336
char *back=(char*)&background;
339
if(cursor->source && cursor->cleanupSource)
340
free(cursor->source);
341
cursor->source=(unsigned char*)calloc(w,cursor->height);
342
cursor->cleanupSource=TRUE;
344
if(format->bigEndian)
347
background=cursor->backRed<<format->redShift|
348
cursor->backGreen<<format->greenShift|cursor->backBlue<<format->blueShift;
350
for(j=0;j<cursor->height;j++)
351
for(i=0,bit=0x80;i<cursor->width;i++,bit=(bit&1)?0x80:bit>>1)
352
if(memcmp(cursor->richSource+j*width+i*bpp,back,bpp))
353
cursor->source[j*w+i/8]|=bit;
356
void MakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor)
358
rfbPixelFormat* format=&rfbScreen->rfbServerFormat;
359
int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8;
360
uint32_t background,foreground;
361
char *back=(char*)&background,*fore=(char*)&foreground;
365
if(cursor->richSource && cursor->cleanupRichSource)
366
free(cursor->richSource);
367
cp=cursor->richSource=(unsigned char*)calloc(cursor->width*bpp,cursor->height);
368
cursor->cleanupRichSource=TRUE;
370
if(format->bigEndian) {
375
background=cursor->backRed<<format->redShift|
376
cursor->backGreen<<format->greenShift|cursor->backBlue<<format->blueShift;
377
foreground=cursor->foreRed<<format->redShift|
378
cursor->foreGreen<<format->greenShift|cursor->foreBlue<<format->blueShift;
380
for(j=0;j<cursor->height;j++)
381
for(i=0,bit=0x80;i<cursor->height;i++,bit=(bit&1)?0x80:bit>>1,cp+=bpp)
382
if(cursor->source[j*w+i/8]&bit) memcpy(cp,fore,bpp);
383
else memcpy(cp,back,bpp);
386
rfbBool rfbGetCursorBounds(rfbScreenInfoPtr screen,
389
rfbCursorPtr cursor = screen->cursor;
392
if (!bounds || !cursor)
395
x1 = screen->cursorX - cursor->xhot;
399
x2 = x1 + cursor->width;
400
if (x2 >= screen->width)
401
x2 = screen->width - 1;
403
y1 = screen->cursorY - cursor->yhot;
407
y2 = y1 + cursor->height;
408
if (y2 >= screen->height)
409
y2 = screen->height - 1;
411
if (x2 <= x1 || y2 <= y1)
422
/* functions to draw/hide cursor directly in the frame buffer */
424
void rfbUndrawCursor(rfbScreenInfoPtr screen,
427
rfbCursorPtr cursor = screen->cursor;
429
int j, bpp, rowstride;
437
if (!rfbGetCursorBounds (screen, bounds))
441
bpp = screen->rfbServerFormat.bitsPerPixel / 8;
442
rowstride = screen->paddedWidthInBytes;
444
/* restore saved data */
445
for (j = 0; j < (bounds->y2 - bounds->y1); j++)
446
memcpy(screen->frameBuffer + (bounds->y1 + j) * rowstride + bounds->x1 * bpp,
447
screen->underCursorBuffer + j * (bounds->x2 - bounds->x1) * bpp,
448
(bounds->x2 - bounds->x1) * bpp);
451
void rfbDrawCursor(rfbScreenInfoPtr screen,
454
rfbCursorPtr cursor = screen->cursor;
466
if (!rfbGetCursorBounds (screen, bounds))
470
bpp = screen->rfbServerFormat.bitsPerPixel / 8;
471
rowstride = screen->paddedWidthInBytes;
473
bufSize = cursor->width * cursor->height * bpp;
474
if (screen->underCursorBufferLen < bufSize) {
475
if (screen->underCursorBuffer)
476
free(screen->underCursorBuffer);
478
screen->underCursorBuffer = malloc(bufSize);
479
screen->underCursorBufferLen = bufSize;
482
i1 = j1 = 0; /* offset in cursor */
483
if (screen->cursorX < cursor->xhot)
484
i1 = cursor->xhot - screen->cursorX;
485
if (screen->cursorY < cursor->yhot)
486
j1 = cursor->xhot - screen->cursorY;
488
/* save what is under the cursor */
489
for (j = 0; j < (bounds->y2 - bounds->y1); j++)
490
memcpy(screen->underCursorBuffer + j * (bounds->x2 - bounds->x1) * bpp,
491
screen->frameBuffer + (bounds->y1 + j) * rowstride + bounds->x1 * bpp,
492
(bounds->x2 - bounds->x1) * bpp);
494
if (!cursor->richSource)
495
MakeRichCursorFromXCursor(screen, cursor);
497
w = (cursor->width + 7) / 8;
499
/* now the cursor has to be drawn */
500
for (j = 0; j < (bounds->y2 - bounds->y1); j++)
501
for (i = 0; i < (bounds->x2 - bounds->x1); i++)
502
if ((cursor->mask[(j + j1) * w + (i + i1) / 8] << ((i + i1) & 7)) & 0x80)
503
memcpy(screen->frameBuffer + (bounds->y1 + j) * rowstride + (bounds->x1 + i) * bpp,
504
cursor->richSource + (j + j1) * cursor->width * bpp + (i + i1) * bpp,
508
void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,rfbBool freeOld)
510
LOCK(rfbScreen->cursorMutex);
512
if(rfbScreen->cursor && (freeOld || rfbScreen->cursor->cleanup))
513
rfbFreeCursor(rfbScreen->cursor);
515
rfbScreen->cursor = c;
517
UNLOCK(rfbScreen->cursorMutex);
520
void rfbSetCursorPosition(rfbScreenInfoPtr screen, rfbClientPtr client, int x, int y)
522
rfbClientIteratorPtr iterator;
525
if (x == screen->cursorX || y == screen->cursorY)
528
LOCK(screen->cursorMutex);
531
UNLOCK(screen->cursorMutex);
533
/* Inform all clients about this cursor movement. */
534
iterator = rfbGetClientIterator(screen);
535
while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
536
cl->cursorWasMoved = TRUE;
538
rfbReleaseClientIterator(iterator);
540
/* The cursor was moved by this client, so don't send CursorPos. */
542
client->cursorWasMoved = FALSE;