533 lines
12 KiB
C++
533 lines
12 KiB
C++
// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved.
|
|
// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
|
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
|
//
|
|
// This file is part of the VNC system.
|
|
//
|
|
// The VNC system is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
// USA.
|
|
//
|
|
// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
|
|
//
|
|
// If the source code for the VNC system is not available from the place
|
|
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
|
|
// the authors on vnc@uk.research.att.com for information on obtaining it.
|
|
|
|
|
|
// ScrBuffer implementation
|
|
|
|
#include "stdhdrs.h"
|
|
|
|
// Header
|
|
|
|
#include "vncDesktop.h"
|
|
#include "vncEncoder.h"
|
|
#include "vncEncodeRRE.h"
|
|
#include "vncEncodeCoRRE.h"
|
|
#include "vncEncodeHexT.h"
|
|
#include "vncEncodeZlib.h"
|
|
#include "vncEncodeTight.h"
|
|
#include "vncEncodeZlibHex.h"
|
|
#include "MinMax.h"
|
|
|
|
#include "vncBuffer.h"
|
|
|
|
// Implementation
|
|
|
|
vncBuffer::vncBuffer(vncDesktop *desktop)
|
|
{
|
|
m_desktop = desktop;
|
|
m_encoder = NULL;
|
|
zlib_encoder_in_use = false;
|
|
m_hold_zlib_encoder = NULL;
|
|
tight_encoder_in_use = false;
|
|
m_hold_tight_encoder = NULL;
|
|
zlibhex_encoder_in_use = false;
|
|
m_hold_zlibhex_encoder = NULL;
|
|
|
|
m_compresslevel = 6;
|
|
m_qualitylevel = -1;
|
|
m_use_xcursor = FALSE;
|
|
m_use_richcursor = FALSE;
|
|
m_use_lastrect = FALSE;
|
|
|
|
m_hcursor = NULL;
|
|
|
|
m_mainbuff = NULL;
|
|
m_mainsize = 0;
|
|
|
|
m_clientbuff = NULL;
|
|
m_clientbuffsize = 0;
|
|
m_clientfmtset = FALSE;
|
|
|
|
// Initialise the screen buffers
|
|
CheckBuffer();
|
|
}
|
|
|
|
vncBuffer::~vncBuffer()
|
|
{
|
|
|
|
|
|
if (m_hold_zlib_encoder != NULL && m_hold_zlib_encoder != m_encoder) {
|
|
m_hold_zlib_encoder->LogStats();
|
|
delete m_hold_zlib_encoder;
|
|
m_hold_zlib_encoder = NULL;
|
|
}
|
|
if (m_hold_tight_encoder != NULL && m_hold_tight_encoder != m_encoder) {
|
|
m_hold_tight_encoder->LogStats();
|
|
delete m_hold_tight_encoder;
|
|
m_hold_tight_encoder = NULL;
|
|
}
|
|
if (m_hold_zlibhex_encoder != NULL && m_hold_zlibhex_encoder != m_encoder) {
|
|
m_hold_zlibhex_encoder->LogStats();
|
|
delete m_hold_zlibhex_encoder;
|
|
m_hold_zlibhex_encoder = NULL;
|
|
}
|
|
if (m_encoder != NULL) {
|
|
m_encoder->LogStats();
|
|
delete m_encoder;
|
|
m_encoder = NULL;
|
|
m_hold_zlib_encoder = NULL;
|
|
m_hold_tight_encoder = NULL;
|
|
m_hold_zlibhex_encoder = NULL;
|
|
}
|
|
if (m_clientbuff != NULL) {
|
|
delete m_clientbuff;
|
|
m_clientbuff = NULL;
|
|
}
|
|
m_clientbuffsize = 0;
|
|
m_mainsize = 0;
|
|
}
|
|
|
|
RECT
|
|
vncBuffer::GetSize()
|
|
{
|
|
RECT rect;
|
|
|
|
rect.left = 0;
|
|
rect.top = 0;
|
|
rect.right = m_scrinfo.framebufferWidth;
|
|
rect.bottom = m_scrinfo.framebufferHeight;
|
|
|
|
return rect;
|
|
}
|
|
|
|
rfbPixelFormat
|
|
vncBuffer::GetLocalFormat()
|
|
{
|
|
return m_scrinfo.format;
|
|
}
|
|
|
|
BYTE *
|
|
vncBuffer::GetClientBuffer()
|
|
{
|
|
return m_clientbuff;
|
|
}
|
|
|
|
BOOL
|
|
vncBuffer::GetRemotePalette(RGBQUAD *quadlist, UINT ncolours)
|
|
{
|
|
// Try to get the RGBQUAD data from the encoder
|
|
// This will only work if the remote client is palette-based,
|
|
// in which case the encoder will be storing RGBQUAD data
|
|
if (m_encoder == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Now get the palette data
|
|
return m_encoder->GetRemotePalette(quadlist, ncolours);
|
|
}
|
|
|
|
BOOL
|
|
vncBuffer::CheckBuffer()
|
|
{
|
|
// Get the screen format, in case it has changed
|
|
m_desktop->FillDisplayInfo(&m_scrinfo);
|
|
|
|
// If the client has not specified a pixel format then set one for it
|
|
if (!m_clientfmtset) {
|
|
m_clientfmtset = TRUE;
|
|
m_clientformat = m_scrinfo.format;
|
|
}
|
|
|
|
// If the client has not selected an encoding then set one for it
|
|
if (m_encoder == NULL) {
|
|
if (!SetEncoding(rfbEncodingRaw))
|
|
return FALSE;
|
|
}
|
|
|
|
m_bytesPerRow = m_scrinfo.framebufferWidth * m_scrinfo.format.bitsPerPixel/8;
|
|
|
|
// Check the client buffer is sufficient
|
|
const UINT clientbuffsize =
|
|
m_encoder->RequiredBuffSize(m_scrinfo.framebufferWidth,
|
|
m_scrinfo.framebufferHeight);
|
|
if (m_clientbuffsize != clientbuffsize)
|
|
{
|
|
if (m_clientbuff != NULL)
|
|
{
|
|
delete [] m_clientbuff;
|
|
m_clientbuff = NULL;
|
|
}
|
|
m_clientbuffsize = 0;
|
|
|
|
m_clientbuff = new BYTE [clientbuffsize];
|
|
if (m_clientbuff == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
m_clientbuffsize = clientbuffsize;
|
|
|
|
ZeroMemory(m_clientbuff, m_clientbuffsize);
|
|
}
|
|
|
|
// Take the main buffer pointer and size from vncDesktop
|
|
m_mainbuff = m_desktop->MainBuffer();
|
|
m_mainrect = m_desktop->MainBufferRect();
|
|
m_mainsize = m_desktop->ScreenBuffSize();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
UINT
|
|
vncBuffer::GetNumCodedRects(RECT &rect)
|
|
{
|
|
// Ask the encoder how many rectangles this update would become
|
|
return m_encoder->NumCodedRects(rect);
|
|
}
|
|
|
|
RECT vncBuffer::GrabMouse()
|
|
{
|
|
// capture uncovered area
|
|
m_desktop->CaptureScreen(m_desktop->MouseRect(), m_mainbuff);
|
|
// capture new mouse area
|
|
m_desktop->CaptureMouse(m_mainbuff, m_mainsize);
|
|
|
|
return m_desktop->MouseRect();
|
|
}
|
|
|
|
BOOL
|
|
vncBuffer::SetClientFormat(rfbPixelFormat &format)
|
|
{
|
|
// Save the desired format
|
|
m_clientfmtset = TRUE;
|
|
m_clientformat = format;
|
|
|
|
// Tell the encoder of the new format
|
|
if (m_encoder != NULL)
|
|
m_encoder->SetRemoteFormat(format);
|
|
|
|
// Check that the output buffer is sufficient
|
|
if (!CheckBuffer())
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
/*
|
|
VOID vncBuffer::UpdateZLibDictionary( AGENT_CTX * lpAgentContext )
|
|
{
|
|
do
|
|
{
|
|
if( !lpAgentContext || !m_encoder )
|
|
break;
|
|
|
|
dprintf( "UpdateZLibDictionary, updating the ZLib dictionaries..." );
|
|
m_encoder->UpdateZLibDictionary( lpAgentContext );
|
|
dprintf( "UpdateZLibDictionary, Finished updating the ZLib dictionaries." );
|
|
|
|
} while( 0 );
|
|
}
|
|
|
|
VOID vncBuffer::DumpZLibDictionary( AGENT_CTX * lpAgentContext )
|
|
{
|
|
do
|
|
{
|
|
if( !lpAgentContext || !m_encoder )
|
|
break;
|
|
|
|
m_encoder->DumpZLibDictionary( lpAgentContext );
|
|
|
|
} while( 0 );
|
|
}
|
|
*/
|
|
|
|
BOOL vncBuffer::SetEncoding(CARD32 encoding)
|
|
{
|
|
|
|
//m_desktop->FillDisplayInfo(&m_scrinfo);
|
|
|
|
// Delete the old encoder
|
|
if (m_encoder != NULL)
|
|
{
|
|
// If a Zlib-like encoders were in use, save corresponding object
|
|
// (with dictionaries) for possible later use on this connection.
|
|
if ( zlib_encoder_in_use )
|
|
{
|
|
m_hold_zlib_encoder = m_encoder;
|
|
}
|
|
else if ( tight_encoder_in_use )
|
|
{
|
|
m_hold_tight_encoder = m_encoder;
|
|
}
|
|
else if ( zlibhex_encoder_in_use )
|
|
{
|
|
m_hold_zlibhex_encoder = m_encoder;
|
|
}
|
|
else
|
|
{
|
|
m_encoder->LogStats();
|
|
delete m_encoder;
|
|
}
|
|
m_encoder = NULL;
|
|
}
|
|
|
|
// Expect to not use the zlib encoder below. However, this
|
|
// is overriden if zlib was selected.
|
|
zlib_encoder_in_use = false;
|
|
tight_encoder_in_use = false;
|
|
zlibhex_encoder_in_use = false;
|
|
|
|
// Returns FALSE if the desired encoding cannot be used
|
|
switch(encoding)
|
|
{
|
|
|
|
case rfbEncodingRaw:
|
|
|
|
// Create a RAW encoder
|
|
m_encoder = new vncEncoder;
|
|
if (m_encoder == NULL)
|
|
return FALSE;
|
|
break;
|
|
|
|
case rfbEncodingRRE:
|
|
|
|
// Create a RRE encoder
|
|
m_encoder = new vncEncodeRRE;
|
|
if (m_encoder == NULL)
|
|
return FALSE;
|
|
break;
|
|
|
|
case rfbEncodingCoRRE:
|
|
|
|
// Create a CoRRE encoder
|
|
m_encoder = new vncEncodeCoRRE;
|
|
if (m_encoder == NULL)
|
|
return FALSE;
|
|
break;
|
|
|
|
case rfbEncodingHextile:
|
|
|
|
// Create a Hextile encoder
|
|
m_encoder = new vncEncodeHexT;
|
|
if (m_encoder == NULL)
|
|
return FALSE;
|
|
break;
|
|
|
|
case rfbEncodingZlib:
|
|
// Create a Zlib encoder, if needed.
|
|
// If a Zlib encoder was used previously, then reuse it here
|
|
// to maintain zlib dictionary synchronization with the viewer.
|
|
if ( m_hold_zlib_encoder == NULL )
|
|
{
|
|
m_encoder = new vncEncodeZlib;
|
|
}
|
|
else
|
|
{
|
|
m_encoder = m_hold_zlib_encoder;
|
|
}
|
|
if (m_encoder == NULL)
|
|
return FALSE;
|
|
zlib_encoder_in_use = true;
|
|
break;
|
|
|
|
case rfbEncodingTight:
|
|
|
|
// Create a Tight encoder, if needed.
|
|
// If a Tight encoder was used previously, then reuse it here
|
|
// to maintain zlib dictionaries synchronization with the viewer.
|
|
if ( m_hold_tight_encoder == NULL )
|
|
{
|
|
m_encoder = new vncEncodeTight;
|
|
}
|
|
else
|
|
{
|
|
m_encoder = m_hold_tight_encoder;
|
|
}
|
|
if (m_encoder == NULL)
|
|
return FALSE;
|
|
tight_encoder_in_use = true;
|
|
break;
|
|
|
|
case rfbEncodingZlibHex:
|
|
|
|
// Create a ZlibHex encoder, if needed.
|
|
// If a ZlibHex encoder was used previously, then reuse it here
|
|
// to maintain zlib dictionary synchronization with the viewer.
|
|
if ( m_hold_zlibhex_encoder == NULL )
|
|
{
|
|
m_encoder = new vncEncodeZlibHex;
|
|
}
|
|
else
|
|
{
|
|
m_encoder = m_hold_zlibhex_encoder;
|
|
}
|
|
if (m_encoder == NULL)
|
|
return FALSE;
|
|
zlibhex_encoder_in_use = true;
|
|
break;
|
|
|
|
default:
|
|
// An unknown encoding was specified
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Initialise it and give it the pixel format
|
|
m_encoder->Init();
|
|
m_encoder->SetLocalFormat(
|
|
m_scrinfo.format,
|
|
m_scrinfo.framebufferWidth,
|
|
m_scrinfo.framebufferHeight);
|
|
|
|
// Duplicate our member fields in new Encoder.
|
|
m_encoder->SetCompressLevel(m_compresslevel);
|
|
m_encoder->SetQualityLevel(m_qualitylevel);
|
|
m_encoder->EnableXCursor(m_use_xcursor);
|
|
m_encoder->EnableRichCursor(m_use_richcursor);
|
|
m_encoder->EnableLastRect(m_use_lastrect);
|
|
|
|
if (m_clientfmtset)
|
|
if (!m_encoder->SetRemoteFormat(m_clientformat))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Check that the client buffer is compatible
|
|
return CheckBuffer();
|
|
}
|
|
|
|
void
|
|
vncBuffer::SetCompressLevel(int level)
|
|
{
|
|
m_compresslevel = (level >= 0 && level <= 9) ? level : 6;
|
|
if (m_encoder != NULL)
|
|
m_encoder->SetCompressLevel(m_compresslevel);
|
|
}
|
|
|
|
void
|
|
vncBuffer::SetQualityLevel(int level)
|
|
{
|
|
m_qualitylevel = (level >= 0 && level <= 9) ? level : -1;
|
|
if (m_encoder != NULL)
|
|
m_encoder->SetQualityLevel(m_qualitylevel);
|
|
}
|
|
|
|
void
|
|
vncBuffer::EnableXCursor(BOOL enable)
|
|
{
|
|
m_use_xcursor = enable;
|
|
if (m_encoder != NULL) {
|
|
m_encoder->EnableXCursor(enable);
|
|
}
|
|
m_hcursor = NULL;
|
|
}
|
|
|
|
void
|
|
vncBuffer::EnableRichCursor(BOOL enable)
|
|
{
|
|
m_use_richcursor = enable;
|
|
if (m_encoder != NULL) {
|
|
m_encoder->EnableRichCursor(enable);
|
|
}
|
|
m_hcursor = NULL;
|
|
}
|
|
|
|
void
|
|
vncBuffer::EnableLastRect(BOOL enable)
|
|
{
|
|
m_use_lastrect = enable;
|
|
if (m_encoder != NULL) {
|
|
m_encoder->EnableLastRect(enable);
|
|
}
|
|
}
|
|
|
|
|
|
// Routine to translate a rectangle between pixel formats
|
|
// semantics changed:
|
|
// offset now is the shared area origin in screen coordinates
|
|
UINT vncBuffer::TranslateRect(
|
|
const RECT &rect,
|
|
VSocket *outConn,
|
|
int shared_org_x,
|
|
int shared_org_y)
|
|
{
|
|
// Call the encoder to encode the rectangle into the client buffer...
|
|
|
|
// Translate (==> EncodeRect also) assumes mainbuff-relative coordinates
|
|
// so we need to adjust the rect.
|
|
// also, presentation (fb) rect is required.
|
|
RECT ar;
|
|
ar.left = rect.left - m_mainrect.left;
|
|
ar.top = rect.top - m_mainrect.top;
|
|
ar.right = rect.right - m_mainrect.left;
|
|
ar.bottom = rect.bottom - m_mainrect.top;
|
|
|
|
return m_encoder->EncodeRect(
|
|
m_mainbuff,
|
|
outConn,
|
|
m_clientbuff,
|
|
ar,
|
|
shared_org_x - m_mainrect.left,
|
|
shared_org_y - m_mainrect.top);
|
|
}
|
|
|
|
// Check if cursor shape update should be sent
|
|
BOOL
|
|
vncBuffer::IsCursorUpdatePending()
|
|
{
|
|
if (m_use_xcursor || m_use_richcursor) {
|
|
HCURSOR temp_hcursor = m_desktop->GetCursor();
|
|
if (temp_hcursor != m_hcursor) {
|
|
m_hcursor = temp_hcursor;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
vncBuffer::SendCursorShape(VSocket *outConn) {
|
|
return m_encoder->SendCursorShape(outConn, m_desktop);
|
|
}
|
|
|
|
BOOL
|
|
vncBuffer::SendEmptyCursorShape(VSocket *outConn) {
|
|
return m_encoder->SendEmptyCursorShape(outConn);
|
|
}
|
|
|
|
void
|
|
vncBuffer::UpdateLocalFormat() {
|
|
CheckBuffer();
|
|
m_encoder->SetLocalFormat(
|
|
m_scrinfo.format,
|
|
m_scrinfo.framebufferWidth,
|
|
m_scrinfo.framebufferHeight);
|
|
}
|