summaryrefslogtreecommitdiff
authortreke <treke>2002-09-05 04:42:25 (UTC)
committer treke <treke>2002-09-05 04:42:25 (UTC)
commite2186a49cb5cbdf01b56f57818a15a760ff25b2e (patch) (side-by-side diff)
treef22e7342c7a3adcccb6eed52a314fe8e301cc624
parentfd3239820e471044bf279a2964702573572859d6 (diff)
downloadopie-e2186a49cb5cbdf01b56f57818a15a760ff25b2e.zip
opie-e2186a49cb5cbdf01b56f57818a15a760ff25b2e.tar.gz
opie-e2186a49cb5cbdf01b56f57818a15a760ff25b2e.tar.bz2
Added support for client side scaling of the screen. Slow, but sometimes usable.
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--noncore/comm/keypebble/krfbbuffer.cpp11
-rw-r--r--noncore/comm/keypebble/krfbcanvas.cpp5
-rw-r--r--noncore/comm/keypebble/krfbdecoder.cpp7
-rw-r--r--noncore/comm/keypebble/krfbdecoder.h5
-rw-r--r--noncore/comm/keypebble/krfbserver.cpp2
-rw-r--r--noncore/comm/keypebble/krfbserver.h1
-rw-r--r--noncore/comm/keypebble/kvnc.cpp8
-rw-r--r--noncore/comm/keypebble/kvnc.h3
-rw-r--r--noncore/comm/keypebble/kvncbookmarkdlg.cpp3
-rw-r--r--noncore/comm/keypebble/kvncconndlg.cpp21
-rw-r--r--noncore/comm/keypebble/kvncconndlg.h1
-rw-r--r--noncore/comm/keypebble/kvncconndlgbase.ui180
12 files changed, 172 insertions, 75 deletions
diff --git a/noncore/comm/keypebble/krfbbuffer.cpp b/noncore/comm/keypebble/krfbbuffer.cpp
index 5a52f31..f1cb929 100644
--- a/noncore/comm/keypebble/krfbbuffer.cpp
+++ b/noncore/comm/keypebble/krfbbuffer.cpp
@@ -1,194 +1,203 @@
#include <assert.h>
#include <qpixmap.h>
#include <qbrush.h>
#include <qimage.h>
#include <qpainter.h>
#include <qapplication.h>
#include "krfbdecoder.h"
#include "krfbbuffer.h"
+#include "krfbconnection.h"
+#include "krfbserver.h"
#include "krfbserverinfo.h"
//
// Endian stuff
//
#ifndef KDE_USE_FINAL
const int endianTest = 1;
#endif
#define Swap16IfLE(s) \
(*(char *)&endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))
#define Swap32IfLE(l) \
(*(char *)&endianTest ? ((((l) & 0xff000000) >> 24) | \
(((l) & 0x00ff0000) >> 8) | \
(((l) & 0x0000ff00) << 8) | \
(((l) & 0x000000ff) << 24)) : (l))
KRFBBuffer::KRFBBuffer( KRFBDecoder *decoder,
QObject *parent, const char *name )
: QObject( parent, name )
{
assert( decoder );
this->decoder = decoder;
pix = new QPixmap();
}
KRFBBuffer::~KRFBBuffer()
{
delete pix;
}
void KRFBBuffer::resize( int w, int h )
{
qWarning( "Resizing buffer" );
pix->resize( w, h );
QPalette pal = qApp->palette();
pix->fill( pal.active().base() );
emit sizeChanged( w, h );
}
void KRFBBuffer::soundBell()
{
emit bell();
}
void KRFBBuffer::mouseEvent( QMouseEvent *e )
{
decoder->sendMouseEvent( e );
}
void KRFBBuffer::keyPressEvent( QKeyEvent *e )
{
qWarning( "Buffer got a key" );
decoder->sendKeyPressEvent( e );
}
void KRFBBuffer::keyReleaseEvent( QKeyEvent *e )
{
decoder->sendKeyReleaseEvent( e );
}
void KRFBBuffer::copyRect( int srcX, int srcY,
int destX, int destY, int w, int h )
{
// qWarning( "Got copy rect" );
bitBlt( pix, destX, destY, pix, srcX, srcY, w, h, CopyROP );
emit updated( destX, destY, w, h );
}
void KRFBBuffer::drawRawRectChunk( void *data,
int x, int y, int w, int h )
{
QImage img( w, h, 32 );
+ int scaleFactor=decoder->con->options()->scaleFactor;
int redMax = Swap16IfLE( decoder->format->redMax );
int greenMax = Swap16IfLE( decoder->format->greenMax );
int blueMax = Swap16IfLE( decoder->format->blueMax );
QPainter p( pix );
if ( decoder->format->bpp == 8 ) {
uchar *d = (unsigned char *) data;
uint r,g,b;
+
for ( int j = 0; j < h; j++ ) {
for ( int i = 0; i < w ; i++ ) {
r = d[ j * w + i ];
r = r >> decoder->format->redShift;
r = r & redMax;
g = d[ j * w + i ];
g = g >> decoder->format->greenShift;
g = g & greenMax;
b = d[ j * w + i ];
b = b >> decoder->format->blueShift;
b = b & blueMax;
r = ( r * 255 ) / redMax;
g = ( g * 255 ) / greenMax;
b = ( b * 255 ) / blueMax;
uint *p = ( uint * ) img.scanLine( j ) + i;
*p = qRgb( r,g,b );
}
}
}
else if ( decoder->format->bpp == 32 ) {
ulong *d = (ulong *) data;
ulong r,g,b;
for ( int j = 0; j < h; j++ ) {
for ( int i = 0; i < w ; i++ ) {
ulong pixel = d[ j * w + i ];
pixel = Swap32IfLE( pixel );
r = pixel;
r = r >> decoder->format->redShift;
r = r & redMax;
g = pixel;
g = g >> decoder->format->greenShift;
g = g & greenMax;
b = pixel;
b = b >> decoder->format->blueShift;
b = b & blueMax;
r = ( r * 255 ) / redMax;
g = ( g * 255 ) / greenMax;
b = ( b * 255 ) / blueMax;
uint *p = ( uint * ) img.scanLine( j ) + i;
*p = qRgb( r,g,b );
}
}
} else if (decoder->format->bpp == 16 ) {
CARD16 *d = (CARD16 *) data;
uint r,g,b;
for ( int j = 0; j < h; j++ ) {
for ( int i = 0; i < w ; i++ ) {
CARD16 pixel = d[ j * w + i ];
pixel = Swap16IfLE( pixel );
r = pixel;
r = r >> decoder->format->redShift;
r = r & redMax;
g = pixel;
g = g >> decoder->format->greenShift;
g = g & greenMax;
b = pixel;
b = b >> decoder->format->blueShift;
b = b & blueMax;
r = ( r * 255 ) / redMax;
g = ( g * 255 ) / greenMax;
b = ( b * 255 ) / blueMax;
ulong *p = ( ulong * ) img.scanLine( j ) + i;
*p = qRgb( r,g,b );
}
}
}
else {
p.setBrush( QBrush( Qt::black ) );
p.drawRect( x, y, w, h );
}
+ if (scaleFactor > 1) {
+ p.drawImage( x/scaleFactor, y/scaleFactor, img.smoothScale(w/scaleFactor,h/scaleFactor) );
+ emit updated( x/scaleFactor, y/scaleFactor, w/scaleFactor, h/scaleFactor );
+ }
+ else {
p.drawImage( x, y, img );
-
emit updated( x, y, w, h );
}
+}
diff --git a/noncore/comm/keypebble/krfbcanvas.cpp b/noncore/comm/keypebble/krfbcanvas.cpp
index 8b56795..896de7f 100644
--- a/noncore/comm/keypebble/krfbcanvas.cpp
+++ b/noncore/comm/keypebble/krfbcanvas.cpp
@@ -1,172 +1,175 @@
#include "krfbconnection.h"
#include "krfbcanvas.h"
#include "krfbserver.h"
#include "krfbbuffer.h"
#include <qpe/config.h>
#include <qpe/qpeapplication.h>
#include <qapplication.h>
#include <qclipboard.h>
#include <qaction.h>
#include <qpixmap.h>
#include <qapplication.h>
#include <qmainwindow.h>
#include <qiconset.h>
KRFBCanvas::KRFBCanvas( QWidget *parent, const char *name )
: QScrollView( parent, name )
{
connection_ = new KRFBConnection();
connect( connection_, SIGNAL( loggedIn() ),
this, SLOT( loggedIn() ) );
loggedIn_ = false;
- QPEApplication::setStylusOperation(viewport(), QPEApplication::RightOnHold);
+
+ //QPEApplication::setStylusOperation(viewport(), QPEApplication::RightOnHold);
viewport()->setFocusPolicy( QWidget::StrongFocus );
viewport()->setFocus();
}
KRFBCanvas::~KRFBCanvas()
{
}
void KRFBCanvas::openConnection(KRFBServer server)
{
QCString host = server.hostname.latin1();
password=server.password;
connection_->connectTo( server);
}
void KRFBCanvas::openURL( const QUrl &url )
{
if ( loggedIn_ ) {
qWarning( "openURL invoked when logged in\n" );
return;
}
QCString host = url.host().latin1();
int display = url.port();
// connection_->connectTo( host, display );
}
void KRFBCanvas::closeConnection()
{
loggedIn_ = false;
connection_->disconnect();
viewport()->setMouseTracking( false );
viewport()->setBackgroundMode( PaletteDark );
setBackgroundMode( PaletteDark );
update();
}
void KRFBCanvas::bell()
{
if ( connection_->options()->deIconify ) {
topLevelWidget()->raise();
topLevelWidget()->show();
}
}
void KRFBCanvas::loggedIn()
{
qWarning( "Ok, we're logged in" );
//
// Get ready for action
//
loggedIn_ = true;
viewport()->setMouseTracking( true );
viewport()->setBackgroundMode( NoBackground );
setBackgroundMode( NoBackground );
// Start using the buffer
connect( connection_->buffer(), SIGNAL( sizeChanged( int, int ) ),
this, SLOT( resizeContents(int,int) ) );
connect( connection_->buffer(), SIGNAL( updated( int, int, int, int ) ),
this, SLOT( viewportUpdate(int,int,int,int) ) );
connect( connection_->buffer(), SIGNAL( bell() ),
this, SLOT( bell() ) );
connect( qApp->clipboard(), SIGNAL( dataChanged() ),
this, SLOT( clipboardChanged() ) );
}
void KRFBCanvas::viewportPaintEvent( QPaintEvent *e )
{
QRect r = e->rect();
if ( loggedIn_ ) {
+ QPixmap p;
+
bitBlt( viewport(), r.x(), r.y(),
connection_->buffer()->pixmap(),
r.x() + contentsX(), r.y() + contentsY(),
r.width(), r.height() );
}
else {
QScrollView::viewportPaintEvent( e );
}
}
void KRFBCanvas::viewportUpdate( int x, int y, int w, int h )
{
updateContents( x, y, w, h );
}
void KRFBCanvas::contentsMousePressEvent( QMouseEvent *e )
{
if ( loggedIn_ )
connection_->buffer()->mouseEvent( e );
}
void KRFBCanvas::contentsMouseReleaseEvent( QMouseEvent *e )
{
if ( loggedIn_ )
connection_->buffer()->mouseEvent( e );
}
void KRFBCanvas::contentsMouseMoveEvent( QMouseEvent *e )
{
if ( loggedIn_ )
connection_->buffer()->mouseEvent( e );
}
void KRFBCanvas::keyPressEvent( QKeyEvent *e )
{
if ( loggedIn_ )
connection_->buffer()->keyPressEvent( e );
}
void KRFBCanvas::keyReleaseEvent( QKeyEvent *e )
{
if ( loggedIn_ )
connection_->buffer()->keyReleaseEvent( e );
}
void KRFBCanvas::refresh()
{
if ( loggedIn_ )
connection_->refresh();
}
void KRFBCanvas::clipboardChanged()
{
if ( loggedIn_ ) {
connection_->sendCutText( qApp->clipboard()->text() );
}
}
void KRFBCanvas::sendCtlAltDel( void)
{
qDebug("Here");
if ( loggedIn_ ) {
connection_->buffer()->keyPressEvent( &QKeyEvent(QEvent::KeyPress,Qt::Key_Delete, 0x7f,ControlButton|AltButton));
// connection_->buffer()->keyPressEvent( &QKeyEvent(QEvent::KeyRelease,Qt::Key_Delete, 0x7f,ControlButton|AltButton));
}
}
diff --git a/noncore/comm/keypebble/krfbdecoder.cpp b/noncore/comm/keypebble/krfbdecoder.cpp
index a964c09..94e3b79 100644
--- a/noncore/comm/keypebble/krfbdecoder.cpp
+++ b/noncore/comm/keypebble/krfbdecoder.cpp
@@ -1,840 +1,841 @@
#include "krfbconnection.h"
#include "krfbserver.h"
#include "krfbserverinfo.h"
#include "krfbdecoder.h"
#include "krfbbuffer.h"
#include <qpe/qpeapplication.h>
#include <qpixmap.h>
#include <qsocket.h>
#include <qevent.h>
#include <qstring.h>
#include <qclipboard.h>
#include <assert.h>
//
// Endian stuff
//
#ifndef KDE_USE_FINAL
const int endianTest = 1;
#endif
#define Swap16IfLE(s) \
(*(char *)&endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))
#define Swap32IfLE(l) \
(*(char *)&endianTest ? ((((l) & 0xff000000) >> 24) | \
(((l) & 0x00ff0000) >> 8) | \
(((l) & 0x0000ff00) << 8) | \
(((l) & 0x000000ff) << 24)) : (l))
//
// The lengths of the messages we need to wait for
//
const int ServerInitLength = 24;
const int UpdateHeaderLength = 4;
const int RectHeaderLength = 12;
const int RectChunkSize = 4;
const int CopyRectPosLength = 4;
const int ServerCutLenLength = 7;
//
// Client -> Server Message Identifiers
//
static CARD8 SetPixelFormatId = 0;
//static CARD8 FixColourMapEntriesId = 1; // Not used
static CARD8 SetEncodingsId = 2;
static CARD8 UpdateRequestId = 3;
static CARD8 KeyEventId = 4;
static CARD8 PointerEventId = 5;
static CARD8 ClientCutTextId = 6;
//
// Server -> Client Message Identifiers
//
static CARD8 UpdateId = 0;
static CARD8 BellId = 2;
static CARD8 ServerCutId = 3;
//
// Encoding identifiers
//
static CARD32 RawEncoding = Swap32IfLE( 0 );
static CARD32 CopyRectEncoding = Swap32IfLE(1 );
static CARD32 RreEncoding = Swap32IfLE( 2 );
static CARD32 CorreEncoding = Swap32IfLE( 4 );
static CARD32 HexTileEncoding = Swap32IfLE( 5 );
static struct {
int keysym;
int keycode;
} keyMap[] = {
{ 0xff08, Qt::Key_Backspace },
{ 0xff09, Qt::Key_Tab },
{ 0xff0d, Qt::Key_Return },
{ 0xff1b, Qt::Key_Escape },
{ 0xff63, Qt::Key_Insert },
{ 0xffff, Qt::Key_Delete },
{ 0xff50, Qt::Key_Home },
{ 0xff57, Qt::Key_End },
{ 0xff55, Qt::Key_Prior },
{ 0xff56, Qt::Key_Next },
{ 0xff51, Qt::Key_Left },
{ 0xff52, Qt::Key_Up },
{ 0xff53, Qt::Key_Right },
{ 0xff54, Qt::Key_Down },
{ 0xffbe, Qt::Key_F1 },
{ 0xffbf, Qt::Key_F2 },
{ 0xffc0, Qt::Key_F3 },
{ 0xffc1, Qt::Key_F4 },
{ 0xffc2, Qt::Key_F5 },
{ 0xffc3, Qt::Key_F6 },
{ 0xffc4, Qt::Key_F7 },
{ 0xffc5, Qt::Key_F8 },
{ 0xffc6, Qt::Key_F9 },
{ 0xffc7, Qt::Key_F10 },
{ 0xffc8, Qt::Key_F11 },
{ 0xffc9, Qt::Key_F12 },
{ 0xffe1, Qt::Key_Shift },
{ 0xffe2, Qt::Key_Shift },
{ 0xffe3, Qt::Key_Control },
{ 0xffe4, Qt::Key_Control },
{ 0xffe7, Qt::Key_Meta },
{ 0xffe8, Qt::Key_Meta },
{ 0xffe9, Qt::Key_Alt },
{ 0xffea, Qt::Key_Alt },
{ 0, 0 }
};
KRFBDecoder::KRFBDecoder( KRFBConnection *con )
: QObject( con, "RFB Decoder" )
{
assert( con );
assert( con->state() == KRFBConnection::Connected );
this->con = con;
this->buf = 0;
this->info = 0;
this->format = 0;
this->buttonMask = 0;
currentState = Idle;
}
KRFBDecoder::~KRFBDecoder()
{
if ( info )
delete info;
if ( format )
delete format;
}
void KRFBDecoder::start()
{
sendClientInit();
}
void KRFBDecoder::sendClientInit()
{
con->write( &( con->options()->shared ), 1 );
// Wait for server init
qWarning( "Waiting for server init" );
static QString statusMsg = tr( "Waiting for server initialisation..." );
emit status( statusMsg );
currentState = AwaitingServerInit;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotServerInit() ) );
con->waitForData( ServerInitLength );
}
void KRFBDecoder::gotServerInit()
{
qWarning( "Got server init" );
disconnect( con, SIGNAL( gotEnoughData() ), this, SLOT( gotServerInit() ) );
if ( info )
delete info;
info = new KRFBServerInfo;
CHECK_PTR( info );
con->read( &(info->width), 2 );
info->width = Swap16IfLE( info->width );
con->read( &info->height, 2 );
info->height = Swap16IfLE( info->height );
con->read( &(info->bpp), 1 );
con->read( &(info->depth), 1 );
con->read( &(info->bigEndian), 1 );
con->read( &(info->trueColor), 1 );
con->read( &(info->redMax), 2 );
info->redMax = Swap16IfLE( info->redMax );
con->read( &(info->greenMax), 2 );
info->greenMax = Swap16IfLE( info->greenMax );
con->read( &(info->blueMax), 2 );
info->blueMax = Swap16IfLE( info->blueMax );
con->read( &(info->redShift), 1 );
con->read( &(info->greenShift), 1 );
con->read( &(info->blueShift), 1 );
con->read( info->padding, 3 );
con->read( &(info->nameLength), 4 );
info->nameLength = Swap32IfLE( info->nameLength );
qWarning( "Width = %d, Height = %d", info->width, info->height );
qWarning( "Bpp = %d, Depth = %d, Big = %d, True = %d",
info->bpp, info->depth, info->bigEndian, info->trueColor );
qWarning( "RedMax = %d, GreenMax = %d, BlueMax = %d",
info->redMax, info->greenMax, info->blueMax );
qWarning( "RedShift = %d, GreenShift = %d, BlueShift = %d",
info->redShift, info->greenShift,info-> blueShift );
- buf->resize( info->width, info->height );
+ buf->resize( info->width/con->options()->scaleFactor, info->height /con->options()->scaleFactor);
// Wait for desktop name
qWarning( "Waiting for desktop name" );
static QString statusMsg = tr( "Waiting for desktop name..." );
emit status( statusMsg );
currentState = AwaitingDesktopName;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotDesktopName() ) );
con->waitForData( info->nameLength );
}
void KRFBDecoder::gotDesktopName()
{
assert( info );
assert( currentState == AwaitingDesktopName );
qWarning( "Got desktop name" );
disconnect( con, SIGNAL( gotEnoughData() ),
this, SLOT( gotDesktopName() ) );
char *buf = new char[ info->nameLength + 1 ];
CHECK_PTR( buf );
con->read( buf, info->nameLength );
buf[ info->nameLength ] = '\0';
info->name = buf;
qWarning( "Desktop: %s", info->name.latin1() );
delete buf;
// Get the format we'll really use and tell the server
decidePixelFormat();
sendPixelFormat();
sendAllowedEncodings();
currentState = Idle;
QString msg;
msg = tr( "Connected to %1" );
msg = msg.arg( info->name );
emit status( msg );
sendUpdateRequest( false );
}
void KRFBDecoder::decidePixelFormat()
{
assert( info );
if ( format )
delete format;
format = new KRFBPixelFormat;
CHECK_PTR( format );
// What depth do we want?
//
// We'll use the minimum of the remote and local depths, UNLESS an
// eight bit session has been specifically requested by the user.
int screenDepth = QPixmap::defaultDepth();
int bestDepth = ( screenDepth > info->depth ) ? info->depth : screenDepth;
int chosenDepth;
if ( con->options()->colors256 )
chosenDepth = 8;
else
chosenDepth = bestDepth;
qWarning( "Screen depth=%d, server depth=%d, best depth=%d, " \
"eight bit %d, chosenDepth=%d",
screenDepth,
info->depth,
bestDepth,
con->options()->colors256, chosenDepth );
format->depth = chosenDepth;
// If we're using the servers native depth
if ( chosenDepth == info->depth ) {
// Use the servers native format
format->bpp = info->bpp;
// format->bigEndian = info->bigEndian;
format->bigEndian = true;
format->trueColor = info->trueColor;
format->redMax = info->redMax;
format->greenMax = info->greenMax;
format->blueMax = info->blueMax;
format->redShift = info->redShift;
format->greenShift = info->greenShift;
format->blueShift = info->blueShift;
}
else {
if ( chosenDepth == 8 ) {
format->bpp = 8;
format->bigEndian = true;
format->trueColor = true;
format->redMax = 7;
format->greenMax = 7;
format->blueMax = 3;
format->redShift = 0;
format->greenShift = 3;
format->blueShift = 6;
}
}
format->redMax = Swap16IfLE( format->redMax );
format->greenMax = Swap16IfLE( format->greenMax );
format->blueMax = Swap16IfLE( format->blueMax );
}
void KRFBDecoder::sendPixelFormat()
{
static char padding[3];
con->write( &SetPixelFormatId, 1 );
con->write( padding, 3 );
con->write( &(format->bpp), 1 );
con->write( &(format->depth), 1 );
con->write( &(format->bigEndian), 1 );
con->write( &(format->trueColor), 1 );
con->write( &(format->redMax), 2 );
con->write( &(format->greenMax), 2 );
con->write( &(format->blueMax), 2 );
con->write( &(format->redShift), 1 );
con->write( &(format->greenShift), 1 );
con->write( &(format->blueShift), 1 );
con->write( format->padding, 3 ); // Padding
}
void KRFBDecoder::sendAllowedEncodings()
{
static CARD8 padding[1];
con->write( &SetEncodingsId, 1 );
con->write( padding, 1 );
static CARD16 noEncodings = con->options()->encodings();
noEncodings = Swap16IfLE( noEncodings );
con->write( &noEncodings, 2 );
if ( con->options()->corre )
con->write( &CorreEncoding, 4 );
if ( con->options()->hexTile )
con->write( &HexTileEncoding, 4 );
if ( con->options()->rre )
con->write( &RreEncoding, 4 );
if ( con->options()->copyrect )
con->write( &CopyRectEncoding, 4 );
// We always support this
con->write( &RawEncoding, 4 );
}
void KRFBDecoder::sendUpdateRequest( bool incremental )
{
if ( currentState != Idle )
return;
con->write( &UpdateRequestId, 1 );
con->write( &incremental, 1 );
static CARD16 x = 0, y = 0;
static CARD16 w = Swap16IfLE( info->width );
static CARD16 h = Swap16IfLE( info->height );
con->write( &x, 2 );
con->write( &y, 2 );
con->write( &w, 2 );
con->write( &h, 2 );
// Now wait for the update
currentState = AwaitingUpdate;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotUpdateHeader() ) );
con->waitForData( UpdateHeaderLength );
}
void KRFBDecoder::gotUpdateHeader()
{
assert( currentState == AwaitingUpdate );
// qWarning( "Got update header" );
disconnect( con, SIGNAL( gotEnoughData() ),
this, SLOT( gotUpdateHeader() ) );
CARD8 msgType;
con->read( &msgType, 1 );
if ( msgType != UpdateId ) {
// We might have a bell or server cut
if ( msgType == ServerCutId ) {
oldState = currentState;
gotServerCut();
}
else if ( msgType == BellId ) {
oldState = currentState;
gotBell();
}
else {
int msg = msgType;
QString protocolError = tr( "Protocol Error: Message Id %1 was "
"found when expecting an update "
"message." ).arg( msg );
currentState = Error;
emit error( protocolError );
}
return;
}
CARD8 padding;
con->read( &padding, 1 );
con->read( &noRects, 2 );
noRects = Swap16IfLE( noRects );
// qWarning( "Expecting %d rects", noRects );
// Now wait for the data
currentState = AwaitingRectHeader;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRectHeader() ) );
con->waitForData( RectHeaderLength );
}
void KRFBDecoder::gotRectHeader()
{
assert( currentState == AwaitingRectHeader );
// qWarning( "Got rect header" );
disconnect( con, SIGNAL( gotEnoughData() ),
this, SLOT( gotRectHeader() ) );
con->read( &x, 2 );
x = Swap16IfLE( x );
con->read( &y, 2 );
y = Swap16IfLE( y );
con->read( &w, 2 );
w = Swap16IfLE( w );
con->read( &h, 2 );
h = Swap16IfLE( h );
con->read( &encoding, 4 );
// CARD32 encodingLocal = Swap32IfLE( encoding );
// qWarning( "Rect: x=%d, y= %d, w=%d, h=%d, encoding=%ld",
// x, y, w, h, encodingLocal );
//
// Each encoding needs to be handled differently. Some require
// waiting for more data, but others like a copyrect do not.
// Our constants have already been byte swapped, so we use
// the remote value as is.
//
if ( encoding == RawEncoding ) {
// qWarning( "Raw encoding" );
handleRawRect();
}
else if ( encoding == CopyRectEncoding ) {
// qWarning( "CopyRect encoding" );
handleCopyRect();
}
else if ( encoding == RreEncoding ) {
qWarning( "RRE encoding" );
handleRRERect();
}
else if ( encoding == CorreEncoding ) {
qWarning( "CoRRE encoding" );
handleCoRRERect();
}
else if ( encoding == HexTileEncoding ) {
qWarning( "HexTile encoding" );
handleHexTileRect();
}
else {
int msg = Swap32IfLE( encoding );
QString protocolError = tr( "Protocol Error: An unknown encoding was "
"used by the server %1" ).arg( msg );
currentState = Error;
qWarning( "Unknown encoding, %d", msg );
emit error( protocolError );
return;
}
}
//
// Raw Encoding
//
void KRFBDecoder::handleRawRect()
{
// We need something a bit cleverer here to handle large
// rectanges nicely. The chunking should be based on the
// overall size (but has to be in complete lines).
// qWarning( "Handling a raw rect chunk" );
// CARD32 lineCount = w * format->bpp / 8;
if ( h > RectChunkSize ) {
// if ( con->sock->size() / lineCount ) {
// getRawRectChunk( con->sock->size() / lineCount );
// }
// else {
getRawRectChunk( RectChunkSize );
// }
}
else {
getRawRectChunk( h );
}
}
void KRFBDecoder::getRawRectChunk( int lines )
{
this->lines = lines;
CARD32 count = lines * w * format->bpp / 8;
// Wait for server init
// qWarning( "Waiting for raw rect chunk, %ld", count );
currentState = AwaitingRawRectChunk;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRawRectChunk() ) );
con->waitForData( count );
}
void KRFBDecoder::gotRawRectChunk()
{
assert( currentState == AwaitingRawRectChunk );
disconnect( con, SIGNAL( gotEnoughData() ),
this, SLOT( gotRawRectChunk() ) );
// qWarning( "Got raw rect chunk" );
//
// Read the rect data and copy it to the buffer.
//
// TODO: Replace this!
int count = lines * w * format->bpp / 8;
char *hack = new char[ count ];
con->read( hack, count );
buf->drawRawRectChunk( hack, x, y, w, lines );
delete hack;
// /TODO:
h = h - lines;
y = y + lines;
if ( h > 0 ) {
handleRawRect();
}
else {
noRects--;
// qWarning( "There are %d rects left", noRects );
if ( noRects ) {
currentState = AwaitingRectHeader;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRectHeader() ) );
con->waitForData( RectHeaderLength );
}
else
currentState = Idle;
}
}
//
// Copy Rectangle Encoding
//
void KRFBDecoder::handleCopyRect()
{
currentState = AwaitingCopyRectPos;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotCopyRectPos() ) );
con->waitForData( CopyRectPosLength );
}
void KRFBDecoder::gotCopyRectPos()
{
disconnect( con, SIGNAL( gotEnoughData() ),
this, SLOT( gotCopyRectPos() ) );
CARD16 srcX;
CARD16 srcY;
con->read( &srcX, 2 );
con->read( &srcY, 2 );
srcX = Swap16IfLE( srcX );
srcY = Swap16IfLE( srcY );
buf->copyRect( srcX, srcY, x, y, w, h );
noRects--;
// qWarning( "There are %d rects left", noRects );
if ( noRects ) {
currentState = AwaitingRectHeader;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRectHeader() ) );
con->waitForData( RectHeaderLength );
}
else
currentState = Idle;
}
void KRFBDecoder::handleRRERect()
{
qWarning( "RRE not implemented" );
}
void KRFBDecoder::handleCoRRERect()
{
qWarning( "CoRRE not implemented" );
}
void KRFBDecoder::handleHexTileRect()
{
qWarning( "HexTile not implemented" );
}
void KRFBDecoder::sendMouseEvent( QMouseEvent *e )
{
// Deal with the buttons
if ( e->type() != QEvent::MouseMove ) {
buttonMask = 0;
if ( e->type() == QEvent::MouseButtonPress ) {
if ( e->button() & LeftButton )
buttonMask |= 0x01;
if ( e->button() & MidButton )
buttonMask |= 0x04;
if ( e->button() & RightButton )
buttonMask |= 0x02;
}
else if ( e->type() == QEvent::MouseButtonRelease ) {
if ( e->button() & LeftButton )
buttonMask &= 0x06;
if ( e->button() & MidButton )
buttonMask |= 0x03;
if ( e->button() & RightButton )
buttonMask |= 0x05;
}
}
- CARD16 x = Swap16IfLE( e->x() );
- CARD16 y = Swap16IfLE( e->y() );
+ // HACK: Scaling
+ CARD16 x = Swap16IfLE( e->x() * con->options()->scaleFactor );
+ CARD16 y = Swap16IfLE( e->y() * con->options()->scaleFactor );
con->write( &PointerEventId, 1 );
con->write( &buttonMask, 1 );
con->write( &x, 2 );
con->write( &y, 2 );
}
void KRFBDecoder::sendCutEvent( const QString &unicode )
{
//
// Warning: There is a bug in the RFB protocol because there is no way to find
// out the codepage in use on the remote machine. This could be fixed by requiring
// the remote server to use utf8 etc. but for now we have to assume they're the
// same. I've reported this problem to the ORL guys, but they apparantly have no
// immediate plans to fix the issue. :-( (rich)
//
CARD8 padding[3];
QCString text = unicode.local8Bit();
CARD32 length = text.length();
length = Swap32IfLE( length );
con->write( &ClientCutTextId, 1 );
con->write( &padding, 3 );
con->write( &length, 4 );
con->write( text.data(), length );
}
void KRFBDecoder::gotServerCut()
{
qWarning( "Got server cut" );
currentState = AwaitingServerCutLength;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotServerCutLength() ) );
con->waitForData( ServerCutLenLength );
}
void KRFBDecoder::gotServerCutLength()
{
assert( currentState = AwaitingServerCutLength );
disconnect( con, SIGNAL( gotEnoughData() ),
this, SLOT( gotServerCutLength() ) );
CARD8 padding[3];
con->read( padding, 3 );
con->read( &serverCutTextLen, 4 );
serverCutTextLen = Swap32IfLE( serverCutTextLen );
currentState = AwaitingServerCutText;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotServerCutText() ) );
con->waitForData( serverCutTextLen );
}
void KRFBDecoder::gotServerCutText()
{
assert( currentState = AwaitingServerCutText );
disconnect( con, SIGNAL( gotEnoughData() ),
this, SLOT( gotServerCutText() ) );
//
// Warning: There is a bug in the RFB protocol because there is no way to find
// out the codepage in use on the remote machine. This could be fixed by requiring
// the remote server to use utf8 etc. but for now we have to assume they're the
// same. I've reported this problem to the ORL guys, but they apparantly have no
// immediate plans to fix the issue. :-( (rich)
//
char *cutbuf = new char[ serverCutTextLen + 1 ];
CHECK_PTR( cutbuf );
con->read( cutbuf, serverCutTextLen );
cutbuf[ serverCutTextLen ] = '\0';
qWarning( "Server cut: %s", cutbuf );
QString cutText( cutbuf ); // DANGER!!
qApp->clipboard()->setText( cutText );
delete cutbuf;
// Now wait for the update (again)
if ( oldState == AwaitingUpdate ) {
currentState = AwaitingUpdate;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotUpdateHeader() ) );
con->waitForData( UpdateHeaderLength );
}
else if ( oldState == Idle ) {
currentState = Idle;
}
else {
qWarning( "Async handled in weird state" );
currentState = oldState;
};
}
void KRFBDecoder::gotBell()
{
qWarning( "Got server bell" );
buf->soundBell();
// Now wait for the update (again)
if ( oldState == AwaitingUpdate ) {
currentState = AwaitingUpdate;
connect( con, SIGNAL( gotEnoughData() ), SLOT( gotUpdateHeader() ) );
con->waitForData( UpdateHeaderLength );
}
else if ( oldState == Idle ) {
currentState = Idle;
}
else {
qWarning( "Async handled in weird state" );
currentState = oldState;
};
}
void KRFBDecoder::sendKeyPressEvent( QKeyEvent *event )
{
int key;
key = toKeySym( event );
if ( key ) {
key = Swap32IfLE( key );
CARD8 mask = true;
CARD16 padding = 0;
con->write( &KeyEventId, 1 );
con->write( &mask, 1 );
con->write( &padding, 2 );
con->write( &key, 4 );
}
}
void KRFBDecoder::sendKeyReleaseEvent( QKeyEvent *event )
{
int key;
key = toKeySym( event );
if ( key ) {
key = Swap32IfLE( key );
CARD8 mask = false;
CARD16 padding = 0;
con->write( &KeyEventId, 1 );
con->write( &mask, 1 );
con->write( &padding, 2 );
con->write( &key, 4 );
}
}
int KRFBDecoder::toKeySym( QKeyEvent *k )
{
int ke = 0;
ke = k->ascii();
// Markus: Crappy hack. I dont know why lower case letters are
// not defined in qkeydefs.h. The key() for e.g. 'l' == 'L'.
// This sucks. :-(
if ( (ke == 'a') || (ke == 'b') || (ke == 'c') || (ke == 'd')
|| (ke == 'e') || (ke == 'f') || (ke == 'g') || (ke == 'h')
|| (ke == 'i') || (ke == 'j') || (ke == 'k') || (ke == 'l')
|| (ke == 'm') || (ke == 'n') || (ke == 'o') || (ke == 'p')
|| (ke == 'q') || (ke == 'r') || (ke == 's') || (ke == 't')
|| (ke == 'u') || (ke == 'v') ||( ke == 'w') || (ke == 'x')
|| (ke == 'y') || (ke == 'z') ) {
ke = k->key();
ke = ke + 0x20;
return ke;
}
// qkeydefs = xkeydefs! :-)
if ( ( k->key() >= 0x0a0 ) && k->key() <= 0x0ff )
return k->key();
if ( ( k->key() >= 0x20 ) && ( k->key() <= 0x7e ) )
return k->key();
// qkeydefs != xkeydefs! :-(
// This is gonna suck :-(
int i = 0;
while ( keyMap[i].keycode ) {
if ( k->key() == keyMap[i].keycode )
return keyMap[i].keysym;
i++;
}
return 0;
}
diff --git a/noncore/comm/keypebble/krfbdecoder.h b/noncore/comm/keypebble/krfbdecoder.h
index 4ba0185..db6271d 100644
--- a/noncore/comm/keypebble/krfbdecoder.h
+++ b/noncore/comm/keypebble/krfbdecoder.h
@@ -1,134 +1,135 @@
// -*- c++ -*-
#ifndef KRFBDECODER_H
#define KRFBDECODER_H
#include <qobject.h>
class KRFBConnection;
class KRFBServerInfo;
class KRFBPixelFormat;
class KRFBBuffer;
typedef unsigned char CARD8;
typedef unsigned short CARD16;
typedef unsigned long CARD32;
/**
* Negotiates the pixel format to be used then decodes the resulting
* data stream.
*
* @author Richard Moore, rich@kde.org
*/
class KRFBDecoder : public QObject
{
Q_OBJECT
public:
friend class KRFBBuffer;
enum State {
AwaitingServerInit,
AwaitingDesktopName,
AwaitingUpdate,
AwaitingRectHeader,
AwaitingRawRectChunk,
AwaitingCopyRectPos,
AwaitingServerCutLength,
AwaitingServerCutText,
Idle,
Error
};
/**
* Create a KRFBDecoder that reads data from a logged in KRFBConnection
* and sends its output to a KRFBBuffer.
*/
KRFBDecoder( KRFBConnection *con );
~KRFBDecoder();
void setBuffer( KRFBBuffer *buf ) { this->buf = buf; };
void start();
int toKeySym( QKeyEvent *k );
//
// Client -> Server messages
//
void sendUpdateRequest( bool incremental );
void sendMouseEvent( QMouseEvent *e );
void sendKeyPressEvent( QKeyEvent *e );
void sendKeyReleaseEvent( QKeyEvent *e );
void sendCutEvent( const QString &text );
protected:
//
// Initial format negotiation
//
void decidePixelFormat();
void sendPixelFormat();
void sendClientInit();
void sendAllowedEncodings();
//
// Rectange processing
//
void handleRawRect();
void handleCopyRect();
void handleRRERect();
void handleCoRRERect();
void handleHexTileRect();
void getRawRectChunk( int lines );
protected slots:
void gotServerInit();
void gotDesktopName();
void gotUpdateHeader();
void gotRectHeader();
void gotRawRectChunk();
void gotCopyRectPos();
void gotServerCut();
void gotServerCutLength();
void gotServerCutText();
void gotBell();
signals:
void error( const QString & );
void status( const QString & );
+protected:
+ /** The connection to the server. */
+ KRFBConnection *con;
private:
State currentState;
// Used to store the state we were in before a cut or bell msg
State oldState;
// The number of rects we're expecting
CARD16 noRects;
//
// Info about the current rect.
//
CARD16 x, y, w, h;
int lines;
CARD32 encoding;
CARD32 serverCutTextLen;
/** Where we draw the data (and the source of our events). */
KRFBBuffer *buf;
- /** The connection to the server. */
- KRFBConnection *con;
/** Info about the RFB server. */
KRFBServerInfo *info;
/** The pixel format we want. */
KRFBPixelFormat *format;
CARD8 buttonMask;
};
#endif // KRFBDECODER_H
diff --git a/noncore/comm/keypebble/krfbserver.cpp b/noncore/comm/keypebble/krfbserver.cpp
index 5775f09..b47534e 100644
--- a/noncore/comm/keypebble/krfbserver.cpp
+++ b/noncore/comm/keypebble/krfbserver.cpp
@@ -1,39 +1,41 @@
#include <qpe/config.h>
#include <qpe/qpeapplication.h>
#include "krfbserver.h"
KRFBServer::KRFBServer()
{
QString name;
QString hostname;
QString password;
display=0;
+ scaleFactor=1;
+
hexTile=0;
corre=0;
rre=0;
copyrect=1;
colors256=1;
shared=0;
readOnly=0;
deIconify=0;
updateRate=0;
}
KRFBServer::~KRFBServer()
{
}
int KRFBServer::encodings()
{
// Initially one because we always support raw encoding
int count = 1;
count += hexTile ? 1 : 0;
count += corre ? 1 : 0;
count += rre ? 1 : 0;
count += copyrect ? 1 : 0;
return count;
}
diff --git a/noncore/comm/keypebble/krfbserver.h b/noncore/comm/keypebble/krfbserver.h
index f87eecc..e38e2d2 100644
--- a/noncore/comm/keypebble/krfbserver.h
+++ b/noncore/comm/keypebble/krfbserver.h
@@ -1,34 +1,35 @@
// -*- c++ -*-
#ifndef KRFBOPTIONS_H
#define KRFBOPTIONS_H
class Config;
class KRFBServer
{
public:
KRFBServer();
~KRFBServer();
int encodings();
QString name;
QString hostname;
QString password;
int display;
bool hexTile;
bool corre;
bool rre;
bool copyrect;
bool colors256;
bool shared;
bool readOnly;
bool deIconify;
int updateRate;
+ int scaleFactor;
};
#endif // KRFBOPTIONS_H
diff --git a/noncore/comm/keypebble/kvnc.cpp b/noncore/comm/keypebble/kvnc.cpp
index aa46e2f..b173004 100644
--- a/noncore/comm/keypebble/kvnc.cpp
+++ b/noncore/comm/keypebble/kvnc.cpp
@@ -1,272 +1,278 @@
#include <qiconset.h>
#include <qdialog.h>
#include <qpixmap.h>
#include <qdom.h>
#include <qaction.h>
#include <qpe/qpemenubar.h>
#include <qstatusbar.h>
#include <qpopupmenu.h>
#include <qpushbutton.h>
#include <qpe/qpetoolbar.h>
#include <qtimer.h>
#include <qmessagebox.h>
#include <qspinbox.h>
#include <qlistbox.h>
#include <qlineedit.h>
#include <qpe/qpeapplication.h>
#include <qpe/global.h>
#include <qpe/qpetoolbar.h>
#include <qpe/resource.h>
#include <assert.h>
#include "kvnc.h"
#include "krfbcanvas.h"
#include "krfbconnection.h"
#include "kvncconndlg.h"
#include "krfbserver.h"
static int u_id = 1;
static int get_unique_id()
{
return u_id++;
}
/* XPM */
static char * menu_xpm[] = {
"12 12 5 1",
" c None",
". c #000000",
"+ c #FFFDAD",
"@ c #FFFF00",
"# c #E5E100",
" ",
" ",
" ......... ",
" .+++++++. ",
" .+@@@@#. ",
" .+@@@#. ",
" .+@@#. ",
" .+@#. ",
" .+#. ",
" .+. ",
" .. ",
" "};
const int StatusTextId = 0;
KVNC::KVNC( const char *name ) : QMainWindow( 0, name )
{
setCaption( tr("VNC Viewer") );
fullscreen = false;
stack = new QWidgetStack( this );
setCentralWidget( stack );
bookmarkSelector=new KVNCBookmarkDlg();
stack->addWidget(bookmarkSelector,get_unique_id());
stack->raiseWidget( bookmarkSelector );
canvas = new KRFBCanvas( stack, "canvas" );
stack->addWidget(canvas,get_unique_id());
setCentralWidget( stack );
+
connect( bookmarkSelector->bookmarkList, SIGNAL(doubleClicked(QListBoxItem *)),
this, SLOT(openConnection(QListBoxItem *)) );
connect( canvas->connection(), SIGNAL(statusChanged(const QString &)),
this, SLOT(statusMessage(const QString &)) );
connect( canvas->connection(), SIGNAL(error(const QString &)),
this, SLOT(error(const QString &)) );
connect( canvas->connection(), SIGNAL(connected()), this, SLOT(connected()) );
connect( canvas->connection(), SIGNAL(loggedIn()), this, SLOT(loggedIn()) );
connect( canvas->connection(), SIGNAL(disconnected()), this, SLOT(disconnected()) );
setupActions();
cornerButton = new QPushButton( this );
cornerButton->setPixmap( QPixmap( (const char**)menu_xpm ) );
connect( cornerButton, SIGNAL(pressed()), this, SLOT(showMenu()) );
canvas->setCornerWidget( cornerButton );
stack->raiseWidget( bookmarkSelector );
- QPEToolBar *bar = new QPEToolBar( this );
+ bar= new QToolBar( this );
+ setToolBarsMovable( false );
+ setRightJustification(false);
+
QAction *n = new QAction( tr( "New Connection" ), Resource::loadPixmap( "new" ),
QString::null, 0, this, 0 );
connect( n, SIGNAL( activated() ),
this, SLOT( newConnection() ) );
n->addTo( bar );
QAction *o = new QAction( tr( "Open Bookmark" ), Resource::loadPixmap( "edit" ),
QString::null, 0, this, 0 );
connect( o, SIGNAL( activated() ),
this, SLOT( openConnection() ) );
o->addTo( bar );
QAction *d = new QAction( tr( "Delete Bookmark" ), Resource::loadPixmap( "trash" ),
QString::null, 0, this, 0 );
connect( d, SIGNAL( activated() ),
this, SLOT( deleteBookmark() ) );
d->addTo( bar );
}
KVNC::~KVNC()
{
}
void KVNC::newConnection()
{
curServer=new KRFBServer;
KVNCConnDlg dlg( curServer,this);
dlg.showMaximized();
if ( dlg.exec()) {
if (!curServer->name.isEmpty())
bookmarkSelector->addBookmark(curServer);
canvas->openConnection(*curServer);
} else
curServer=0;
}
void KVNC::openConnection( QString name)
{
curServer=bookmarkSelector->getServer(name);
if (curServer) {
KVNCConnDlg dlg( curServer,this);
dlg.showMaximized();
if ( dlg.exec() ) {
canvas->openConnection(*curServer);
bookmarkSelector->writeBookmarks();
} else
curServer=0;
}
}
void KVNC::openConnection( void )
{
openConnection( bookmarkSelector->selectedBookmark());
}
void KVNC::openConnection( QListBoxItem * item)
{
openConnection(item->text());
}
void KVNC::setupActions()
{
cornerMenu = new QPopupMenu( this );
fullScreenAction = new QAction( tr("Full Screen"), QString::null, 0, 0 );
connect( fullScreenAction, SIGNAL(activated()),
this, SLOT( toggleFullScreen() ) );
fullScreenAction->addTo( cornerMenu );
fullScreenAction->setEnabled( false );
ctlAltDelAction = new QAction( tr("Send Contrl-Alt-Delete"), QString::null, 0, 0 );
connect( ctlAltDelAction, SIGNAL(activated()),
canvas, SLOT( sendCtlAltDel() ) );
ctlAltDelAction->addTo( cornerMenu );
ctlAltDelAction->setEnabled( false );
disconnectAction = new QAction( tr("Disconnect"), QString::null, 0, 0 );
connect( disconnectAction, SIGNAL(activated()),
this, SLOT( closeConnection() ) );
disconnectAction->addTo( cornerMenu );
disconnectAction->setEnabled( false );
}
void KVNC::toggleFullScreen()
{
if ( fullscreen ) {
canvas->releaseKeyboard();
canvas->reparent( stack, 0, QPoint(0,0), false );
canvas->setFrameStyle( QFrame::Panel | QFrame::Sunken );
setCentralWidget( stack );
stack->addWidget(canvas,get_unique_id());
stack->raiseWidget(canvas);
canvas->show();
stack->show();
fullScreenAction->setText( tr("Full Screen") );
} else {
canvas->setFrameStyle( QFrame::NoFrame );
stack->removeWidget(canvas);
canvas->reparent( 0,WStyle_Tool | WStyle_Customize | WStyle_StaysOnTop,
QPoint(0,0),false);
canvas->resize(qApp->desktop()->width(), qApp->desktop()->height());
canvas->raise();
canvas->setFocus();
canvas->grabKeyboard();
canvas->show();
fullScreenAction->setText( tr("Stop Full Screen") );
}
fullscreen = !fullscreen;
}
void KVNC::closeConnection()
{
if ( fullscreen )
toggleFullScreen();
canvas->closeConnection();
}
void KVNC::showMenu()
{
QPoint pt = mapToGlobal(cornerButton->pos());
QSize s = cornerMenu->sizeHint();
pt.ry() -= s.height();
pt.rx() -= s.width();
cornerMenu->popup( pt );
}
void KVNC::connected()
{
static QString msg = tr( "Connected to remote host" );
statusMessage( msg );
ctlAltDelAction->setEnabled(true);
disconnectAction->setEnabled( true );
fullScreenAction->setEnabled( true );
stack->raiseWidget(canvas);
+ bar->hide();
}
void KVNC::loggedIn()
{
static QString msg = tr( "Logged in to remote host" );
statusMessage( msg );
}
void KVNC::disconnected()
{
if ( fullscreen )
toggleFullScreen();
static QString msg = tr( "Connection closed" );
statusMessage( msg );
ctlAltDelAction->setEnabled(false);
disconnectAction->setEnabled( false );
fullScreenAction->setEnabled( false );
stack->raiseWidget(bookmarkSelector);
+ bar->show();
}
void KVNC::statusMessage( const QString &m )
{
Global::statusMessage( m );
}
void KVNC::error( const QString &msg )
{
statusMessage( msg );
QMessageBox::warning( this, tr("VNC Viewer"), msg );
}
void KVNC::deleteBookmark(void)
{
bookmarkSelector->deleteBookmark(bookmarkSelector->selectedBookmark());
}
diff --git a/noncore/comm/keypebble/kvnc.h b/noncore/comm/keypebble/kvnc.h
index 6e0a385..82a24fc 100644
--- a/noncore/comm/keypebble/kvnc.h
+++ b/noncore/comm/keypebble/kvnc.h
@@ -1,66 +1,67 @@
// -*- c++ -*-
#ifndef KVNC_H
#define KVNC_H
#include <qmainwindow.h>
-#include <qurl.h>
+#include <qtoolbar.h>
#include <qwidgetstack.h>
#include "kvncbookmarkdlg.h"
class QAction;
class KRFBCanvas;
class QPushButton;
class QToolBar;
/**
* Top level window for Keystone.
*
* @author Richard Moore, rich@kde.org
* @version $Id$
*/
class KVNC : public QMainWindow
{
Q_OBJECT
public:
KVNC( const char *name = 0 );
~KVNC();
public slots:
void newConnection();
void deleteBookmark();
void openConnection(QListBoxItem *);
void openConnection(QString);
void openConnection(void);
void toggleFullScreen();
void closeConnection();
protected:
void setupActions();
protected slots:
void showMenu();
void connected();
void loggedIn();
void disconnected();
void statusMessage( const QString & );
void error( const QString & );
private:
bool fullscreen;
KRFBCanvas *canvas;
QPopupMenu *cornerMenu;
QPushButton *cornerButton;
QAction *fullScreenAction;
QAction *optionsAction;
QAction *disconnectAction;
QAction *ctlAltDelAction;;
QAction *connectAction;
+ QToolBar * bar;
KVNCBookmarkDlg * bookmarkSelector;
QWidgetStack * stack;
KRFBServer * curServer;
};
#endif // KVNC_H
diff --git a/noncore/comm/keypebble/kvncbookmarkdlg.cpp b/noncore/comm/keypebble/kvncbookmarkdlg.cpp
index 1f97d13..1e2f3c3 100644
--- a/noncore/comm/keypebble/kvncbookmarkdlg.cpp
+++ b/noncore/comm/keypebble/kvncbookmarkdlg.cpp
@@ -1,220 +1,223 @@
#include <qframe.h>
#include <qvbox.h>
#include <qcheckbox.h>
#include <qspinbox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qwhatsthis.h>
#include <qfile.h>
#include <qdir.h>
#include <qstring.h>
#include <qapplication.h>
#include <qlineedit.h>
#include <qtextstream.h>
#include <qpushbutton.h>
#include <qlistbox.h>
#include <qpe/config.h>
#include <qpe/global.h>
#include "krfbserver.h"
#include "kvncbookmarkdlg.h"
KVNCBookmarkDlg::KVNCBookmarkDlg( QWidget * parent=0, const char * name=0, WFlags f=0 )
: KVNCBookmarkDlgBase( parent, name,f)
{
readBookmarks();
refresh();
}
KVNCBookmarkDlg::~KVNCBookmarkDlg()
{
}
void KVNCBookmarkDlg::addBookmark(KRFBServer * server)
{
if (server) {
servers.append(server);
bookmarkList->insertItem(server->name);
writeBookmarks();
refresh();
}
}
void KVNCBookmarkDlg::deleteBookmark(QString name)
{
KRFBServer * server=0;
for ( server=servers.first(); server != 0; server=servers.next() ) {
if (server->name==name) {
servers.remove(servers.at());
writeBookmarks();
refresh();
return;
}
}
}
KRFBServer *KVNCBookmarkDlg::getServer(QString name)
{
KRFBServer * server=0;
for ( server=servers.first(); server != 0; server=servers.next() ) {
if (server->name==name)
return server;
}
return 0;
}
/*
Note that the degree of protection offered by the encryption here is
only sufficient to avoid the most casual observation of the configuration
files. People with access to the files can write down the contents and
decrypt it using this source code.
Conceivably, and at some burden to the user, this encryption could
be improved.
*/
QString KVNCBookmarkDlg::encipher(const QString& plain)
{
// mainly, we make it long
QString cipher;
int mix=28730492;
for (int i=0; i<(int)plain.length(); i++) {
int u = plain[i].unicode();
int c = u ^ mix;
QString x = QString::number(c,36);
cipher.append(QChar('a'+x.length()));
cipher.append(x);
mix *= u;
}
return cipher;
}
QString KVNCBookmarkDlg::decipher(const QString& cipher)
{
QString plain;
int mix=28730492;
for (int i=0; i<(int)cipher.length();) {
int l = cipher[i].unicode()-'a';
QString x = cipher.mid(i+1,l); i+=l+1;
int u = x.toInt(0,36) ^ mix;
plain.append(QChar(u));
mix *= u;
}
return plain;
}
void KVNCBookmarkDlg::readBookmarks(void)
{
QFile f(QDir::homeDirPath() + QString("/Applications/keypebble/bookmarks"));
QStringList entry;
QString key, val;
KRFBServer * server=0;
if ( f.open(IO_ReadOnly) ) {
QTextStream t( &f );
QString s;
int n = 1;
while ( !t.eof() ) {
s = t.readLine();
entry=QStringList::split('=',s);
key=entry[0].stripWhiteSpace().lower();
val=entry[1].stripWhiteSpace();
if (key=="server") {
if (server){
servers.append(server);
server=0;
}
server = new KRFBServer();
if (!server)
return;
server->name=val;
}
else if (key=="hostname")
server->hostname=val;
else if (key=="password")
server->password=decipher(val);
else if (key=="display")
server->display=val.toInt();
else if (key=="hextile")
server->hexTile=val.toInt();
else if (key=="corre")
server->corre=val.toInt();
else if (key=="rre")
server->rre=val.toInt();
else if (key=="copyrect")
server->copyrect=val.toInt();
else if (key=="colors256")
server->colors256=val.toInt();
else if (key=="shared")
server->shared=val.toInt();
else if (key=="readonly")
server->readOnly=val.toInt();
else if (key=="deiconify")
server->deIconify=val.toInt();
else if (key=="updaterate")
server->updateRate=val.toInt();
+ else if (key=="scalefactor")
+ server->scaleFactor=val.toInt();
}
if (server){
servers.append(server);
server=0;
}
f.close();
}
}
void KVNCBookmarkDlg::writeBookmarks(void)
{
QString filename=Global::applicationFileName("keypebble","bookmarks");
QFile f(filename);
QString key, val;
KRFBServer * server=0;
if ( f.open(IO_ReadWrite) ) {
QTextStream t( &f );
QString s;
int n = 1;
KRFBServer *server;
for ( server=servers.first(); server != 0; server=servers.next() ) {
qDebug(server->name);
t << "server=" << server->name << '\n';
t << "\thostname=" << server->hostname << '\n';
t << "\tpassword=" << encipher(server->password )<< '\n';
t << "\tdisplay=" << server->display << '\n';
t << "\thextile=" << server->hexTile << '\n';
t << "\tcorre=" << server->corre << '\n';
t << "\trre=" << server->rre << '\n';
t << "\tcopyrect=" << server->copyrect << '\n';
t << "\tshared=" << server->shared << '\n';
t << "\treadonly=" << server->readOnly << '\n';
t << "\tdeiconify=" << server->deIconify << '\n';
t << "\tupdaterate=" << server->updateRate << '\n';
+ t << "\tscalefactor=" << server->scaleFactor << '\n';
}
f.close();
}
}
void KVNCBookmarkDlg::refresh(void)
{
bookmarkList->clear();
KRFBServer * server=0;
for ( server=servers.first(); server != 0; server=servers.next() ) {
bookmarkList->insertItem(server->name);
}
}
QString KVNCBookmarkDlg::selectedBookmark()
{
return bookmarkList->currentText();
}
diff --git a/noncore/comm/keypebble/kvncconndlg.cpp b/noncore/comm/keypebble/kvncconndlg.cpp
index 6873feb..2f073f8 100644
--- a/noncore/comm/keypebble/kvncconndlg.cpp
+++ b/noncore/comm/keypebble/kvncconndlg.cpp
@@ -1,75 +1,94 @@
#include <qframe.h>
#include <qvbox.h>
#include <qcheckbox.h>
+#include <qcombobox.h>
#include <qspinbox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qwhatsthis.h>
#include <qapplication.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include "krfbserver.h"
#include "kvncconndlg.h"
KVNCConnDlg::KVNCConnDlg( KRFBServer *options,
QWidget *parent, char *name, bool modal )
: KVNCConnDlgBase( parent, name, modal )
{
this->options=options;
tmpOptions=*options;
serverHostname->setText(options->hostname);
serverDisplay->setValue(options->display);
serverPassword->setText(options->password);
serverBookmark->setText(options->name);
hex->setChecked( options->hexTile );
corre->setChecked( options->corre );
rre->setChecked( options->rre );
copyRect->setChecked( options->copyrect );
+ for (int i=0; i < scaleFactor->count(); ++i) {
+ if (scaleFactor->text(i).toInt()==tmpOptions.scaleFactor) {
+ scaleFactor->setCurrentItem(i);
+ }
+ }
+
// TODO
hex->setEnabled( false );
corre->setEnabled( false );
rre->setEnabled( false );
// /TODO
deIconify->setChecked( options->deIconify );
bit->setChecked( options->colors256 );
shared->setChecked( options->shared );
timeBox->setValue( options->updateRate );
+ serverPassword->setEchoMode(QLineEdit::Password);
+
+ connect(togglePassword, SIGNAL( stateChanged(int) ), this, SLOT( showPassword(int) ) );
-}
+}
KVNCConnDlg::~KVNCConnDlg()
{
}
void KVNCConnDlg::accept()
{
save();
QDialog::accept();
}
void KVNCConnDlg::save()
{
tmpOptions.hexTile = hex->isChecked();
tmpOptions.corre = corre->isChecked();
tmpOptions.rre = rre->isChecked();
tmpOptions.copyrect = copyRect->isChecked();
tmpOptions.deIconify = deIconify->isChecked();
tmpOptions.colors256 = bit->isChecked();
tmpOptions.shared = shared->isChecked();
tmpOptions.hostname = serverHostname->text();
tmpOptions.password = serverPassword->text();
tmpOptions.display = serverDisplay->value();
tmpOptions.name = serverBookmark->text();
+ tmpOptions.scaleFactor = scaleFactor->currentText().toInt();
if (!serverBookmark->text().isEmpty()) {
if ( options) {
*options=tmpOptions;
}
}
}
+
+void KVNCConnDlg::showPassword(int show)
+{
+ if (show)
+ serverPassword->setEchoMode(QLineEdit::Normal);
+ else
+ serverPassword->setEchoMode(QLineEdit::Password);
+}
diff --git a/noncore/comm/keypebble/kvncconndlg.h b/noncore/comm/keypebble/kvncconndlg.h
index fae7d62..5e84750 100644
--- a/noncore/comm/keypebble/kvncconndlg.h
+++ b/noncore/comm/keypebble/kvncconndlg.h
@@ -1,33 +1,34 @@
// -*- c++ -*-
#ifndef KVNCCONNECTION_H
#define KVNCCONNECTION_H
#include "kvncconndlgbase.h"
#include "krfbserver.h"
class KVNCConnDlg : public KVNCConnDlgBase
{
Q_OBJECT
public:
KVNCConnDlg( KRFBServer *options,
QWidget *parent = 0, char *name = 0, bool modal = true );
~KVNCConnDlg();
protected:
void accept();
private slots:
void save();
+ void showPassword(int);
private:
KRFBServer tmpOptions;
KRFBServer * options;
};
#endif // KVNCCONNECTION_H
diff --git a/noncore/comm/keypebble/kvncconndlgbase.ui b/noncore/comm/keypebble/kvncconndlgbase.ui
index df0d375..42765c1 100644
--- a/noncore/comm/keypebble/kvncconndlgbase.ui
+++ b/noncore/comm/keypebble/kvncconndlgbase.ui
@@ -1,432 +1,482 @@
<!DOCTYPE UI><UI>
<class>KVNCConnDlgBase</class>
<widget>
<class>QDialog</class>
<property stdset="1">
<name>name</name>
<cstring>KVNCConnDlgBase</cstring>
</property>
<property stdset="1">
<name>geometry</name>
<rect>
<x>0</x>
<y>0</y>
- <width>984</width>
- <height>676</height>
+ <width>260</width>
+ <height>242</height>
</rect>
</property>
<property stdset="1">
<name>caption</name>
<string>VNC Viewer Connection</string>
</property>
<property>
<name>layoutMargin</name>
</property>
- <vbox>
- <property stdset="1">
- <name>margin</name>
- <number>0</number>
- </property>
- <property stdset="1">
- <name>spacing</name>
- <number>6</number>
- </property>
<widget>
<class>QTabWidget</class>
<property stdset="1">
<name>name</name>
<cstring>TabWidget3</cstring>
</property>
+ <property stdset="1">
+ <name>geometry</name>
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>266</width>
+ <height>233</height>
+ </rect>
+ </property>
<widget>
<class>QWidget</class>
<property stdset="1">
<name>name</name>
<cstring>tab</cstring>
</property>
<attribute>
<name>title</name>
<string>Server</string>
</attribute>
<grid>
<property stdset="1">
<name>margin</name>
<number>11</number>
</property>
<property stdset="1">
<name>spacing</name>
<number>6</number>
</property>
- <widget row="0" column="0" >
- <class>QLabel</class>
+ <widget row="2" column="1" rowspan="1" colspan="2" >
+ <class>QLineEdit</class>
<property stdset="1">
<name>name</name>
- <cstring>hostname</cstring>
+ <cstring>serverPassword</cstring>
</property>
<property stdset="1">
- <name>text</name>
- <string>Host Name:</string>
+ <name>autoMask</name>
+ <bool>true</bool>
+ </property>
+ <property stdset="1">
+ <name>echoMode</name>
+ <enum>Password</enum>
</property>
</widget>
- <widget row="1" column="0" >
- <class>QLabel</class>
+ <widget row="3" column="1" >
+ <class>QCheckBox</class>
<property stdset="1">
<name>name</name>
- <cstring>TextLabel2_2</cstring>
+ <cstring>togglePassword</cstring>
</property>
<property stdset="1">
<name>text</name>
- <string>Display Number:</string>
+ <string>Show Password</string>
</property>
</widget>
- <widget row="2" column="1" rowspan="1" colspan="2" >
- <class>QLineEdit</class>
+ <widget row="2" column="0" >
+ <class>QLabel</class>
<property stdset="1">
<name>name</name>
- <cstring>serverPassword</cstring>
+ <cstring>TextLabel3</cstring>
</property>
<property stdset="1">
- <name>autoMask</name>
- <bool>true</bool>
+ <name>text</name>
+ <string>Password:</string>
</property>
</widget>
- <widget row="1" column="1" rowspan="1" colspan="2" >
- <class>QSpinBox</class>
+ <widget row="4" column="1" rowspan="1" colspan="2" >
+ <class>QLineEdit</class>
<property stdset="1">
<name>name</name>
- <cstring>serverDisplay</cstring>
- </property>
- <property stdset="1">
- <name>maxValue</name>
- <number>500</number>
+ <cstring>serverBookmark</cstring>
</property>
+ </widget>
+ <widget row="4" column="0" >
+ <class>QLabel</class>
<property stdset="1">
- <name>minValue</name>
- <number>0</number>
+ <name>name</name>
+ <cstring>TextLabel1_2</cstring>
</property>
<property stdset="1">
- <name>value</name>
- <number>0</number>
+ <name>text</name>
+ <string>Bookmark Name:</string>
</property>
</widget>
<widget row="0" column="1" rowspan="1" colspan="2" >
<class>QLineEdit</class>
<property stdset="1">
<name>name</name>
<cstring>serverHostname</cstring>
</property>
</widget>
- <widget row="2" column="0" >
+ <widget row="0" column="0" >
<class>QLabel</class>
<property stdset="1">
<name>name</name>
- <cstring>TextLabel3</cstring>
+ <cstring>hostname</cstring>
</property>
<property stdset="1">
<name>text</name>
- <string>Password:</string>
+ <string>Host Name:</string>
</property>
</widget>
- <widget row="3" column="1" rowspan="1" colspan="2" >
- <class>QLineEdit</class>
+ <widget row="1" column="0" >
+ <class>QLabel</class>
<property stdset="1">
<name>name</name>
- <cstring>serverBookmark</cstring>
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property stdset="1">
+ <name>text</name>
+ <string>Display Number:</string>
</property>
</widget>
- <widget row="3" column="0" >
- <class>QLabel</class>
+ <widget row="1" column="1" rowspan="1" colspan="2" >
+ <class>QSpinBox</class>
<property stdset="1">
<name>name</name>
- <cstring>TextLabel1_2</cstring>
+ <cstring>serverDisplay</cstring>
</property>
<property stdset="1">
- <name>text</name>
- <string>Bookmark Name:</string>
+ <name>maxValue</name>
+ <number>500</number>
+ </property>
+ <property stdset="1">
+ <name>minValue</name>
+ <number>0</number>
+ </property>
+ <property stdset="1">
+ <name>value</name>
+ <number>0</number>
</property>
</widget>
<spacer row="5" column="2" >
<property>
<name>name</name>
<cstring>Spacer2</cstring>
</property>
<property stdset="1">
<name>orientation</name>
<enum>Vertical</enum>
</property>
<property stdset="1">
<name>sizeType</name>
<enum>Expanding</enum>
</property>
<property>
<name>sizeHint</name>
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
- <widget row="4" column="1" >
- <class>QLayoutWidget</class>
- <property stdset="1">
- <name>name</name>
- <cstring>Layout3</cstring>
- </property>
- <hbox>
- <property stdset="1">
- <name>margin</name>
- <number>0</number>
- </property>
- <property stdset="1">
- <name>spacing</name>
- <number>6</number>
- </property>
- </hbox>
- </widget>
</grid>
</widget>
<widget>
<class>QWidget</class>
<property stdset="1">
<name>name</name>
<cstring>tab</cstring>
</property>
<attribute>
<name>title</name>
<string>Options</string>
</attribute>
<vbox>
<property stdset="1">
<name>margin</name>
<number>11</number>
</property>
<property stdset="1">
<name>spacing</name>
<number>6</number>
</property>
<widget>
<class>QLabel</class>
<property stdset="1">
<name>name</name>
<cstring>TextLabel1</cstring>
</property>
<property stdset="1">
<name>text</name>
<string>Check for screen updates every:</string>
</property>
</widget>
<widget>
<class>QLayoutWidget</class>
<property stdset="1">
<name>name</name>
<cstring>Layout2</cstring>
</property>
<hbox>
<property stdset="1">
<name>margin</name>
<number>0</number>
</property>
<property stdset="1">
<name>spacing</name>
<number>6</number>
</property>
<widget>
<class>QSpinBox</class>
<property stdset="1">
<name>name</name>
<cstring>timeBox</cstring>
</property>
<property stdset="1">
<name>maxValue</name>
<number>500</number>
</property>
<property stdset="1">
<name>minValue</name>
<number>1</number>
</property>
</widget>
<widget>
<class>QLabel</class>
<property stdset="1">
<name>name</name>
<cstring>TextLabel2</cstring>
</property>
<property stdset="1">
<name>text</name>
<string>Milliseconds</string>
</property>
</widget>
</hbox>
</widget>
<widget>
<class>QCheckBox</class>
<property stdset="1">
<name>name</name>
<cstring>bit</cstring>
</property>
<property stdset="1">
<name>text</name>
<string>Request 8-bit session</string>
</property>
</widget>
<widget>
<class>QCheckBox</class>
<property stdset="1">
<name>name</name>
<cstring>deIconify</cstring>
</property>
<property stdset="1">
<name>text</name>
<string>Raise on bell</string>
</property>
</widget>
<widget>
<class>QCheckBox</class>
<property stdset="1">
<name>name</name>
<cstring>shared</cstring>
</property>
<property stdset="1">
<name>text</name>
<string>Request shared session</string>
</property>
</widget>
+ <widget>
+ <class>QLayoutWidget</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>Layout3</cstring>
+ </property>
+ <hbox>
+ <property stdset="1">
+ <name>margin</name>
+ <number>0</number>
+ </property>
+ <property stdset="1">
+ <name>spacing</name>
+ <number>6</number>
+ </property>
+ <widget>
+ <class>QComboBox</class>
+ <item>
+ <property>
+ <name>text</name>
+ <string>1</string>
+ </property>
+ </item>
+ <item>
+ <property>
+ <name>text</name>
+ <string>2</string>
+ </property>
+ </item>
+ <item>
+ <property>
+ <name>text</name>
+ <string>4</string>
+ </property>
+ </item>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>scaleFactor</cstring>
+ </property>
+ </widget>
+ <widget>
+ <class>QLabel</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>TextLabel2_3</cstring>
+ </property>
+ <property stdset="1">
+ <name>text</name>
+ <string>Scale Factor</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
<spacer>
<property>
<name>name</name>
<cstring>Spacer2</cstring>
</property>
<property stdset="1">
<name>orientation</name>
<enum>Vertical</enum>
</property>
<property stdset="1">
<name>sizeType</name>
<enum>Expanding</enum>
</property>
<property>
<name>sizeHint</name>
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</vbox>
</widget>
<widget>
<class>QWidget</class>
<property stdset="1">
<name>name</name>
<cstring>tab</cstring>
</property>
<attribute>
<name>title</name>
<string>Encodings</string>
</attribute>
<vbox>
<property stdset="1">
<name>margin</name>
<number>11</number>
</property>
<property stdset="1">
<name>spacing</name>
<number>6</number>
</property>
<widget>
<class>QCheckBox</class>
<property stdset="1">
<name>name</name>
<cstring>hex</cstring>
</property>
<property stdset="1">
<name>enabled</name>
<bool>false</bool>
</property>
<property stdset="1">
<name>text</name>
<string>Hextile encoding</string>
</property>
</widget>
<widget>
<class>QCheckBox</class>
<property stdset="1">
<name>name</name>
<cstring>corre</cstring>
</property>
<property stdset="1">
<name>enabled</name>
<bool>false</bool>
</property>
<property stdset="1">
<name>text</name>
<string>CoRRE encoding</string>
</property>
</widget>
<widget>
<class>QCheckBox</class>
<property stdset="1">
<name>name</name>
<cstring>rre</cstring>
</property>
<property stdset="1">
<name>enabled</name>
<bool>false</bool>
</property>
<property stdset="1">
<name>text</name>
<string>RRE encoding</string>
</property>
</widget>
<widget>
<class>QCheckBox</class>
<property stdset="1">
<name>name</name>
<cstring>copyRect</cstring>
</property>
<property stdset="1">
<name>text</name>
<string>Copy rectangle encoding</string>
</property>
</widget>
<spacer>
<property>
<name>name</name>
<cstring>Spacer3</cstring>
</property>
<property stdset="1">
<name>orientation</name>
<enum>Vertical</enum>
</property>
<property stdset="1">
<name>sizeType</name>
<enum>Expanding</enum>
</property>
<property>
<name>sizeHint</name>
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</vbox>
</widget>
</widget>
- </vbox>
</widget>
<tabstops>
<tabstop>TabWidget3</tabstop>
<tabstop>serverHostname</tabstop>
<tabstop>serverDisplay</tabstop>
<tabstop>serverPassword</tabstop>
<tabstop>serverBookmark</tabstop>
<tabstop>timeBox</tabstop>
<tabstop>bit</tabstop>
<tabstop>deIconify</tabstop>
<tabstop>shared</tabstop>
<tabstop>hex</tabstop>
<tabstop>corre</tabstop>
<tabstop>rre</tabstop>
<tabstop>copyRect</tabstop>
</tabstops>
</UI>