You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DeviceFrameBuffer.cxx 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  16. * USA.
  17. */
  18. // -=- DeviceFrameBuffer.cxx
  19. //
  20. // The DeviceFrameBuffer class encapsulates the pixel data of the system
  21. // display.
  22. #define WIN32_LEAN_AND_MEAN
  23. #include <windows.h>
  24. #include <assert.h>
  25. #include <vector>
  26. #include <rfb/LogWriter.h>
  27. #include <rfb/Exception.h>
  28. #include <rdr/types.h>
  29. #include <rfb/VNCServer.h>
  30. #include <rfb_win32/DeviceFrameBuffer.h>
  31. #include <rfb_win32/Win32Util.h>
  32. #include <rfb_win32/OSVersion.h>
  33. namespace rfb {
  34. namespace win32 {
  35. static LogWriter vlog("FrameBuffer");
  36. // -=- DeviceFrameBuffer class
  37. DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
  38. : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext),
  39. ignoreGrabErrors(false)
  40. {
  41. // -=- Firstly, let's check that the device has suitable capabilities
  42. int capabilities = GetDeviceCaps(device, RASTERCAPS);
  43. if (!(capabilities & RC_BITBLT)) {
  44. throw Exception("device does not support BitBlt");
  45. }
  46. if (!(capabilities & RC_DI_BITMAP)) {
  47. throw Exception("device does not support GetDIBits");
  48. }
  49. /*
  50. if (GetDeviceCaps(device, PLANES) != 1) {
  51. throw Exception("device does not support planar displays");
  52. }
  53. */
  54. // -=- Get the display dimensions and pixel format
  55. // Get the display dimensions
  56. RECT cr;
  57. if (!GetClipBox(device, &cr))
  58. throw rdr::SystemException("GetClipBox", GetLastError());
  59. deviceCoords = Rect(cr.left, cr.top, cr.right, cr.bottom);
  60. if (!wRect.is_empty())
  61. deviceCoords = wRect.translate(deviceCoords.tl);
  62. int w = deviceCoords.width();
  63. int h = deviceCoords.height();
  64. // We can't handle uneven widths :(
  65. if (w % 2) w--;
  66. // Configure the underlying DIB to match the device
  67. DIBSectionBuffer::setPF(DeviceContext::getPF(device));
  68. DIBSectionBuffer::setSize(w, h);
  69. // Configure the cursor buffer
  70. cursorBm.setPF(format);
  71. // Set up a palette if required
  72. if (!format.trueColour)
  73. updateColourMap();
  74. }
  75. DeviceFrameBuffer::~DeviceFrameBuffer() {
  76. }
  77. void
  78. DeviceFrameBuffer::setPF(const PixelFormat &pf) {
  79. throw Exception("setPF not supported");
  80. }
  81. void
  82. DeviceFrameBuffer::setSize(int w, int h) {
  83. throw Exception("setSize not supported");
  84. }
  85. #ifndef CAPTUREBLT
  86. #define CAPTUREBLT 0x40000000
  87. #endif
  88. void
  89. DeviceFrameBuffer::grabRect(const Rect &rect) {
  90. BitmapDC tmpDC(device, bitmap);
  91. // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0)
  92. Point src = desktopToDevice(rect.tl);
  93. // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME
  94. // If you try CAPTUREBLT on 98 then you get blank output...
  95. if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y,
  96. osVersion.isPlatformNT ? CAPTUREBLT | SRCCOPY : SRCCOPY)) {
  97. if (ignoreGrabErrors)
  98. vlog.error("BitBlt failed:%ld", GetLastError());
  99. else
  100. throw rdr::SystemException("BitBlt failed", GetLastError());
  101. }
  102. }
  103. void
  104. DeviceFrameBuffer::grabRegion(const Region &rgn) {
  105. std::vector<Rect> rects;
  106. std::vector<Rect>::const_iterator i;
  107. rgn.get_rects(&rects);
  108. for(i=rects.begin(); i!=rects.end(); i++) {
  109. grabRect(*i);
  110. }
  111. ::GdiFlush();
  112. }
  113. void copyDevicePaletteToDIB(HDC dc, DIBSectionBuffer* dib) {
  114. // - Fetch the system palette for the framebuffer
  115. PALETTEENTRY syspalette[256];
  116. UINT entries = ::GetSystemPaletteEntries(dc, 0, 256, syspalette);
  117. if (entries == 0) {
  118. vlog.info("resorting to standard 16 color palette");
  119. for (unsigned int i=0;i<256;i++) {
  120. int v = (i%16) >= 8 ? 127 : 255;
  121. syspalette[i].peRed = i & 1 ? v : 0;
  122. syspalette[i].peGreen = i & 2 ? v : 0;
  123. syspalette[i].peBlue = i & 4 ? v : 0;
  124. }
  125. } else {
  126. vlog.info("framebuffer has %u palette entries", entries);
  127. }
  128. // - Update the bitmap's stored copy of the palette
  129. for (unsigned int i=0;i<256;i++) {
  130. int r, g, b;
  131. r = (syspalette[i].peRed << 8) + 0x80;
  132. g = (syspalette[i].peGreen << 8) + 0x80;
  133. b = (syspalette[i].peBlue << 8) + 0x80;
  134. dib->setColour(i, r, g, b);
  135. }
  136. // - Update the DIB section to use the palette
  137. dib->refreshPalette();
  138. }
  139. void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
  140. {
  141. // - If hCursor is null then there is no cursor - clear the old one
  142. if (hCursor == 0) {
  143. server->setCursor(0, 0, 0, 0, 0, 0);
  144. return;
  145. }
  146. try {
  147. // - Get the size and other details about the cursor.
  148. IconInfo iconInfo((HICON)hCursor);
  149. BITMAP maskInfo;
  150. if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
  151. throw rdr::SystemException("GetObject() failed", GetLastError());
  152. assert(maskInfo.bmPlanes == 1 && maskInfo.bmBitsPixel == 1);
  153. // - Create the cursor pixel buffer and mask storage
  154. // NB: The cursor pixel buffer is NOT used here. Instead, we
  155. // pass the cursorBm.data pointer directly, to save overhead.
  156. cursor.setSize(maskInfo.bmWidth, maskInfo.bmHeight);
  157. cursor.setPF(format);
  158. cursor.hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
  159. // - Get the AND and XOR masks. There is only an XOR mask if this is not a
  160. // colour cursor.
  161. if (!iconInfo.hbmColor)
  162. cursor.setSize(cursor.width(), cursor.height() / 2);
  163. rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
  164. rdr::U8* xorMask = mask.buf + cursor.height() * maskInfo.bmWidthBytes;
  165. if (!GetBitmapBits(iconInfo.hbmMask,
  166. maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
  167. throw rdr::SystemException("GetBitmapBits failed", GetLastError());
  168. // Configure the cursor bitmap
  169. cursorBm.setSize(cursor.width(), cursor.height());
  170. // Copy the palette into it if required
  171. if (format.bpp <= 8)
  172. copyDevicePaletteToDIB(device, &cursorBm);
  173. // Draw the cursor into the bitmap
  174. BitmapDC dc(device, cursorBm.bitmap);
  175. if (!DrawIconEx(dc, 0, 0, hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT))
  176. throw rdr::SystemException("unable to render cursor", GetLastError());
  177. // Replace any XORed pixels with xorColour, because RFB doesn't support
  178. // XORing of cursors. XORing is used for the I-beam cursor, which is most
  179. // often used over a white background, but also sometimes over a black
  180. // background. We set the XOR'd pixels to black, then draw a white outline
  181. // around the whole cursor.
  182. // *** should we replace any pixels not set in mask to zero, to ensure
  183. // that irrelevant data doesn't screw compression?
  184. bool doOutline = false;
  185. if (!iconInfo.hbmColor) {
  186. Pixel xorColour = format.pixelFromRGB(0, 0, 0, cursorBm.getColourMap());
  187. for (int y = 0; y < cursor.height(); y++) {
  188. bool first = true;
  189. for (int x = 0; x < cursor.width(); x++) {
  190. int byte = y * maskInfo.bmWidthBytes + x / 8;
  191. int bit = 7 - x % 8;
  192. if ((mask.buf[byte] & (1 << bit)) && (xorMask[byte] & (1 << bit)))
  193. {
  194. mask.buf[byte] &= ~(1 << bit);
  195. switch (format.bpp) {
  196. case 8:
  197. ((rdr::U8*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
  198. case 16:
  199. ((rdr::U16*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
  200. case 32:
  201. ((rdr::U32*)cursorBm.data)[y * cursor.width() + x] = xorColour; break;
  202. }
  203. doOutline = true;
  204. }
  205. }
  206. }
  207. }
  208. // Finally invert the AND mask so it's suitable for RFB and pack it into
  209. // the minimum number of bytes per row.
  210. int maskBytesPerRow = (cursor.width() + 7) / 8;
  211. for (int j = 0; j < cursor.height(); j++) {
  212. for (int i = 0; i < maskBytesPerRow; i++)
  213. cursor.mask.buf[j * maskBytesPerRow + i]
  214. = ~mask.buf[j * maskInfo.bmWidthBytes + i];
  215. }
  216. if (doOutline) {
  217. vlog.debug("drawing cursor outline!");
  218. memcpy(cursor.data, cursorBm.data, cursor.dataLen());
  219. cursor.drawOutline(format.pixelFromRGB(0xffff, 0xffff, 0xffff, cursorBm.getColourMap()));
  220. memcpy(cursorBm.data, cursor.data, cursor.dataLen());
  221. }
  222. server->setCursor(cursor.width(), cursor.height(),
  223. cursor.hotspot.x, cursor.hotspot.y,
  224. cursorBm.data, cursor.mask.buf);
  225. } catch (rdr::Exception& e) {
  226. vlog.error(e.str());
  227. }
  228. }
  229. void
  230. DeviceFrameBuffer::updateColourMap() {
  231. if (!format.trueColour)
  232. copyDevicePaletteToDIB(device, this);
  233. }
  234. }; // namespace win32
  235. }; // namespace rfb