您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

PollingManager.cxx 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /* Copyright (C) 2004-2005 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. // FIXME: Don't compare pixels already marked as changed.
  22. // FIXME: Use Image::copyPixels() instead of Image::updateRect()?
  23. // In that case, note the fact that arguments are not checked.
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <time.h>
  27. #include <X11/Xlib.h>
  28. #include <rfb/VNCServer.h>
  29. #include <rfb/Configuration.h>
  30. #include <rfb/ServerCore.h>
  31. #include <x0vncserver/PollingManager.h>
  32. BoolParameter PollingManager::pollPointer
  33. ("PollPointer",
  34. "DEBUG: Poll area under the pointer with higher priority",
  35. true);
  36. IntParameter PollingManager::pollingType
  37. ("PollingType",
  38. "DEBUG: Select particular polling algorithm (0..3)",
  39. 3);
  40. const int PollingManager::m_pollingOrder[32] = {
  41. 0, 16, 8, 24, 4, 20, 12, 28,
  42. 10, 26, 18, 2, 22, 6, 30, 14,
  43. 1, 17, 9, 25, 7, 23, 15, 31,
  44. 19, 3, 27, 11, 29, 13, 5, 21
  45. };
  46. //
  47. // Constructor.
  48. //
  49. // Note that dpy and image should remain valid during the object
  50. // lifetime, while factory is used only in the constructor itself.
  51. //
  52. PollingManager::PollingManager(Display *dpy, Image *image,
  53. ImageFactory *factory)
  54. : m_dpy(dpy), m_server(0), m_image(image), m_pointerPosKnown(false),
  55. m_pollingStep(0)
  56. {
  57. // Save width and height of the screen (and the image).
  58. m_width = m_image->xim->width;
  59. m_height = m_image->xim->height;
  60. // Compute width and height in 32x32 tiles.
  61. m_widthTiles = (m_width + 31) / 32;
  62. m_heightTiles = (m_height + 31) / 32;
  63. // Create additional images used in the polling algorithm.
  64. // FIXME: verify that these images use the same pixel format as in m_image.
  65. m_rowImage = factory->newImage(m_dpy, m_width, 1);
  66. m_tileImage = factory->newImage(m_dpy, 32, 32);
  67. m_areaImage = factory->newImage(m_dpy, 128, 128);
  68. // FIXME: Extend the comment.
  69. // Create a matrix with one byte per each 32x32 tile. It will be
  70. // used to limit the rate of updates on continuously-changed screen
  71. // areas (like video).
  72. int numTiles = m_widthTiles * m_heightTiles;
  73. m_statusMatrix = new char[numTiles];
  74. memset(m_statusMatrix, 0, numTiles);
  75. // FIXME: Extend the comment.
  76. // Create a matrix with one byte per each 32x32 tile. It will be
  77. // used to limit the rate of updates on continuously-changed screen
  78. // areas (like video).
  79. m_rateMatrix = new char[numTiles];
  80. m_videoFlags = new char[numTiles];
  81. m_changedFlags = new char[numTiles];
  82. memset(m_rateMatrix, 0, numTiles);
  83. memset(m_videoFlags, 0, numTiles);
  84. memset(m_changedFlags, 0, numTiles);
  85. }
  86. PollingManager::~PollingManager()
  87. {
  88. delete[] m_changedFlags;
  89. delete[] m_videoFlags;
  90. delete[] m_rateMatrix;
  91. delete[] m_statusMatrix;
  92. delete m_areaImage;
  93. delete m_tileImage;
  94. delete m_rowImage;
  95. }
  96. //
  97. // Register VNCServer object.
  98. //
  99. void PollingManager::setVNCServer(VNCServer *s)
  100. {
  101. m_server = s;
  102. }
  103. //
  104. // Update current pointer position which may be used as a hint for
  105. // polling algorithms.
  106. //
  107. void PollingManager::setPointerPos(const Point &pos)
  108. {
  109. m_pointerPosTime = time(NULL);
  110. m_pointerPos = pos;
  111. m_pointerPosKnown = true;
  112. }
  113. //
  114. // Indicate that current pointer position is unknown.
  115. //
  116. void PollingManager::unsetPointerPos()
  117. {
  118. m_pointerPosKnown = false;
  119. }
  120. //
  121. // DEBUG: Measuring time spent in the poll() function,
  122. // as well as time intervals between poll() calls.
  123. //
  124. #ifdef DEBUG
  125. void PollingManager::debugBeforePoll()
  126. {
  127. TimeMillis timeNow;
  128. int diff = timeNow.diffFrom(m_timeSaved);
  129. fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
  130. m_timeSaved = timeNow;
  131. }
  132. void PollingManager::debugAfterPoll()
  133. {
  134. TimeMillis timeNow;
  135. int diff = timeNow.diffFrom(m_timeSaved);
  136. fprintf(stderr, "[poll%4dms]\n", diff);
  137. m_timeSaved = timeNow;
  138. }
  139. #endif
  140. //
  141. // Search for changed rectangles on the screen.
  142. //
  143. void PollingManager::poll()
  144. {
  145. #ifdef DEBUG
  146. debugBeforePoll();
  147. #endif
  148. // First step: full-screen polling.
  149. bool changes1 = false;
  150. switch((int)pollingType) {
  151. case 0:
  152. changes1 = poll_Dumb();
  153. break;
  154. case 1:
  155. changes1 = poll_Traditional();
  156. break;
  157. case 2:
  158. changes1 = poll_SkipCycles();
  159. break;
  160. //case 3:
  161. default:
  162. changes1 = poll_DetectVideo();
  163. break;
  164. }
  165. // Second step: optional thorough polling of the area around the pointer.
  166. // We do that only if the pointer position is known and was set recently.
  167. bool changes2 = false;
  168. if (pollPointer) {
  169. if (m_pointerPosKnown && time(NULL) - m_pointerPosTime >= 5) {
  170. unsetPointerPos();
  171. }
  172. if (m_pointerPosKnown) {
  173. changes2 = pollPointerArea();
  174. }
  175. }
  176. // Update if needed.
  177. if (changes1 || changes2)
  178. m_server->tryUpdate();
  179. #ifdef DEBUG
  180. debugAfterPoll();
  181. #endif
  182. }
  183. bool PollingManager::poll_DetectVideo()
  184. {
  185. if (!m_server)
  186. return false;
  187. const int GRAND_STEP_DIVISOR = 8;
  188. const int VIDEO_THRESHOLD_0 = 3;
  189. const int VIDEO_THRESHOLD_1 = 5;
  190. bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
  191. // FIXME: Save shortcuts in member variables?
  192. int scanLine = m_pollingOrder[m_pollingStep++ % 32];
  193. int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
  194. int bytesPerLine = m_image->xim->bytes_per_line;
  195. Rect rect;
  196. int nTilesChanged = 0;
  197. int idx = 0;
  198. for (int y = 0; y * 32 < m_height; y++) {
  199. int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
  200. if (scanLine >= tile_h)
  201. break;
  202. int scan_y = y * 32 + scanLine;
  203. m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
  204. char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
  205. char *ptr_new = m_rowImage->xim->data;
  206. for (int x = 0; x * 32 < m_width; x++) {
  207. int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
  208. int nBytes = tile_w * bytesPerPixel;
  209. char wasChanged = (memcmp(ptr_old, ptr_new, nBytes) != 0);
  210. m_rateMatrix[idx] += wasChanged;
  211. if (grandStep) {
  212. if (m_rateMatrix[idx] <= VIDEO_THRESHOLD_0) {
  213. m_videoFlags[idx] = 0;
  214. } else if (m_rateMatrix[idx] >= VIDEO_THRESHOLD_1) {
  215. m_videoFlags[idx] = 1;
  216. }
  217. m_rateMatrix[idx] = 0;
  218. }
  219. m_changedFlags[idx] |= wasChanged;
  220. if ( m_changedFlags[idx] && (!m_videoFlags[idx] || grandStep) ) {
  221. if (tile_w == 32 && tile_h == 32) {
  222. m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
  223. } else {
  224. m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
  225. tile_w, tile_h);
  226. }
  227. m_image->updateRect(m_tileImage, x * 32, y * 32);
  228. rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
  229. m_server->add_changed(rect);
  230. nTilesChanged++;
  231. m_changedFlags[idx] = 0;
  232. }
  233. ptr_old += nBytes;
  234. ptr_new += nBytes;
  235. idx++;
  236. }
  237. }
  238. if (grandStep)
  239. adjustVideoArea();
  240. return (nTilesChanged != 0);
  241. }
  242. bool PollingManager::poll_SkipCycles()
  243. {
  244. if (!m_server)
  245. return false;
  246. enum {
  247. NOT_CHANGED, CHANGED_ONCE, CHANGED_AGAIN
  248. };
  249. bool grandStep = (m_pollingStep % 8 == 0);
  250. int nTilesChanged = 0;
  251. int scanLine = m_pollingOrder[m_pollingStep++ % 32];
  252. int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
  253. int bytesPerLine = m_image->xim->bytes_per_line;
  254. char *pstatus = m_statusMatrix;
  255. bool wasChanged;
  256. Rect rect;
  257. for (int y = 0; y * 32 < m_height; y++) {
  258. int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
  259. if (scanLine >= tile_h)
  260. scanLine %= tile_h;
  261. int scan_y = y * 32 + scanLine;
  262. m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
  263. char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
  264. char *ptr_new = m_rowImage->xim->data;
  265. for (int x = 0; x * 32 < m_width; x++) {
  266. int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
  267. int nBytes = tile_w * bytesPerPixel;
  268. if (grandStep || *pstatus != CHANGED_AGAIN) {
  269. wasChanged = (*pstatus == CHANGED_AGAIN) ?
  270. true : (memcmp(ptr_old, ptr_new, nBytes) != 0);
  271. if (wasChanged) {
  272. if (grandStep || *pstatus == NOT_CHANGED) {
  273. if (tile_w == 32 && tile_h == 32) {
  274. m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
  275. } else {
  276. m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
  277. tile_w, tile_h);
  278. }
  279. m_image->updateRect(m_tileImage, x * 32, y * 32);
  280. rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
  281. m_server->add_changed(rect);
  282. nTilesChanged++;
  283. *pstatus = CHANGED_ONCE;
  284. } else {
  285. *pstatus = CHANGED_AGAIN;
  286. }
  287. } else if (grandStep) {
  288. *pstatus = NOT_CHANGED;
  289. }
  290. }
  291. ptr_old += nBytes;
  292. ptr_new += nBytes;
  293. pstatus++;
  294. }
  295. }
  296. return (nTilesChanged != 0);
  297. }
  298. bool PollingManager::poll_Traditional()
  299. {
  300. if (!m_server)
  301. return false;
  302. int nTilesChanged = 0;
  303. int scanLine = m_pollingOrder[m_pollingStep++ % 32];
  304. int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
  305. int bytesPerLine = m_image->xim->bytes_per_line;
  306. Rect rect;
  307. for (int y = 0; y * 32 < m_height; y++) {
  308. int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
  309. if (scanLine >= tile_h)
  310. break;
  311. int scan_y = y * 32 + scanLine;
  312. m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
  313. char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
  314. char *ptr_new = m_rowImage->xim->data;
  315. for (int x = 0; x * 32 < m_width; x++) {
  316. int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
  317. int nBytes = tile_w * bytesPerPixel;
  318. if (memcmp(ptr_old, ptr_new, nBytes)) {
  319. if (tile_w == 32 && tile_h == 32) {
  320. m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
  321. } else {
  322. m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
  323. tile_w, tile_h);
  324. }
  325. m_image->updateRect(m_tileImage, x * 32, y * 32);
  326. rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
  327. m_server->add_changed(rect);
  328. nTilesChanged++;
  329. }
  330. ptr_old += nBytes;
  331. ptr_new += nBytes;
  332. }
  333. }
  334. return (nTilesChanged != 0);
  335. }
  336. //
  337. // Simplest polling method, from the original x0vncserver of VNC4.
  338. //
  339. bool PollingManager::poll_Dumb()
  340. {
  341. if (!m_server)
  342. return false;
  343. m_image->get(DefaultRootWindow(m_dpy));
  344. Rect rect(0, 0, m_width, m_height);
  345. m_server->add_changed(rect);
  346. // Report that some changes have been detected.
  347. return true;
  348. }
  349. //
  350. // Compute coordinates of the rectangle around the pointer.
  351. //
  352. // ASSUMES: (m_pointerPosKnown != false)
  353. //
  354. void PollingManager::computePointerArea(Rect *r)
  355. {
  356. int x = m_pointerPos.x - 64;
  357. int y = m_pointerPos.y - 64;
  358. int w = 128;
  359. int h = 128;
  360. if (x < 0) {
  361. w += x; x = 0;
  362. }
  363. if (x + w > m_width) {
  364. w = m_width - x;
  365. }
  366. if (y < 0) {
  367. h += y; y = 0;
  368. }
  369. if (y + h > m_height) {
  370. h = m_height - y;
  371. }
  372. r->setXYWH(x, y, w, h);
  373. }
  374. //
  375. // Poll the area under current pointer position. Each pixel of the
  376. // area should be compared. Using such polling option gives higher
  377. // priority to screen area under the pointer.
  378. //
  379. // ASSUMES: (m_server != NULL && m_pointerPosKnown != false)
  380. //
  381. bool PollingManager::pollPointerArea()
  382. {
  383. Rect r;
  384. computePointerArea(&r);
  385. // Shortcuts for coordinates.
  386. int x = r.tl.x, y = r.tl.y;
  387. int w = r.width(), h = r.height();
  388. // Get new pixels.
  389. if (w == 128 && h == 128) {
  390. m_areaImage->get(DefaultRootWindow(m_dpy), x, y);
  391. } else {
  392. m_areaImage->get(DefaultRootWindow(m_dpy), x, y, w, h);
  393. }
  394. // Now, try to minimize the rectangle by cutting out unchanged
  395. // borders (at top and bottom).
  396. //
  397. // FIXME: Perhaps we should work on 32x32 tiles (properly aligned)
  398. // to produce a region instead of a rectangle. If there would
  399. // be just one universal polling algorithm, it could be
  400. // better to integrate pointer area polling into that
  401. // algorithm, instead of a separate pollPointerArea()
  402. // function.
  403. // Shortcuts.
  404. int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
  405. int oldBytesPerLine = m_image->xim->bytes_per_line;
  406. int newBytesPerLine = m_areaImage->xim->bytes_per_line;
  407. char *oldPtr = m_image->xim->data + y * oldBytesPerLine + x * bytesPerPixel;
  408. char *newPtr = m_areaImage->xim->data;
  409. // Check and cut out unchanged rows at the top.
  410. int ty;
  411. for (ty = 0; ty < h; ty++) {
  412. if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
  413. break;
  414. oldPtr += oldBytesPerLine;
  415. newPtr += newBytesPerLine;
  416. }
  417. if (ty == h) {
  418. return false; // no changes at all
  419. }
  420. y += ty; h -= ty;
  421. // Check and cut out unchanged rows at the bottom.
  422. oldPtr = m_image->xim->data + (y+h-1) * oldBytesPerLine + x * bytesPerPixel;
  423. newPtr = m_areaImage->xim->data + (ty+h-1) * newBytesPerLine;
  424. int by;
  425. for (by = 0; by < h - 1; by++) {
  426. if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
  427. break;
  428. oldPtr -= oldBytesPerLine;
  429. newPtr -= newBytesPerLine;
  430. }
  431. h -= by;
  432. // Copy pixels.
  433. m_image->updateRect(m_areaImage, x, y, 0, ty, w, h);
  434. // Report updates to the server.
  435. Rect rect(x, y, x+w, y+h);
  436. m_server->add_changed(rect);
  437. return true;
  438. }
  439. //
  440. // Make video area pattern more regular.
  441. //
  442. // FIXME: Replace the above with a normal comment.
  443. // FIXME: Is the function efficient enough?
  444. //
  445. void PollingManager::adjustVideoArea()
  446. {
  447. char newFlags[m_widthTiles * m_heightTiles];
  448. char *ptr = newFlags;
  449. int x, y;
  450. // DEBUG:
  451. // int nVideoTiles = 0;
  452. for (y = 0; y < m_heightTiles; y++) {
  453. for (x = 0; x < m_widthTiles; x++) {
  454. // DEBUG:
  455. // nVideoTiles += m_videoFlags[y * m_widthTiles + x];
  456. int weightedSum = 0, n;
  457. if (y > 0 && x > 0) {
  458. n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
  459. m_videoFlags[(y-1) * m_widthTiles + (x-1)] +
  460. m_videoFlags[(y-1) * m_widthTiles + x ]);
  461. if (n == 3) {
  462. *ptr++ = 1;
  463. continue;
  464. }
  465. weightedSum += n;
  466. }
  467. if (y > 0 && x < m_widthTiles - 1) {
  468. n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
  469. m_videoFlags[(y-1) * m_widthTiles + (x+1)] +
  470. m_videoFlags[(y-1) * m_widthTiles + x ]);
  471. if (n == 3) {
  472. *ptr++ = 1;
  473. continue;
  474. }
  475. weightedSum += n;
  476. }
  477. if (y < m_heightTiles - 1 && x > 0) {
  478. n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
  479. m_videoFlags[(y+1) * m_widthTiles + (x-1)] +
  480. m_videoFlags[(y+1) * m_widthTiles + x ]);
  481. if (n == 3) {
  482. *ptr++ = 1;
  483. continue;
  484. }
  485. weightedSum += n;
  486. }
  487. if (y < m_heightTiles - 1 && x < m_widthTiles - 1) {
  488. n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
  489. m_videoFlags[(y+1) * m_widthTiles + (x+1)] +
  490. m_videoFlags[(y+1) * m_widthTiles + x ]);
  491. if (n == 3) {
  492. *ptr++ = 1;
  493. continue;
  494. }
  495. weightedSum += n;
  496. }
  497. *ptr++ = (weightedSum <= 3) ? 0 : m_videoFlags[y * m_widthTiles + x];
  498. }
  499. }
  500. /*
  501. /// DEBUG: ------------------------------------------------------
  502. if (nVideoTiles) {
  503. for (y = 0; y < m_heightTiles; y++) {
  504. for (x = 0; x < m_widthTiles; x++) {
  505. printf("%c", m_videoFlags[y * m_widthTiles + x] ? '@' : ':');
  506. }
  507. printf(" ");
  508. for (x = 0; x < m_widthTiles; x++) {
  509. printf("%c", newFlags[y * m_widthTiles + x] ? '@' : ':');
  510. }
  511. printf("\n");
  512. }
  513. printf("\n");
  514. }
  515. /// -------------------------------------------------------------
  516. */
  517. memcpy(m_videoFlags, newFlags, m_widthTiles * m_heightTiles);
  518. }