aboutsummaryrefslogtreecommitdiff
path: root/nxcompshad/Core.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'nxcompshad/Core.cpp')
-rw-r--r--nxcompshad/Core.cpp614
1 files changed, 614 insertions, 0 deletions
diff --git a/nxcompshad/Core.cpp b/nxcompshad/Core.cpp
new file mode 100644
index 000000000..6f9449387
--- /dev/null
+++ b/nxcompshad/Core.cpp
@@ -0,0 +1,614 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include <string.h>
+#include <sys/time.h>
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include "Core.h"
+#include "Logger.h"
+
+const int CorePoller::maxSliceHeight_ = 20;
+const int CorePoller::minSliceHeight_ = 3;
+
+const char CorePoller::interlace_[] =
+{
+ 0, 16,
+ 8, 24,
+ 4, 20, 12, 28,
+ 2, 18, 10, 26, 6, 22, 14, 30,
+ 1, 17,
+ 9, 25,
+ 5, 21, 13, 29,
+ 3, 19, 11, 27, 7, 23, 15, 31
+};
+
+CorePoller::CorePoller(Input *input, Display *display) : input_(input)
+{
+ logTrace("CorePoller::CorePoller");
+
+ buffer_ = NULL;
+ lastUpdatedRegion_ = NULL;
+ lineStatus_ = NULL;
+ linePriority_ = NULL;
+ lefts_ = NULL;
+ rights_ = NULL;
+}
+
+CorePoller::~CorePoller()
+{
+ logTrace("CorePoller::~CorePoller");
+
+ if (buffer_ != NULL)
+ {
+ delete [] buffer_;
+
+ buffer_ = NULL;
+ }
+
+ if (lastUpdatedRegion_ != NULL)
+ {
+ XDestroyRegion(lastUpdatedRegion_);
+
+ lastUpdatedRegion_ = NULL;
+ }
+
+ if (lineStatus_ != NULL)
+ {
+ delete [] lineStatus_;
+
+ lineStatus_ = NULL;
+ }
+
+ if (linePriority_ != NULL)
+ {
+ delete [] linePriority_;
+
+ linePriority_ = NULL;
+ }
+
+ if (lefts_ != NULL)
+ {
+ delete [] lefts_;
+
+ lefts_ = NULL;
+ }
+
+ if (rights_ != NULL)
+ {
+ delete [] rights_;
+
+ rights_ = NULL;
+ }
+}
+
+int CorePoller::init()
+{
+ logTrace("CorePoller::init");
+
+ createFrameBuffer();
+
+ if (buffer_ == NULL)
+ {
+ logError("CorePoller::init", ESET(ENOMEM));
+
+ return -1;
+ }
+
+ logTest("CorePoller::init", "Allocated frame buffer at [%p] for [%d] bytes.",
+ buffer_, bpl_ * height_);
+
+ if (lastUpdatedRegion_ != NULL)
+ {
+ XDestroyRegion(lastUpdatedRegion_);
+
+ lastUpdatedRegion_ = NULL;
+ }
+
+ lastUpdatedRegion_ = XCreateRegion();
+
+ if (lineStatus_ != NULL)
+ {
+ delete[] lineStatus_;
+ }
+
+ lineStatus_ = new LineStatus[height_ + 1];
+
+ if (lineStatus_ == NULL)
+ {
+ logError("CorePoller::init", ESET(ENOMEM));
+
+ return -1;
+ }
+
+ //
+ // We need this boundary element to
+ // speed up the algo.
+ //
+
+ if (linePriority_ != NULL)
+ {
+ delete[] linePriority_;
+ }
+
+ linePriority_ = new int [height_ + 1];
+
+ if (linePriority_ == NULL)
+ {
+ logError("CorePoller::init", ESET(ENOMEM));
+
+ return -1;
+ }
+
+ for (unsigned int i = 0; i < height_; i++)
+ {
+ linePriority_[i] = HIGHEST_PRIORITY;
+ }
+
+ if (lefts_ != NULL)
+ {
+ delete[] lefts_;
+ }
+
+ lefts_ = new int [height_];
+
+ if (rights_ != NULL)
+ {
+ delete[] rights_;
+ }
+
+ rights_ = new int [height_];
+
+ for (unsigned int i = 0; i < height_; i++)
+ {
+ rights_[i] = lefts_[i] = 0;
+ }
+
+ return 1;
+}
+
+int CorePoller::isChanged(int (*checkIfInputCallback)(void *), void *arg, int *suspended)
+{
+ logTrace("CorePoller::isChanged");
+
+#if defined(__CYGWIN32__) || defined(WIN32)
+
+ checkDesktop();
+
+#endif
+
+#if !defined(__CYGWIN32__) && !defined(WIN32)
+
+ if (mirror_ == 1)
+ {
+ int result = mirrorChanges_;
+
+ mirrorChanges_ = 0;
+
+ return result;
+ }
+
+#endif
+
+ logDebug("CorePoller:isChanged", "Going to use default polling algorithm.\n");
+
+ //
+ // In order to allow this function to
+ // be suspended and resumed later, we
+ // need to save these two status vars.
+ //
+
+ static int idxIlace = 0;
+ static int curLine = 0;
+
+
+ const long timeout = 50;
+ long oldTime;
+ long newTime;
+ struct timeval ts;
+
+ gettimeofday(&ts, NULL);
+
+ oldTime = ts.tv_sec * 1000 + ts.tv_usec / 1000;
+
+ if (curLine == 0) // && idxIlace == 0 ?
+ {
+ for (unsigned int i = 0; i < height_; i++)
+ {
+ lineStatus_[i] = LINE_NOT_CHECKED;
+ }
+ }
+
+ int foundChanges = 0;
+
+ foundChanges = 0;
+
+ int curIlace = interlace_[idxIlace];
+
+ bool moveBackward = false;
+
+ logDebug("CorePoller::isChanged", "Interlace index [%d] interlace [%d].", idxIlace, curIlace);
+
+ for (; curLine < (int) height_; curLine++)
+ {
+ logDebug("CorePoller::isChanged", "Analizing line [%d] move backward [%d] status [%d] priority [%d].",
+ curLine, moveBackward, lineStatus_[curIlace], linePriority_[curLine]);
+
+ //
+ // Ask the caller if the polling have to be suspended.
+ //
+
+ if ((*checkIfInputCallback)(arg) == 1)
+ {
+ *suspended = 1;
+
+ break;
+ }
+
+ //
+ // Suspend if too much time is elapsed.
+ //
+
+ gettimeofday(&ts, NULL);
+
+ newTime = ts.tv_sec * 1000 + ts.tv_usec / 1000;
+
+ if (newTime - oldTime >= timeout)
+ {
+ *suspended = 1;
+
+ break;
+ }
+
+ oldTime = newTime;
+
+ if (lineStatus_[curLine] != LINE_NOT_CHECKED)
+ {
+ continue;
+ }
+
+ if (moveBackward)
+ {
+ moveBackward = false;
+ }
+ else
+ {
+ switch (linePriority_[curLine])
+ {
+ case 1:
+ case 29:
+ {
+ //
+ // It was a priority,
+ // but now it may not be.
+ //
+ }
+ case 31:
+ {
+ //
+ // Not a priority, still isn't.
+ //
+
+ linePriority_[curLine] = NOT_PRIORITY;
+
+ break;
+ }
+ case 0:
+ {
+ //
+ // Make it a priority.
+ //
+
+ linePriority_[curLine] = PRIORITY;
+
+ break;
+ }
+ default:
+ {
+ linePriority_[curLine]--;
+
+ break;
+ }
+ }
+
+ if ((linePriority_[curLine] > PRIORITY) && ((curLine & 31) != curIlace))
+ {
+ continue;
+ }
+ }
+
+ XRectangle rect = {0, curLine, width_, 1};
+
+ char *buffer;
+
+ logDebug("CorePoller::isChanged", "Checking line [%d].", curLine);
+
+ if ((buffer = getRect(rect)) == NULL)
+ {
+ logDebug("CorePoller::isChanged", "Failed to retrieve line [%d].", curLine);
+
+ return -1;
+ }
+
+ if (memcmp(buffer, buffer_ + curLine * bpl_, bpl_) == 0 || differ(buffer, rect) == 0)
+ {
+ logDebug("CorePoller::isChanged", "Data buffer didn't change.");
+
+ lineStatus_[curLine] = LINE_NOT_CHANGED;
+
+ continue;
+ }
+
+ rect.x = lefts_[rect.y];
+ rect.width = rights_[rect.y] - lefts_[rect.y] + 1;
+
+ update(buffer + rect.x * bpp_, rect);
+
+ foundChanges = 1;
+
+ lineStatus_[curLine] = LINE_HAS_CHANGED;
+
+ //
+ // Wake up the next line.
+ //
+
+ if (linePriority_[curLine + 1] > PRIORITY)
+ {
+ linePriority_[curLine + 1] = HIGHEST_PRIORITY;
+ }
+
+ //
+ // Give this line priority.
+ //
+
+ linePriority_[curLine] = HIGHEST_PRIORITY;
+
+ //
+ // Wake up previous line.
+ //
+
+ if (curLine > 0 && lineStatus_[curLine - 1] == LINE_NOT_CHECKED)
+ {
+ moveBackward = true;
+ curLine -= 2;
+ }
+ }
+
+ //
+ // Execution reached the end of loop.
+ //
+
+ if (curLine == (int) height_)
+ {
+ idxIlace = (idxIlace + 1) % 32;
+
+ curLine = 0;
+ }
+
+ //
+ // Create the region of changed pixels.
+ //
+
+ if (foundChanges)
+ {
+ int start, last, curLine, left, right;
+
+ for (curLine = 0; curLine < (int) height_; curLine++)
+ {
+ if (lineStatus_[curLine] == LINE_HAS_CHANGED)
+ {
+ break;
+ }
+ }
+
+ start = curLine;
+ last = curLine;
+
+ left = lefts_[curLine];
+ right = rights_[curLine];
+ curLine++;
+
+ while (1)
+ {
+ for (; curLine < (int) height_; curLine++)
+ {
+ if (lineStatus_[curLine] == LINE_HAS_CHANGED)
+ {
+ break;
+ }
+ }
+
+ if (curLine == (int) height_)
+ {
+ break;
+ }
+
+ if ((curLine - last > minSliceHeight_) || (last - start > maxSliceHeight_))
+ {
+ XRectangle rect = {left, start, right - left + 1, last - start + 1};
+
+ XUnionRectWithRegion(&rect, lastUpdatedRegion_, lastUpdatedRegion_);
+
+ start = curLine;
+ left = lefts_[curLine];
+ right = rights_[curLine];
+ }
+ else
+ {
+ if (lefts_[curLine] < left)
+ {
+ left = lefts_[curLine];
+ }
+
+ if (rights_[curLine] > right)
+ {
+ right = rights_[curLine];
+ }
+ }
+
+ last = curLine;
+
+ curLine++;
+ }
+
+ //
+ // Send last block.
+ //
+
+ if (last >= start)
+ {
+ XRectangle rect = {left, start, right - left + 1, last - start + 1};
+
+ XUnionRectWithRegion(&rect, lastUpdatedRegion_, lastUpdatedRegion_);
+ }
+ }
+
+ return foundChanges;
+}
+
+int CorePoller::differ(char *buffer, XRectangle r)
+{
+ logTrace("CorePoller::differ");
+
+ int bpl = bpp_ * r.width;
+ int i;
+ char *pBuf;
+ char *pFb;
+
+ pBuf = (buffer);
+ pFb = (buffer_ + r.x + r.y * bpl_);
+
+ for (i = 0; i < bpl; i++)
+ {
+ if (*pFb++ != *pBuf++)
+ {
+ lefts_[r.y] = i / bpp_;
+
+ break;
+ }
+ }
+
+ if (i == bpl)
+ {
+ return 0;
+ }
+
+ pBuf = (buffer) + bpl - 1;
+ pFb = (buffer_ + r.x + r.y * bpl_) + bpl - 1;
+
+ int j = i - 1;
+
+ for (i = bpl - 1; i > j; i--)
+ {
+ if (*pFb-- != *pBuf--)
+ {
+ rights_[r.y] = i / bpp_;
+
+ break;
+ }
+ }
+
+ return 1;
+}
+
+void CorePoller::update(char *src, XRectangle r)
+{
+ logTrace("CorePoller::update");
+
+ char *dst = buffer_ + r.x * bpp_ + r.y * bpl_;
+ int bpl = bpp_ * r.width;
+
+ for (unsigned int i = 0; i < r.height; i++)
+ {
+ if(((r.x * bpp_ + r.y * bpl_) + bpl) > (bpl_ * height_))
+ {
+ //
+ // Out of bounds. Maybe a resize is going on.
+ //
+
+ continue;
+ }
+
+ memcpy(dst, src, bpl);
+
+ src += bpl;
+
+ dst += bpl_;
+ }
+}
+
+void CorePoller::handleEvent(Display *display, XEvent *event)
+{
+ logTrace("CorePoller::handleEvent");
+
+ switch (event -> type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ {
+ handleKeyboardEvent(display, event);
+ break;
+ }
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ {
+ handleMouseEvent(display, event);
+ break;
+ }
+ default:
+ {
+ logTest("CorePoller::handleEvent", "Handling unexpected event [%d] from display [%p].",
+ event -> type, display);
+ break;
+ }
+ }
+}
+
+void CorePoller::handleWebKeyEvent(KeySym keysym, Bool isKeyPress)
+{
+ logTrace("CorePoller::handleWebKeyEvent");
+
+ handleWebKeyboardEvent(keysym, isKeyPress);
+}
+
+void CorePoller::handleInput()
+{
+ while (input_ -> checkIfEvent())
+ {
+ Display *display = input_ -> currentDisplay();
+ XEvent *event = input_ -> popEvent();
+
+ handleEvent(display, event);
+
+ delete event;
+ }
+}
+
+void CorePoller::createFrameBuffer()
+{
+ logTrace("CorePoller::createFrameBuffer");
+
+ if (buffer_ == NULL)
+ {
+ buffer_ = new char[bpl_ * height_];
+ }
+}