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

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