SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
: imageBufIdealSize(0), cp(cp_), os(os_), currentEncoding(0),
nRectsInUpdate(0), nRectsInHeader(0),
- wsccb(0), needSetDesktopSize(false),
- needExtendedDesktopSize(false), needSetDesktopName(false),
+ needSetDesktopSize(false), needExtendedDesktopSize(false),
+ needSetDesktopName(false), needSetCursor(false), needSetXCursor(false),
lenBeforeRect(0), updatesSent(0), rawBytesEquivalent(0),
imageBuf(0), imageBufSize(0)
{
return true;
}
-void SMsgWriter::cursorChange(WriteSetCursorCallback* cb)
+bool SMsgWriter::writeSetCursor()
{
- wsccb = cb;
-}
-
-void SMsgWriter::writeSetCursor(int width, int height, const Point& hotspot,
- void* data, void* mask)
-{
- if (!wsccb)
- return;
+ if (!cp->supportsLocalCursor)
+ return false;
- if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
- throw Exception("SMsgWriter::writeSetCursor: nRects out of sync");
+ needSetCursor = true;
- os->writeS16(hotspot.x);
- os->writeS16(hotspot.y);
- os->writeU16(width);
- os->writeU16(height);
- os->writeU32(pseudoEncodingCursor);
- os->writeBytes(data, width * height * (cp->pf().bpp/8));
- os->writeBytes(mask, (width+7)/8 * height);
+ return true;
}
-void SMsgWriter::writeSetXCursor(int width, int height, int hotspotX,
- int hotspotY, void* data, void* mask)
+bool SMsgWriter::writeSetXCursor()
{
- if (!wsccb)
- return;
+ if (!cp->supportsLocalXCursor)
+ return false;
- if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
- throw Exception("SMsgWriter::writeSetXCursor: nRects out of sync");
+ needSetXCursor = true;
- os->writeS16(hotspotX);
- os->writeS16(hotspotY);
- os->writeU16(width);
- os->writeU16(height);
- os->writeU32(pseudoEncodingXCursor);
- // FIXME: We only support black and white cursors, currently. We
- // could pass the correct color by using the pix0/pix1 values
- // returned from getBitmap, in writeSetCursorCallback. However, we
- // would then need to undo the conversion from rgb to Pixel that is
- // done by FakeAllocColor.
- if (width * height) {
- os->writeU8(0);
- os->writeU8(0);
- os->writeU8(0);
- os->writeU8(255);
- os->writeU8(255);
- os->writeU8(255);
- os->writeBytes(data, (width+7)/8 * height);
- os->writeBytes(mask, (width+7)/8 * height);
- }
+ return true;
}
bool SMsgWriter::needFakeUpdate()
{
- return wsccb || needSetDesktopName || needNoDataUpdate();
+ if (needSetDesktopName)
+ return true;
+ if (needSetCursor || needSetXCursor)
+ return true;
+ if (needNoDataUpdate())
+ return true;
+
+ return false;
}
bool SMsgWriter::needNoDataUpdate()
{
- return needSetDesktopSize || needExtendedDesktopSize ||
- !extendedDesktopSizeMsgs.empty();
+ if (needSetDesktopSize)
+ return true;
+ if (needExtendedDesktopSize || !extendedDesktopSizeMsgs.empty())
+ return true;
+
+ return false;
}
void SMsgWriter::writeNoDataUpdate()
os->pad(1);
if (nRects != 0xFFFF) {
- if (wsccb)
- nRects++;
if (needSetDesktopName)
nRects++;
+ if (needSetCursor)
+ nRects++;
+ if (needSetXCursor)
+ nRects++;
}
os->writeU16(nRects);
void SMsgWriter::writePseudoRects()
{
- if (wsccb) {
- wsccb->writeSetCursorCallback();
- wsccb = 0;
+ if (needSetCursor) {
+ rdr::U8* data;
+ int stride;
+
+ const Cursor& cursor = cp->cursor();
+
+ data = new rdr::U8[cursor.area() * cp->pf().bpp/8];
+ cursor.getImage(cp->pf(), data, cursor.getRect());
+
+ writeSetCursorRect(cursor.width(), cursor.height(),
+ cursor.hotspot.x, cursor.hotspot.y,
+ data, cursor.mask.buf);
+ needSetCursor = false;
+
+ delete [] data;
+ }
+
+ if (needSetXCursor) {
+ const Cursor& cursor = cp->cursor();
+ Pixel pix0, pix1;
+ rdr::U8 rgb0[3], rgb1[3];
+ rdr::U8Array bitmap(cursor.getBitmap(&pix0, &pix1));
+
+ if (!bitmap.buf) {
+ // FIXME: We could reduce to two colors.
+ throw Exception("SMsgWriter::writePseudoRects: Unable to send multicolor cursor: RichCursor not supported by client");
+ }
+
+ cp->pf().rgbFromPixel(pix0, &rgb0[0], &rgb0[1], &rgb0[2]);
+ cp->pf().rgbFromPixel(pix1, &rgb1[0], &rgb1[1], &rgb1[2]);
+
+ writeSetXCursorRect(cursor.width(), cursor.height(),
+ cursor.hotspot.x, cursor.hotspot.y,
+ rgb0, rgb1, bitmap.buf, cursor.mask.buf);
+ needSetXCursor = false;
}
if (needSetDesktopName) {
os->writeU32(pseudoEncodingDesktopName);
os->writeString(name);
}
+
+void SMsgWriter::writeSetCursorRect(int width, int height,
+ int hotspotX, int hotspotY,
+ const void* data, const void* mask)
+{
+ if (!cp->supportsLocalCursor)
+ throw Exception("Client does not support local cursors");
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
+
+ os->writeS16(hotspotX);
+ os->writeS16(hotspotY);
+ os->writeU16(width);
+ os->writeU16(height);
+ os->writeU32(pseudoEncodingCursor);
+ os->writeBytes(data, width * height * (cp->pf().bpp/8));
+ os->writeBytes(mask, (width+7)/8 * height);
+}
+
+void SMsgWriter::writeSetXCursorRect(int width, int height,
+ int hotspotX, int hotspotY,
+ const rdr::U8 pix0[],
+ const rdr::U8 pix1[],
+ const void* data, const void* mask)
+{
+ if (!cp->supportsLocalXCursor)
+ throw Exception("Client does not support local cursors");
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
+
+ os->writeS16(hotspotX);
+ os->writeS16(hotspotY);
+ os->writeU16(width);
+ os->writeU16(height);
+ os->writeU32(pseudoEncodingXCursor);
+ if (width * height) {
+ os->writeU8(pix0[0]);
+ os->writeU8(pix0[1]);
+ os->writeU8(pix0[2]);
+ os->writeU8(pix1[0]);
+ os->writeU8(pix1[1]);
+ os->writeU8(pix1[2]);
+ os->writeBytes(data, (width+7)/8 * height);
+ os->writeBytes(mask, (width+7)/8 * height);
+ }
+}
class ConnParams;
class ScreenSet;
- class WriteSetCursorCallback {
- public:
- virtual void writeSetCursorCallback() = 0;
- };
-
class SMsgWriter {
public:
SMsgWriter(ConnParams* cp, rdr::OutStream* os);
bool writeSetDesktopName();
- // Like setDesktopSize, we can't just write out a setCursor message
- // immediately. Instead of calling writeSetCursor() directly,
- // you must call cursorChange(), and then invoke writeSetCursor()
- // in response to the writeSetCursorCallback() callback. This will
- // happen when the next update is sent.
- void cursorChange(WriteSetCursorCallback* cb);
- void writeSetCursor(int width, int height, const Point& hotspot,
- void* data, void* mask);
- void writeSetXCursor(int width, int height, int hotspotX, int hotspotY,
- void* data, void* mask);
+ // Like setDesktopSize, we can't just write out a cursor message
+ // immediately.
+ bool writeSetCursor();
+ bool writeSetXCursor();
// needFakeUpdate() returns true when an immediate update is needed in
// order to flush out pseudo-rectangles to the client.
int fb_width, int fb_height,
const ScreenSet& layout);
void writeSetDesktopNameRect(const char *name);
+ void writeSetCursorRect(int width, int height,
+ int hotspotX, int hotspotY,
+ const void* data, const void* mask);
+ void writeSetXCursorRect(int width, int height,
+ int hotspotX, int hotspotY,
+ const rdr::U8 pix0[], const rdr::U8 pix1[],
+ const void* data, const void* mask);
ConnParams* cp;
rdr::OutStream* os;
int nRectsInUpdate;
int nRectsInHeader;
- WriteSetCursorCallback* wsccb;
-
bool needSetDesktopSize;
bool needExtendedDesktopSize;
bool needSetDesktopName;
bool needLastRect;
+ bool needSetCursor;
+ bool needSetXCursor;
int lenBeforeRect;
int updatesSent;
writer()->writeEndOfContinuousUpdates();
}
-void VNCSConnectionST::writeSetCursorCallback()
-{
- if (cp.supportsLocalXCursor) {
- Pixel pix0, pix1;
- rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
- if (bitmap.buf) {
- // The client supports XCursor and the cursor only has two
- // colors. Use the XCursor encoding.
- writer()->writeSetXCursor(server->cursor.width(),
- server->cursor.height(),
- server->cursor.hotspot.x,
- server->cursor.hotspot.y,
- bitmap.buf, server->cursor.mask.buf);
- return;
- } else {
- // More than two colors
- if (!cp.supportsLocalCursor) {
- // FIXME: We could reduce to two colors.
- vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
- return;
- }
- }
- }
-
- // Use RichCursor
- rdr::U8* transBuffer;
- int stride;
- const rdr::U8* buffer;
-
- transBuffer = writer()->getImageBuf(server->cursor.area());
-
- buffer = server->cursor.getBuffer(server->cursor.getRect(), &stride);
- image_getter.translatePixels(buffer, transBuffer, server->cursor.area());
-
- writer()->writeSetCursor(server->cursor.width(),
- server->cursor.height(),
- server->cursor.hotspot,
- transBuffer, server->cursor.mask.buf);
-}
-
bool VNCSConnectionST::handleTimeout(Timer* t)
{
{
if (state() != RFBSTATE_NORMAL)
return;
- if (!cp.supportsLocalCursor)
- return;
- writer()->cursorChange(this);
+ cp.setCursor(server->cursor);
+
+ if (!writer()->writeSetCursor()) {
+ if (!writer()->writeSetXCursor()) {
+ // No client support
+ return;
+ }
+ }
+
writeFramebufferUpdate();
}