mirror of
https://github.com/KDE/krfb
synced 2026-07-01 07:31:16 -07:00
My patches in this local fork have been merged upstream, but they have been merged after 0.9.8 and 0.9.9 hasn't been released yet, so we cannot yet switch back to finding libvncserver externally. So I am syncing now with basically what is 0.9.8 + my patches and a few bugfixes. svn path=/trunk/KDE/kdenetwork/krfb/; revision=1258493
301 lines
8.7 KiB
C
301 lines
8.7 KiB
C
#include <ctype.h>
|
|
#include "rfb/rfb.h"
|
|
#include "rfb/keysym.h"
|
|
|
|
typedef struct {
|
|
rfbScreenInfoPtr screen;
|
|
rfbFontDataPtr font;
|
|
char** list;
|
|
int listSize;
|
|
int selected;
|
|
int displayStart;
|
|
int x1,y1,x2,y2,textH,pageH;
|
|
int xhot,yhot;
|
|
int buttonWidth,okBX,cancelBX,okX,cancelX,okY;
|
|
rfbBool okInverted,cancelInverted;
|
|
int lastButtons;
|
|
rfbPixel colour,backColour;
|
|
SelectionChangedHookPtr selChangedHook;
|
|
enum { SELECTING, OK, CANCEL } state;
|
|
} rfbSelectData;
|
|
|
|
static const char* okStr="OK";
|
|
static const char* cancelStr="Cancel";
|
|
|
|
static void selPaintButtons(rfbSelectData* m,rfbBool invertOk,rfbBool invertCancel)
|
|
{
|
|
rfbScreenInfoPtr s = m->screen;
|
|
rfbPixel bcolour = m->backColour;
|
|
rfbPixel colour = m->colour;
|
|
|
|
rfbFillRect(s,m->x1,m->okY-m->textH,m->x2,m->okY,bcolour);
|
|
|
|
if(invertOk) {
|
|
rfbFillRect(s,m->okBX,m->okY-m->textH,m->okBX+m->buttonWidth,m->okY,colour);
|
|
rfbDrawStringWithClip(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,
|
|
m->x1,m->okY-m->textH,m->x2,m->okY,
|
|
bcolour,colour);
|
|
} else
|
|
rfbDrawString(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,colour);
|
|
|
|
if(invertCancel) {
|
|
rfbFillRect(s,m->cancelBX,m->okY-m->textH,
|
|
m->cancelBX+m->buttonWidth,m->okY,colour);
|
|
rfbDrawStringWithClip(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,
|
|
cancelStr,m->x1,m->okY-m->textH,m->x2,m->okY,
|
|
bcolour,colour);
|
|
} else
|
|
rfbDrawString(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,cancelStr,colour);
|
|
|
|
m->okInverted = invertOk;
|
|
m->cancelInverted = invertCancel;
|
|
}
|
|
|
|
/* line is relative to displayStart */
|
|
static void selPaintLine(rfbSelectData* m,int line,rfbBool invert)
|
|
{
|
|
int y1 = m->y1+line*m->textH, y2 = y1+m->textH;
|
|
if(y2>m->y2)
|
|
y2=m->y2;
|
|
rfbFillRect(m->screen,m->x1,y1,m->x2,y2,invert?m->colour:m->backColour);
|
|
if(m->displayStart+line<m->listSize)
|
|
rfbDrawStringWithClip(m->screen,m->font,m->x1+m->xhot,y2-1+m->yhot,
|
|
m->list[m->displayStart+line],
|
|
m->x1,y1,m->x2,y2,
|
|
invert?m->backColour:m->colour,
|
|
invert?m->backColour:m->colour);
|
|
}
|
|
|
|
static void selSelect(rfbSelectData* m,int _index)
|
|
{
|
|
int delta;
|
|
|
|
if(_index==m->selected || _index<0 || _index>=m->listSize)
|
|
return;
|
|
|
|
if(m->selected>=0)
|
|
selPaintLine(m,m->selected-m->displayStart,FALSE);
|
|
|
|
if(_index<m->displayStart || _index>=m->displayStart+m->pageH) {
|
|
/* targetLine is the screen line in which the selected line will
|
|
be displayed.
|
|
targetLine = m->pageH/2 doesn't look so nice */
|
|
int targetLine = m->selected-m->displayStart;
|
|
int lineStart,lineEnd;
|
|
|
|
/* scroll */
|
|
if(_index<targetLine)
|
|
targetLine = _index;
|
|
else if(_index+m->pageH-targetLine>=m->listSize)
|
|
targetLine = _index+m->pageH-m->listSize;
|
|
delta = _index-(m->displayStart+targetLine);
|
|
|
|
if(delta>-m->pageH && delta<m->pageH) {
|
|
if(delta>0) {
|
|
lineStart = m->pageH-delta;
|
|
lineEnd = m->pageH;
|
|
rfbDoCopyRect(m->screen,m->x1,m->y1,m->x2,m->y1+lineStart*m->textH,
|
|
0,-delta*m->textH);
|
|
} else {
|
|
lineStart = 0;
|
|
lineEnd = -delta;
|
|
rfbDoCopyRect(m->screen,
|
|
m->x1,m->y1+lineEnd*m->textH,m->x2,m->y2,
|
|
0,-delta*m->textH);
|
|
}
|
|
} else {
|
|
lineStart = 0;
|
|
lineEnd = m->pageH;
|
|
}
|
|
m->displayStart += delta;
|
|
for(delta=lineStart;delta<lineEnd;delta++)
|
|
if(delta!=_index)
|
|
selPaintLine(m,delta,FALSE);
|
|
}
|
|
|
|
m->selected = _index;
|
|
selPaintLine(m,m->selected-m->displayStart,TRUE);
|
|
|
|
if(m->selChangedHook)
|
|
m->selChangedHook(_index);
|
|
|
|
/* todo: scrollbars */
|
|
}
|
|
|
|
static void selKbdAddEvent(rfbBool down,rfbKeySym keySym,rfbClientPtr cl)
|
|
{
|
|
if(down) {
|
|
if(keySym>' ' && keySym<0xff) {
|
|
int i;
|
|
rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
|
|
char c = tolower(keySym);
|
|
|
|
for(i=m->selected+1;m->list[i] && tolower(m->list[i][0])!=c;i++);
|
|
if(!m->list[i])
|
|
for(i=0;i<m->selected && tolower(m->list[i][0])!=c;i++);
|
|
selSelect(m,i);
|
|
} else if(keySym==XK_Escape) {
|
|
rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
|
|
m->state = CANCEL;
|
|
} else if(keySym==XK_Return) {
|
|
rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
|
|
m->state = OK;
|
|
} else {
|
|
rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
|
|
int curSel=m->selected;
|
|
if(keySym==XK_Up) {
|
|
if(curSel>0)
|
|
selSelect(m,curSel-1);
|
|
} else if(keySym==XK_Down) {
|
|
if(curSel+1<m->listSize)
|
|
selSelect(m,curSel+1);
|
|
} else {
|
|
if(keySym==XK_Page_Down) {
|
|
if(curSel+m->pageH<m->listSize)
|
|
selSelect(m,curSel+m->pageH);
|
|
else
|
|
selSelect(m,m->listSize-1);
|
|
} else if(keySym==XK_Page_Up) {
|
|
if(curSel-m->pageH>=0)
|
|
selSelect(m,curSel-m->pageH);
|
|
else
|
|
selSelect(m,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void selPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl)
|
|
{
|
|
rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
|
|
if(y<m->okY && y>=m->okY-m->textH) {
|
|
if(x>=m->okBX && x<m->okBX+m->buttonWidth) {
|
|
if(!m->okInverted)
|
|
selPaintButtons(m,TRUE,FALSE);
|
|
if(buttonMask)
|
|
m->state = OK;
|
|
} else if(x>=m->cancelBX && x<m->cancelBX+m->buttonWidth) {
|
|
if(!m->cancelInverted)
|
|
selPaintButtons(m,FALSE,TRUE);
|
|
if(buttonMask)
|
|
m->state = CANCEL;
|
|
} else if(m->okInverted || m->cancelInverted)
|
|
selPaintButtons(m,FALSE,FALSE);
|
|
} else {
|
|
if(m->okInverted || m->cancelInverted)
|
|
selPaintButtons(m,FALSE,FALSE);
|
|
if(!m->lastButtons && buttonMask) {
|
|
if(x>=m->x1 && x<m->x2 && y>=m->y1 && y<m->y2)
|
|
selSelect(m,m->displayStart+(y-m->y1)/m->textH);
|
|
}
|
|
}
|
|
m->lastButtons = buttonMask;
|
|
|
|
/* todo: scrollbars */
|
|
}
|
|
|
|
static rfbCursorPtr selGetCursorPtr(rfbClientPtr cl)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int rfbSelectBox(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,
|
|
char** list,
|
|
int x1,int y1,int x2,int y2,
|
|
rfbPixel colour,rfbPixel backColour,
|
|
int border,SelectionChangedHookPtr selChangedHook)
|
|
{
|
|
int bpp = rfbScreen->bitsPerPixel/8;
|
|
char* frameBufferBackup;
|
|
void* screenDataBackup = rfbScreen->screenData;
|
|
rfbKbdAddEventProcPtr kbdAddEventBackup = rfbScreen->kbdAddEvent;
|
|
rfbPtrAddEventProcPtr ptrAddEventBackup = rfbScreen->ptrAddEvent;
|
|
rfbGetCursorProcPtr getCursorPtrBackup = rfbScreen->getCursorPtr;
|
|
rfbDisplayHookPtr displayHookBackup = rfbScreen->displayHook;
|
|
rfbSelectData selData;
|
|
int i,j,k;
|
|
int fx1,fy1,fx2,fy2; /* for font bbox */
|
|
|
|
if(list==0 || *list==0)
|
|
return(-1);
|
|
|
|
rfbWholeFontBBox(font, &fx1, &fy1, &fx2, &fy2);
|
|
selData.textH = fy2-fy1;
|
|
/* I need at least one line for the choice and one for the buttons */
|
|
if(y2-y1<selData.textH*2+3*border)
|
|
return(-1);
|
|
selData.xhot = -fx1;
|
|
selData.yhot = -fy2;
|
|
selData.x1 = x1+border;
|
|
selData.y1 = y1+border;
|
|
selData.y2 = y2-selData.textH-3*border;
|
|
selData.x2 = x2-2*border;
|
|
selData.pageH = (selData.y2-selData.y1)/selData.textH;
|
|
|
|
i = rfbWidthOfString(font,okStr);
|
|
j = rfbWidthOfString(font,cancelStr);
|
|
selData.buttonWidth= k = 4*border+(i<j)?j:i;
|
|
selData.okBX = x1+(x2-x1-2*k)/3;
|
|
if(selData.okBX<x1+border) /* too narrow! */
|
|
return(-1);
|
|
selData.cancelBX = x1+k+(x2-x1-2*k)*2/3;
|
|
selData.okX = selData.okBX+(k-i)/2;
|
|
selData.cancelX = selData.cancelBX+(k-j)/2;
|
|
selData.okY = y2-border;
|
|
|
|
frameBufferBackup = (char*)malloc(bpp*(x2-x1)*(y2-y1));
|
|
|
|
selData.state = SELECTING;
|
|
selData.screen = rfbScreen;
|
|
selData.font = font;
|
|
selData.list = list;
|
|
selData.colour = colour;
|
|
selData.backColour = backColour;
|
|
for(i=0;list[i];i++);
|
|
selData.selected = i;
|
|
selData.listSize = i;
|
|
selData.displayStart = i;
|
|
selData.lastButtons = 0;
|
|
selData.selChangedHook = selChangedHook;
|
|
|
|
rfbScreen->screenData = &selData;
|
|
rfbScreen->kbdAddEvent = selKbdAddEvent;
|
|
rfbScreen->ptrAddEvent = selPtrAddEvent;
|
|
rfbScreen->getCursorPtr = selGetCursorPtr;
|
|
rfbScreen->displayHook = NULL;
|
|
|
|
/* backup screen */
|
|
for(j=0;j<y2-y1;j++)
|
|
memcpy(frameBufferBackup+j*(x2-x1)*bpp,
|
|
rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp,
|
|
(x2-x1)*bpp);
|
|
|
|
/* paint list and buttons */
|
|
rfbFillRect(rfbScreen,x1,y1,x2,y2,colour);
|
|
selPaintButtons(&selData,FALSE,FALSE);
|
|
selSelect(&selData,0);
|
|
|
|
/* modal loop */
|
|
while(selData.state == SELECTING)
|
|
rfbProcessEvents(rfbScreen,20000);
|
|
|
|
/* copy back screen data */
|
|
for(j=0;j<y2-y1;j++)
|
|
memcpy(rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp,
|
|
frameBufferBackup+j*(x2-x1)*bpp,
|
|
(x2-x1)*bpp);
|
|
free(frameBufferBackup);
|
|
rfbMarkRectAsModified(rfbScreen,x1,y1,x2,y2);
|
|
rfbScreen->screenData = screenDataBackup;
|
|
rfbScreen->kbdAddEvent = kbdAddEventBackup;
|
|
rfbScreen->ptrAddEvent = ptrAddEventBackup;
|
|
rfbScreen->getCursorPtr = getCursorPtrBackup;
|
|
rfbScreen->displayHook = displayHookBackup;
|
|
|
|
if(selData.state==CANCEL)
|
|
selData.selected=-1;
|
|
return(selData.selected);
|
|
}
|
|
|