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.

PollingManager.cxx 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /* Copyright (C) 2004-2008 Constantin Kaplinsky. 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. //
  19. // PollingManager.cxx
  20. //
  21. #ifdef HAVE_CONFIG_H
  22. #include <config.h>
  23. #endif
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <time.h>
  27. #include <X11/Xlib.h>
  28. #include <rfb/LogWriter.h>
  29. #include <rfb/VNCServer.h>
  30. #include <rfb/Configuration.h>
  31. #include <rfb/ServerCore.h>
  32. #include <x0vncserver/PollingManager.h>
  33. using namespace rfb;
  34. static LogWriter vlog("PollingMgr");
  35. const int PollingManager::m_pollingOrder[32] = {
  36. 0, 16, 8, 24, 4, 20, 12, 28,
  37. 10, 26, 18, 2, 22, 6, 30, 14,
  38. 1, 17, 9, 25, 7, 23, 15, 31,
  39. 19, 3, 27, 11, 29, 13, 5, 21
  40. };
  41. //
  42. // Constructor.
  43. //
  44. // Note that dpy and image should remain valid during the object
  45. // lifetime, while factory is used only in the constructor itself.
  46. //
  47. PollingManager::PollingManager(Display *dpy, const Image *image,
  48. ImageFactory &factory,
  49. int offsetLeft, int offsetTop)
  50. : m_dpy(dpy),
  51. m_image(image),
  52. m_bytesPerPixel(image->xim->bits_per_pixel / 8),
  53. m_offsetLeft(offsetLeft),
  54. m_offsetTop(offsetTop),
  55. m_width(image->xim->width),
  56. m_height(image->xim->height),
  57. m_widthTiles((image->xim->width + 31) / 32),
  58. m_heightTiles((image->xim->height + 31) / 32),
  59. m_numTiles(((image->xim->width + 31) / 32) *
  60. ((image->xim->height + 31) / 32)),
  61. m_pollingStep(0)
  62. {
  63. // Create additional images used in polling algorithm, warn if
  64. // underlying class names are different from the class name of the
  65. // primary image.
  66. m_rowImage = factory.newImage(m_dpy, m_width, 1);
  67. m_columnImage = factory.newImage(m_dpy, 1, m_height);
  68. const char *primaryImgClass = m_image->className();
  69. const char *rowImgClass = m_rowImage->className();
  70. const char *columnImgClass = m_columnImage->className();
  71. if (strcmp(rowImgClass, primaryImgClass) != 0 ||
  72. strcmp(columnImgClass, primaryImgClass) != 0) {
  73. vlog.error("Image types do not match (%s, %s, %s)",
  74. primaryImgClass, rowImgClass, columnImgClass);
  75. }
  76. m_changeFlags = new bool[m_numTiles];
  77. memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
  78. }
  79. PollingManager::~PollingManager()
  80. {
  81. delete[] m_changeFlags;
  82. delete m_rowImage;
  83. delete m_columnImage;
  84. }
  85. //
  86. // DEBUG: Measuring time spent in the poll() function,
  87. // as well as time intervals between poll() calls.
  88. //
  89. #ifdef DEBUG
  90. void PollingManager::debugBeforePoll()
  91. {
  92. TimeMillis timeNow;
  93. int diff = timeNow.diffFrom(m_timeSaved);
  94. fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
  95. m_timeSaved = timeNow;
  96. }
  97. void PollingManager::debugAfterPoll()
  98. {
  99. TimeMillis timeNow;
  100. int diff = timeNow.diffFrom(m_timeSaved);
  101. fprintf(stderr, "[poll%4dms]\n", diff);
  102. m_timeSaved = timeNow;
  103. }
  104. #endif
  105. //
  106. // Search for changed rectangles on the screen.
  107. //
  108. void PollingManager::poll(VNCServer *server)
  109. {
  110. #ifdef DEBUG
  111. debugBeforePoll();
  112. #endif
  113. pollScreen(server);
  114. #ifdef DEBUG
  115. debugAfterPoll();
  116. #endif
  117. }
  118. #ifdef DEBUG_REPORT_CHANGED_TILES
  119. #define DBG_REPORT_CHANGES(title) printChanges((title))
  120. #else
  121. #define DBG_REPORT_CHANGES(title)
  122. #endif
  123. bool PollingManager::pollScreen(VNCServer *server)
  124. {
  125. if (!server)
  126. return false;
  127. // Clear the m_changeFlags[] array, indicating that no changes have
  128. // been detected yet.
  129. memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
  130. // First pass over the framebuffer. Here we scan 1/32 part of the
  131. // framebuffer -- that is, one line in each (32 * m_width) stripe.
  132. // We compare the pixels of that line with previous framebuffer
  133. // contents and raise corresponding elements of m_changeFlags[].
  134. int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
  135. int nTilesChanged = 0;
  136. for (int y = scanOffset; y < m_height; y += 32) {
  137. nTilesChanged += checkRow(0, y, m_width);
  138. }
  139. DBG_REPORT_CHANGES("After 1st pass");
  140. // If some changes have been detected:
  141. if (nTilesChanged) {
  142. // Try to find more changes around.
  143. checkNeighbors();
  144. DBG_REPORT_CHANGES("After checking neighbors");
  145. // Inform the server about the changes.
  146. nTilesChanged = sendChanges(server);
  147. }
  148. #ifdef DEBUG_PRINT_NUM_CHANGED_TILES
  149. printf("%3d ", nTilesChanged);
  150. if (m_pollingStep % 32 == 0) {
  151. printf("\n");
  152. }
  153. #endif
  154. #ifdef DEBUG
  155. if (nTilesChanged != 0) {
  156. fprintf(stderr, "#%d# ", nTilesChanged);
  157. }
  158. #endif
  159. return (nTilesChanged != 0);
  160. }
  161. int PollingManager::checkRow(int x, int y, int w)
  162. {
  163. // If necessary, expand the row to the left, to the tile border.
  164. // In other words, x must be a multiple of 32.
  165. if (x % 32 != 0) {
  166. int correction = x % 32;
  167. x -= correction;
  168. w += correction;
  169. }
  170. // Read a row from the screen into m_rowImage.
  171. getRow(x, y, w);
  172. // Compute a pointer to the initial element of m_changeFlags.
  173. bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
  174. // Compute pointers to image data to be compared.
  175. char *ptr_old = m_image->locatePixel(x, y);
  176. char *ptr_new = m_rowImage->xim->data;
  177. // Compare pixels, raise corresponding elements of m_changeFlags[].
  178. // First, handle full-size (32 pixels wide) tiles.
  179. int nTilesChanged = 0;
  180. int nBytesPerTile = 32 * m_bytesPerPixel;
  181. for (int i = 0; i < w / 32; i++) {
  182. if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
  183. *pChangeFlags = true;
  184. nTilesChanged++;
  185. }
  186. pChangeFlags++;
  187. ptr_old += nBytesPerTile;
  188. ptr_new += nBytesPerTile;
  189. }
  190. // Handle the rightmost pixels, if the width is not a multiple of 32.
  191. int nBytesLeft = (w % 32) * m_bytesPerPixel;
  192. if (nBytesLeft != 0) {
  193. if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
  194. *pChangeFlags = true;
  195. nTilesChanged++;
  196. }
  197. }
  198. return nTilesChanged;
  199. }
  200. int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
  201. {
  202. getColumn(x, y, h);
  203. int nTilesChanged = 0;
  204. for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
  205. if (!*pChangeFlags) {
  206. int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
  207. for (int i = 0; i < tile_h; i++) {
  208. // FIXME: Do not compute these pointers in the inner cycle.
  209. char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
  210. char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
  211. if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
  212. *pChangeFlags = true;
  213. nTilesChanged++;
  214. break;
  215. }
  216. }
  217. }
  218. pChangeFlags += m_widthTiles;
  219. }
  220. return nTilesChanged;
  221. }
  222. int PollingManager::sendChanges(VNCServer *server) const
  223. {
  224. const bool *pChangeFlags = m_changeFlags;
  225. int nTilesChanged = 0;
  226. Rect rect;
  227. for (int y = 0; y < m_heightTiles; y++) {
  228. for (int x = 0; x < m_widthTiles; x++) {
  229. if (*pChangeFlags++) {
  230. // Count successive tiles marked as changed.
  231. int count = 1;
  232. while (x + count < m_widthTiles && *pChangeFlags++) {
  233. count++;
  234. }
  235. nTilesChanged += count;
  236. // Compute the coordinates and the size of this band.
  237. rect.setXYWH(x * 32, y * 32, count * 32, 32);
  238. if (rect.br.x > m_width)
  239. rect.br.x = m_width;
  240. if (rect.br.y > m_height)
  241. rect.br.y = m_height;
  242. // Add to the changed region maintained by the server.
  243. server->add_changed(rect);
  244. // Skip processed tiles.
  245. x += count;
  246. }
  247. }
  248. }
  249. return nTilesChanged;
  250. }
  251. void
  252. PollingManager::checkNeighbors()
  253. {
  254. int x, y;
  255. // Check neighboring pixels above and below changed tiles.
  256. // FIXME: Fast skip to the first changed tile (and to the last, too).
  257. // FIXME: Check the full-width line above the first changed tile?
  258. for (y = 0; y < m_heightTiles; y++) {
  259. bool doneAbove = false;
  260. bool doneBelow = false;
  261. for (x = 0; x < m_widthTiles; x++) {
  262. if (!doneAbove && y > 0 &&
  263. m_changeFlags[y * m_widthTiles + x] &&
  264. !m_changeFlags[(y - 1) * m_widthTiles + x]) {
  265. // FIXME: Check m_changeFlags[] to decrease height of the row.
  266. checkRow(x * 32, y * 32 - 1, m_width - x * 32);
  267. doneAbove = true;
  268. }
  269. if (!doneBelow && y < m_heightTiles - 1 &&
  270. m_changeFlags[y * m_widthTiles + x] &&
  271. !m_changeFlags[(y + 1) * m_widthTiles + x]) {
  272. // FIXME: Check m_changeFlags[] to decrease height of the row.
  273. checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
  274. doneBelow = true;
  275. }
  276. if (doneBelow && doneAbove)
  277. break;
  278. }
  279. }
  280. // Check neighboring pixels at the right side of changed tiles.
  281. for (x = 0; x < m_widthTiles - 1; x++) {
  282. for (y = 0; y < m_heightTiles; y++) {
  283. if (m_changeFlags[y * m_widthTiles + x] &&
  284. !m_changeFlags[y * m_widthTiles + x + 1]) {
  285. // FIXME: Check m_changeFlags[] to decrease height of the column.
  286. checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
  287. &m_changeFlags[y * m_widthTiles + x + 1]);
  288. break;
  289. }
  290. }
  291. }
  292. // Check neighboring pixels at the left side of changed tiles.
  293. for (x = m_widthTiles - 1; x > 0; x--) {
  294. for (y = 0; y < m_heightTiles; y++) {
  295. if (m_changeFlags[y * m_widthTiles + x] &&
  296. !m_changeFlags[y * m_widthTiles + x - 1]) {
  297. // FIXME: Check m_changeFlags[] to decrease height of the column.
  298. checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
  299. &m_changeFlags[y * m_widthTiles + x - 1]);
  300. break;
  301. }
  302. }
  303. }
  304. }
  305. void
  306. PollingManager::printChanges(const char *header) const
  307. {
  308. fprintf(stderr, "%s:", header);
  309. const bool *pChangeFlags = m_changeFlags;
  310. for (int y = 0; y < m_heightTiles; y++) {
  311. for (int x = 0; x < m_widthTiles; x++) {
  312. if (*pChangeFlags++) {
  313. // Count successive tiles marked as changed.
  314. int count = 1;
  315. while (x + count < m_widthTiles && *pChangeFlags++) {
  316. count++;
  317. }
  318. // Print.
  319. fprintf(stderr, " (%d,%d)*%d", x, y, count);
  320. // Skip processed tiles.
  321. x += count;
  322. }
  323. }
  324. }
  325. fprintf(stderr, "\n");
  326. }