選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

DeviceFrameBuffer.cxx 9.4KB

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