1/*
2 * cursor.c - support for cursor shape updates.
3 */
4
5/*
6 *  Copyright (C) 2000, 2001 Const Kaplinsky.  All Rights Reserved.
7 *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
8 *
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.
13 *
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.
18 *
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,
22 *  USA.
23 */
24
25#include <rfb/rfb.h>
26#include <rfb/rfbregion.h>
27#include "private.h"
28
29void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2);
30
31/*
32 * Send cursor shape either in X-style format or in client pixel format.
33 */
34
35rfbBool
36rfbSendCursorShape(rfbClientPtr cl)
37{
38    rfbCursorPtr pCursor;
39    rfbFramebufferUpdateRectHeader rect;
40    rfbXCursorColors colors;
41    int saved_ublen;
42    int bitmapRowBytes, maskBytes, dataBytes;
43    int i, j;
44    uint8_t *bitmapData;
45    uint8_t bitmapByte;
46
47    /* TODO: scale the cursor data to the correct size */
48
49    pCursor = cl->screen->getCursorPtr(cl);
50    /*if(!pCursor) return TRUE;*/
51
52    if (cl->useRichCursorEncoding) {
53      if(pCursor && !pCursor->richSource)
54	rfbMakeRichCursorFromXCursor(cl->screen,pCursor);
55      rect.encoding = Swap32IfLE(rfbEncodingRichCursor);
56    } else {
57       if(pCursor && !pCursor->source)
58	 rfbMakeXCursorFromRichCursor(cl->screen,pCursor);
59       rect.encoding = Swap32IfLE(rfbEncodingXCursor);
60    }
61
62    /* If there is no cursor, send update with empty cursor data. */
63
64    if ( pCursor && pCursor->width == 1 &&
65	 pCursor->height == 1 &&
66	 pCursor->mask[0] == 0 ) {
67	pCursor = NULL;
68    }
69
70    if (pCursor == NULL) {
71	if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE ) {
72	    if (!rfbSendUpdateBuf(cl))
73		return FALSE;
74	}
75	rect.r.x = rect.r.y = 0;
76	rect.r.w = rect.r.h = 0;
77	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
78	       sz_rfbFramebufferUpdateRectHeader);
79	cl->ublen += sz_rfbFramebufferUpdateRectHeader;
80
81	if (!rfbSendUpdateBuf(cl))
82	    return FALSE;
83
84	return TRUE;
85    }
86
87    /* Calculate data sizes. */
88
89    bitmapRowBytes = (pCursor->width + 7) / 8;
90    maskBytes = bitmapRowBytes * pCursor->height;
91    dataBytes = (cl->useRichCursorEncoding) ?
92	(pCursor->width * pCursor->height *
93	 (cl->format.bitsPerPixel / 8)) : maskBytes;
94
95    /* Send buffer contents if needed. */
96
97    if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader +
98	 sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) {
99	if (!rfbSendUpdateBuf(cl))
100	    return FALSE;
101    }
102
103    if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader +
104	 sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) {
105	return FALSE;		/* FIXME. */
106    }
107
108    saved_ublen = cl->ublen;
109
110    /* Prepare rectangle header. */
111
112    rect.r.x = Swap16IfLE(pCursor->xhot);
113    rect.r.y = Swap16IfLE(pCursor->yhot);
114    rect.r.w = Swap16IfLE(pCursor->width);
115    rect.r.h = Swap16IfLE(pCursor->height);
116
117    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
118    cl->ublen += sz_rfbFramebufferUpdateRectHeader;
119
120    /* Prepare actual cursor data (depends on encoding used). */
121
122    if (!cl->useRichCursorEncoding) {
123	/* XCursor encoding. */
124	colors.foreRed   = (char)(pCursor->foreRed   >> 8);
125	colors.foreGreen = (char)(pCursor->foreGreen >> 8);
126	colors.foreBlue  = (char)(pCursor->foreBlue  >> 8);
127	colors.backRed   = (char)(pCursor->backRed   >> 8);
128	colors.backGreen = (char)(pCursor->backGreen >> 8);
129	colors.backBlue  = (char)(pCursor->backBlue  >> 8);
130
131	memcpy(&cl->updateBuf[cl->ublen], (char *)&colors, sz_rfbXCursorColors);
132	cl->ublen += sz_rfbXCursorColors;
133
134	bitmapData = (uint8_t *)pCursor->source;
135
136	for (i = 0; i < pCursor->height; i++) {
137	    for (j = 0; j < bitmapRowBytes; j++) {
138		bitmapByte = bitmapData[i * bitmapRowBytes + j];
139		cl->updateBuf[cl->ublen++] = (char)bitmapByte;
140	    }
141	}
142    } else {
143	/* RichCursor encoding. */
144       int bpp1=cl->screen->serverFormat.bitsPerPixel/8,
145	 bpp2=cl->format.bitsPerPixel/8;
146       (*cl->translateFn)(cl->translateLookupTable,
147		       &(cl->screen->serverFormat),
148                       &cl->format, (char*)pCursor->richSource,
149		       &cl->updateBuf[cl->ublen],
150                       pCursor->width*bpp1, pCursor->width, pCursor->height);
151
152       cl->ublen += pCursor->width*bpp2*pCursor->height;
153    }
154
155    /* Prepare transparency mask. */
156
157    bitmapData = (uint8_t *)pCursor->mask;
158
159    for (i = 0; i < pCursor->height; i++) {
160	for (j = 0; j < bitmapRowBytes; j++) {
161	    bitmapByte = bitmapData[i * bitmapRowBytes + j];
162	    cl->updateBuf[cl->ublen++] = (char)bitmapByte;
163	}
164    }
165
166    /* Send everything we have prepared in the cl->updateBuf[]. */
167    rfbStatRecordEncodingSent(cl, (cl->useRichCursorEncoding ? rfbEncodingRichCursor : rfbEncodingXCursor),
168        sz_rfbFramebufferUpdateRectHeader + (cl->ublen - saved_ublen), sz_rfbFramebufferUpdateRectHeader + (cl->ublen - saved_ublen));
169
170    if (!rfbSendUpdateBuf(cl))
171	return FALSE;
172
173    return TRUE;
174}
175
176/*
177 * Send cursor position (PointerPos pseudo-encoding).
178 */
179
180rfbBool
181rfbSendCursorPos(rfbClientPtr cl)
182{
183  rfbFramebufferUpdateRectHeader rect;
184
185  if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
186    if (!rfbSendUpdateBuf(cl))
187      return FALSE;
188  }
189
190  rect.encoding = Swap32IfLE(rfbEncodingPointerPos);
191  rect.r.x = Swap16IfLE(cl->screen->cursorX);
192  rect.r.y = Swap16IfLE(cl->screen->cursorY);
193  rect.r.w = 0;
194  rect.r.h = 0;
195
196  memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
197	 sz_rfbFramebufferUpdateRectHeader);
198  cl->ublen += sz_rfbFramebufferUpdateRectHeader;
199
200  rfbStatRecordEncodingSent(cl, rfbEncodingPointerPos, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader);
201
202  if (!rfbSendUpdateBuf(cl))
203    return FALSE;
204
205  return TRUE;
206}
207
208/* conversion routine for predefined cursors in LSB order */
209unsigned char rfbReverseByte[0x100] = {
210  /* copied from Xvnc/lib/font/util/utilbitmap.c */
211	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
212	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
213	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
214	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
215	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
216	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
217	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
218	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
219	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
220	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
221	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
222	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
223	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
224	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
225	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
226	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
227	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
228	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
229	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
230	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
231	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
232	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
233	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
234	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
235	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
236	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
237	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
238	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
239	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
240	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
241	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
242	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
243};
244
245void rfbConvertLSBCursorBitmapOrMask(int width,int height,unsigned char* bitmap)
246{
247   int i,t=(width+7)/8*height;
248   for(i=0;i<t;i++)
249     bitmap[i]=rfbReverseByte[(int)bitmap[i]];
250}
251
252/* Cursor creation. You "paint" a cursor and let these routines do the work */
253
254rfbCursorPtr rfbMakeXCursor(int width,int height,char* cursorString,char* maskString)
255{
256   int i,j,w=(width+7)/8;
257   rfbCursorPtr cursor = (rfbCursorPtr)calloc(1,sizeof(rfbCursor));
258   char* cp;
259   unsigned char bit;
260
261   cursor->cleanup=TRUE;
262   cursor->width=width;
263   cursor->height=height;
264   /*cursor->backRed=cursor->backGreen=cursor->backBlue=0xffff;*/
265   cursor->foreRed=cursor->foreGreen=cursor->foreBlue=0xffff;
266
267   cursor->source = (unsigned char*)calloc(w,height);
268   cursor->cleanupSource = TRUE;
269   for(j=0,cp=cursorString;j<height;j++)
270      for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>1,cp++)
271	if(*cp!=' ') cursor->source[j*w+i/8]|=bit;
272
273   if(maskString) {
274      cursor->mask = (unsigned char*)calloc(w,height);
275      for(j=0,cp=maskString;j<height;j++)
276	for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>1,cp++)
277	  if(*cp!=' ') cursor->mask[j*w+i/8]|=bit;
278   } else
279     cursor->mask = (unsigned char*)rfbMakeMaskForXCursor(width,height,(char*)cursor->source);
280   cursor->cleanupMask = TRUE;
281
282   return(cursor);
283}
284
285char* rfbMakeMaskForXCursor(int width,int height,char* source)
286{
287   int i,j,w=(width+7)/8;
288   char* mask=(char*)calloc(w,height);
289   unsigned char c;
290
291   for(j=0;j<height;j++)
292     for(i=w-1;i>=0;i--) {
293	c=source[j*w+i];
294	if(j>0) c|=source[(j-1)*w+i];
295	if(j<height-1) c|=source[(j+1)*w+i];
296
297	if(i>0 && (c&0x80))
298	  mask[j*w+i-1]|=0x01;
299	if(i<w-1 && (c&0x01))
300	  mask[j*w+i+1]|=0x80;
301
302	mask[j*w+i]|=(c<<1)|c|(c>>1);
303     }
304
305   return(mask);
306}
307
308/* this function dithers the alpha using Floyd-Steinberg */
309
310char* rfbMakeMaskFromAlphaSource(int width,int height,unsigned char* alphaSource)
311{
312	int* error=(int*)calloc(sizeof(int),width);
313	int i,j,currentError=0,maskStride=(width+7)/8;
314	unsigned char* result=(unsigned char*)calloc(maskStride,height);
315
316	for(j=0;j<height;j++)
317		for(i=0;i<width;i++) {
318			int right,middle,left;
319			currentError+=alphaSource[i+width*j]+error[i];
320
321			if(currentError<0x80) {
322				/* set to transparent */
323				/* alpha was treated as 0 */
324			} else {
325				/* set to solid */
326				result[i/8+j*maskStride]|=(0x100>>(i&7));
327				/* alpha was treated as 0xff */
328				currentError-=0xff;
329			}
330			/* propagate to next row */
331			right=currentError/16;
332			middle=currentError*5/16;
333			left=currentError*3/16;
334			currentError-=right+middle+left;
335			error[i]=right;
336			if(i>0) {
337				error[i-1]=middle;
338				if(i>1)
339					error[i-2]=left;
340			}
341		}
342	free(error);
343	return (char *) result;
344}
345
346void rfbFreeCursor(rfbCursorPtr cursor)
347{
348   if(cursor) {
349       if(cursor->cleanupRichSource && cursor->richSource)
350	   free(cursor->richSource);
351       if(cursor->cleanupRichSource && cursor->alphaSource)
352	   free(cursor->alphaSource);
353       if(cursor->cleanupSource && cursor->source)
354	   free(cursor->source);
355       if(cursor->cleanupMask && cursor->mask)
356	   free(cursor->mask);
357       if(cursor->cleanup)
358	   free(cursor);
359       else {
360	   cursor->cleanup=cursor->cleanupSource=cursor->cleanupMask
361	       =cursor->cleanupRichSource=FALSE;
362	   cursor->source=cursor->mask=cursor->richSource=NULL;
363	   cursor->alphaSource=NULL;
364       }
365   }
366
367}
368
369/* background and foregroud colour have to be set beforehand */
370void rfbMakeXCursorFromRichCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor)
371{
372   rfbPixelFormat* format=&rfbScreen->serverFormat;
373   int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8,
374     width=cursor->width*bpp;
375   uint32_t background;
376   char *back=(char*)&background;
377   unsigned char bit;
378   int interp = 0, db = 0;
379
380   if(cursor->source && cursor->cleanupSource)
381       free(cursor->source);
382   cursor->source=(unsigned char*)calloc(w,cursor->height);
383   cursor->cleanupSource=TRUE;
384
385   if(format->bigEndian) {
386      back+=4-bpp;
387   }
388
389	/* all zeros means we should interpolate to black+white ourselves */
390	if (!cursor->backRed && !cursor->backGreen && !cursor->backBlue &&
391	    !cursor->foreRed && !cursor->foreGreen && !cursor->foreBlue) {
392		if (format->trueColour && (bpp == 1 || bpp == 2 || bpp == 4)) {
393			interp = 1;
394			cursor->foreRed = cursor->foreGreen = cursor->foreBlue = 0xffff;
395		}
396	}
397
398   background = ((format->redMax   * cursor->backRed)   / 0xffff) << format->redShift   |
399                ((format->greenMax * cursor->backGreen) / 0xffff) << format->greenShift |
400                ((format->blueMax  * cursor->backBlue)  / 0xffff) << format->blueShift;
401
402#define SETRGB(u) \
403   r = (255 * (((format->redMax   << format->redShift)   & (*u)) >> format->redShift))   / format->redMax; \
404   g = (255 * (((format->greenMax << format->greenShift) & (*u)) >> format->greenShift)) / format->greenMax; \
405   b = (255 * (((format->blueMax  << format->blueShift)  & (*u)) >> format->blueShift))  / format->blueMax;
406
407   if (db) fprintf(stderr, "interp: %d\n", interp);
408
409   for(j=0;j<cursor->height;j++) {
410	for(i=0,bit=0x80;i<cursor->width;i++,bit=(bit&1)?0x80:bit>>1) {
411		if (interp) {
412			int r = 0, g = 0, b = 0, grey;
413			unsigned char *p = cursor->richSource+j*width+i*bpp;
414			if (bpp == 1) {
415				unsigned char*  uc = (unsigned char*)  p;
416				SETRGB(uc);
417			} else if (bpp == 2) {
418				unsigned short* us = (unsigned short*) p;
419				SETRGB(us);
420			} else if (bpp == 4) {
421				unsigned int*   ui = (unsigned int*)   p;
422				SETRGB(ui);
423			}
424			grey = (r + g + b) / 3;
425			if (grey >= 128) {
426				cursor->source[j*w+i/8]|=bit;
427				if (db) fprintf(stderr, "1");
428			} else {
429				if (db) fprintf(stderr, "0");
430			}
431
432		} else if(memcmp(cursor->richSource+j*width+i*bpp, back, bpp)) {
433			cursor->source[j*w+i/8]|=bit;
434		}
435	}
436	if (db) fprintf(stderr, "\n");
437   }
438}
439
440void rfbMakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor)
441{
442   rfbPixelFormat* format=&rfbScreen->serverFormat;
443   int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8;
444   uint32_t background,foreground;
445   char *back=(char*)&background,*fore=(char*)&foreground;
446   unsigned char *cp;
447   unsigned char bit;
448
449   if(cursor->richSource && cursor->cleanupRichSource)
450       free(cursor->richSource);
451   cp=cursor->richSource=(unsigned char*)calloc(cursor->width*bpp,cursor->height);
452   cursor->cleanupRichSource=TRUE;
453
454   if(format->bigEndian) {
455      back+=4-bpp;
456      fore+=4-bpp;
457   }
458
459   background=cursor->backRed<<format->redShift|
460     cursor->backGreen<<format->greenShift|cursor->backBlue<<format->blueShift;
461   foreground=cursor->foreRed<<format->redShift|
462     cursor->foreGreen<<format->greenShift|cursor->foreBlue<<format->blueShift;
463
464   for(j=0;j<cursor->height;j++)
465     for(i=0,bit=0x80;i<cursor->width;i++,bit=(bit&1)?0x80:bit>>1,cp+=bpp)
466       if(cursor->source[j*w+i/8]&bit) memcpy(cp,fore,bpp);
467       else memcpy(cp,back,bpp);
468}
469
470/* functions to draw/hide cursor directly in the frame buffer */
471
472void rfbHideCursor(rfbClientPtr cl)
473{
474   rfbScreenInfoPtr s=cl->screen;
475   rfbCursorPtr c=s->cursor;
476   int j,x1,x2,y1,y2,bpp=s->serverFormat.bitsPerPixel/8,
477     rowstride=s->paddedWidthInBytes;
478   LOCK(s->cursorMutex);
479   if(!c) {
480     UNLOCK(s->cursorMutex);
481     return;
482   }
483
484   /* restore what is under the cursor */
485   x1=cl->cursorX-c->xhot;
486   x2=x1+c->width;
487   if(x1<0) x1=0;
488   if(x2>=s->width) x2=s->width-1;
489   x2-=x1; if(x2<=0) {
490     UNLOCK(s->cursorMutex);
491     return;
492   }
493   y1=cl->cursorY-c->yhot;
494   y2=y1+c->height;
495   if(y1<0) y1=0;
496   if(y2>=s->height) y2=s->height-1;
497   y2-=y1; if(y2<=0) {
498     UNLOCK(s->cursorMutex);
499     return;
500   }
501
502   /* get saved data */
503   for(j=0;j<y2;j++)
504     memcpy(s->frameBuffer+(y1+j)*rowstride+x1*bpp,
505	    s->underCursorBuffer+j*x2*bpp,
506	    x2*bpp);
507
508   /* Copy to all scaled versions */
509   rfbScaledScreenUpdate(s, x1, y1, x1+x2, y1+y2);
510
511   UNLOCK(s->cursorMutex);
512}
513
514void rfbShowCursor(rfbClientPtr cl)
515{
516   rfbScreenInfoPtr s=cl->screen;
517   rfbCursorPtr c=s->cursor;
518   int i,j,x1,x2,y1,y2,i1,j1,bpp=s->serverFormat.bitsPerPixel/8,
519     rowstride=s->paddedWidthInBytes,
520     bufSize,w;
521   rfbBool wasChanged=FALSE;
522
523   if(!c) return;
524   LOCK(s->cursorMutex);
525
526   bufSize=c->width*c->height*bpp;
527   w=(c->width+7)/8;
528   if(s->underCursorBufferLen<bufSize) {
529      if(s->underCursorBuffer!=NULL)
530	free(s->underCursorBuffer);
531      s->underCursorBuffer=malloc(bufSize);
532      s->underCursorBufferLen=bufSize;
533   }
534
535   /* save what is under the cursor */
536   i1=j1=0; /* offset in cursor */
537   x1=cl->cursorX-c->xhot;
538   x2=x1+c->width;
539   if(x1<0) { i1=-x1; x1=0; }
540   if(x2>=s->width) x2=s->width-1;
541   x2-=x1; if(x2<=0) {
542     UNLOCK(s->cursorMutex);
543     return; /* nothing to do */
544   }
545
546   y1=cl->cursorY-c->yhot;
547   y2=y1+c->height;
548   if(y1<0) { j1=-y1; y1=0; }
549   if(y2>=s->height) y2=s->height-1;
550   y2-=y1; if(y2<=0) {
551     UNLOCK(s->cursorMutex);
552     return; /* nothing to do */
553   }
554
555   /* save data */
556   for(j=0;j<y2;j++) {
557     char* dest=s->underCursorBuffer+j*x2*bpp;
558     const char* src=s->frameBuffer+(y1+j)*rowstride+x1*bpp;
559     unsigned int count=x2*bpp;
560     if(wasChanged || memcmp(dest,src,count)) {
561       wasChanged=TRUE;
562       memcpy(dest,src,count);
563     }
564   }
565
566   if(!c->richSource)
567     rfbMakeRichCursorFromXCursor(s,c);
568
569   if (c->alphaSource) {
570	int rmax, rshift;
571	int gmax, gshift;
572	int bmax, bshift;
573	int amax = 255;	/* alphaSource is always 8bits of info per pixel */
574	unsigned int rmask, gmask, bmask;
575
576	rmax   = s->serverFormat.redMax;
577	gmax   = s->serverFormat.greenMax;
578	bmax   = s->serverFormat.blueMax;
579	rshift = s->serverFormat.redShift;
580	gshift = s->serverFormat.greenShift;
581	bshift = s->serverFormat.blueShift;
582
583	rmask = (rmax << rshift);
584	gmask = (gmax << gshift);
585	bmask = (bmax << bshift);
586
587	for(j=0;j<y2;j++) {
588		for(i=0;i<x2;i++) {
589			/*
590			 * we loop over the whole cursor ignoring c->mask[],
591			 * using the extracted alpha value instead.
592			 */
593			char *dest;
594			unsigned char *src, *aptr;
595			unsigned int val, dval, sval;
596			int rdst, gdst, bdst;		/* fb RGB */
597			int asrc, rsrc, gsrc, bsrc;	/* rich source ARGB */
598
599			dest = s->frameBuffer + (j+y1)*rowstride + (i+x1)*bpp;
600			src  = c->richSource  + (j+j1)*c->width*bpp + (i+i1)*bpp;
601			aptr = c->alphaSource + (j+j1)*c->width + (i+i1);
602
603			asrc = *aptr;
604			if (!asrc) {
605				continue;
606			}
607
608			if (bpp == 1) {
609				dval = *((unsigned char*) dest);
610				sval = *((unsigned char*) src);
611			} else if (bpp == 2) {
612				dval = *((unsigned short*) dest);
613				sval = *((unsigned short*) src);
614			} else if (bpp == 3) {
615				unsigned char *dst = (unsigned char *) dest;
616				dval = 0;
617				dval |= ((*(dst+0)) << 0);
618				dval |= ((*(dst+1)) << 8);
619				dval |= ((*(dst+2)) << 16);
620				sval = 0;
621				sval |= ((*(src+0)) << 0);
622				sval |= ((*(src+1)) << 8);
623				sval |= ((*(src+2)) << 16);
624			} else if (bpp == 4) {
625				dval = *((unsigned int*) dest);
626				sval = *((unsigned int*) src);
627			} else {
628				continue;
629			}
630
631			/* extract dest and src RGB */
632			rdst = (dval & rmask) >> rshift;	/* fb */
633			gdst = (dval & gmask) >> gshift;
634			bdst = (dval & bmask) >> bshift;
635
636			rsrc = (sval & rmask) >> rshift;	/* richcursor */
637			gsrc = (sval & gmask) >> gshift;
638			bsrc = (sval & bmask) >> bshift;
639
640			/* blend in fb data. */
641			if (! c->alphaPreMultiplied) {
642				rsrc = (asrc * rsrc)/amax;
643				gsrc = (asrc * gsrc)/amax;
644				bsrc = (asrc * bsrc)/amax;
645			}
646			rdst = rsrc + ((amax - asrc) * rdst)/amax;
647			gdst = gsrc + ((amax - asrc) * gdst)/amax;
648			bdst = bsrc + ((amax - asrc) * bdst)/amax;
649
650			val = 0;
651			val |= (rdst << rshift);
652			val |= (gdst << gshift);
653			val |= (bdst << bshift);
654
655			/* insert the cooked pixel into the fb */
656			memcpy(dest, &val, bpp);
657		}
658	}
659   } else {
660      /* now the cursor has to be drawn */
661      for(j=0;j<y2;j++)
662        for(i=0;i<x2;i++)
663          if((c->mask[(j+j1)*w+(i+i1)/8]<<((i+i1)&7))&0x80)
664   	 memcpy(s->frameBuffer+(j+y1)*rowstride+(i+x1)*bpp,
665   		c->richSource+(j+j1)*c->width*bpp+(i+i1)*bpp,bpp);
666   }
667
668   /* Copy to all scaled versions */
669   rfbScaledScreenUpdate(s, x1, y1, x1+x2, y1+y2);
670
671   UNLOCK(s->cursorMutex);
672}
673
674/*
675 * If enableCursorShapeUpdates is FALSE, and the cursor is hidden, make sure
676 * that if the frameBuffer was transmitted with a cursor drawn, then that
677 * region gets redrawn.
678 */
679
680void rfbRedrawAfterHideCursor(rfbClientPtr cl,sraRegionPtr updateRegion)
681{
682    rfbScreenInfoPtr s = cl->screen;
683    rfbCursorPtr c = s->cursor;
684
685    if(c) {
686	int x,y,x2,y2;
687
688	x = cl->cursorX-c->xhot;
689	y = cl->cursorY-c->yhot;
690	x2 = x+c->width;
691	y2 = y+c->height;
692
693	if(sraClipRect2(&x,&y,&x2,&y2,0,0,s->width,s->height)) {
694	    sraRegionPtr rect;
695	    rect = sraRgnCreateRect(x,y,x2,y2);
696	    if(updateRegion) {
697	    	sraRgnOr(updateRegion,rect);
698	    } else {
699		    LOCK(cl->updateMutex);
700		    sraRgnOr(cl->modifiedRegion,rect);
701		    UNLOCK(cl->updateMutex);
702	    }
703	    sraRgnDestroy(rect);
704	}
705    }
706}
707
708#ifdef DEBUG
709
710static void rfbPrintXCursor(rfbCursorPtr cursor)
711{
712   int i,i1,j,w=(cursor->width+7)/8;
713   unsigned char bit;
714   for(j=0;j<cursor->height;j++) {
715      for(i=0,i1=0,bit=0x80;i1<cursor->width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1)
716	if(cursor->source[j*w+i]&bit) putchar('#'); else putchar(' ');
717      putchar(':');
718      for(i=0,i1=0,bit=0x80;i1<cursor->width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1)
719	if(cursor->mask[j*w+i]&bit) putchar('#'); else putchar(' ');
720      putchar('\n');
721   }
722}
723
724#endif
725
726void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c)
727{
728  rfbClientIteratorPtr iterator;
729  rfbClientPtr cl;
730
731  LOCK(rfbScreen->cursorMutex);
732
733  if(rfbScreen->cursor) {
734    iterator=rfbGetClientIterator(rfbScreen);
735    while((cl=rfbClientIteratorNext(iterator)))
736	if(!cl->enableCursorShapeUpdates)
737	  rfbRedrawAfterHideCursor(cl,NULL);
738    rfbReleaseClientIterator(iterator);
739
740    if(rfbScreen->cursor->cleanup)
741	 rfbFreeCursor(rfbScreen->cursor);
742  }
743
744  rfbScreen->cursor = c;
745
746  iterator=rfbGetClientIterator(rfbScreen);
747  while((cl=rfbClientIteratorNext(iterator))) {
748    cl->cursorWasChanged = TRUE;
749    if(!cl->enableCursorShapeUpdates)
750      rfbRedrawAfterHideCursor(cl,NULL);
751  }
752  rfbReleaseClientIterator(iterator);
753
754  UNLOCK(rfbScreen->cursorMutex);
755}
756
757