if (supportsLocalCursor) { | if (supportsLocalCursor) { | ||||
encodings.push_back(pseudoEncodingCursorWithAlpha); | encodings.push_back(pseudoEncodingCursorWithAlpha); | ||||
encodings.push_back(pseudoEncodingVMwareCursor); | |||||
encodings.push_back(pseudoEncodingCursor); | encodings.push_back(pseudoEncodingCursor); | ||||
encodings.push_back(pseudoEncodingXCursor); | encodings.push_back(pseudoEncodingXCursor); | ||||
} | } | ||||
encodings.push_back(pseudoEncodingDesktopSize); | encodings.push_back(pseudoEncodingDesktopSize); | ||||
encodings.push_back(pseudoEncodingExtendedDesktopSize); | encodings.push_back(pseudoEncodingExtendedDesktopSize); | ||||
} | } | ||||
if (supportsLEDState) | |||||
if (supportsLEDState) { | |||||
encodings.push_back(pseudoEncodingLEDState); | encodings.push_back(pseudoEncodingLEDState); | ||||
encodings.push_back(pseudoEncodingVMwareLEDState); | |||||
} | |||||
encodings.push_back(pseudoEncodingDesktopName); | encodings.push_back(pseudoEncodingDesktopName); | ||||
encodings.push_back(pseudoEncodingLastRect); | encodings.push_back(pseudoEncodingLastRect); |
case pseudoEncodingCursorWithAlpha: | case pseudoEncodingCursorWithAlpha: | ||||
readSetCursorWithAlpha(w, h, Point(x,y)); | readSetCursorWithAlpha(w, h, Point(x,y)); | ||||
break; | break; | ||||
case pseudoEncodingVMwareCursor: | |||||
readSetVMwareCursor(w, h, Point(x,y)); | |||||
break; | |||||
case pseudoEncodingDesktopName: | case pseudoEncodingDesktopName: | ||||
readSetDesktopName(x, y, w, h); | readSetDesktopName(x, y, w, h); | ||||
break; | break; | ||||
break; | break; | ||||
case pseudoEncodingLEDState: | case pseudoEncodingLEDState: | ||||
readLEDState(); | readLEDState(); | ||||
break; | |||||
case pseudoEncodingVMwareLEDState: | |||||
readVMwareLEDState(); | |||||
break; | |||||
case pseudoEncodingQEMUKeyEvent: | case pseudoEncodingQEMUKeyEvent: | ||||
handler->supportsQEMUKeyEvent(); | handler->supportsQEMUKeyEvent(); | ||||
break; | break; | ||||
pb.getBuffer(pb.getRect(), &stride)); | pb.getBuffer(pb.getRect(), &stride)); | ||||
} | } | ||||
void CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot) | |||||
{ | |||||
if (width > maxCursorSize || height > maxCursorSize) | |||||
throw Exception("Too big cursor"); | |||||
rdr::U8 type; | |||||
type = is->readU8(); | |||||
is->skip(1); | |||||
if (type == 0) { | |||||
int len = width * height * (handler->server.pf().bpp/8); | |||||
rdr::U8Array andMask(len); | |||||
rdr::U8Array xorMask(len); | |||||
rdr::U8Array data(width*height*4); | |||||
rdr::U8* andIn; | |||||
rdr::U8* xorIn; | |||||
rdr::U8* out; | |||||
int Bpp; | |||||
is->readBytes(andMask.buf, len); | |||||
is->readBytes(xorMask.buf, len); | |||||
andIn = andMask.buf; | |||||
xorIn = xorMask.buf; | |||||
out = data.buf; | |||||
Bpp = handler->server.pf().bpp/8; | |||||
for (int y = 0;y < height;y++) { | |||||
for (int x = 0;x < width;x++) { | |||||
Pixel andPixel, xorPixel; | |||||
andPixel = handler->server.pf().pixelFromBuffer(andIn); | |||||
xorPixel = handler->server.pf().pixelFromBuffer(xorIn); | |||||
andIn += Bpp; | |||||
xorIn += Bpp; | |||||
if (andPixel == 0) { | |||||
rdr::U8 r, g, b; | |||||
// Opaque pixel | |||||
handler->server.pf().rgbFromPixel(xorPixel, &r, &g, &b); | |||||
*out++ = r; | |||||
*out++ = g; | |||||
*out++ = b; | |||||
*out++ = 0xff; | |||||
} else if (xorPixel == 0) { | |||||
// Fully transparent pixel | |||||
*out++ = 0; | |||||
*out++ = 0; | |||||
*out++ = 0; | |||||
*out++ = 0; | |||||
} else if (andPixel == xorPixel) { | |||||
// Inverted pixel | |||||
// We don't really support this, so just turn the pixel black | |||||
// FIXME: Do an outline like WinVNC does? | |||||
*out++ = 0; | |||||
*out++ = 0; | |||||
*out++ = 0; | |||||
*out++ = 0xff; | |||||
} else { | |||||
// Partially transparent/inverted pixel | |||||
// We _really_ can't handle this, just make it black | |||||
*out++ = 0; | |||||
*out++ = 0; | |||||
*out++ = 0; | |||||
*out++ = 0xff; | |||||
} | |||||
} | |||||
} | |||||
handler->setCursor(width, height, hotspot, data.buf); | |||||
} else if (type == 1) { | |||||
rdr::U8Array data(width*height*4); | |||||
// FIXME: Is alpha premultiplied? | |||||
is->readBytes(data.buf, width*height*4); | |||||
handler->setCursor(width, height, hotspot, data.buf); | |||||
} else { | |||||
throw Exception("Unknown cursor type"); | |||||
} | |||||
} | |||||
void CMsgReader::readSetDesktopName(int x, int y, int w, int h) | void CMsgReader::readSetDesktopName(int x, int y, int w, int h) | ||||
{ | { | ||||
char* name = is->readString(); | char* name = is->readString(); | ||||
handler->setLEDState(state); | handler->setLEDState(state); | ||||
} | } | ||||
void CMsgReader::readVMwareLEDState() | |||||
{ | |||||
rdr::U32 state; | |||||
state = is->readU32(); | |||||
// As luck has it, this extension uses the same bit definitions, | |||||
// so no conversion required | |||||
handler->setLEDState(state); | |||||
} |
void readSetXCursor(int width, int height, const Point& hotspot); | void readSetXCursor(int width, int height, const Point& hotspot); | ||||
void readSetCursor(int width, int height, const Point& hotspot); | void readSetCursor(int width, int height, const Point& hotspot); | ||||
void readSetCursorWithAlpha(int width, int height, const Point& hotspot); | void readSetCursorWithAlpha(int width, int height, const Point& hotspot); | ||||
void readSetVMwareCursor(int width, int height, const Point& hotspot); | |||||
void readSetDesktopName(int x, int y, int w, int h); | void readSetDesktopName(int x, int y, int w, int h); | ||||
void readExtendedDesktopSize(int x, int y, int w, int h); | void readExtendedDesktopSize(int x, int y, int w, int h); | ||||
void readLEDState(); | void readLEDState(); | ||||
void readVMwareLEDState(); | |||||
CMsgHandler* handler; | CMsgHandler* handler; | ||||
rdr::InStream* is; | rdr::InStream* is; |
{ | { | ||||
if (supportsEncoding(pseudoEncodingCursorWithAlpha)) | if (supportsEncoding(pseudoEncodingCursorWithAlpha)) | ||||
return true; | return true; | ||||
if (supportsEncoding(pseudoEncodingVMwareCursor)) | |||||
return true; | |||||
if (supportsEncoding(pseudoEncodingCursor)) | if (supportsEncoding(pseudoEncodingCursor)) | ||||
return true; | return true; | ||||
if (supportsEncoding(pseudoEncodingXCursor)) | if (supportsEncoding(pseudoEncodingXCursor)) | ||||
{ | { | ||||
if (supportsEncoding(pseudoEncodingLEDState)) | if (supportsEncoding(pseudoEncodingLEDState)) | ||||
return true; | return true; | ||||
if (supportsEncoding(pseudoEncodingVMwareLEDState)) | |||||
return true; | |||||
return false; | return false; | ||||
} | } | ||||
{ | { | ||||
if (!client->supportsEncoding(pseudoEncodingCursor) && | if (!client->supportsEncoding(pseudoEncodingCursor) && | ||||
!client->supportsEncoding(pseudoEncodingXCursor) && | !client->supportsEncoding(pseudoEncodingXCursor) && | ||||
!client->supportsEncoding(pseudoEncodingCursorWithAlpha)) | |||||
!client->supportsEncoding(pseudoEncodingCursorWithAlpha) && | |||||
!client->supportsEncoding(pseudoEncodingVMwareCursor)) | |||||
throw Exception("Client does not support local cursor"); | throw Exception("Client does not support local cursor"); | ||||
needCursor = true; | needCursor = true; | ||||
void SMsgWriter::writeLEDState() | void SMsgWriter::writeLEDState() | ||||
{ | { | ||||
if (!client->supportsEncoding(pseudoEncodingLEDState)) | |||||
if (!client->supportsEncoding(pseudoEncodingLEDState) && | |||||
!client->supportsEncoding(pseudoEncodingVMwareLEDState)) | |||||
throw Exception("Client does not support LED state"); | throw Exception("Client does not support LED state"); | ||||
if (client->ledState() == ledUnknown) | if (client->ledState() == ledUnknown) | ||||
throw Exception("Server has not specified LED state"); | throw Exception("Server has not specified LED state"); | ||||
writeSetCursorWithAlphaRect(cursor.width(), cursor.height(), | writeSetCursorWithAlphaRect(cursor.width(), cursor.height(), | ||||
cursor.hotspot().x, cursor.hotspot().y, | cursor.hotspot().x, cursor.hotspot().y, | ||||
cursor.getBuffer()); | cursor.getBuffer()); | ||||
} else if (client->supportsEncoding(pseudoEncodingVMwareCursor)) { | |||||
writeSetVMwareCursorRect(cursor.width(), cursor.height(), | |||||
cursor.hotspot().x, cursor.hotspot().y, | |||||
cursor.getBuffer()); | |||||
} else if (client->supportsEncoding(pseudoEncodingCursor)) { | } else if (client->supportsEncoding(pseudoEncodingCursor)) { | ||||
rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8)); | rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8)); | ||||
rdr::U8Array mask(cursor.getMask()); | rdr::U8Array mask(cursor.getMask()); | ||||
} | } | ||||
} | } | ||||
void SMsgWriter::writeSetVMwareCursorRect(int width, int height, | |||||
int hotspotX, int hotspotY, | |||||
const rdr::U8* data) | |||||
{ | |||||
if (!client->supportsEncoding(pseudoEncodingVMwareCursor)) | |||||
throw Exception("Client does not support local cursors"); | |||||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||||
throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync"); | |||||
os->writeS16(hotspotX); | |||||
os->writeS16(hotspotY); | |||||
os->writeU16(width); | |||||
os->writeU16(height); | |||||
os->writeU32(pseudoEncodingVMwareCursor); | |||||
os->writeU8(1); // Alpha cursor | |||||
os->pad(1); | |||||
// FIXME: Should alpha be premultiplied? | |||||
os->writeBytes(data, width*height*4); | |||||
} | |||||
void SMsgWriter::writeLEDStateRect(rdr::U8 state) | void SMsgWriter::writeLEDStateRect(rdr::U8 state) | ||||
{ | { | ||||
if (!client->supportsEncoding(pseudoEncodingLEDState)) | |||||
if (!client->supportsEncoding(pseudoEncodingLEDState) && | |||||
!client->supportsEncoding(pseudoEncodingVMwareLEDState)) | |||||
throw Exception("Client does not support LED state updates"); | throw Exception("Client does not support LED state updates"); | ||||
if (client->ledState() == ledUnknown) | if (client->ledState() == ledUnknown) | ||||
throw Exception("Server does not support LED state updates"); | throw Exception("Server does not support LED state updates"); | ||||
os->writeS16(0); | os->writeS16(0); | ||||
os->writeU16(0); | os->writeU16(0); | ||||
os->writeU16(0); | os->writeU16(0); | ||||
os->writeU32(pseudoEncodingLEDState); | |||||
os->writeU8(state); | |||||
if (client->supportsEncoding(pseudoEncodingLEDState)) { | |||||
os->writeU32(pseudoEncodingLEDState); | |||||
os->writeU8(state); | |||||
} else { | |||||
os->writeU32(pseudoEncodingVMwareLEDState); | |||||
os->writeU32(state); | |||||
} | |||||
} | } | ||||
void SMsgWriter::writeQEMUKeyEventRect() | void SMsgWriter::writeQEMUKeyEventRect() |
void writeSetCursorWithAlphaRect(int width, int height, | void writeSetCursorWithAlphaRect(int width, int height, | ||||
int hotspotX, int hotspotY, | int hotspotX, int hotspotY, | ||||
const rdr::U8* data); | const rdr::U8* data); | ||||
void writeSetVMwareCursorRect(int width, int height, | |||||
int hotspotX, int hotspotY, | |||||
const rdr::U8* data); | |||||
void writeLEDStateRect(rdr::U8 state); | void writeLEDStateRect(rdr::U8 state); | ||||
void writeQEMUKeyEventRect(); | void writeQEMUKeyEventRect(); | ||||
const int pseudoEncodingSubsamp8X = -764; | const int pseudoEncodingSubsamp8X = -764; | ||||
const int pseudoEncodingSubsamp16X = -763; | const int pseudoEncodingSubsamp16X = -763; | ||||
// VMware-specific | |||||
const int pseudoEncodingVMwareCursor = 0x574d5664; | |||||
const int pseudoEncodingVMwareLEDState = 0x574d5668; | |||||
int encodingNum(const char* name); | int encodingNum(const char* name); | ||||
const char* encodingName(int num); | const char* encodingName(int num); | ||||
} | } |