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