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.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2014-2017 Pierre Ossman for Cendio AB
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. // -=- DeviceFrameBuffer.cxx
  20. //
  21. // The DeviceFrameBuffer class encapsulates the pixel data of the system
  22. // display.
  23. #ifdef HAVE_CONFIG_H
  24. #include <config.h>
  25. #endif
  26. #include <vector>
  27. #include <rfb_win32/DeviceFrameBuffer.h>
  28. #include <rfb_win32/DeviceContext.h>
  29. #include <rfb_win32/IconInfo.h>
  30. #include <rfb/VNCServer.h>
  31. #include <rfb/LogWriter.h>
  32. using namespace rfb;
  33. using namespace win32;
  34. static LogWriter vlog("DeviceFrameBuffer");
  35. BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt",
  36. "Use a slower capture method that ensures that alpha blended windows appear correctly",
  37. true);
  38. // -=- DeviceFrameBuffer class
  39. DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
  40. : DIBSectionBuffer(deviceContext), device(deviceContext),
  41. ignoreGrabErrors(false)
  42. {
  43. // -=- Firstly, let's check that the device has suitable capabilities
  44. int capabilities = GetDeviceCaps(device, RASTERCAPS);
  45. if (!(capabilities & RC_BITBLT)) {
  46. throw Exception("device does not support BitBlt");
  47. }
  48. if (!(capabilities & RC_DI_BITMAP)) {
  49. throw Exception("device does not support GetDIBits");
  50. }
  51. /*
  52. if (GetDeviceCaps(device, PLANES) != 1) {
  53. throw Exception("device does not support planar displays");
  54. }
  55. */
  56. // -=- Get the display dimensions and pixel format
  57. // Get the display dimensions
  58. deviceCoords = DeviceContext::getClipBox(device);
  59. if (!wRect.is_empty())
  60. deviceCoords = wRect.translate(deviceCoords.tl);
  61. int w = deviceCoords.width();
  62. int h = deviceCoords.height();
  63. // We can't handle uneven widths :(
  64. if (w % 2) w--;
  65. // Configure the underlying DIB to match the device
  66. initBuffer(DeviceContext::getPF(device), w, h);
  67. }
  68. DeviceFrameBuffer::~DeviceFrameBuffer() {
  69. }
  70. #ifndef CAPTUREBLT
  71. #define CAPTUREBLT 0x40000000
  72. #endif
  73. void
  74. DeviceFrameBuffer::grabRect(const Rect &rect) {
  75. BitmapDC tmpDC(device, bitmap);
  76. // Map the rectangle coords from VNC Desktop-relative to device relative - usually (0,0)
  77. Point src = desktopToDevice(rect.tl);
  78. if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y,
  79. rect.width(), rect.height(), device, src.x, src.y,
  80. useCaptureBlt ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) {
  81. if (ignoreGrabErrors)
  82. vlog.error("BitBlt failed:%ld", GetLastError());
  83. else
  84. throw rdr::SystemException("BitBlt failed", GetLastError());
  85. }
  86. }
  87. void
  88. DeviceFrameBuffer::grabRegion(const Region &rgn) {
  89. std::vector<Rect> rects;
  90. std::vector<Rect>::const_iterator i;
  91. rgn.get_rects(&rects);
  92. for(i=rects.begin(); i!=rects.end(); i++) {
  93. grabRect(*i);
  94. }
  95. ::GdiFlush();
  96. }
  97. void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
  98. {
  99. // - If hCursor is null then there is no cursor - clear the old one
  100. if (hCursor == 0) {
  101. server->setCursor(0, 0, Point(), NULL);
  102. return;
  103. }
  104. try {
  105. int width, height;
  106. rdr::U8Array buffer;
  107. // - Get the size and other details about the cursor.
  108. IconInfo iconInfo((HICON)hCursor);
  109. BITMAP maskInfo;
  110. if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
  111. throw rdr::SystemException("GetObject() failed", GetLastError());
  112. if (maskInfo.bmPlanes != 1)
  113. throw rdr::Exception("unsupported multi-plane cursor");
  114. if (maskInfo.bmBitsPixel != 1)
  115. throw rdr::Exception("unsupported cursor mask format");
  116. width = maskInfo.bmWidth;
  117. height = maskInfo.bmHeight;
  118. if (!iconInfo.hbmColor)
  119. height /= 2;
  120. buffer.buf = new rdr::U8[width * height * 4];
  121. Point hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
  122. if (iconInfo.hbmColor) {
  123. // Colour cursor
  124. BITMAPV5HEADER bi;
  125. BitmapDC dc(device, iconInfo.hbmColor);
  126. memset(&bi, 0, sizeof(BITMAPV5HEADER));
  127. bi.bV5Size = sizeof(BITMAPV5HEADER);
  128. bi.bV5Width = width;
  129. bi.bV5Height = -height; // Negative for top-down
  130. bi.bV5Planes = 1;
  131. bi.bV5BitCount = 32;
  132. bi.bV5Compression = BI_BITFIELDS;
  133. bi.bV5RedMask = 0x000000FF;
  134. bi.bV5GreenMask = 0x0000FF00;
  135. bi.bV5BlueMask = 0x00FF0000;
  136. bi.bV5AlphaMask = 0xFF000000;
  137. if (!GetDIBits(dc, iconInfo.hbmColor, 0, height,
  138. buffer.buf, (LPBITMAPINFO)&bi, DIB_RGB_COLORS))
  139. throw rdr::SystemException("GetDIBits", GetLastError());
  140. // We may not get the RGBA order we want, so shuffle things around
  141. int ridx, gidx, bidx, aidx;
  142. ridx = __builtin_ffs(bi.bV5RedMask) / 8;
  143. gidx = __builtin_ffs(bi.bV5GreenMask) / 8;
  144. bidx = __builtin_ffs(bi.bV5BlueMask) / 8;
  145. // Usually not set properly
  146. aidx = 6 - ridx - gidx - bidx;
  147. if ((bi.bV5RedMask != ((unsigned)0xff << ridx*8)) ||
  148. (bi.bV5GreenMask != ((unsigned)0xff << gidx*8)) ||
  149. (bi.bV5BlueMask != ((unsigned)0xff << bidx*8)))
  150. throw rdr::Exception("unsupported cursor colour format");
  151. rdr::U8* rwbuffer = buffer.buf;
  152. for (int y = 0; y < height; y++) {
  153. for (int x = 0; x < width; x++) {
  154. rdr::U8 r, g, b, a;
  155. r = rwbuffer[ridx];
  156. g = rwbuffer[gidx];
  157. b = rwbuffer[bidx];
  158. a = rwbuffer[aidx];
  159. rwbuffer[0] = r;
  160. rwbuffer[1] = g;
  161. rwbuffer[2] = b;
  162. rwbuffer[3] = a;
  163. rwbuffer += 4;
  164. }
  165. }
  166. } else {
  167. // B/W cursor
  168. rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
  169. rdr::U8* andMask = mask.buf;
  170. rdr::U8* xorMask = mask.buf + height * maskInfo.bmWidthBytes;
  171. if (!GetBitmapBits(iconInfo.hbmMask,
  172. maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
  173. throw rdr::SystemException("GetBitmapBits", GetLastError());
  174. bool doOutline = false;
  175. rdr::U8* rwbuffer = buffer.buf;
  176. for (int y = 0; y < height; y++) {
  177. for (int x = 0; x < width; x++) {
  178. int byte = y * maskInfo.bmWidthBytes + x / 8;
  179. int bit = 7 - x % 8;
  180. if (!(andMask[byte] & (1 << bit))) {
  181. // Valid pixel, so make it opaque
  182. rwbuffer[3] = 0xff;
  183. // Black or white?
  184. if (xorMask[byte] & (1 << bit))
  185. rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0xff;
  186. else
  187. rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0;
  188. } else if (xorMask[byte] & (1 << bit)) {
  189. // Replace any XORed pixels with black, because RFB doesn't support
  190. // XORing of cursors. XORing is used for the I-beam cursor, which is most
  191. // often used over a white background, but also sometimes over a black
  192. // background. We set the XOR'd pixels to black, then draw a white outline
  193. // around the whole cursor.
  194. rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0;
  195. rwbuffer[3] = 0xff;
  196. doOutline = true;
  197. } else {
  198. // Transparent pixel
  199. rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = rwbuffer[3] = 0;
  200. }
  201. rwbuffer += 4;
  202. }
  203. }
  204. if (doOutline) {
  205. vlog.debug("drawing cursor outline!");
  206. // The buffer needs to be slightly larger to make sure there
  207. // is room for the outline pixels
  208. rdr::U8Array outline((width + 2)*(height + 2)*4);
  209. memset(outline.buf, 0, (width + 2)*(height + 2)*4);
  210. // Pass 1, outline everything
  211. rdr::U8* in = buffer.buf;
  212. rdr::U8* out = outline.buf + width*4 + 4;
  213. for (int y = 0; y < height; y++) {
  214. for (int x = 0; x < width; x++) {
  215. // Visible pixel?
  216. if (in[3] > 0) {
  217. // Outline above...
  218. memset(out - (width+2)*4 - 4, 0xff, 4 * 3);
  219. // ...besides...
  220. memset(out - 4, 0xff, 4 * 3);
  221. // ...and above
  222. memset(out + (width+2)*4 - 4, 0xff, 4 * 3);
  223. }
  224. in += 4;
  225. out += 4;
  226. }
  227. // outline is slightly larger
  228. out += 2*4;
  229. }
  230. // Pass 2, overwrite with actual cursor
  231. in = buffer.buf;
  232. out = outline.buf + width*4 + 4;
  233. for (int y = 0; y < height; y++) {
  234. for (int x = 0; x < width; x++) {
  235. if (in[3] > 0)
  236. memcpy(out, in, 4);
  237. in += 4;
  238. out += 4;
  239. }
  240. out += 2*4;
  241. }
  242. width += 2;
  243. height += 2;
  244. hotspot.x += 1;
  245. hotspot.y += 1;
  246. delete [] buffer.buf;
  247. buffer.buf = outline.takeBuf();
  248. }
  249. }
  250. server->setCursor(width, height, hotspot, buffer.buf);
  251. } catch (rdr::Exception& e) {
  252. vlog.error("%s", e.str());
  253. }
  254. }