123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2014-2017 Pierre Ossman for Cendio AB
- *
- * This 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 software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
- // -=- DeviceFrameBuffer.cxx
- //
- // The DeviceFrameBuffer class encapsulates the pixel data of the system
- // display.
-
- #include <vector>
- #include <rfb_win32/DeviceFrameBuffer.h>
- #include <rfb_win32/DeviceContext.h>
- #include <rfb_win32/IconInfo.h>
- #include <rfb/VNCServer.h>
- #include <rfb/LogWriter.h>
-
- using namespace rfb;
- using namespace win32;
-
- static LogWriter vlog("DeviceFrameBuffer");
-
- BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt",
- "Use a slower capture method that ensures that alpha blended windows appear correctly",
- true);
-
-
- // -=- DeviceFrameBuffer class
-
- DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
- : DIBSectionBuffer(deviceContext), device(deviceContext),
- ignoreGrabErrors(false)
- {
-
- // -=- Firstly, let's check that the device has suitable capabilities
-
- int capabilities = GetDeviceCaps(device, RASTERCAPS);
- if (!(capabilities & RC_BITBLT)) {
- throw Exception("device does not support BitBlt");
- }
- if (!(capabilities & RC_DI_BITMAP)) {
- throw Exception("device does not support GetDIBits");
- }
- /*
- if (GetDeviceCaps(device, PLANES) != 1) {
- throw Exception("device does not support planar displays");
- }
- */
-
- // -=- Get the display dimensions and pixel format
-
- // Get the display dimensions
- deviceCoords = DeviceContext::getClipBox(device);
- if (!wRect.is_empty())
- deviceCoords = wRect.translate(deviceCoords.tl);
- int w = deviceCoords.width();
- int h = deviceCoords.height();
-
- // We can't handle uneven widths :(
- if (w % 2) w--;
-
- // Configure the underlying DIB to match the device
- DIBSectionBuffer::setPF(DeviceContext::getPF(device));
- DIBSectionBuffer::setSize(w, h);
- }
-
- DeviceFrameBuffer::~DeviceFrameBuffer() {
- }
-
-
- void
- DeviceFrameBuffer::setPF(const PixelFormat &pf) {
- throw Exception("setPF not supported");
- }
-
- void
- DeviceFrameBuffer::setSize(int w, int h) {
- throw Exception("setSize not supported");
- }
-
-
- #ifndef CAPTUREBLT
- #define CAPTUREBLT 0x40000000
- #endif
-
- void
- DeviceFrameBuffer::grabRect(const Rect &rect) {
- BitmapDC tmpDC(device, bitmap);
-
- // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0)
- Point src = desktopToDevice(rect.tl);
-
- if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y,
- rect.width(), rect.height(), device, src.x, src.y,
- useCaptureBlt ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) {
- if (ignoreGrabErrors)
- vlog.error("BitBlt failed:%ld", GetLastError());
- else
- throw rdr::SystemException("BitBlt failed", GetLastError());
- }
- }
-
- void
- DeviceFrameBuffer::grabRegion(const Region &rgn) {
- std::vector<Rect> rects;
- std::vector<Rect>::const_iterator i;
- rgn.get_rects(&rects);
- for(i=rects.begin(); i!=rects.end(); i++) {
- grabRect(*i);
- }
- ::GdiFlush();
- }
-
-
- void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
- {
- // - If hCursor is null then there is no cursor - clear the old one
-
- if (hCursor == 0) {
- server->setCursor(0, 0, Point(), NULL);
- return;
- }
-
- try {
-
- int width, height;
- rdr::U8Array buffer;
-
- // - Get the size and other details about the cursor.
-
- IconInfo iconInfo((HICON)hCursor);
-
- BITMAP maskInfo;
- if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
- throw rdr::SystemException("GetObject() failed", GetLastError());
- if (maskInfo.bmPlanes != 1)
- throw rdr::Exception("unsupported multi-plane cursor");
- if (maskInfo.bmBitsPixel != 1)
- throw rdr::Exception("unsupported cursor mask format");
-
- width = maskInfo.bmWidth;
- height = maskInfo.bmHeight;
- if (!iconInfo.hbmColor)
- height /= 2;
-
- buffer.buf = new rdr::U8[width * height * 4];
-
- Point hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
-
- if (iconInfo.hbmColor) {
- // Colour cursor
-
- BITMAPV5HEADER bi;
- BitmapDC dc(device, iconInfo.hbmColor);
-
- memset(&bi, 0, sizeof(BITMAPV5HEADER));
-
- bi.bV5Size = sizeof(BITMAPV5HEADER);
- bi.bV5Width = width;
- bi.bV5Height = -height; // Negative for top-down
- bi.bV5Planes = 1;
- bi.bV5BitCount = 32;
- bi.bV5Compression = BI_BITFIELDS;
- bi.bV5RedMask = 0x000000FF;
- bi.bV5GreenMask = 0x0000FF00;
- bi.bV5BlueMask = 0x00FF0000;
- bi.bV5AlphaMask = 0xFF000000;
-
- if (!GetDIBits(dc, iconInfo.hbmColor, 0, height,
- buffer.buf, (LPBITMAPINFO)&bi, DIB_RGB_COLORS))
- throw rdr::SystemException("GetDIBits", GetLastError());
-
- // We may not get the RGBA order we want, so shuffle things around
- int ridx, gidx, bidx, aidx;
-
- ridx = __builtin_ffs(bi.bV5RedMask) / 8;
- gidx = __builtin_ffs(bi.bV5GreenMask) / 8;
- bidx = __builtin_ffs(bi.bV5BlueMask) / 8;
- // Usually not set properly
- aidx = 6 - ridx - gidx - bidx;
-
- if ((bi.bV5RedMask != ((unsigned)0xff << ridx*8)) ||
- (bi.bV5GreenMask != ((unsigned)0xff << gidx*8)) ||
- (bi.bV5BlueMask != ((unsigned)0xff << bidx*8)))
- throw rdr::Exception("unsupported cursor colour format");
-
- rdr::U8* rwbuffer = buffer.buf;
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- rdr::U8 r, g, b, a;
-
- r = rwbuffer[ridx];
- g = rwbuffer[gidx];
- b = rwbuffer[bidx];
- a = rwbuffer[aidx];
-
- rwbuffer[0] = r;
- rwbuffer[1] = g;
- rwbuffer[2] = b;
- rwbuffer[3] = a;
-
- rwbuffer += 4;
- }
- }
- } else {
- // B/W cursor
-
- rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
- rdr::U8* andMask = mask.buf;
- rdr::U8* xorMask = mask.buf + height * maskInfo.bmWidthBytes;
-
- if (!GetBitmapBits(iconInfo.hbmMask,
- maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
- throw rdr::SystemException("GetBitmapBits", GetLastError());
-
- bool doOutline = false;
- rdr::U8* rwbuffer = buffer.buf;
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int byte = y * maskInfo.bmWidthBytes + x / 8;
- int bit = 7 - x % 8;
-
- if (!(andMask[byte] & (1 << bit))) {
- // Valid pixel, so make it opaque
- rwbuffer[3] = 0xff;
-
- // Black or white?
- if (xorMask[byte] & (1 << bit))
- rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0xff;
- else
- rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0;
- } else if (xorMask[byte] & (1 << bit)) {
- // Replace any XORed pixels with black, because RFB doesn't support
- // XORing of cursors. XORing is used for the I-beam cursor, which is most
- // often used over a white background, but also sometimes over a black
- // background. We set the XOR'd pixels to black, then draw a white outline
- // around the whole cursor.
-
- rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0;
- rwbuffer[3] = 0xff;
-
- doOutline = true;
- } else {
- // Transparent pixel
- rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = rwbuffer[3] = 0;
- }
-
- rwbuffer += 4;
- }
- }
-
- if (doOutline) {
- vlog.debug("drawing cursor outline!");
-
- // The buffer needs to be slightly larger to make sure there
- // is room for the outline pixels
- rdr::U8Array outline((width + 2)*(height + 2)*4);
- memset(outline.buf, 0, (width + 2)*(height + 2)*4);
-
- // Pass 1, outline everything
- rdr::U8* in = buffer.buf;
- rdr::U8* out = outline.buf + width*4 + 4;
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- // Visible pixel?
- if (in[3] > 0) {
- // Outline above...
- memset(out - (width+2)*4 - 4, 0xff, 4 * 3);
- // ...besides...
- memset(out - 4, 0xff, 4 * 3);
- // ...and above
- memset(out + (width+2)*4 - 4, 0xff, 4 * 3);
- }
- in += 4;
- out += 4;
- }
- // outline is slightly larger
- out += 2*4;
- }
-
- // Pass 2, overwrite with actual cursor
- in = buffer.buf;
- out = outline.buf + width*4 + 4;
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- if (in[3] > 0)
- memcpy(out, in, 4);
- in += 4;
- out += 4;
- }
- out += 2*4;
- }
-
- width += 2;
- height += 2;
- hotspot.x += 1;
- hotspot.y += 1;
-
- delete [] buffer.buf;
- buffer.buf = outline.takeBuf();
- }
- }
-
- server->setCursor(width, height, hotspot, buffer.buf);
-
- } catch (rdr::Exception& e) {
- vlog.error("%s", e.str());
- }
- }
|