fix disconnect bug in libvncserver
[presencevnc] / libvnc / vncterm / VNConsole.c
1 #include <stdarg.h>
2 #include <rfb/keysym.h>
3 #include "VNConsole.h"
4
5 #define DEBUG(x)
6
7 unsigned char colourMap16[16*3]={
8   /* 0 black       #000000 */ 0x00,0x00,0x00,
9   /* 1 maroon      #800000 */ 0x80,0x00,0x00,
10   /* 2 green       #008000 */ 0x00,0x80,0x00,
11   /* 3 khaki       #808000 */ 0x80,0x80,0x00,
12   /* 4 navy        #000080 */ 0x00,0x00,0x80,
13   /* 5 purple      #800080 */ 0x80,0x00,0x80,
14   /* 6 aqua-green  #008080 */ 0x00,0x80,0x80,
15   /* 7 light grey  #c0c0c0 */ 0xc0,0xc0,0xc0,
16   /* 8 dark grey   #808080 */ 0x80,0x80,0x80,
17   /* 9 red         #ff0000 */ 0xff,0x00,0x00,
18   /* a light green #00ff00 */ 0x00,0xff,0x00,
19   /* b yellow      #ffff00 */ 0xff,0xff,0x00,
20   /* c blue        #0000ff */ 0x00,0x00,0xff,
21   /* d pink        #ff00ff */ 0xff,0x00,0xff,
22   /* e light blue  #00ffff */ 0x00,0xff,0xff,
23   /* f white       #ffffff */ 0xff,0xff,0xff
24 };
25
26 void MakeColourMap16(vncConsolePtr c)
27 {
28   rfbColourMap* colourMap=&(c->screen->colourMap);
29   if(colourMap->count)
30     free(colourMap->data.bytes);
31   colourMap->data.bytes=malloc(16*3);
32   memcpy(colourMap->data.bytes,colourMap16,16*3);
33   colourMap->count=16;
34   colourMap->is16=FALSE;
35   c->screen->serverFormat.trueColour=FALSE;
36 }
37
38 void vcDrawOrHideCursor(vncConsolePtr c)
39 {
40   int i,j,w=c->screen->paddedWidthInBytes;
41   char *b=c->screen->frameBuffer+c->y*c->cHeight*w+c->x*c->cWidth;
42   for(j=c->cy1;j<c->cy2;j++)
43     for(i=c->cx1;i<c->cx2;i++)
44       b[j*w+i]^=0x0f;
45   rfbMarkRectAsModified(c->screen,
46                         c->x*c->cWidth+c->cx1,c->y*c->cHeight+c->cy1,
47                         c->x*c->cWidth+c->cx2,c->y*c->cHeight+c->cy2);
48   c->cursorIsDrawn=c->cursorIsDrawn?FALSE:TRUE;
49 }
50
51 void vcDrawCursor(vncConsolePtr c)
52 {
53   if(c->cursorActive && c->y<c->height && c->x<c->width) {
54     /* rfbLog("DrawCursor: %d,%d\n",c->x,c->y); */
55     vcDrawOrHideCursor(c);
56   }
57 }
58
59 void vcHideCursor(vncConsolePtr c)
60 {
61   if(c->currentlyMarking)
62     vcUnmark(c);
63   vcDrawOrHideCursor(c);
64 }
65
66 void vcMakeSureCursorIsDrawn(rfbClientPtr cl)
67 {
68   vncConsolePtr c=(vncConsolePtr)cl->screen->screenData;
69   if(!c->dontDrawCursor)
70     vcDrawCursor(c);
71 }
72
73 vncConsolePtr vcGetConsole(int *argc,char **argv,
74                            int width,int height,rfbFontDataPtr font
75 #ifdef USE_ATTRIBUTE_BUFFER
76                            ,rfbBool withAttributes
77 #endif
78                            )
79 {
80   vncConsolePtr c=(vncConsolePtr)malloc(sizeof(vncConsole));
81
82   c->font=font;
83   c->width=width;
84   c->height=height;
85   c->screenBuffer=(char*)malloc(width*height);
86   memset(c->screenBuffer,' ',width*height);
87 #ifdef USE_ATTRIBUTE_BUFFER
88   if(withAttributes) {
89     c->attributeBuffer=(char*)malloc(width*height);
90     memset(c->attributeBuffer,0x07,width*height);
91   } else
92     c->attributeBuffer=NULL;
93 #endif
94   c->x=0;
95   c->y=0;
96   c->wrapBottomToTop=FALSE;
97   c->cursorActive=TRUE;
98   c->cursorIsDrawn=FALSE;
99   c->dontDrawCursor=FALSE;
100   c->inputBuffer=(char*)malloc(1024);
101   c->inputSize=1024;
102   c->inputCount=0;
103   c->selection=0;
104   c->selectTimeOut=40000; /* 40 ms */
105   c->doEcho=TRUE;
106
107   c->wasRightButtonDown=FALSE;
108   c->currentlyMarking=FALSE;
109
110   rfbWholeFontBBox(font,&c->xhot,&c->cHeight,&c->cWidth,&c->yhot);
111   c->cWidth-=c->xhot;
112   c->cHeight=-c->cHeight-c->yhot;
113
114   /* text cursor */
115   c->cx1=c->cWidth/8;
116   c->cx2=c->cWidth*7/8;
117   c->cy2=c->cHeight-1-c->yhot+c->cHeight/16;
118   if(c->cy2>=c->cHeight)
119     c->cy2=c->cHeight-1;
120   c->cy1=c->cy2-c->cHeight/8;
121   if(c->cy1<0)
122     c->cy2=0;
123
124   c->screen=
125     rfbGetScreen(argc,argv,c->cWidth*c->width,c->cHeight*c->height,8,1,1);
126   c->screen->screenData=(void*)c;
127   c->screen->displayHook=vcMakeSureCursorIsDrawn;
128   c->screen->frameBuffer=
129     (char*)malloc(c->screen->width*c->screen->height);
130   memset(c->screen->frameBuffer,c->backColour,
131          c->screen->width*c->screen->height);
132   c->screen->kbdAddEvent=vcKbdAddEventProc;
133   c->screen->ptrAddEvent=vcPtrAddEventProc;
134   c->screen->setXCutText=vcSetXCutTextProc;
135
136   MakeColourMap16(c);
137   c->foreColour=0x7;
138   c->backColour=0;
139
140   rfbInitServer(c->screen);
141
142   return(c);
143 }
144
145 #include <rfb/rfbregion.h>
146
147 /* before using this function, hide the cursor */
148 void vcScroll(vncConsolePtr c,int lineCount)
149 {
150   int y1,y2;
151   rfbScreenInfoPtr s=c->screen;
152
153   if(lineCount==0)
154     return;
155
156   /* rfbLog("begin scroll\n"); */
157   vcHideCursor(c);
158   c->dontDrawCursor=TRUE;
159
160   if(lineCount>=c->height || lineCount<=-c->height) {
161     y1=0; y2=s->height;
162   } else if(lineCount>0) {
163     y1=s->height-lineCount*c->cHeight; y2=s->height;
164     rfbDoCopyRect(s,0,0,s->width,y1,0,-lineCount*c->cHeight);
165     memmove(c->screenBuffer,
166             c->screenBuffer+(c->height-lineCount)*c->width,
167             (c->height-lineCount)*c->width);
168 #ifdef USE_ATTRIBUTE_BUFFER
169     if(c->attributeBuffer)
170             memmove(c->attributeBuffer,
171                     c->attributeBuffer+(c->height-lineCount)*c->width,
172                     (c->height-lineCount)*c->width);
173 #endif
174   } else {
175     y1=0; y2=-lineCount*c->cHeight;
176     rfbDoCopyRect(s,0,y2,s->width,s->height,0,-lineCount*c->cHeight);
177     memmove(c->screenBuffer-lineCount*c->width,
178             c->screenBuffer,
179             (c->height+lineCount)*c->width);
180 #ifdef USE_ATTRIBUTE_BUFFER
181     if(c->attributeBuffer)
182             memmove(c->attributeBuffer-lineCount*c->width,
183                     c->attributeBuffer,
184                     (c->height+lineCount)*c->width);
185 #endif
186   }
187
188   c->dontDrawCursor=FALSE;
189   memset(s->frameBuffer+y1*s->width,c->backColour,(y2-y1)*s->width);
190   rfbMarkRectAsModified(s,0,y1-c->cHeight,s->width,y2);
191   memset(c->screenBuffer+y1/c->cHeight*c->width,' ',
192          (y2-y1)/c->cHeight*c->width);
193 #ifdef USE_ATTRIBUTE_BUFFER
194   if(c->attributeBuffer)
195           memset(c->attributeBuffer+y1/c->cHeight*c->width,0x07,
196                  (y2-y1)/c->cHeight*c->width);
197 #endif
198   /* rfbLog("end scroll\n"); */
199 }  
200
201 void vcCheckCoordinates(vncConsolePtr c)
202 {
203   if(c->x>=c->width) {
204     c->x=0;
205     c->y++;
206   }
207   if(c->y>=c->height) {
208     if(c->wrapBottomToTop)
209       c->y=0;
210     else {
211       vcScroll(c,c->y+1-c->height);
212       c->y=c->height-1;
213     }
214   }
215 }
216
217 void vcPutChar(vncConsolePtr c,unsigned char ch)
218 {
219 #ifdef USE_ATTRIBUTE_BUFFER
220   if(c->attributeBuffer) {
221     unsigned char colour=c->attributeBuffer[c->x+c->y*c->width];
222     vcPutCharColour(c,ch,colour&0x7,colour>>4);
223   } else
224 #endif
225     vcPutCharColour(c,ch,c->foreColour,c->backColour);
226 }
227
228 void vcPutCharColour(vncConsolePtr c,unsigned char ch,unsigned char foreColour,unsigned char backColour)
229 {
230   rfbScreenInfoPtr s=c->screen;
231   int j,x,y;
232
233   vcHideCursor(c);
234   if(ch<' ') {
235     switch(ch) {
236     case 7:
237     case 13:
238       break;
239     case 8: /* BackSpace */
240       if(c->x>0) {
241         c->x--;
242         vcPutChar(c,' ');
243         c->x--;
244       }
245       break;
246     case 10: /* return */
247       c->x=0;
248       c->y++;
249       vcCheckCoordinates(c);
250       break;
251     case 9: /* tabulator */
252       do {
253         vcPutChar(c,' ');
254       } while(c->x%8);
255       break;
256     default:
257        rfbLog("putchar of unknown character: %c(%d).\n",ch,ch);
258       vcPutChar(c,' ');
259     }
260   } else {
261 #ifdef USE_ATTRIBUTE_BUFFER
262     if(c->attributeBuffer)
263       c->attributeBuffer[c->x+c->y*c->width]=foreColour|(backColour<<4);
264 #endif
265     x=c->x*c->cWidth;
266     y=c->y*c->cHeight;
267     for(j=y+c->cHeight-1;j>=y;j--)
268       memset(s->frameBuffer+j*s->width+x,backColour,c->cWidth);
269     rfbDrawChar(s,c->font,
270                 x-c->xhot+(c->cWidth-rfbWidthOfChar(c->font,ch))/2,
271                 y+c->cHeight-c->yhot-1,
272                 ch,foreColour);
273     c->screenBuffer[c->y*c->width+c->x]=ch;
274     c->x++;
275     rfbMarkRectAsModified(s,x,y-c->cHeight+1,x+c->cWidth,y+c->cHeight+1);
276     vcCheckCoordinates(c);
277   }
278 }
279
280 void vcPrint(vncConsolePtr c,unsigned char* str)
281 {
282   while(*str) {
283     vcPutChar(c,*str);
284     str++;
285   }
286 }
287
288 void vcPrintColour(vncConsolePtr c,unsigned char* str,unsigned char foreColour,unsigned char backColour)
289 {
290   while(*str) {
291     vcPutCharColour(c,*str,foreColour,backColour);
292     str++;
293   }
294 }
295
296 void vcPrintF(vncConsolePtr c,char* format,...)
297 {
298   va_list args;
299   char buf[4096];
300   va_start(args, format);
301   vsprintf(buf, format, args);
302   vcPrint(c,buf);
303   va_end(args);
304 }
305
306 void vcPrintFColour(vncConsolePtr c,unsigned char foreColour,unsigned char backColour,char* format,...)
307 {
308   va_list args;
309   char buf[4096];
310   va_start(args, format);
311   vsprintf(buf, format, args);
312   vcPrintColour(c,buf,foreColour,backColour);
313   va_end(args);
314 }
315
316 char vcGetCh(vncConsolePtr c)
317 {
318   if(c->inputCount>0) {
319     char ch;
320     ch=c->inputBuffer[0];
321     c->inputCount--;
322     if(c->inputCount>0)
323       memmove(c->inputBuffer,c->inputBuffer+1,c->inputCount);
324     return(ch);
325   } else
326     return(0);
327 }
328
329 char vcGetChar(vncConsolePtr c)
330 {
331   while(rfbIsActive(c->screen) && c->inputCount==0)
332     vcProcessEvents(c);
333   return(vcGetCh(c));
334 }
335
336 char *vcGetString(vncConsolePtr c,char *buffer,int bufferSize)
337 {
338   char *bufferBackup=c->inputBuffer;
339   int i,count=bufferSize-1;
340
341   if(count>c->inputCount)
342     count=c->inputCount;
343   for(i=1;i<count && bufferBackup[i-1]!='\n';i++);
344   if(i<count || i==bufferSize-1) {
345     memcpy(buffer,bufferBackup,i);
346     buffer[i+1]=0;
347     c->inputCount-=i;
348     memmove(bufferBackup,bufferBackup+i+2,c->inputCount);
349     return(buffer);
350   }
351   memcpy(buffer,bufferBackup,c->inputCount);
352   count=c->inputSize;
353   c->inputSize=bufferSize;
354   c->inputBuffer=buffer;
355   while(rfbIsActive(c->screen)
356       && c->inputCount<bufferSize-1 && buffer[c->inputCount-1]!='\n')
357     vcProcessEvents(c);
358   buffer[c->inputCount]=0;
359   c->inputBuffer=bufferBackup;
360   c->inputSize=count;
361   c->inputCount=0;
362   return(buffer);
363 }
364
365 void vcKbdAddEventProc(rfbBool down,rfbKeySym keySym,rfbClientPtr cl)
366 {
367   vncConsolePtr c=(vncConsolePtr)cl->screen->screenData;
368   if(down) {
369     if(c->inputCount<c->inputSize) {
370       if(keySym<0 || keySym>0xff) {
371         if(keySym==XK_Return) keySym='\n';
372         else if(keySym==XK_BackSpace) keySym=8;
373         else if(keySym==XK_Tab) keySym=9;
374         else keySym=0;
375       }
376       if(keySym>0) {
377         if(keySym==8) {
378           if(c->inputCount>0)
379             c->inputCount--;
380         } else
381           c->inputBuffer[c->inputCount++]=(char)keySym;
382         if(c->doEcho)
383           vcPutChar(c,(unsigned char)keySym);
384       }
385     }
386   }
387 }
388
389 void vcPtrAddEventProc(int buttonMask,int x,int y,rfbClientPtr cl)
390 {
391   vncConsolePtr c=(vncConsolePtr)cl->screen->screenData;
392
393   if(c->wasRightButtonDown) {
394     if((buttonMask&4)==0) {
395       if(c->selection) {
396         char* s;
397         for(s=c->selection;*s;s++) {
398           c->screen->kbdAddEvent(1,*s,cl);
399           c->screen->kbdAddEvent(0,*s,cl);
400         }
401       }
402       c->wasRightButtonDown=0;
403     }
404   } else if(buttonMask&4)
405     c->wasRightButtonDown=1;
406       
407   if(buttonMask&1) {
408     int cx=x/c->cWidth,cy=y/c->cHeight,pos;
409     if(cx<0) cx=0; else if(cx>=c->width) cx=c->width-1;
410     if(cy<0) cy=0; else if(cy>=c->height) cy=c->height-1;
411     pos=cy*c->width+cx;
412
413     /* mark */
414     if(!c->currentlyMarking) {
415       c->currentlyMarking=TRUE;
416       c->markStart=pos;
417       c->markEnd=pos;
418       vcToggleMarkCell(c,pos);
419     } else {
420       DEBUG(rfbLog("markStart: %d, markEnd: %d, pos: %d\n",
421               c->markStart,c->markEnd,pos));
422       if(c->markEnd!=pos) {
423         if(c->markEnd<pos) {
424           cx=c->markEnd; cy=pos;
425         } else {
426           cx=pos; cy=c->markEnd;
427         }
428         if(cx<c->markStart) {
429           if(cy<c->markStart)
430             cy--;
431         } else
432           cx++;
433         while(cx<=cy) {
434           vcToggleMarkCell(c,cx);
435           cx++;
436         }
437         c->markEnd=pos;
438       }
439     }
440   } else if(c->currentlyMarking) {
441     int i,j;
442     if(c->markStart<c->markEnd) {
443       i=c->markStart; j=c->markEnd+1;
444     } else {
445       i=c->markEnd; j=c->markStart;
446     }
447     if(c->selection) free(c->selection);
448     c->selection=(char*)malloc(j-i+1);
449     memcpy(c->selection,c->screenBuffer+i,j-i);
450     c->selection[j-i]=0;
451     vcUnmark(c);
452     rfbGotXCutText(c->screen,c->selection,j-i);
453   }
454   rfbDefaultPtrAddEvent(buttonMask,x,y,cl);
455 }
456
457 void vcSetXCutTextProc(char* str,int len, struct _rfbClientRec* cl)
458 {
459   vncConsolePtr c=(vncConsolePtr)cl->screen->screenData;
460
461   if(c->selection) free(c->selection);
462   c->selection=(char*)malloc(len+1);
463   memcpy(c->selection,str,len);
464   c->selection[len]=0;
465 }
466
467 void vcToggleMarkCell(vncConsolePtr c,int pos)
468 {
469   int x=(pos%c->width)*c->cWidth,
470     y=(pos/c->width)*c->cHeight;
471   int i,j;
472   rfbScreenInfoPtr s=c->screen;
473   char *b=s->frameBuffer+y*s->width+x;
474   for(j=0;j<c->cHeight;j++)
475     for(i=0;i<c->cWidth;i++)
476       b[j*s->width+i]^=0x0f;
477   rfbMarkRectAsModified(c->screen,x,y,x+c->cWidth,y+c->cHeight);
478 }
479
480 void vcUnmark(vncConsolePtr c)
481 {
482   int i,j;
483   c->currentlyMarking=FALSE;
484   if(c->markStart<c->markEnd) {
485     i=c->markStart; j=c->markEnd+1;
486   } else {
487     i=c->markEnd; j=c->markStart;
488   }
489   for(;i<j;i++)
490     vcToggleMarkCell(c,i);
491 }
492
493 void vcProcessEvents(vncConsolePtr c)
494 {
495   rfbProcessEvents(c->screen,c->selectTimeOut);
496 }
497