diff options
Diffstat (limited to 'nxcompshad/src')
-rw-r--r-- | nxcompshad/src/Core.cpp | 626 | ||||
-rw-r--r-- | nxcompshad/src/Core.h | 212 | ||||
-rw-r--r-- | nxcompshad/src/Input.cpp | 179 | ||||
-rw-r--r-- | nxcompshad/src/Input.h | 99 | ||||
-rw-r--r-- | nxcompshad/src/Logger.cpp | 128 | ||||
-rw-r--r-- | nxcompshad/src/Logger.h | 167 | ||||
-rw-r--r-- | nxcompshad/src/Makefile.am | 45 | ||||
-rw-r--r-- | nxcompshad/src/Manager.cpp | 264 | ||||
-rw-r--r-- | nxcompshad/src/Manager.h | 123 | ||||
-rw-r--r-- | nxcompshad/src/Misc.h | 50 | ||||
-rw-r--r-- | nxcompshad/src/Poller.h | 39 | ||||
-rw-r--r-- | nxcompshad/src/Regions.h | 43 | ||||
-rw-r--r-- | nxcompshad/src/Shadow.cpp | 482 | ||||
-rw-r--r-- | nxcompshad/src/Updater.cpp | 395 | ||||
-rw-r--r-- | nxcompshad/src/Updater.h | 103 | ||||
-rw-r--r-- | nxcompshad/src/Win.cpp | 1149 | ||||
-rw-r--r-- | nxcompshad/src/Win.h | 232 | ||||
-rw-r--r-- | nxcompshad/src/X11.cpp | 1598 | ||||
-rw-r--r-- | nxcompshad/src/X11.h | 139 | ||||
-rw-r--r-- | nxcompshad/src/X11/include/XTest_nxcompshad.h | 71 | ||||
-rw-r--r-- | nxcompshad/src/X11/include/Xdamage_nxcompshad.h | 92 | ||||
-rw-r--r-- | nxcompshad/src/X11/include/Xrandr_nxcompshad.h | 80 |
22 files changed, 6316 insertions, 0 deletions
diff --git a/nxcompshad/src/Core.cpp b/nxcompshad/src/Core.cpp new file mode 100644 index 000000000..de5f9a897 --- /dev/null +++ b/nxcompshad/src/Core.cpp @@ -0,0 +1,626 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#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_]; + } +} diff --git a/nxcompshad/src/Core.h b/nxcompshad/src/Core.h new file mode 100644 index 000000000..91f3f1e22 --- /dev/null +++ b/nxcompshad/src/Core.h @@ -0,0 +1,212 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef CorePoller_H +#define CorePoller_H + +#include <stdio.h> + +#include "Logger.h" +#include "Regions.h" +#include "Input.h" + +typedef enum{ + LINE_HAS_CHANGED, + LINE_NOT_CHECKED, + LINE_NOT_CHANGED +} LineStatus; + +typedef enum{ + HIGHEST_PRIORITY = 0, + PRIORITY = 30, + NOT_PRIORITY = 90 +} LinePriority; + +class CorePoller +{ + public: + + CorePoller(Input*, Display*); + + virtual ~CorePoller(); + + virtual int init(); + + unsigned int width() const; + + unsigned int height() const; + + unsigned char depth() const; + + int isChanged(int (*)(void*), void *, int *); + + char *getFrameBuffer() const; + + void destroyFrameBuffer(); + + void createFrameBuffer(); + + Region lastUpdatedRegion(); + + Region getLastUpdatedRegion(); + + void handleInput(); + + void handleEvent(Display *, XEvent *); + + void handleWebKeyEvent(KeySym keysym, Bool isKeyPress); + + Display *getShadowDisplay(); + + void setShadowDisplay(Display *shadowDisplay); + + protected: + + unsigned int bpp_; + + unsigned int bpl_; + + unsigned int width_; + + unsigned int height_; + + int depth_; + + char *buffer_; + + unsigned long redMask_; + unsigned long greenMask_; + unsigned long blueMask_; + unsigned long colorMask_[3]; + + char mirror_; + + char mirrorChanges_; + + virtual int updateShadowFrameBuffer(void) = 0; + + virtual char *getRect(XRectangle r) = 0; + + int imageByteOrder_; + + #ifdef __CYGWIN32__ + virtual char checkDesktop(void) = 0; + #endif + + Display *shadowDisplay_; + + void update(char *src, XRectangle r); + + Region lastUpdatedRegion_; + + private: + + virtual void handleKeyboardEvent(Display *, XEvent *) = 0; + + virtual void handleWebKeyboardEvent(KeySym keysym, Bool isKeyPress) = 0; + + virtual void handleMouseEvent(Display *, XEvent *) = 0; + + Input *input_; + + static const int maxSliceHeight_; + static const int minSliceHeight_; + + LineStatus *lineStatus_; + int *linePriority_; + + static const char interlace_[]; + + int *lefts_; + int *rights_; + + // FIXME: Make them friend. + + int differ(char *src, XRectangle r); +}; + +inline unsigned int CorePoller::width() const +{ + return width_; +} + +inline unsigned int CorePoller::height() const +{ + return height_; +} + +inline unsigned char CorePoller::depth() const +{ + return depth_; +} + +inline char *CorePoller::getFrameBuffer() const +{ + return buffer_; +} + +inline void CorePoller::destroyFrameBuffer() +{ + if (buffer_ != NULL) + { + delete[] buffer_; + buffer_ = NULL; + } +} + +inline Region CorePoller::lastUpdatedRegion() +{ + Region region = lastUpdatedRegion_; + + lastUpdatedRegion_ = XCreateRegion(); + + if (lastUpdatedRegion_ == NULL) + { + logError("CorePoller::lastUpdatedRegion", ESET(ENOMEM)); + + lastUpdatedRegion_ = region; + + return NULL; + } + + return region; +} + +inline Region CorePoller::getLastUpdatedRegion() +{ + return lastUpdatedRegion_; +} + +inline Display *CorePoller::getShadowDisplay() +{ + return shadowDisplay_ ; +} + +inline void CorePoller::setShadowDisplay(Display *shadowDisplay) +{ + shadowDisplay_ = shadowDisplay; +} + +#endif /* CorePoller_H */ diff --git a/nxcompshad/src/Input.cpp b/nxcompshad/src/Input.cpp new file mode 100644 index 000000000..8de74c3c3 --- /dev/null +++ b/nxcompshad/src/Input.cpp @@ -0,0 +1,179 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include "Input.h" +#include "Logger.h" + +Input::Input() +{ + logTrace("Input::Input"); + + eventsHead_ = NULL; + eventsTail_ = NULL; + keymap_ = NULL; +} + +Input::~Input() +{ + logTrace("Input::~Input"); + + Event *head = eventsHead_; + + while (head) + { + Event *next = head -> next; + + delete head -> event; + delete head; + + head = next; + } + + if (keymap_ != NULL) + { + logDebug("Input::~Input", "Delete keymap_ [%p].", keymap_); + + delete [] keymap_; + } +} + +void Input::pushEvent(Display *display, XEvent *event) +{ + Event *tail = new Event; + + if (tail == NULL) + { + logError("Input::pushEvent", ESET(ENOMEM)); + + return; + } + + tail -> next = NULL; + tail -> display = display; + tail -> event = event; + + if (eventsHead_ == NULL) + { + eventsHead_ = tail; + } + else + { + eventsTail_ -> next = tail; + } + + eventsTail_ = tail; +} + +XEvent *Input::popEvent() +{ + Event *head = eventsHead_; + + if (head == NULL) + { + return 0; + } + + XEvent *event = head -> event; + + eventsHead_ = head -> next; + + delete head; + + if (eventsHead_ == NULL) + { + eventsTail_ = NULL; + } + + return event; +} + +int Input::removeAllEvents(Display *display) +{ + logTrace("Input::removeAllEvents"); + + int nRemoved = 0; + + Event *current = eventsHead_; + + while (current) + { + if (display == current -> display) + { + // + // Update head of list. + // + + if (current == eventsHead_) + { + eventsHead_ = current -> next; + } + + // + // Update tail of list. + // + + if (current == eventsTail_) + { + eventsTail_ = eventsHead_; + + while (eventsTail_ && eventsTail_ -> next) + { + eventsTail_ = eventsTail_ -> next; + } + } + + // + // Remove event. + // + + Event *next = current -> next; + + delete current -> event; + delete current; + + current = next; + + nRemoved++; + } + else + { + current = current -> next; + } + } + + return nRemoved; +} + diff --git a/nxcompshad/src/Input.h b/nxcompshad/src/Input.h new file mode 100644 index 000000000..cbba029a9 --- /dev/null +++ b/nxcompshad/src/Input.h @@ -0,0 +1,99 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Input_H +#define Input_H + +#include <nx-X11/Xlib.h> + +typedef struct Event +{ + struct Event *next; + Display *display; + XEvent *event; +} Event; + +class Input +{ + public: + + Input(); + + ~Input(); + + int checkIfEvent(); + + void pushEvent(Display *, XEvent *); + + XEvent *popEvent(); + Display *currentDisplay(); + + int removeAllEvents(Display *); + + void setKeymap(char *keymap); + char *getKeymap(); + + void setShadowDisplayName(char *shadowDisplayName); + char *getShadowDisplayName(); + + private: + + Event *eventsHead_; + Event *eventsTail_; + char *keymap_; + char *shadowDisplayName_; +}; + +inline Display *Input::currentDisplay() +{ + return eventsHead_ ? eventsHead_ -> display : NULL; +} + +inline int Input::checkIfEvent() +{ + return (eventsHead_ != NULL); +} + +inline void Input::setKeymap(char *keymap) +{ + keymap_ = keymap; +} + +inline char *Input::getKeymap() +{ + return keymap_; +} + +inline void Input::setShadowDisplayName(char *shadowDisplayName) +{ + shadowDisplayName_ = shadowDisplayName; +} + +inline char *Input::getShadowDisplayName() +{ + return shadowDisplayName_; +} + +#endif /* Input_H */ diff --git a/nxcompshad/src/Logger.cpp b/nxcompshad/src/Logger.cpp new file mode 100644 index 000000000..9648509b8 --- /dev/null +++ b/nxcompshad/src/Logger.cpp @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> + +#define PANIC +#define WARNING +#undef TEST +#define DEBUG + +#include "Misc.h" +#include "Logger.h" + +Logger logger; + +void Logger::user(const char *format, va_list arguments) +{ + char string[1024]; + + vsnprintf(string, 1024, format, arguments); + + fprintf(stderr, "%s\n", string); +} + +void Logger::error(const char *name, int error) +{ + fprintf(stderr, "PANIC! %s: Failed with code %d: %s\n", + name, error, strerror(error)); +} + +void Logger::warning(const char *name, const char *format, va_list arguments) +{ + char string[1024]; + + vsnprintf(string, 1024, format, arguments); + + fprintf(stderr, "%s: WARNING! %s\n", name, string); +} + +void Logger::test(const char *name, const char *format, va_list arguments) +{ + char string[1024]; + + vsnprintf(string, 1024, format, arguments); + + fprintf(stderr, "%s: %s\n", name, string); +} + +void Logger::trace(const char *name) +{ + fprintf(stderr, "%s\n", name); +} + +void Logger::debug(const char *name, const char *format, va_list arguments) +{ + char string[1024]; + + vsnprintf(string, 1024, format, arguments); + + fprintf(stderr, "%s: %s\n", name, string); +} + +void Logger::dump(const char *name, const char *data, int size) +{ + fprintf(stderr, "%s: Dumping %d bytes of data at %p\n", + name, size, data); + + for (int i = 0; i < size;) + { + fprintf(stderr, "[%d]\t", i); + + int t = i; + + for (unsigned int ii = 0; i < size && ii < 8; i++, ii++) + { + fprintf(stderr, "%02x/%d\t", data[i] & 0xff, data[i]); + } + + for (unsigned int ii = i % 8; ii > 0 && ii < 8; ii++) + { + fprintf(stderr, "\t"); + } + + i = t; + + for (unsigned int ii = 0; i < size && ii < 8; i++, ii++) + { + if (isprint(data[i])) + { + fprintf(stderr, "%c", data[i]); + } + else + { + fprintf(stderr, "."); + } + } + + fprintf(stderr, "\n"); + } +} diff --git a/nxcompshad/src/Logger.h b/nxcompshad/src/Logger.h new file mode 100644 index 000000000..eba81f642 --- /dev/null +++ b/nxcompshad/src/Logger.h @@ -0,0 +1,167 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Logger_H +#define Logger_H + +#include <cerrno> +#include <cstdarg> + +// +// Error handling macros. +// + +#define ESET(e) (errno = (e)) +#define EGET() (errno) +#define ESTR() strerror(errno) + +extern class Logger logger; + +class Logger +{ + public: + + void user(const char *format, va_list arguments); + + void error(const char *name, int error); + + void warning(const char *name, const char *format, va_list arguments); + + void test(const char *name, const char *format, va_list arguments); + + void trace(const char *name); + + void debug(const char *name, const char *format, va_list arguments); + + void dump(const char *name, const char *data, int size); +}; + +static inline void logUser(const char *format, ...) \ + __attribute__((format(printf, 1, 2))) __attribute__((__unused__)); + +static inline void logError(const char *name, int error) \ + __attribute__((__unused__)); + +static inline void logWarning(const char *name, const char *format, ...) \ + __attribute__((__unused__)); + +static inline void logTest(const char *name, const char *format, ...) \ + __attribute__((format(printf, 2, 3))) __attribute__((__unused__)); + +static inline void logTrace(const char *name) \ + __attribute__((__unused__)); + +static inline void logDebug(const char *name, const char *format, ...) \ + __attribute__((format(printf, 2, 3))) __attribute__((__unused__)); + +static inline void logDump(const char *name, const char *data, int size) \ + __attribute__((__unused__)); + +static inline void logUser(const char *format, ...) +{ + va_list arguments; + + va_start(arguments, format); + + logger.user(format, arguments); + + va_end(arguments); +} + +static inline void logError(const char *name, int error) +{ + #if defined(DEBUG) || defined(TEST) || \ + defined(WARNING) || defined(PANIC) + + logger.error(name, error); + + #endif +} + +static inline void logWarning(const char *name, const char *format, ...) +{ + #if defined(DEBUG) || defined(TEST) || \ + defined(WARNING) + + va_list arguments; + + va_start(arguments, format); + + logger.warning(name, format, arguments); + + va_end(arguments); + + #endif +} + +static inline void logTest(const char *name, const char *format, ...) +{ + #if defined(TEST) + + va_list arguments; + + va_start(arguments, format); + + logger.test(name, format, arguments); + + va_end(arguments); + + #endif +} + +static inline void logTrace(const char *name) +{ + #if defined(DEBUG) + + logger.trace(name); + + #endif +} + +static inline void logDebug(const char *name, const char *format, ...) +{ + #if defined(DEBUG) + + va_list arguments; + + va_start(arguments, format); + + logger.debug(name, format, arguments); + + va_end(arguments); + + #endif +} + +static inline void logDump(const char *name, const char *data, int size) +{ + #if defined(TEST) + + logger.dump(name, data, size); + + #endif +} + +#endif /* Logger_H */ diff --git a/nxcompshad/src/Makefile.am b/nxcompshad/src/Makefile.am new file mode 100644 index 000000000..c7f45ba27 --- /dev/null +++ b/nxcompshad/src/Makefile.am @@ -0,0 +1,45 @@ +NULL = + +lib_LTLIBRARIES = libXcompshad.la + +libXcompshad_la_SOURCES = \ + Core.cpp \ + Input.cpp \ + Logger.cpp \ + Manager.cpp \ + Shadow.cpp \ + Updater.cpp \ + Win.cpp \ + X11.cpp \ + $(NULL) + +libXcompshad_la_LIBADD = \ + @Xext_LIBS@ \ + @Xdamage_LIBS@ \ + @Xrandr_LIBS@ \ + @Xtst_LIBS@ \ + -L$(top_srcdir)/../nx-X11/exports/lib -lNX_X11 \ + $(NULL) + +AM_CFLAGS = \ + $(Xext_CFLAGS) \ + $(Xdamage_CFLAGS) \ + $(Xrandr_CFLAGS) \ + $(Xtst_CFLAGS) \ + $(NULL) + +AM_CXXFLAGS = \ + $(BASE_CXXFLAGS) \ + $(NULL) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/../nx-X11/exports/include \ + $(NULL) + +libXcompshad_la_LDFLAGS = -version-number @LT_COMPSHAD_VERSION@ -no-undefined + +libXcompshadincludedir = $(includedir)/nx +libXcompshadinclude_HEADERS = \ + $(top_srcdir)/include/Shadow.h \ + $(NULL) diff --git a/nxcompshad/src/Manager.cpp b/nxcompshad/src/Manager.cpp new file mode 100644 index 000000000..6e3f6b1b0 --- /dev/null +++ b/nxcompshad/src/Manager.cpp @@ -0,0 +1,264 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <nx-X11/Xlib.h> +#include <nx-X11/Xutil.h> +#include <nx-X11/keysym.h> +#include <string.h> + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include "Manager.h" +#include "Logger.h" + +UpdateManager::UpdateManager(int w, int h, char *f, Input *i) + : width_(w), height_(h), frameBuffer_(f), input_(i) +{ + logTrace("UpdateManager::UpdateManager"); + + nUpdater = 0; + updaterVector = NULL; + updateManagerRegion_ = NULL; +} + +UpdateManager::~UpdateManager() +{ + logTrace("UpdateManager::~UpdateManager"); + + for (int i = 0; i < nUpdater; i++) + { + delete updaterVector[i]; + } + + delete [] updaterVector; +} + +Updater *UpdateManager::createUpdater(char *displayName, Display *display) +{ + Updater *updater = new Updater(displayName, display); + + if (updater == NULL) + { + logError("UpdateManager::createUpdater", ESET(ENOMEM)); + + return NULL; + } + + if (updater -> init(width_, height_, frameBuffer_, input_) == -1) + { + logError("UpdateManager::createUpdater", EGET()); + + delete updater; + + return NULL; + } + + return updater; +} + +UpdaterHandle UpdateManager::addUpdater(char *displayName, Display *display) +{ + Updater *newUpdater = createUpdater(displayName, display); + + if (newUpdater == NULL) + { + logError("UpdateManager::addUpdater", EGET()); + + return NULL; + } + + Updater **newUpdaterVector = new Updater*[nUpdater + 1]; + + if (newUpdaterVector == NULL) + { + logError("UpdateManager::addUpdater", ESET(ENOMEM)); + + delete newUpdater; + + return NULL; + } + + for (int i = 0; i < nUpdater; i++) + { + newUpdaterVector[i] = updaterVector[i]; + } + + newUpdaterVector[nUpdater] = newUpdater; + + delete [] updaterVector; + + updaterVector = newUpdaterVector; + + nUpdater++; + + logTest("UpdateManager::AddUpdater", "Number of updaters [%d].", nUpdater); + + return reinterpret_cast<UpdaterHandle>(newUpdater); +} + +int UpdateManager::removeAllUpdaters() +{ + logTest("UpdateManager::removeAllUpdaters", "Number of updaters [%d].", nUpdater); + + int nullUpdaters = 0; + + for (int i = nUpdater; i > 0; i--) + { + if (removeUpdater(reinterpret_cast<UpdaterHandle>(updaterVector[i - 1])) == 0) + { + nullUpdaters++; + } + } + + if (nUpdater == 0) + { + return 1; + } + + if (nUpdater == nullUpdaters) + { + logTest("UpdateManager::removeAllUpdaters", "Ignored null records in Updater vector."); + + return 0; + } + + logTest("UpdateManager::removeAllUpdaters", "Failed to remove some updaters."); + + return -1; +} + +int UpdateManager::removeUpdater(UpdaterHandle handle) +{ + Updater * const updater = (Updater*) handle; + + logTest("UpdateManager::removeUpdater", "Removing Updater [%p].", updater); + + if (updater == NULL) + { + return 0; + } + + for (int i = 0; i < nUpdater; i++) + { + if (updater == updaterVector[i]) + { + updaterVector[i] = updaterVector[nUpdater - 1]; + + nUpdater--; + + delete updater; + + return 1; + } + } + + logTest("UpdateManager::removeUpdater", "Couldn't find Updater [%p].", updater); + + return -1; +} + +void UpdateManager::addRegion(Region region) +{ + logTrace("UpdateManager::addRegion"); + + for (int i = 0; i < nUpdater; i++) + { + updaterVector[i] -> addRegion(region); + } + + XDestroyRegion(region); +} + +void UpdateManager::update() +{ + logTrace("UpdateManager::update"); + + for (int i = 0; i < nUpdater; i++) + { + /*updaterVector[i] -> update();*/ + if (updaterVector[i] -> getUpdateRegion()) + { + logDebug("UpdateManager::update", "pRegion [%p] rect[%ld].", + updaterVector[i] -> getUpdateRegion(), (updaterVector[i] -> getUpdateRegion()) -> numRects); + + updateManagerRegion_ = updaterVector[i] -> getUpdateRegion(); + // + // FIXME: Remove me. + // + for (int j = 0; j < updateManagerRegion_ -> numRects; j++) + { + int x = updateManagerRegion_ -> rects[j].x1; + int y = updateManagerRegion_ -> rects[j].y1; + unsigned int width = updateManagerRegion_ -> rects[j].x2 - updateManagerRegion_ -> rects[j].x1; + unsigned int height = updateManagerRegion_ -> rects[j].y2 - updateManagerRegion_ -> rects[j].y1; + logDebug("UpdateManager::update", "x[%d]y[%d]width[%u]height[%u], updateManagerRegion_[%p]", + x, y, width, height, updateManagerRegion_); + } + } + } +} + +void UpdateManager::handleInput() +{ + logTrace("UpdateManager::handleInput"); + + for (int i = 0; i < nUpdater; i++) + { + try + { + updaterVector[i] -> handleInput(); + } + catch (UpdaterClosing u) + { + logTest("UpdateManager::handleInput", "Catched exception UpdaterClosing()."); + + removeUpdater((UpdaterHandle)updaterVector[i]); + + // + // Now the i-element of the updaterVector + // is changed. We don't want to skip it. + // + + i--; + } + } +} + +void UpdateManager::newRegion() +{ + logTrace("UpdateManager::newRegion"); + + for (int i = 0; i < nUpdater; i++) + { + updaterVector[i] -> newRegion(); + } +} diff --git a/nxcompshad/src/Manager.h b/nxcompshad/src/Manager.h new file mode 100644 index 000000000..92a82cd32 --- /dev/null +++ b/nxcompshad/src/Manager.h @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef UpdateManager_H +#define UpdateManager_H + +#include <nx-X11/Xlib.h> + +#include "Updater.h" +#include "Regions.h" +#include "Input.h" + +typedef char* UpdaterHandle; + +class UpdateManager +{ + public: + + UpdateManager(int, int, char *, Input *); + + ~UpdateManager(); + + void handleInput(); + + void addRegion(Region); + + void update(); + + UpdaterHandle addUpdater(char *displayName, Display *display); + + int removeUpdater(UpdaterHandle); + + int removeAllUpdaters(); + + int numberOfUpdaters(); + + int getWidth(); + + int getHeight(); + + char *getBuffer(); + + Region getUpdateManagerRegion(); + + void destroyUpdateManagerRegion(); + + void newRegion(); + + private: + + Updater *createUpdater(char *displayName, Display *display); + + int width_; + int height_; + char *frameBuffer_; + Input *input_; + + int nUpdater; + + Updater **updaterVector; + + Region updateManagerRegion_; + +}; + +inline int UpdateManager::numberOfUpdaters() +{ + return nUpdater; +} + +inline int UpdateManager::getWidth() +{ + return width_; +} + +inline int UpdateManager::getHeight() +{ + return height_; +} + +inline char *UpdateManager::getBuffer() +{ + return frameBuffer_; +} + +inline Region UpdateManager::getUpdateManagerRegion() +{ + return updateManagerRegion_; +} + +inline void UpdateManager::destroyUpdateManagerRegion() +{ + if (updateManagerRegion_ != NULL) + { + XDestroyRegion(updateManagerRegion_); + + updateManagerRegion_ = NULL; + } +} + +#endif /* UpdateManager_H */ diff --git a/nxcompshad/src/Misc.h b/nxcompshad/src/Misc.h new file mode 100644 index 000000000..9386dc080 --- /dev/null +++ b/nxcompshad/src/Misc.h @@ -0,0 +1,50 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Misc_H +#define Misc_H + +#include <iostream> + +#include <cerrno> +#include <cstring> + +using namespace std; + +// +// Error handling macros. +// + +#define ESET(e) (errno = (e)) +#define EGET() (errno) +#define ESTR() strerror(errno) + +// +// Log file. +// + +extern ostream *logofs; + +#endif /* Misc_H */ diff --git a/nxcompshad/src/Poller.h b/nxcompshad/src/Poller.h new file mode 100644 index 000000000..6236f872f --- /dev/null +++ b/nxcompshad/src/Poller.h @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Poller_H +#define Poller_H + +#if defined(__CYGWIN32__) || defined(WIN32) + +#include "Win.h" + +#else + +#include "X11.h" + +#endif + +#endif /* Poller_H */ diff --git a/nxcompshad/src/Regions.h b/nxcompshad/src/Regions.h new file mode 100644 index 000000000..59fdcb46c --- /dev/null +++ b/nxcompshad/src/Regions.h @@ -0,0 +1,43 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Region_H +#define Region_H + +#include <nx-X11/Xlib.h> +#include <nx-X11/Xutil.h> + +typedef struct { + short x1, x2, y1, y2; +} Box, BOX, BoxRec, *BoxPtr; + +typedef struct _XRegion { + long size; + long numRects; + BOX *rects; + BOX extents; +}; + +#endif /* Region_H */ diff --git a/nxcompshad/src/Shadow.cpp b/nxcompshad/src/Shadow.cpp new file mode 100644 index 000000000..3db5039ce --- /dev/null +++ b/nxcompshad/src/Shadow.cpp @@ -0,0 +1,482 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <signal.h> +#include <string.h> + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include "Logger.h" +#include "Shadow.h" +#include "Poller.h" +#include "Manager.h" + +typedef struct { + KeySym *map; + KeyCode minKeyCode, + maxKeyCode; + int mapWidth; +} KeySymsRec, *KeySymsPtr; + +KeySymsPtr NXShadowKeymap = NULL; + +ShadowOptions NXShadowOptions = {1, 1, -1}; + +static int mirrorException = 0; + +static UpdateManager *updateManager; +static Poller *poller; +static Input *input; + +int NXShadowRemoveAllUpdaters(); + +inline bool NXShadowNotInitialized() +{ + // + // updateManager depends on input and poller. + // So this test seem redundant. + // + // return (input == NULL) || (poller == NULL) || (updateManager == NULL); + // + + return (updateManager == NULL); +} + +#ifdef NEED_SIGNAL_HANDLER +static void NXSignalHandler(int signal) +{ + logTest("NXSignalHandler", "Got signal [%d]", signal); + + if (signal == SIGINT) + { + mirrorException = 1; + } + else if (signal == SIGTERM) + { + mirrorException = 1; + } +} + +static int NXInitSignal() +{ + logTrace("NXInitSignal"); + + struct sigaction sa; + + sa.sa_handler = NXSignalHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; + + int res; + + while ((res = sigaction(SIGINT, &sa, NULL)) == -1 && + errno == EINTR); + + if (res == -1) + { + logError("NXInitSignal", EGET()); + + return -1; + } + + return 1; +} +#endif + +static void NXHandleException() +{ + if (mirrorException) + { + mirrorException = 0; + + NXShadowRemoveAllUpdaters(); + } +} + +static int NXCreateInput(char *keymap, char *shadowDisplayName) +{ + logTrace("NXCreateInput"); + + input = new Input; + + if (input == NULL) + { + logError("NXCreateInput", ESET(ENOMEM)); + + return -1; + } + + input -> setKeymap(keymap); + + input -> setShadowDisplayName(shadowDisplayName); + + return 1; +} + +static int NXCreatePoller(Display *display, Display **shadowDisplay) +{ + logTrace("NXCreatePoller"); + + if (input == NULL) + { + logError("NXCreatePoller", ESET(EBADF)); + + return -1; + } + + poller = new Poller(input,display); + + if (poller == NULL) + { + logError("NXCreatePoller", ESET(ENOMEM)); + + return -1; + } + + if (poller -> init() == -1) + { + logWarning("NXCreatePoller", "Failed to initialize poller."); + + return -1; + } + + *shadowDisplay = poller -> getShadowDisplay(); + + logTest("NXCreatePoller", "Poller geometry [%d, %d], ShadowDisplay[%p].", poller -> width(), + poller -> height(), (Display *) *shadowDisplay); + + return 1; +} + +static int NXCreateUpdateManager() +{ + logTrace("NXCreateUpdateManager"); + + if (input == NULL || poller == NULL) + { + logError("NXCreateUpdateManager", ESET(EBADF)); + + return -1; + } + + updateManager = new UpdateManager(poller -> width(), poller -> height(), + poller -> getFrameBuffer(), input); + + if (updateManager == NULL) + { + logError("NXCreateUpdateManager", ESET(ENOMEM)); + + return -1; + } + + return 1; +} + +void NXShadowResetOptions() +{ + NXShadowOptions.optionShmExtension = 1; + NXShadowOptions.optionDamageExtension = 1; +} + +// +// Exported functions. +// + +int NXShadowHasUpdaters() +{ + logTrace("NXShadowHasUpdaters"); + + return (updateManager && updateManager -> numberOfUpdaters()) ? 1 : 0; +} + +int NXShadowRemoveAllUpdaters() +{ + logTrace("NXShadowRemoveAllUpdaters"); + + return updateManager ? updateManager -> removeAllUpdaters() : 0; +} + +int NXShadowRemoveUpdater(UpdaterHandle handle) +{ + logTrace("NXShadowRemoveUpdater"); + + return updateManager ? updateManager -> removeUpdater(handle) : 0; +} + +UpdaterHandle NXShadowAddUpdater(char *displayName) +{ + logTrace("NXShadowAddUpdater"); + + return updateManager ? updateManager -> addUpdater(displayName, NULL) : NULL; +} + +int NXShadowAddUpdaterDisplay(void *dpy, int *w, int *h, unsigned char *d) +{ + Display *display = reinterpret_cast<Display*>(dpy); + + logTrace("NXShadowAddUpdaterDisplay"); + + if ((updateManager ? updateManager -> addUpdater(NULL, display) : NULL) == NULL) + { + logTest("NXShadowAddUpdaterDisplay", "Error"); + + return 0; + } + + *w = updateManager -> getWidth(); + *h = updateManager -> getHeight(); + *d = poller -> depth(); + + return 1; +} + +int NXShadowCreate(void *dpy, char *keymap, char* shadowDisplayName, void **shadowDpy) +{ + logTrace("NXShadowCreate"); + + Display *display = reinterpret_cast<Display*>(dpy); + Display **shadowDisplay = reinterpret_cast<Display**>(shadowDpy); + +/* if (NXInitSignal() != 1) + { + logError("NXShadowCreate", EGET()); + + return -1; + }*/ + + if (NXCreateInput(keymap, shadowDisplayName) != 1) + { + logError("NXShadowCreate", EGET()); + + return -1; + } + + if (NXCreatePoller(display, shadowDisplay) != 1) + { + logWarning("NXShadowCreate", "NXCreatePoller failed."); + + return -1; + } + + if (NXCreateUpdateManager() != 1) + { + logError("NXShadowCreate", EGET()); + + return -1; + } + + return 1; +} + +#if !defined(__CYGWIN32__) && !defined(WIN32) + +void NXShadowSetDisplayUid(int uid) +{ + NXShadowOptions.optionShadowDisplayUid = uid; +} + +void NXShadowDisableShm(void) +{ + logUser("NXShadowDisableShm: Disabling SHM.\n"); + + NXShadowOptions.optionShmExtension = 0; +} + +void NXShadowDisableDamage(void) +{ + NXShadowOptions.optionDamageExtension = 0; +} + +void NXShadowGetScreenSize(int *w, int *h) +{ + poller -> getScreenSize(w, h); +} + +void NXShadowSetScreenSize(int *w, int *h) +{ + poller -> setScreenSize(w, h); +} + +#endif + +void NXShadowDestroy() +{ + if (poller) + { + delete poller; + + poller = NULL; + } + + if (updateManager) + { + delete updateManager; + + updateManager = NULL; + } + + if (input) + { + delete input; + + input = NULL; + } +} + +void NXShadowHandleInput() +{ + logTrace("NXShadowHandleInput"); + + if (NXShadowNotInitialized()) + { + logError("NXShadowHandleInput - NXShadow not properly initialized.", ESET(EBADF)); + + return; + } + + NXHandleException(); + + updateManager -> handleInput(); + + poller -> handleInput(); +} + +int NXShadowHasChanged(int (*callback)(void *), void *arg, int *suspended) +{ + int result; + + logTrace("NXShadowHasChanged"); + + if (NXShadowNotInitialized()) + { + logError("NXShadowHasChanged - NXShadow not properly initialized.", ESET(EBADF)); + + return -1; + } + + // + // FIXME + //updateManager -> destroyUpdateManagerRegion(); + // + + updateManager -> newRegion(); + +#if !defined(__CYGWIN32__) && !defined(WIN32) + poller -> getEvents(); +#endif + + result = poller -> isChanged(callback, arg, suspended); + + if (result == 1) + { + updateManager -> addRegion(poller -> lastUpdatedRegion()); + + return 1; + } + else if (result == -1) + { + logTest("NXShadowHasChanged", "Scanline error."); + return -1; + } + + return 0; +} + +void NXShadowExportChanges(long *numRects, char **pBox) +{ + Region pReg; + + logTrace("NXShadowExportChanges"); + + if (NXShadowNotInitialized()) + { + logError("NXShadowExportChanges - NXShadow not properly initialized.", ESET(EBADF)); + } + + updateManager -> update(); + pReg = updateManager -> getUpdateManagerRegion(); + *numRects = pReg -> numRects; + *pBox = (char *)pReg -> rects; + + logTest("NXShadowExportChanges", "numRects [%ld] pBox[%p], pReg->numRects[%ld], rects[%p], size[%lu]", + *numRects, *pBox, pReg -> numRects, &(pReg -> rects -> x2), + (unsigned long) sizeof(pReg -> rects -> x2)); +} + +void NXShadowEvent(Display *display, XEvent event) +{ + poller -> handleEvent(display, &event); +} + +void NXShadowWebKeyEvent(KeySym keysym, Bool isKeyPress) +{ + poller -> handleWebKeyEvent(keysym, isKeyPress); +} + +#ifdef __CYGWIN32__ + +int NXShadowCaptureCursor(unsigned int wnd, void *vis) +{ + Window window = (Window)wnd; + Visual *visual = reinterpret_cast<Visual*>(vis); + + logTrace("NXShadowCaptureCursor"); + + logTest("NXShadowCaptureCursor","Init"); + + return poller -> updateCursor(window, visual); +} + +#endif + +void NXShadowUpdateBuffer(void **buffer) +{ + char **fBuffer = reinterpret_cast<char **>(buffer); + + if (*fBuffer != NULL) + { + poller -> destroyFrameBuffer(); + + poller -> init(); + } + + *fBuffer = poller -> getFrameBuffer(); + + logTest("NXShadowUpdateBuffer","New frame buffer [0x%p]", (void *)*fBuffer); +} + +void NXShadowInitKeymap(void *keysyms) +{ + NXShadowKeymap = (KeySymsPtr) keysyms; + + logTest("NXShadowInitKeymap","KeySyms pointer [0x%p]", (void *)NXShadowKeymap); +} diff --git a/nxcompshad/src/Updater.cpp b/nxcompshad/src/Updater.cpp new file mode 100644 index 000000000..4b677a78c --- /dev/null +++ b/nxcompshad/src/Updater.cpp @@ -0,0 +1,395 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <nx-X11/Xlib.h> +#include <nx-X11/Xutil.h> +#include <nx-X11/keysym.h> +#include <string.h> + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +// +#include <stdio.h> +// +#include "Updater.h" +#include "Logger.h" + +Updater::Updater(char *displayName, Display *display) +{ + logTrace("Updater::Updater"); + + displayName_ = displayName; + display_ = display; + closeDisplay_ = false; + image_ = NULL; + updateRegion_ = NULL; + buffer_ = NULL; +} + +Updater::~Updater() +{ + logTrace("Updater::~Updater"); + + if (input_) + { + int removedEvents = input_ -> removeAllEvents(display_); + + logTest("Updater::~Updater", "Removed events in input queue is [%d].", removedEvents); + } + + if (display_) + { + XDestroyWindow(display_, window_); + XFreePixmap(display_, pixmap_); + + if (closeDisplay_) + { + XCloseDisplay(display_); + } + } + + if (image_) + { + image_ -> data = NULL; + + XDestroyImage(image_); + } + + if (updateRegion_) + { + XDestroyRegion(updateRegion_); + } +} + +int Updater::init(int width, int height, char *fb, Input *input) +{ + logTrace("Updater::init"); + + if (fb == NULL || input == NULL || width <= 0 || height <= 0) + { + logError("Updater::init", ESET(EINVAL)); + + return -1; + } + + width_ = width; + height_ = height; + buffer_ = fb; + input_ = input; +/* + if (display_ == NULL) + { + display_ = XOpenDisplay(displayName_); + + closeDisplay_ = true; + + if (display_ == NULL) + { + logError("Updater::init", ESET(ENOMSG)); + + return -1; + } + } +*/ + depth_ = DefaultDepth(display_, DefaultScreen(display_)); + + if (depth_ == 8) bpl_ = width_; + else if (depth_ == 16) bpl_ = width_ * 2; + else bpl_ = width_ * 4; + + logTest("Updater::init", "Server geometry [%d, %d] depth [%d] bpl [%d].", width_, height_, depth_, bpl_); + +/* int bitmap_pad = 8; + + image_ = XCreateImage(display_, DefaultVisual(display_, DefaultScreen(display_)), depth_, ZPixmap, 0, + buffer_, width_, height_, bitmap_pad, 0); + + if (image_ == NULL) + { + logError("Updater::init", ESET(ENOMSG)); + + logTest("Updater::init", "Failed to create default image."); + + return -1; + } + + pixmap_ = XCreatePixmap(display_, DefaultRootWindow(display_), width_, height_, depth_); + + unsigned int mask = CWBackPixmap | CWBorderPixel | CWEventMask; + + XSetWindowAttributes attributes; + + attributes.background_pixmap = pixmap_; + attributes.border_pixel = WhitePixel(display_, DefaultScreen(display_)); + attributes.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask; + + window_ = XCreateWindow(display_, DefaultRootWindow(display_), + 0, 0, width_, height_, 0, depth_, InputOutput, + DefaultVisual(display_, DefaultScreen(display_)), mask, &attributes); + + if (window_ == None) + { + logError("Updater::init", ESET(ENOMSG)); + + return -1; + } + + XSizeHints *size_hints; + + if ((size_hints = XAllocSizeHints()) == NULL) + { + logError("Updater::init", ESET(ENOMEM)); + + return -1; + } + + size_hints -> flags = PMinSize | PMaxSize; + size_hints -> min_width = width_; + size_hints -> max_width = width_; + size_hints -> min_height = height_; + size_hints -> max_height = height_; + + XSetWMNormalHints(display_, window_, size_hints); + + XFree(size_hints); + + Atom deleteWMatom = XInternAtom(display_, "WM_DELETE_WINDOW", 1); + + XSetWMProtocols(display_, window_, &deleteWMatom, 1); + + XMapWindow(display_, window_);*/ + + updateRegion_ = XCreateRegion(); + + logTest("Updater::init", "updateRegion_[%p]", updateRegion_); + return 1; +} + +void Updater::addRegion(Region region) +{ + // + // FIXME: Is this too paranoid ? + // + + if (updateRegion_ == NULL) + { + logError("Updater::addRegion", ESET(EINVAL)); + + return; + } + + XUnionRegion(region, updateRegion_, updateRegion_); +} + +void Updater::update() +{ + logTrace("Updater::update"); + + if (updateRegion_ == NULL) + { + logError("Updater::update", ESET(EINVAL)); + + return; + } + + logTest("Updater::update", "Number of rectangles [%ld].", updateRegion_ -> numRects); + +/* for (; updateRegion_ -> numRects > 0; updateRegion_ -> numRects--) + { + int n = updateRegion_ -> numRects - 1; + + int x = updateRegion_ -> rects[n].x1; + int y = updateRegion_ -> rects[n].y1; + unsigned int width = updateRegion_ -> rects[n].x2 - updateRegion_ -> rects[n].x1; + unsigned int height = updateRegion_ -> rects[n].y2 - updateRegion_ -> rects[n].y1; + + logDebug("Updater::update", "Sending rectangle: [%d, %d, %d, %d].", x, y, width, height); + + // + // We need to update the extents. + // + + int bitmap_pad; + + if (depth_ == 32 || depth_ == 24) + { + bitmap_pad = 32; + } + else if (depth_ == 16) + { + if ((width & 1) == 0) + { + bitmap_pad = 32; + } + else + { + bitmap_pad = 16; + } + } + else if ((width & 3) == 0) + { + bitmap_pad = 32; + } + else if ((width & 1) == 0) + { + bitmap_pad = 16; + } + else + { + bitmap_pad = 8; + }*/ + +/* image_ -> bitmap_pad = bitmap_pad;*/ + + /* NXShadowCorrectColor(x, y, width, height);*/ + +/* XPutImage(display_, pixmap_, DefaultGC(display_, DefaultScreen(display_)), + image_, x, y, x, y, width, height); + + XClearArea(display_, window_, x, y, width, height, 0); + }*/ + + // + // Should we reduces the box vector ? + // + // BOX *box = Xrealloc(updateRegion_ -> rects, + // updateRegion_ -> numRects == 0 ? sizeof(BOX) : + // updateRegion_ -> numRects * sizeof(BOX)); + // + // if (box) + // { + // updateRegion_ -> rects = box; + // updateRegion_ -> size = 1; + // } + // + + if (updateRegion_ -> numRects == 0) + { + updateRegion_ -> extents.x1 = 0; + updateRegion_ -> extents.y1 = 0; + updateRegion_ -> extents.x2 = 0; + updateRegion_ -> extents.y2 = 0; + } + else + { + // + // FIXME: We have to update the region extents. + // + + logTest("Updater::update", "Region extents has not been updated."); + } +} + +void Updater::handleInput() +{ + logTrace("Updater::handleInput"); + + XEvent *event = new XEvent; + + if (event == NULL) + { + logError("Updater::handleInput", ESET(ENOMEM)); + + return; + } + + while (XCheckIfEvent(display_, event, anyEventPredicate, NULL)) + { + switch (event -> type) + { + /* case ClientMessage: + { + Atom wmProtocols = XInternAtom(display_, "WM_PROTOCOLS", 0); + Atom wmDeleteWindow = XInternAtom(display_, "WM_DELETE_WINDOW", 0); + + if (event -> xclient.message_type == wmProtocols && + (Atom)event -> xclient.data.l[0] == wmDeleteWindow) + { + logTest("Updater::handleInput", "Got client message of type WM_PROTOCOLS and value WM_DELETE_WINDOW," + " throwing exception UpdaterClosing."); + + delete event; + + throw UpdaterClosing(); + } + else + { + logTest("Updater::handleInput", "Unexpected client message type [%ld] format [%d] first value [%ld]", + event -> xclient.message_type, event -> xclient.format, event -> xclient.data.l[0]); + } + + break; + }*/ + case KeyPress: + case KeyRelease: + case ButtonPress: + case ButtonRelease: + case MotionNotify: + { + input_ -> pushEvent(display_, event); + + event = new XEvent; + + if (event == NULL) + { + logError("Updater::handleInput", ESET(ENOMEM)); + + return; + } + + break; + } + default: + { + logTest("Updater::handleInput", "Handling unexpected event [%d].", event -> type); + + break; + } + } + } + + delete event; +} + +void Updater::newRegion() +{ + if (updateRegion_ != NULL) + { + XDestroyRegion(updateRegion_); + } + + updateRegion_ = XCreateRegion(); + + logTest("Updater::newRegion", "updateRegion_ [%p].", updateRegion_); +} +// +// Private functions. +// diff --git a/nxcompshad/src/Updater.h b/nxcompshad/src/Updater.h new file mode 100644 index 000000000..10d75c0d6 --- /dev/null +++ b/nxcompshad/src/Updater.h @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Updater_H +#define Updater_H + +#include <nx-X11/Xlib.h> + +#include "Regions.h" +#include "Input.h" + +class UpdaterClosing {}; + +class Updater +{ + public: + + Updater(char *displayName, Display *display); + + ~Updater(); + + int init(int, int, char *, Input*); + + void addRegion(Region r); + + void update(); + + void handleInput(); + + XImage *getImage(); + + Region getUpdateRegion(); + + void newRegion(); + + private: + + Input *input_; + + static inline Bool anyEventPredicate(Display*, XEvent*, XPointer); + + void handleKeyboardEvent(XEvent &event); + + char *displayName_; + + char *buffer_; + + bool closeDisplay_; + + Display *display_; + + int depth_; + + int width_; + int height_; + + int bpl_; + + Window window_; + XImage *image_; + + Pixmap pixmap_; + + Region updateRegion_; + +}; + +Bool Updater::anyEventPredicate(Display*, XEvent*, XPointer) +{ + return true; +} + +inline XImage* Updater::getImage() +{ + return image_; +} +inline Region Updater::getUpdateRegion() +{ + return updateRegion_; +} +#endif /* Updater_H */ diff --git a/nxcompshad/src/Win.cpp b/nxcompshad/src/Win.cpp new file mode 100644 index 000000000..6033f2c1b --- /dev/null +++ b/nxcompshad/src/Win.cpp @@ -0,0 +1,1149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(__CYGWIN32__) || defined(WIN32) + +#include <nx-X11/keysym.h> + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include "Poller.h" +#include "Logger.h" + +Poller::Poller(Input *input, Display *display, int depth) : CorePoller(input, display) +{ + logTrace("Poller::Poller"); + + screenDC_ = NULL; + screenBmp_ = NULL; + memoryDC_ = NULL; + pDIBbits_ = NULL; + DIBBuffer_ = NULL; + pKey_ = NULL; + pMouse_ = NULL; + path_ = NULL; + keymapName_ = input -> getKeymap(); + keymap_ = NULL; + toggleButtonState_ = 0; + serverModifierState_ = 0; + display_ = display; + depth_ = DefaultDepth(display_, DefaultScreen(display_)); + oldCursor_ = 0; + xCursor_ = 0; +} + +Poller::~Poller() +{ + logTrace("Poller::~Poller"); + + if (screenDC_) + { + BOOL result = ReleaseDC(NULL, screenDC_); + + logTest("Poller::~Poller", "ReleaseDC returned [%d].", result); + + screenDC_ = NULL; + } + + if (memoryDC_) + { + BOOL result = DeleteDC(memoryDC_); + + logTest("Poller::~Poller", "DeleteDC returned [%d].", result); + + memoryDC_ = NULL; + } + + if (screenBmp_) + { + BOOL result = DeleteObject(screenBmp_); + + logTest("Poller::~Poller", "DeleteObject returned [%d].", result); + + screenBmp_ = NULL; + } + + if (DIBBuffer_) + { + logDebug("Poller::~Poller", "Delete DIBBuffer_ [%p].", DIBBuffer_); + + delete [] DIBBuffer_; + } + + if (pKey_) + { + logDebug("Poller::~Poller", " pKey_[%p].", pKey_); + + delete [] pKey_; + } + + if (pMouse_) + { + logDebug("Poller::~Poller", " pMouse_[%p].", pMouse_); + + delete [] pMouse_; + } + + if (keymap_) + { + logDebug("Poller::~Poller", " keymap_[%p].", keymap_); + + delete [] keymap_; + } +} + +int Poller::init() +{ + logTrace("Poller::init"); + + int maxLengthArrayINPUT = 6; + + platformOS(); + + pKey_ = new INPUT [maxLengthArrayINPUT]; + + if (pKey_ == NULL) + { + logError("Poller::init", ESET(ENOMEM)); + + return -1; + } + + for (int i = 0; i < maxLengthArrayINPUT; i++) + { + pKey_[i].type = INPUT_KEYBOARD; + pKey_[i].ki.wVk = (WORD) 0; + pKey_[i].ki.time = (DWORD) 0; + pKey_[i].ki.dwExtraInfo = (DWORD) 0; + } + + pMouse_ = new INPUT; + + if (pMouse_ == NULL) + { + logError("Poller::init", ESET(ENOMEM)); + + return -1; + } + + pMouse_ -> type = INPUT_MOUSE; + + pMouse_ -> mi.dx = 0; + pMouse_ -> mi.dy = 0; + pMouse_ -> mi.mouseData = (DWORD) 0; + pMouse_ -> mi.time = 0; + pMouse_ -> mi.dwExtraInfo = (ULONG_PTR) NULL; + + screenDC_ = GetDC(NULL); + + if (screenDC_ == NULL) + { + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + + switch(depth_) + { + case 8: + { + depth_ = 16; + break; + } + case 16: + { + depth_ = 16; + break; + } + case 24: + { + depth_ = 32; + break; + } + default: + { + logError("Poller::init", ESET(EINVAL)); + + return -1; + } + } + + width_ = GetDeviceCaps(screenDC_, HORZRES); + height_ = GetDeviceCaps(screenDC_, VERTRES); + + bpl_ = width_ * (depth_ >> 3); + bpp_ = (depth_ >> 3); + + logTest("Poller::init", "Screen geometry is [%d, %d] depth is [%d] bpl [%d] bpp [%d].", + width_, height_, depth_, bpl_, bpp_); + + logTest("Poller::init", "Got device context at [%p] screen size is (%d,%d).", + screenDC_, width_, height_); + + memoryDC_ = CreateCompatibleDC(screenDC_); + + if (memoryDC_ == NULL) + { + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + + // + // Delete the old bitmap for the memory device. + // + + HBITMAP bitmap = (HBITMAP) GetCurrentObject(memoryDC_, OBJ_BITMAP); + + if (bitmap && DeleteObject(bitmap) == 0) + { + logError("Poller::init", ESET(ENOMSG)); + } + + // + // Bitmap header describing the bitmap we want to get from GetDIBits. + // + + bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi_.bmiHeader.biWidth = width_; + bmi_.bmiHeader.biHeight = -height_; + bmi_.bmiHeader.biPlanes = 1; + bmi_.bmiHeader.biBitCount = depth_; + bmi_.bmiHeader.biCompression = BI_RGB; + bmi_.bmiHeader.biSizeImage = 0; + bmi_.bmiHeader.biXPelsPerMeter = 0; + bmi_.bmiHeader.biYPelsPerMeter = 0; + bmi_.bmiHeader.biClrUsed = 0; + bmi_.bmiHeader.biClrImportant = 0; + + screenBmp_ = CreateDIBSection(memoryDC_, &bmi_, DIB_RGB_COLORS, &pDIBbits_, NULL, 0); + ReleaseDC(NULL,memoryDC_); + + if (screenBmp_ == NULL) + { + logTest ("Poller::init", "This video device is not supporting DIB section"); + + pDIBbits_ = NULL; + + screenBmp_ = CreateCompatibleBitmap(screenDC_, width_, height_); + + if (screenBmp_ == NULL) + { + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + + if (SelectObject(memoryDC_, screenBmp_) == NULL) + { + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + } + else + { + logTest ("Poller::init", "Enabled the DIB section"); + + if (SelectObject(memoryDC_, screenBmp_) == NULL) + { + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + } + + // + // Check if the screen device raster capabilities + // support the bitmap transfer. + // + + if ((GetDeviceCaps(screenDC_, RASTERCAPS) & RC_BITBLT) == 0) + { + logTest("Poller::init", "This video device is not supporting the bitmap transfer."); + + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + + // + // Check if the memory device raster capabilities + // support the GetDIBits and SetDIBits functions. + // + + if ((GetDeviceCaps(memoryDC_, RASTERCAPS) & RC_DI_BITMAP) == 0) + { + logTest("Poller::init", "This memory device is not supporting the GetDIBits and SetDIBits " + "function."); + + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + + if (GetDeviceCaps(screenDC_, PLANES) != 1) + { + logTest("Poller::init", "This video device has more than 1 color plane."); + + logError("Poller::init", ESET(ENOMSG)); + + return -1; + } + + return CorePoller::init(); +} + +// +// FIXME: Remove me. +// + +void ErrorExit(LPTSTR lpszFunction) +{ + LPVOID lpMsgBuf; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); + + logTest(lpszFunction, " Failed with error [%ld]: %s", dw, (char*)lpMsgBuf); + + LocalFree(lpMsgBuf); + ExitProcess(dw); +} + +// +// FIXME: End. +// + +char *Poller::getRect(XRectangle r) +{ + logTrace("Poller::getRect"); + + logDebug("Poller::getRect", "Going to retrive rectangle [%d, %d, %d, %d].", + r.x, r.y, r.width, r.height); + + // + // The CAPTUREBLT operation could be a very + // cpu-consuming task. We should make some + // test to see how much it is expensive. + // Anyway we get tooltip windows and any + // other special effect not included with + // only the SRCCOPY operation. + // + + if (BitBlt(memoryDC_, r.x, r.y, r.width, r.height, + screenDC_, r.x, r.y, SRCCOPY | CAPTUREBLT) == 0) + { + logError("Poller::getRect", ESET(ENOMSG)); + + logTest("Poller::getRect", "Failed to perform a bit-block transfer."); + logTest("Poller::getRect", "bit-block error=%lu", GetLastError()); + + return NULL; + } + + // bmi_.bmiHeader.biWidth = r.width; + // bmi_.bmiHeader.biHeight = -r.height; + + if (pDIBbits_ == NULL) + { + static long nPixel = 0; + + if (nPixel < r.width * r.height) + { + + if (DIBBuffer_) + { + delete [] DIBBuffer_; + } + + nPixel = r.width * r.height; + + DIBBuffer_ = new char [nPixel * bpp_]; + + if (DIBBuffer_ == NULL) + { + logError("Poller::getRect", ESET(ENOMEM)); + + nPixel = 0; + + return NULL; + } + } + + if (GetDIBits(memoryDC_, screenBmp_, height_ - r.height - r.y, r.height, + DIBBuffer_, &bmi_, DIB_RGB_COLORS) == 0) + { + logError("Poller::getRect", ESET(ENOMSG)); + + logTest("Poller::getRect", "Failed to retrieve the screen bitmap."); + + return NULL; + } + + return DIBBuffer_; + } + else + { + return (char *) pDIBbits_ + r.y * bpl_ + r.x * bpp_; + } +} + +void Poller::handleKeyboardEvent(Display *display, XEvent *event) +{ + KeySym keysym; + char *keyname = new char [31]; + keyTranslation tr = {0, 0}; + unsigned char scancode = 0; + int lengthArrayINPUT = 0; + + if (XLookupString((XKeyEvent *) event, keyname, 30, &keysym, NULL) > 0) + { + logTest("Poller::handleKeyboardEvent", "keyname %s, keysym [%x]", keyname, (unsigned int)keysym); + } + + if (specialKeys(keysym, event -> xkey.state, event -> type) == 1) + { + delete[] keyname; + return; + } + + tr = xkeymapTranslateKey(keysym, event -> xkey.keycode, event -> xkey.state); + scancode = tr.scancode; + + logTest("Poller::handleKeyboardEvent", "keyname [%s] scancode [0x%x], keycode[0x%x], keysym [%x]", keyname, + tr.scancode, event ->xkey.keycode, (unsigned int)keysym); + + if (scancode == 0) + { + delete[] keyname; + return; + } + + if (event -> type == KeyPress) + { + int test1 = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); + int test2 = MapVirtualKey(0x24, MAPVK_VSC_TO_VK_EX); + + if (test1 == test2) + { + simulateCtrlAltDel(); + } + + if (isModifier(scancode) == 0) + { + savedServerModifierState_ = serverModifierState_; + } + + ensureServerModifiers(tr, &lengthArrayINPUT); + if (sendInput(scancode, 1, &lengthArrayINPUT) == 0) + { + logTest("Poller::handleKeyboardEvent", "lengthArrayINPUT [%d].", lengthArrayINPUT); + } + restoreServerModifiers(scancode); + } + else if (event -> type == KeyRelease) + { + if (sendInput(scancode, 0, &lengthArrayINPUT) == 0) + { + logTest("Poller::handleKeyboardEvent", "lengthArrayINPUT [%d].", lengthArrayINPUT); + } + } + + updateModifierState(scancode, (event -> type == KeyPress)); + + delete[] keyname; +} + +void Poller::handleWebKeyboardEvent(KeySym keysym, Bool isKeyPress) +{ +/* +FIXME +*/ +} + +void Poller::handleMouseEvent(Display *display, XEvent *event) +{ + DWORD flg = 0; + DWORD whl = 0; + + if (event -> type == ButtonPress) + { + logTest("Poller::handleMouseEvent", "ButtonPress.\n"); + switch (event -> xbutton.button) + { + case Button1: + { + flg = MOUSEEVENTF_LEFTDOWN; + break; + } + case Button2: + { + flg = MOUSEEVENTF_MIDDLEDOWN; + break; + } + case Button3: + { + flg = MOUSEEVENTF_RIGHTDOWN; + break; + } + case Button4: + { + flg = MOUSEEVENTF_WHEEL; + whl = WHEEL_DELTA; + pMouse_ -> mi.mouseData = whl; + break; + } + case Button5: + { + flg = MOUSEEVENTF_WHEEL; + whl = (DWORD) (-WHEEL_DELTA); + pMouse_ -> mi.mouseData = whl; + break; + } + } + } + else if (event -> type == ButtonRelease) + { + switch (event -> xbutton.button) + { + case Button1: + { + flg = MOUSEEVENTF_LEFTUP; + break; + } + case Button2: + { + flg = MOUSEEVENTF_MIDDLEUP; + break; + } + case Button3: + { + flg = MOUSEEVENTF_RIGHTUP; + break; + } + case Button4: + { + flg = MOUSEEVENTF_WHEEL; + whl = 0; + pMouse_ -> mi.mouseData = whl; + break; + } + case Button5: + { + flg = MOUSEEVENTF_WHEEL; + whl = 0; + pMouse_ -> mi.mouseData = whl; + break; + } + } + } + else if (event -> type == MotionNotify) + { + logTest("Poller::handleMouseEvent", "SetCursor - MotionNotify"); + + SetCursorPos(event -> xmotion.x, event -> xmotion.y); + } + + if (flg > 0) + { + // logTest("Poller::handleMouseEvent", "SetCursor - flg > 0"); + // + // FIXME: move the cursor to the pace the event occurred + // + + SetCursorPos(event -> xbutton.x, event -> xbutton.y); + + // + // FIXME: Remove me: send the click/release event + // mouse_event(flg, 0, 0, whl, (ULONG_PTR)NULL); + // + + pMouse_ -> mi.dwFlags = flg; + + if (SendInput(1, pMouse_, sizeof(INPUT)) == 0) + { + logTest("Poller::handleMouseEvent", "Failed SendInput"); + } + } +} + +int Poller::updateCursor(Window wnd, Visual* vis) +{ + BYTE *mBits, *andBits, *xorBits; + + logTrace("Poller::Cursor"); + + // + // Retrieve mouse cursor handle. + // + + CURSORINFO cursorInfo; + cursorInfo.cbSize = sizeof(CURSORINFO); + + if (GetCursorInfo(&cursorInfo) == 0) + { + logTest("Poller::Cursor", "GetCursorInfo() failed [%u].\n", (unsigned int)GetLastError()); + LocalFree(&cursorInfo); + return -1; + } + + HCURSOR hCursor = cursorInfo.hCursor; + + if (hCursor == 0) + { + logTest("Poller::Cursor","Cursor Handle is NULL. Error[%u].\n", (unsigned int)GetLastError()); + return 1; + } + + if (hCursor == oldCursor_) + { + LocalFree(&cursorInfo); + return 1; + } + else + { + oldCursor_ = hCursor; + } + + // + // Get cursor info. + // + + // logTest("Poller::Cursor","hCursor [%xH] GetCursor [%xH].\n", hCursor, GetCursor()); + + ICONINFO iconInfo; + if (GetIconInfo(hCursor, &iconInfo) == 0) + { + logTest("Poller::Cursor","GetIconInfo() failed. Error[%d].", (unsigned int)GetLastError()); + LocalFree(&iconInfo); + // return -1; + } + + BOOL isColorCursor = FALSE; + if (iconInfo.hbmColor != NULL) + { + isColorCursor = TRUE; + } + + if (iconInfo.hbmMask == NULL) + { + logTest("Poller::Cursor","Cursor bitmap handle is NULL.\n"); + return -1; + } + + // + // Check bitmap info for the cursor + // + + BITMAP bmMask; + if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), (LPVOID)&bmMask)) + { + logTest("Poller::Cursor","GetObject() for bitmap failed.\n"); + DeleteObject(iconInfo.hbmMask); + LocalFree(&bmMask); + return -1; + } + + if (bmMask.bmPlanes != 1 || bmMask.bmBitsPixel != 1) + { + logTest("Poller::Cursor","Incorrect data in cursor bitmap.\n"); + LocalFree(&bmMask); + DeleteObject(iconInfo.hbmMask); + return -1; + } + + // Get monochrome bitmap data for cursor + // NOTE: they say we should use GetDIBits() instead of GetBitmapBits(). + mBits = new BYTE[bmMask.bmWidthBytes * bmMask.bmHeight]; + + if (mBits == NULL)//Data bitmap + { + DeleteObject(iconInfo.hbmMask); + DestroyCursor(cursorInfo.hCursor); + LocalFree(&iconInfo); + LocalFree(&bmMask); + delete[] mBits; + return -1; + } + + BOOL success = GetBitmapBits(iconInfo.hbmMask, bmMask.bmWidthBytes * bmMask.bmHeight, mBits); + + if (!success) + { + logTest("Poller::Cursor","GetBitmapBits() failed.\n"); + delete[] mBits; + return -1; + } + + andBits = mBits; + + long width = bmMask.bmWidth; + long height = (isColorCursor) ? bmMask.bmHeight : bmMask.bmHeight/2; + + // + // The bitmask is formatted so that the upper half is + // the icon AND bitmask and the lower half is the icon XOR bitmask. + // + + if (!isColorCursor) + { + xorBits = andBits + bmMask.bmWidthBytes * height; + +/* logTest("Poller::Cursor","no color widthB[%ld] width[%ld] height[%ld] totByte[%ld] mbits[%ld].\n", + bmMask.bmWidthBytes,width,height,success,bmMask.bmHeight * bmMask.bmWidthBytes);*/ + + if (xCursor_ > 0) + { + XFreeCursor(display_, xCursor_); + } + + xCursor_ = createCursor(wnd, vis, (unsigned int)iconInfo.xHotspot, (unsigned int)iconInfo.yHotspot, + width, height, (unsigned char *)xorBits, (unsigned char *)andBits); + + XDefineCursor(display_, wnd, xCursor_); + } + + delete []mBits; + DeleteObject(iconInfo.hbmMask); + LocalFree(&bmMask); + DestroyCursor(cursorInfo.hCursor); + LocalFree(&iconInfo); + + return success; +} + +unsigned char Poller::specialKeys(unsigned int keysym, unsigned int state, int pressed) +{ + return 0; +} + +void Poller::ensureServerModifiers(keyTranslation tr, int *lengthArrayINPUT) +{ + return; +} + +void Poller::restoreServerModifiers(UINT scancode) +{ + keyTranslation dummy; + int lengthArrayINPUT = 0; + + if (isModifier(scancode) == 1) + { + return; + } + + dummy.scancode = 0; + dummy.modifiers = savedServerModifierState_; + ensureServerModifiers(dummy, &lengthArrayINPUT); + if (sendInput(0, 0, &lengthArrayINPUT) == 0) + { + logTest("Poller::restoreServerModifiers", "lengthArrayINPUT [%d]", lengthArrayINPUT); + } +} + +int Poller::updateShadowFrameBuffer(void) +{ + return 1; +} + +void Poller::addToKeymap(char *keyname, unsigned char scancode, unsigned short modifiers, char *mapname) +{ + return; +} + +FILE *Poller::xkeymapOpen(char *filename) +{ + return NULL; +} + +int Poller::xkeymapRead(char *mapname) +{ + return 1; +} + +void Poller::xkeymapInit(char *keyMapName) +{ + return; +} + +keyTranslation Poller::xkeymapTranslateKey(unsigned int keysym, unsigned int keycode, + unsigned int state) +{ + keyTranslation tr = { 0, 0 }; + + return tr; +} + +unsigned char Poller::getKeyState(unsigned int state, unsigned int keysym) +{ + return 0; +} + +char *Poller::getKsname(unsigned int keysym) +{ + char *ksname = NULL; + + return ksname; +} + +// +// Routine used to fool Winlogon into thinking CtrlAltDel was pressed +// +char Poller::simulateCtrlAltDel(void) +{ + HDESK oldDesktop = GetThreadDesktop(GetCurrentThreadId()); + + // + // Switch into the Winlogon desktop. + // + if (selectDesktopByName("Winlogon") == 0) + { + logTest("SimulateCtrlAltDelThreadFn","Failed to select logon desktop."); + return 0; + } + + logTest("SimulateCtrlAltDelThreadFn","Generating ctrl-alt-del."); + + // + // Winlogon uses hotkeys to trap Ctrl-Alt-Del. + // + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE)); + + // + // Switch back to our original desktop. + // + if (oldDesktop != NULL) + { + selectDesktop(oldDesktop); + } + + return 1; +} + +// Switches the current thread into a different desktop by desktop handle +// This call takes care of all the evil memory management involved +char Poller::selectDesktop(HDESK newDesktop) +{ + // + // Only on NT. + // + if (isWinNT()) + { + HDESK oldDesktop = GetThreadDesktop(GetCurrentThreadId()); + + DWORD dummy; + char newName[256]; + + if (GetUserObjectInformation(newDesktop, UOI_NAME, &newName, 256, &dummy) == 0) + { + logDebug("Poller::selectDesktop","GetUserObjectInformation() failed. Error[%lu].", GetLastError()); + return 0; + } + + logTest("Poller::selectDesktop","New Desktop to [%s] (%x) from (%x).", + newName, (unsigned int)newDesktop, (unsigned int)oldDesktop); + + // + // Switch the desktop. + // + if(SetThreadDesktop(newDesktop) == 0) + { + logDebug("Poller::SelectDesktop","Unable to SetThreadDesktop(), Error=%lu.", GetLastError()); + return 0; + } + + // + // Switched successfully - destroy the old desktop. + // + if (CloseDesktop(oldDesktop) == 0) + { + logDebug("Poller::selectHdesk","Failed to close old desktop (%x), Error=%lu.", + (unsigned int)oldDesktop, GetLastError()); + return 0; + } + } + + return 1; +} + +// +// Switches the current thread into a different desktop, by name +// Calling with a valid desktop name will place the thread in that desktop. +// Calling with a NULL name will place the thread in the current input desktop. +// + +char Poller::selectDesktopByName(char *name) +{ + // + // Only on NT. + // + if (isWinNT()) + { + HDESK desktop; + + if (name != NULL) + { + // + // Attempt to open the named desktop. + // + desktop = OpenDesktop(name, 0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE); + } + else + { + // + // Open the input desktop. + // + desktop = OpenInputDesktop(0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE); + } + + if (desktop == NULL) + { + logDebug("Poller::selectDesktopByName","Unable to open desktop, Error=%lu.", GetLastError()); + return 0; + } + + // + // Switch to the new desktop + // + if (selectDesktop(desktop) == 0) + { + // + // Failed to enter the new desktop, so free it! + // + logDebug("Poller::selectDesktopByName","Failed to select desktop."); + + if (CloseDesktop(desktop) == 0) + { + logDebug("Poller::selectDesktopByName","Failed to close desktop, Error=%lu.", GetLastError()); + return 0; + } + } + + return 1; + } + + return (name == NULL); +} + +void Poller::platformOS() +{ + OSVERSIONINFO osversioninfo; + osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo); + + // + // Get the current OS version. + // + if (GetVersionEx(&osversioninfo) == 0) + { + platformID_ = 0; + } + platformID_ = osversioninfo.dwPlatformId; + +// +// versionMajor = osversioninfo.dwMajorVersion; +// versionMinor = osversioninfo.dwMinorVersion; +// +} + +char Poller::checkDesktop() +{ + // + // Only on NT. + // + if (isWinNT()) + { + // + // Get the input and thread desktops. + // + HDESK desktop = GetThreadDesktop(GetCurrentThreadId()); + HDESK inputDesktop = OpenInputDesktop(0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE); + + if (inputDesktop == NULL) + { + return 0; + } + + DWORD dummy; + char desktopName[256]; + char inputName[256]; + + if (GetUserObjectInformation(desktop, UOI_NAME, &desktopName, 256, &dummy) == 0) + { + if (CloseDesktop(inputDesktop) == 0) + { + logDebug("Poller::checkDesktop", "Failed to close desktop, Error[%d].", (unsigned int)GetLastError()); + return 0; + } + } + + if (GetUserObjectInformation(inputDesktop, UOI_NAME, &inputName, 256, &dummy) == 0) + { + if (CloseDesktop(inputDesktop) == 0) + { + logDebug("Poller::checkDesktop", "Failed to close input desktop, Error[%d].", (unsigned int)GetLastError()); + return 0; + } + } + + if (strcmp(desktopName, inputName) != 0) + { + // + // Switch to new desktop. + // + selectDesktop(inputDesktop); + } + + if (CloseDesktop(desktop) == 0) + { + logDebug("Poller::checkDesktop", "Failed to close input desktop, Error[%d].", (unsigned int)GetLastError()); + return 0; + } + + if (CloseDesktop(inputDesktop) == 0) + { + logDebug("Poller::checkDesktop", "Failed to close input desktop, Error[%d].", (unsigned int)GetLastError()); + return 0; + } + } + + return 1; +} + +unsigned char Poller::isModifier(UINT scancode) +{ + return 0; +} + +void Poller::updateModifierState(UINT scancode, unsigned char pressed) +{ + return; +} + +Cursor Poller::createCursor(Window wnd, Visual *vis,unsigned int x, unsigned int y, + int width, int height, unsigned char *xormask, unsigned char *andmask) +{ + Pixmap maskglyph, cursorglyph; + XColor bg, fg; + Cursor xcursor; + unsigned char *cursor; + unsigned char *mask, *pmask, *pcursor, tmp; + int scanline, offset; + + scanline = (width + 7) / 8; + offset = scanline * height; + + pmask = andmask; + pcursor = xormask; + for (int i = 0; i < offset; i++) + { + // + // The pixel is black if both the bit of andmask and xormask is one. + // + + tmp = *pcursor & *pmask; + *pcursor ^= tmp; + *pmask ^= tmp; + + *pmask = ~(*pmask); + + pmask++; + pcursor++; + } + + cursor = new unsigned char[offset]; + memcpy(cursor, xormask, offset); + + mask = new unsigned char[offset]; + memcpy(mask, andmask, offset); + + fg.red = fg.blue = fg.green = 0xffff; + bg.red = bg.blue = bg.green = 0x0000; + fg.flags = bg.flags = DoRed | DoBlue | DoGreen; + + cursorglyph = createGlyph(wnd, vis, width, height, cursor); + maskglyph = createGlyph(wnd, vis, width, height, mask); + + xcursor = XCreatePixmapCursor(display_, cursorglyph, maskglyph, &fg, &bg, x, y); + + XFreePixmap(display_, maskglyph); + XFreePixmap(display_, cursorglyph); + delete[]mask; + delete[]cursor; + + return xcursor; +} + +Pixmap Poller::createGlyph(Window wnd, Visual *visual, int width, int height, unsigned char *data) +{ + XImage *image; + Pixmap bitmap; + int scanline; + GC glyphGC; + + scanline = (width + 7) / 8; + + bitmap = XCreatePixmap(display_, wnd, width, height, 1); + glyphGC = XCreateGC(display_, bitmap, 0, NULL); + + image = XCreateImage(display_, visual, 1, ZPixmap, 0, (char *)data, width, height, 8, scanline); + image->byte_order = 1; // MSBFirst -- LSBFirst = 0 + image->bitmap_bit_order = 1; + XInitImage(image); + +/* logTest("Poller::createGlyph","XPutImage on pixmap %d,%d,%d,%d.\n", + 0, 0, width, height);*/ + XPutImage(display_, bitmap, glyphGC, image, 0, 0, 0, 0, width, height); + XFree(image); + + return bitmap; +} +#endif /* defined(__CYGWIN32__) || defined(WIN32) */ diff --git a/nxcompshad/src/Win.h b/nxcompshad/src/Win.h new file mode 100644 index 000000000..615f9a5f5 --- /dev/null +++ b/nxcompshad/src/Win.h @@ -0,0 +1,232 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef __CYGWIN32__ + +#ifndef Win32Poller_H +#define Win32Poller_H + +//#include <nx-X11/X.h> + +#include <Windows.h> +#include <wingdi.h> +#include <winable.h> +#include <winuser.h> + +#define CAPTUREBLT 0x40000000 + +#define KEYEVENTF_SCANCODE 0x00000008 +#define MAPVK_VSC_TO_VK_EX 3 +// +// The CAPTUREBLT is a raster operation used +// in bit blit transfer. +// +// Using this operation includes any windows +// that are layered on top of your window in +// the resulting image. By default, the image +// only contains your window. +// + +#include "Core.h" + +typedef struct _keyTranslation +{ + unsigned char scancode; + unsigned short modifiers; + +}keyTranslation; + +class Poller : public CorePoller +{ + public: + + Display *display_; + keyTranslation *keymap_; + unsigned char keymapLoaded_; + int minKeycode_; + + Poller(Input *, Display *display, int = 16); + + ~Poller(); + + int init(); + + int updateCursor(Window, Visual*); + + private: + + + int Poller::updateShadowFrameBuffer(void); + void handleKeyboardEvent(Display *, XEvent *); + void handleWebKeyboardEvent(KeySym keysym, Bool isKeyPress); + void addToKeymap(char *keyname, unsigned char scancode, unsigned short modifiers, char *mapname); + int xkeymapRead(char *mapname); + FILE *xkeymapOpen(char *filename); + void xkeymapInit(char *keyMapName); + keyTranslation xkeymapTranslateKey(unsigned int keysym, unsigned int keycode, unsigned int state); + unsigned char getKeyState(unsigned int state, unsigned int keysym); + char *getKsname(unsigned int keysym); + unsigned char specialKeys(unsigned int keysym, unsigned int state, int pressed); + + unsigned char toggleSwitch(unsigned char ToggleStateClient, unsigned char ToggleStateServer, UINT scancode, + int *lengthArrayINPUT); + + void updateModifierState(UINT, unsigned char); + + unsigned char toggleServerState(UINT scancode); + unsigned char keyState(UINT scancode, UINT mapType); + unsigned char keyStateAsync(UINT scancode); + + void handleMouseEvent(Display *, XEvent *); + + Cursor createCursor(Window wnd, Visual *vis, unsigned int x, unsigned int y, int width, + int height, unsigned char *xormask, unsigned char *andmask); + + Pixmap createGlyph(Window wnd, Visual *visual, int width, int height, unsigned char *data); + + char isWinNT(); + char selectDesktop(HDESK new_desktop); + char selectDesktopByName(char *name); + void platformOS(); + char simulateCtrlAltDel(void); + DWORD platformID_; + + INPUT *pKey_, *pMouse_; + + char *keymapName_; + char *path_; + + unsigned char toggleButtonState_; + unsigned short serverModifierState_; + unsigned short savedServerModifierState_; + + void ensureServerModifiers(keyTranslation tr, int *lenghtArrayINPUT); + void restoreServerModifiers(UINT scancode); + unsigned char isModifier(UINT scancode); + + char sendInput(unsigned char scancode, unsigned char pressed, int *lengthArrayINPUT); + + char *getRect(XRectangle); + char checkDesktop(); + + char *DIBBuffer_; + + HCURSOR oldCursor_; + + VOID *pDIBbits_; + HDC screenDC_; + HDC memoryDC_; + BITMAPINFO bmi_; + HBITMAP screenBmp_; + + Cursor xCursor_; + +}; + +#undef TEST + +inline unsigned char Poller::toggleSwitch(unsigned char ToggleStateClient, unsigned char ToggleStateServer, + UINT scancode, int *lengthArrayINPUT) +{ + return 1; +} + +inline unsigned char Poller::toggleServerState(UINT scancode) +{ + return (GetKeyState(MapVirtualKeyEx(scancode, 3, GetKeyboardLayout((DWORD)NULL))) & 0x1); +} + +inline unsigned char Poller::keyStateAsync(UINT vKeycode) +{ + return GetAsyncKeyState(vKeycode); +} + +inline unsigned char Poller::keyState(UINT code, UINT mapType) +{ + if (mapType == 0) + { + // + // Virtual Keycode + // + return ((GetKeyState(code) & 0x80) == 0x80); + } + else + { + // + // scancode + // + return ((GetKeyState(MapVirtualKeyEx(code, 3, GetKeyboardLayout((DWORD)NULL))) & 0x80) == 0x80); + } +} + +inline char Poller::isWinNT() +{ + return (platformID_ == VER_PLATFORM_WIN32_NT); +} + +inline char Poller::sendInput(unsigned char scancode, unsigned char pressed, int *lengthArrayINPUT) +{ + DWORD keyEvent = 0; + DWORD extended = 0; + + if (scancode > 0) + { + if (pressed == 0) + { + keyEvent = KEYEVENTF_KEYUP; + } + + if (scancode & 0x80) + { + scancode &= ~0x80; + extended = KEYEVENTF_EXTENDEDKEY; + } + + pKey_[*lengthArrayINPUT].ki.wScan = (WORD) scancode; + pKey_[*lengthArrayINPUT].ki.dwFlags = (DWORD) (keyEvent | KEYEVENTF_SCANCODE | extended); + (*lengthArrayINPUT)++; + } + + + if (*lengthArrayINPUT > 0) { + // FIXME: Remove me. + logTest("Poller::sendInput", "length Input [%d] event: %s", *lengthArrayINPUT, + pressed == 1 ? "KeyPress": "KeyRelease"); + + if (SendInput(*lengthArrayINPUT, pKey_, sizeof(INPUT)) == 0) + { + logTest("Poller::sendInput", "Failed SendInput, event: %s", pressed == 1 ? "KeyPress": "KeyRelease"); + *lengthArrayINPUT = 0; + return 0; + } + + *lengthArrayINPUT = 0; + } + + return 1; +} +#endif /* Win32Poller_H */ + +#endif /* __CYGWIN32__ */ diff --git a/nxcompshad/src/X11.cpp b/nxcompshad/src/X11.cpp new file mode 100644 index 000000000..3643df731 --- /dev/null +++ b/nxcompshad/src/X11.cpp @@ -0,0 +1,1598 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if !defined(__CYGWIN32__) && !defined(WIN32) + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include <nx-X11/Xlibint.h> +#include <nx-X11/Xproto.h> +#include <nx-X11/keysym.h> +#include "X11/include/XTest_nxcompshad.h" +#include <string.h> +#include <sys/ipc.h> +#include <sys/shm.h> + +#include "Poller.h" +#include "Logger.h" +#include "Shadow.h" + +#define ROUNDUP(nbytes, pad) ((((nbytes) + ((pad)-1)) / (pad)) * ((pad)>>3)) + +#undef TRANSLATE_KEYCODES +#define TRANSLATE_ALWAYS + +typedef struct { + KeySym *map; + KeyCode minKeyCode, + maxKeyCode; + int mapWidth; +} KeySymsRec, *KeySymsPtr; + +extern KeySymsPtr NXShadowKeymap; + +typedef struct _KeyPressed +{ + KeyCode keyRcvd; + KeyCode keySent; + struct _KeyPressed *next; +} KeyPressedRec; + +static KeyPressedRec *shadowKeyPressedPtr = NULL; + +static KeySym *shadowKeysyms = NULL; +static KeySym *masterKeysyms = NULL; + +static KeySym *shadowKeymap = NULL; + +static int shadowMinKey, shadowMaxKey, shadowMapWidth; +static int masterMinKey, masterMaxKey, masterMapWidth; + +static int leftShiftOn = 0; +static int rightShiftOn = 0; +static int modeSwitchOn = 0; +static int level3ShiftOn = 0; +static int altROn = 0; + +static int sentFakeLShiftPress = 0; +static int sentFakeLShiftRelease = 0; +static int sentFakeRShiftRelease = 0; +static int sentFakeModeSwitchPress = 0; +static int sentFakeModeSwitchRelease = 0; +static int sentFakeLevel3ShiftPress = 0; +static int sentFakeLevel3ShiftRelease = 0; +static int sentFakeAltRRelease = 0; + +static int shmInitTrap = 0; + +Poller::Poller(Input *input, Display *display, int depth) : CorePoller(input, display) +{ + logTrace("Poller::Poller"); + + display_ = NULL; + shadowDisplayName_ = input -> getShadowDisplayName(); + + tmpBuffer_ = NULL; + + xtestExtension_ = -1; + shmExtension_ = -1; + randrExtension_ = -1; + damageExtension_ = -1; + + shadowDisplayUid_ = -1; + + image_ = NULL; + + shminfo_ = NULL; +} + +Poller::~Poller() +{ + logTrace("Poller::~Poller"); + + if (shmExtension_ == 1) + { + XShmDetach(display_, shminfo_); + XDestroyImage(image_); + shmdt(shminfo_ -> shmaddr); + shmctl(shminfo_ -> shmid, IPC_RMID, 0); + } + + if (shminfo_ != NULL) + { + delete shminfo_; + + shminfo_ = NULL; + } + + if (display_ != NULL) + { + XCloseDisplay(display_); + } + + if (tmpBuffer_ != NULL && shmExtension_ != -1 && damageExtension_ == 1) + { + XFree(tmpBuffer_); + + tmpBuffer_ = NULL; + } +} + +int Poller::init() +{ + logTrace("Poller::init"); + + if (display_ == NULL) + { + display_ = XOpenDisplay(shadowDisplayName_); + + setShadowDisplay(display_); + } + + logTest("Poller::init:" ,"Shadow display [%p] name [%s].", (Display *) display_, shadowDisplayName_); + + if (display_ == NULL) + { + logTest("Poller::init", "Failed to connect to display [%s].", shadowDisplayName_ ? shadowDisplayName_ : ""); + + return -1; + } + + setRootSize(); + + logTest("Poller::init", "Screen geometry is [%d, %d] depth is [%d] bpl [%d] bpp [%d].", + width_, height_, depth_, bpl_, bpp_); + + xtestInit(); + + shmInit(); + + randrInit(); + + damageInit(); + + return CorePoller::init(); +} + +int Poller::updateShadowFrameBuffer(void) +{ + if (shmExtension_ == 1) + { + if (XShmGetImage(display_, DefaultRootWindow(display_), image_, 0, 0, AllPlanes) == 0) + { + logDebug("Poller::updateShadowFrameBuffer", "XShmGetImage failed!"); + + return -1; + } + } + else + { + image_ = XGetImage(display_, DefaultRootWindow(display_), 0, 0, width_, + height_, AllPlanes, ZPixmap); + + if (image_ == NULL) + { + logDebug("Poller::updateShadowFrameBuffer", "XGetImage failed!"); + + return -1; + } + } + + return 1; +} + +char *Poller::getRect(XRectangle r) +{ + logTrace("Poller::getRect"); + + logDebug("Poller::getRect", "Going to retrive rectangle [%d, %d, %d, %d].", + r.x, r.y, r.width, r.height); + + if (shmExtension_ == 1) + { + if (damageExtension_ == 1) + { + image_ -> width = r.width; + image_ -> height = r.height; + + image_ -> bytes_per_line = ROUNDUP((image_ -> bits_per_pixel * image_ -> width), image_ -> bitmap_pad); + + if (XShmGetImage(display_, DefaultRootWindow(display_), image_, r.x, r.y, AllPlanes) == 0) + { + logDebug("Poller::getRect", "XShmGetImage failed!"); + + return NULL; + } + + tmpBuffer_ = image_ -> data; + } + else + { + image_ -> width = r.width; + image_ -> height = r.height; + + image_ -> bytes_per_line = ROUNDUP((image_ -> bits_per_pixel * image_ -> width), image_ -> bitmap_pad); + + if (XShmGetImage(display_, DefaultRootWindow(display_), image_, r.x, r.y, AllPlanes) == 0) + { + logDebug("Poller::getRect", "XShmGetImage failed!"); + } + + tmpBuffer_ = image_ -> data; + } + } + else + { + if (tmpBuffer_) + { + XFree(tmpBuffer_); + tmpBuffer_ = NULL; + } + + image_ = XGetImage(display_, DefaultRootWindow(display_), r.x, r.y, r.width, r.height, AllPlanes, ZPixmap); + + if (image_ == NULL) + { + logError("Poller::getRect", ESET(ENOMSG)); + + return NULL; + } + + tmpBuffer_ = image_ -> data; + + if (image_ -> obdata) + { + XFree(image_ -> obdata); + } + + XFree(image_); + + image_ = NULL; + } + + return tmpBuffer_; +} + +void Poller::shmInit(void) +{ + int major, minor; + int pixmaps; + + logTest("Poller::shmInit", "Added shmExtension_ [%d].", shmExtension_); + + if (shmExtension_ >= 0) + { + logDebug("Poller::shmInit", "Called with shared memory already initialized."); + + if (shmInitTrap == 0) + { + return; + } + } + + if (shmExtension_ < 0 && NXShadowOptions.optionShmExtension == 0) + { + shmExtension_ = 0; + + logUser("Poller::shmInit: Disabling use of MIT-SHM extension.\n"); + + return; + } + + if (XShmQueryVersion(display_, &major, &minor, &pixmaps) == 0) + { + logDebug("Poller::shmInit", "MIT_SHM: Shared memory extension not available."); + + shmExtension_ = 0; + } + else + { + logDebug("Poller::shmInit", "MIT_SHM: Shared memory extension available."); + + if (shminfo_ != NULL) + { + destroyShmImage(); + } + + shminfo_ = (XShmSegmentInfo* ) new XShmSegmentInfo; + + if (shminfo_ == NULL) + { + logError("Poller::shmInit", ESET(ENOMEM)); + + shmExtension_ = 0; + + return; + } + + image_ = (XImage *)XShmCreateImage(display_, display_ -> screens[0].root_visual, depth_, ZPixmap, + NULL, shminfo_, width_, height_); + + if (image_ == NULL) + { + logError("Poller::shmInit", ESET(ENOMSG)); + + shmExtension_ = 0; + + return; + } + + shadowDisplayUid_ = NXShadowOptions.optionShadowDisplayUid; + + logDebug("Poller::shmInit", "Master X server uid [%d].", NXShadowOptions.optionShadowDisplayUid); + + shminfo_ -> shmid = shmget(IPC_PRIVATE, image_ -> bytes_per_line * image_ -> height, IPC_CREAT | 0666); + + if (shminfo_ -> shmid < 0) + { + logDebug("Poller::shmInit", "kernel id error."); + + shmExtension_ = 0; + + return; + } + + logDebug("Poller::shmInit", "Created shm segment with shmid [%d].", shminfo_ -> shmid); + + shminfo_ -> shmaddr = (char *)shmat(shminfo_ -> shmid, 0, 0); + + if (shminfo_ -> shmaddr < 0) + { + logWarning("Poller::shmInit", "Couldn't attach to shm segment."); + } + + logDebug("Poller::shmInit", "shminfo_ -> shmaddr [%p].", shminfo_ -> shmaddr); + + image_ -> data = shminfo_ -> shmaddr; + + shminfo_ -> readOnly = 0; + + if (XShmAttach(display_, shminfo_) == 0) + { + logDebug("Poller::shmInit", "XShmAttach failed."); + + shmExtension_ = 0; + + return; + } + + // + // Mark the shm segment to be destroyed after + // the last process detach. Let the X server + // complete the X_ShmAttach request, before. + // + + XSync(display_, 0); + + struct shmid_ds ds; + + shmctl(shminfo_ -> shmid, IPC_STAT, &ds); + + if (shadowDisplayUid_ != -1) + { + ds.shm_perm.uid = (ushort) shadowDisplayUid_; + } + else + { + logWarning("Poller::shmInit", "Couldn't set uid for shm segment."); + } + + ds.shm_perm.mode = 0600; + + shmctl(shminfo_ -> shmid, IPC_SET, &ds); + + shmctl(shminfo_ -> shmid, IPC_STAT, &ds); + + shmctl(shminfo_ -> shmid, IPC_RMID, 0); + + logDebug("Poller::shmInit", "Number of attaches to shm segment [%d] are [%d].\n", + shminfo_ -> shmid, (int) ds.shm_nattch); + + if (ds.shm_nattch > 2) + { + logWarning("Poller::shmInit", "More than two attaches to the shm segment."); + + destroyShmImage(); + + shmExtension_ = 0; + + return; + } + + shmExtension_ = 1; + } +} + +void Poller::keymapShadowInit(Display *display) +{ + int i, len; + CARD32 *map; + + if (NXShadowKeymap != NULL) + { + shadowMinKey = NXShadowKeymap -> minKeyCode; + shadowMaxKey = NXShadowKeymap -> maxKeyCode; + shadowMapWidth = NXShadowKeymap -> mapWidth; + + len = (shadowMaxKey - shadowMinKey + 1) * shadowMapWidth; + + map = (CARD32 *) NXShadowKeymap -> map; + + if (shadowKeymap != NULL) + { + free(shadowKeymap); + } + + shadowKeymap = (KeySym *) malloc(len * sizeof(KeySym)); + + if (shadowKeymap != NULL) + { + for (i = 0; i < len; i++) + { + shadowKeymap[i] = map[i]; + } + + shadowKeysyms = shadowKeymap; + } + } + + if (shadowKeysyms == NULL) + { + XDisplayKeycodes(display, &shadowMinKey, &shadowMaxKey); + + shadowKeysyms = XGetKeyboardMapping(display, shadowMinKey, shadowMaxKey - shadowMinKey + 1, + &shadowMapWidth); + } + + #ifdef DEBUG + if (shadowKeysyms != NULL) + { + for (i = 0; i < (shadowMaxKey - shadowMinKey + 1) * shadowMapWidth; i++) + { + if (i % shadowMapWidth == 0) + { + logDebug("Poller::keymapShadowInit", "keycode [%d]", (int) (i / shadowMapWidth)); + } + + logDebug("\tkeysym", " [%x] [%s]", (unsigned int) shadowKeysyms[i], XKeysymToString(shadowKeysyms[i])); + } + } + #endif +} + +void Poller::keymapMasterInit() +{ + XDisplayKeycodes(display_, &masterMinKey, &masterMaxKey); + + masterKeysyms = XGetKeyboardMapping(display_, masterMinKey, masterMaxKey - masterMinKey + 1, + &masterMapWidth); + + #ifdef DEBUG + if (masterKeysyms != NULL) + { + for (int i = 0; i < (masterMaxKey - masterMinKey + 1) * masterMapWidth; i++) + { + if (i % masterMapWidth == 0) + { + logDebug("Poller::keymapMasterInit", "keycode [%d]", (int) (i / masterMapWidth)); + } + + logDebug("\tkeysym", " [%x] [%s]", (unsigned int) masterKeysyms[i], XKeysymToString(masterKeysyms[i])); + } + } + #endif +} + +KeySym Poller::keymapKeycodeToKeysym(KeyCode keycode, KeySym *keysyms, + int minKey, int mapWidth, int col) +{ + int index = ((keycode - minKey) * mapWidth) + col; + return keysyms[index]; +} + +KeyCode Poller::keymapKeysymToKeycode(KeySym keysym, KeySym *keysyms, + int minKey, int maxKey, int mapWidth, int *col) +{ + for (int i = 0; i < (maxKey - minKey + 1) * mapWidth; i++) + { + if (keysyms[i] == keysym) + { + *col = i % mapWidth; + return i / mapWidth + minKey; + } + } + return 0; +} + +KeyCode Poller::translateKeysymToKeycode(KeySym keysym, int *col) +{ + KeyCode keycode; + + keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col); + + if (keycode == 0) + { + if (((keysym >> 8) == 0) && (keysym >= XK_a) && (keysym <= XK_z)) + { + /* + * The master session has a Solaris keyboard. + */ + + keysym -= XK_a - XK_A; + + keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col); + } + else if (keysym == XK_Shift_R) + { + keysym = XK_Shift_L; + + keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col); + } + else if (keysym == XK_Shift_L) + { + keysym = XK_Shift_R; + + keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col); + } + else if (keysym == XK_ISO_Level3_Shift) + { + keysym = XK_Mode_switch; + + if ((keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col)) == 0) + { + keysym = XK_Alt_R; + + keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col); + } + } + else if (keysym == XK_Alt_R) + { + keysym = XK_ISO_Level3_Shift; + + if ((keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col)) == 0) + { + keysym = XK_Mode_switch; + + keycode = keymapKeysymToKeycode(keysym, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, col); + } + } + } + return keycode; +} + +Bool Poller::checkModifierKeys(KeySym keysym, Bool isKeyPress) +{ + switch (keysym) + { + case XK_Shift_L: + leftShiftOn = isKeyPress; + return True; + case XK_Shift_R: + rightShiftOn = isKeyPress; + return True; + case XK_Mode_switch: + modeSwitchOn = isKeyPress; + return True; + case XK_ISO_Level3_Shift: + level3ShiftOn = isKeyPress; + return True; + case XK_Alt_R: + altROn = isKeyPress; + return True; + default: + return False; + } +} + +void Poller::sendFakeModifierEvents(int pos, Bool skip) +{ + KeySym fakeKeysym; + int col; + + if ((!leftShiftOn && !rightShiftOn) && + (!modeSwitchOn && !level3ShiftOn && !altROn)) + { + if (pos == 1 || pos == 3) + { + fakeKeysym = keymapKeysymToKeycode(XK_Shift_L, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + sentFakeLShiftPress = 1; + } + if (pos == 2 || pos == 3) + { + fakeKeysym = keymapKeysymToKeycode(XK_ISO_Level3_Shift, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + + if (fakeKeysym == 0) + { + fakeKeysym = keymapKeysymToKeycode(XK_Mode_switch, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + sentFakeModeSwitchPress = 1; + } + else + { + sentFakeLevel3ShiftPress = 1; + } + + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + } + } + + else if ((leftShiftOn || rightShiftOn) && + (!modeSwitchOn && !level3ShiftOn && !altROn)) + { + if ((pos == 0 && !skip) || pos == 2) + { + if (leftShiftOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Shift_L, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeLShiftRelease = 1; + } + if (rightShiftOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Shift_R, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeRShiftRelease = 1; + } + } + if (pos == 2 || pos ==3) + { + fakeKeysym = keymapKeysymToKeycode(XK_ISO_Level3_Shift, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + + if (fakeKeysym == 0) + { + fakeKeysym = keymapKeysymToKeycode(XK_Mode_switch, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + sentFakeModeSwitchPress = 1; + } + else + { + sentFakeLevel3ShiftPress = 1; + } + + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + } + } + + else if ((!leftShiftOn && !rightShiftOn) && + (modeSwitchOn || level3ShiftOn || altROn)) + { + if (pos == 1 || pos == 3) + { + fakeKeysym = keymapKeysymToKeycode(XK_Shift_L, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + sentFakeLShiftPress = 1; + } + if (pos == 0 || pos == 1) + { + if (modeSwitchOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Mode_switch, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeModeSwitchRelease = 1; + } + if (level3ShiftOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_ISO_Level3_Shift, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeLevel3ShiftRelease = 1; + } + if (altROn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Alt_R, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeAltRRelease = 1; + } + } + } + + else if ((leftShiftOn || rightShiftOn) && + (modeSwitchOn || level3ShiftOn || altROn)) + { + if (pos == 0 || pos == 2) + { + if (leftShiftOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Shift_L, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeLShiftRelease = 1; + } + if (rightShiftOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Shift_R, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeRShiftRelease = 1; + } + } + if (pos == 0 || pos == 1) + { + if (modeSwitchOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Mode_switch, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeModeSwitchRelease = 1; + } + if (level3ShiftOn) + { + fakeKeysym = keymapKeysymToKeycode(XK_ISO_Level3_Shift, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeLevel3ShiftRelease = 1; + } + if (altROn) + { + fakeKeysym = keymapKeysymToKeycode(XK_Alt_R, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + sentFakeAltRRelease = 1; + } + } + } +} + +void Poller::cancelFakeModifierEvents() +{ + KeySym fakeKeysym; + int col; + + if (sentFakeLShiftPress) + { + logTest("Poller::handleKeyboardEvent", "Fake Shift_L key press event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending fake Shift_L key release event"); + + fakeKeysym = keymapKeysymToKeycode(XK_Shift_L, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + + sentFakeLShiftPress = 0; + } + + if (sentFakeLShiftRelease) + { + logTest("Poller::handleKeyboardEvent", "Fake Shift_L key release event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending fake Shift_L key press event"); + + fakeKeysym = keymapKeysymToKeycode(XK_Shift_L, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + + sentFakeLShiftRelease = 0; + } + + if (sentFakeRShiftRelease) + { + logTest("Poller::handleKeyboardEvent", "Fake Shift_R key release event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending fake Shift_R key press event"); + + fakeKeysym = keymapKeysymToKeycode(XK_Shift_R, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + + sentFakeRShiftRelease = 0; + } + + if (sentFakeModeSwitchPress) + { + logTest("Poller::handleKeyboardEvent", "Fake Mode_switch key press event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending fake Mode_switch key release event"); + + fakeKeysym = keymapKeysymToKeycode(XK_Mode_switch, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + + sentFakeModeSwitchPress = 0; + } + + if (sentFakeModeSwitchRelease) + { + logTest("Poller::handleKeyboardEvent", "Fake Mode_switch key release event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending Mode_switch key press event"); + + fakeKeysym = keymapKeysymToKeycode(XK_Mode_switch, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + + sentFakeModeSwitchRelease = 0; + } + + if (sentFakeLevel3ShiftPress) + { + logTest("Poller::handleKeyboardEvent", "Fake ISO_Level3_Shift key press event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending fake ISO_Level3_Shift key release event"); + + fakeKeysym = keymapKeysymToKeycode(XK_ISO_Level3_Shift, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 0, 0); + + sentFakeLevel3ShiftPress = 0; + } + + if (sentFakeLevel3ShiftRelease) + { + logTest("Poller::handleKeyboardEvent", "Fake ISO_Level3_Shift key release event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending fake ISO_Level3_Shift key press event"); + + fakeKeysym = keymapKeysymToKeycode(XK_ISO_Level3_Shift, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + + sentFakeLevel3ShiftRelease = 0; + } + + if (sentFakeAltRRelease) + { + logTest("Poller::handleKeyboardEvent", "Fake XK_Alt_R key release event has been sent"); + logTest("Poller::handleKeyboardEvent", "Sending fake XK_Alt_R key press event"); + + fakeKeysym = keymapKeysymToKeycode(XK_Alt_R, masterKeysyms, masterMinKey, + masterMaxKey, masterMapWidth, &col); + XTestFakeKeyEvent(display_, fakeKeysym, 1, 0); + + sentFakeAltRRelease = 0; + } +} + +Bool Poller::keyIsDown(KeyCode keycode) +{ + KeyPressedRec *downKey; + + downKey = shadowKeyPressedPtr; + + while (downKey) + { + if (downKey -> keyRcvd == keycode) + { + return True; + } + downKey = downKey -> next; + } + + return False; +} + +void Poller::addKeyPressed(KeyCode received, KeyCode sent) +{ + KeyPressedRec *downKey; + + if (!keyIsDown(received)) + { + if (shadowKeyPressedPtr == NULL) + { + shadowKeyPressedPtr = (KeyPressedRec *) malloc(sizeof(KeyPressedRec)); + + shadowKeyPressedPtr -> keyRcvd = received; + shadowKeyPressedPtr -> keySent = sent; + shadowKeyPressedPtr -> next = NULL; + } + else + { + downKey = shadowKeyPressedPtr; + + while (downKey -> next != NULL) + { + downKey = downKey -> next; + } + + downKey -> next = (KeyPressedRec *) malloc(sizeof(KeyPressedRec)); + + downKey -> next -> keyRcvd = received; + downKey -> next -> keySent = sent; + downKey -> next -> next = NULL; + } + } +} + +KeyCode Poller::getKeyPressed(KeyCode received) +{ + KeyCode sent; + KeyPressedRec *downKey; + KeyPressedRec *tempKey; + + if (shadowKeyPressedPtr != NULL) + { + if (shadowKeyPressedPtr -> keyRcvd == received) + { + sent = shadowKeyPressedPtr -> keySent; + + tempKey = shadowKeyPressedPtr; + shadowKeyPressedPtr = shadowKeyPressedPtr -> next; + free(tempKey); + + return sent; + } + else + { + downKey = shadowKeyPressedPtr; + + while (downKey -> next != NULL) + { + if (downKey -> next -> keyRcvd == received) + { + sent = downKey -> next -> keySent; + + tempKey = downKey -> next; + downKey -> next = downKey -> next -> next; + free(tempKey); + + return sent; + } + else + { + downKey = downKey -> next; + } + } + } + } + return 0; +} + +void Poller::handleKeyboardEvent(Display *display, XEvent *event) +{ + if (xtestExtension_ == 0 || display_ == 0) + { + return; + } + + logTest("Poller::handleKeyboardEvent", "Handling event at [%p]", event); + +#ifdef TRANSLATE_ALWAYS + + KeyCode keycode; + KeySym keysym; + + int col = 0; + + Bool isKeyPress = False; + Bool isModifier = False; + Bool isShiftComb = False; + Bool skip = False; + + if (event -> type == KeyPress) + { + isKeyPress = True; + } + + if (shadowKeysyms == NULL) + { + keymapShadowInit(event -> xkey.display); + } + + if (masterKeysyms == NULL) + { + keymapMasterInit(); + } + + if (shadowKeysyms == NULL || masterKeysyms == NULL) + { + logTest("Poller::handleKeyboardEvent", "Unable to initialize keymaps. Do not translate"); + + keycode = event -> xkey.keycode; + + goto SendKeycode; + } + + keysym = keymapKeycodeToKeysym(event -> xkey.keycode, shadowKeysyms, + shadowMinKey, shadowMapWidth, 0); + + isModifier = checkModifierKeys(keysym, isKeyPress); + + if (event -> type == KeyRelease) + { + KeyCode keycodeToSend; + + keycodeToSend = getKeyPressed(event -> xkey.keycode); + + if (keycodeToSend) + { + keycode = keycodeToSend; + + goto SendKeycode; + } + } + + /* + * Convert case for Solaris keyboard. + */ + + if (((keysym >> 8) == 0) && (keysym >= XK_A) && (keysym <= XK_Z)) + { + if (!leftShiftOn && !rightShiftOn) + { + keysym += XK_a - XK_A; + } + else + { + skip = True; + } + } + + if (!isModifier) + { + if ((leftShiftOn || rightShiftOn) && + (!modeSwitchOn && !level3ShiftOn && !altROn) && + !skip) + { + KeySym tempKeysym = keymapKeycodeToKeysym(event -> xkey.keycode, shadowKeysyms, + shadowMinKey, shadowMapWidth, 1); + + if (tempKeysym == 0) + { + isShiftComb = True; + } + else + { + keysym = tempKeysym; + } + } + else if ((!leftShiftOn && !rightShiftOn) && + (modeSwitchOn || level3ShiftOn || altROn)) + { + keysym = keymapKeycodeToKeysym(event -> xkey.keycode, shadowKeysyms, + shadowMinKey, shadowMapWidth, 2); + } + else if ((leftShiftOn || rightShiftOn) && + (modeSwitchOn || level3ShiftOn || altROn)) + { + keysym = keymapKeycodeToKeysym(event -> xkey.keycode, shadowKeysyms, + shadowMinKey, shadowMapWidth, 3); + } + } + + if (keysym == 0) + { + logTest("Poller::handleKeyboardEvent", "Null keysym. Return"); + + return; + } + + logTest("Poller::handleKeyboardEvent", "keysym [%x] [%s]", + (unsigned int)keysym, XKeysymToString(keysym)); + + if (keysym == XK_Mode_switch) + { + keysym = XK_ISO_Level3_Shift; + } + + keycode = translateKeysymToKeycode(keysym, &col); + + if (keycode == 0) + { + logTest("Poller::handleKeyboardEvent", "No keycode found for keysym [%x] [%s]. Return", + (unsigned int)keysym, XKeysymToString(keysym)); + return; + } + + logTest("Poller::handleKeyboardEvent", "keycode [%d] translated into keycode [%d]", + (int)event -> xkey.keycode, (unsigned int)keycode); + + if (event -> type == KeyPress) + { + addKeyPressed(event -> xkey.keycode, keycode); + } + + /* + * Send fake modifier events. + */ + + if (!isModifier && isShiftComb == False) + { + sendFakeModifierEvents(col, ((keysym >> 8) == 0) && (keysym >= XK_A) && (keysym <= XK_Z)); + } + +SendKeycode: + + /* + * Send the event. + */ + + XTestFakeKeyEvent(display_, keycode, isKeyPress, 0); + + /* + * Check if fake modifier events have been sent. + */ + + cancelFakeModifierEvents(); + +#else // TRANSLATE_ALWAYS + + // + // Use keysyms to translate keycodes across different + // keyboard models. Unuseful when both keyboards have + // same keycodes (e.g. both are pc keyboards). + // + + #ifdef TRANSLATE_KEYCODES + + KeyCode keycode = XKeysymToKeycode(display_, XK_A); + + if (XKeysymToKeycode(event -> xkey.display, XK_A) != keycode) + { + KeySym keysym = XKeycodeToKeysym(event -> xkey.display, event -> xkey.keycode, 0); + + if (keysym == XK_Mode_switch || keysym == XK_ISO_Level3_Shift) + { + logUser("Poller::handleKeyboardEvent: keysym [%x].\n", (unsigned int)keysym); + + if (XKeycodeToKeysym(display_, 113, 0) == XK_ISO_Level3_Shift || + (XKeycodeToKeysym(display_, 124, 0) == XK_ISO_Level3_Shift)) + { + event -> xkey.keycode = 113; + } + else + { + event -> xkey.keycode = XKeysymToKeycode(display_, XK_Mode_switch); + } + + logUser("Poller::handleKeyboardEvent: keycode translated to [%x].\n", (unsigned int)event -> xkey.keycode); + } + else + { + event -> xkey.keycode = XKeysymToKeycode(display_, keysym); + } + } + + #endif // TRANSLATE_KEYCODES + + if (event -> type == KeyPress) + { + XTestFakeKeyEvent(display_, event -> xkey.keycode, 1, 0); + } + else if (event -> type == KeyRelease) + { + XTestFakeKeyEvent(display_, event -> xkey.keycode, 0, 0); + } + +#endif // TRANSLATE_ALWAYS +} + +void Poller::handleWebKeyboardEvent(KeySym keysym, Bool isKeyPress) +{ + KeyCode keycode; + int col; + + if (masterKeysyms == NULL) + { + keymapMasterInit(); + } + + if (masterKeysyms == NULL) + { + logTest("Poller::handleWebKeyboardEvent", "Unable to initialize keymap"); + + return; + } + + keycode = translateKeysymToKeycode(keysym, &col); + + if (keycode == 0) + { + logTest("Poller::handleKeyboardEvent", "No keycode found for keysym [%x] [%s]. Return", + (unsigned int)keysym, XKeysymToString(keysym)); + return; + } + + logTest("Poller::handleKeyboardEvent", "keysym [%x] [%s] translated into keycode [%x]", + (unsigned int)keysym, XKeysymToString(keysym), (unsigned int)keycode); + + /* + * Send fake modifier events. + */ + + if (!checkModifierKeys(keysym, isKeyPress)) + { + sendFakeModifierEvents(col, False); + } + + /* + * Send the event. + */ + + XTestFakeKeyEvent(display_, keycode, isKeyPress, 0); + + /* + * Check if fake modifier events have been sent. + */ + + cancelFakeModifierEvents(); + +} + +void Poller::handleMouseEvent(Display *display, XEvent *event) +{ + if (xtestExtension_ == 0 || display_ == 0) + { + return; + } + + if (event -> type == MotionNotify) + { + XTestFakeMotionEvent(display_, 0, event -> xmotion.x, event -> xmotion.y, 0); + } + else if (event -> type == ButtonPress) + { + XTestFakeButtonEvent(display_, event -> xbutton.button, True, 0); + } + else if (event -> type == ButtonRelease) + { + XTestFakeButtonEvent(display_, event -> xbutton.button, False, 0); + } + + XFlush(display_); +} + +void Poller::setRootSize(void) +{ + width_ = WidthOfScreen(DefaultScreenOfDisplay(display_)); + height_ = HeightOfScreen(DefaultScreenOfDisplay(display_)); + depth_ = DefaultDepth(display_, DefaultScreen(display_)); + + if (depth_ == 8) bpp_ = 1; + else if (depth_ == 16) bpp_ = 2; + else bpp_ = 4; + + bpl_ = width_ * bpp_; +} + +void Poller::destroyShmImage(void) +{ + XShmDetach(display_, shminfo_); + XDestroyImage(image_); + image_ = NULL; + + shmdt(shminfo_ -> shmaddr); + shmctl(shminfo_ -> shmid, IPC_RMID, 0); + + delete shminfo_; + shminfo_ = NULL; +} + +void Poller::xtestInit(void) +{ + int eventBase; + int errorBase; + int versionMajor; + int versionMinor; + int result; + + xtestExtension_ = 0; + + result = XTestQueryExtension(display_, &eventBase, &errorBase, &versionMajor, &versionMinor); + + if (result == 0) + { + xtestExtension_ = 0; + + logWarning("Poller::xtestInit", "Failed while querying for XTEST extension."); + } + else + { + logDebug("Poller::xtestInit", "XTEST version %d.%d.", versionMajor, versionMinor); + + xtestExtension_ = 1; + } + + // + // Make this client impervious to grabs. + // + + if (xtestExtension_ == 1) + { + XTestGrabControl(display_, 1); + } +} + +void Poller::randrInit(void) +{ + int randrEventBase; + int randrErrorBase; + + if (XRRQueryExtension(display_, &randrEventBase, &randrErrorBase) == 0) + { + logWarning("Poller::randrInit", "Randr extension not supported on this " + "display."); + + randrExtension_ = 0; + + return; + } + + XRRSelectInput(display_, DefaultRootWindow(display_), + RRScreenChangeNotifyMask); + + randrEventBase_ = randrEventBase; + + randrExtension_ = 1; + + return; +} + +void Poller::damageInit(void) +{ + int damageMajorVersion = 0; + int damageMinorVersion = 0; + + int damageEventBase = 0; + int damageErrorBase = 0; + + if (damageExtension_ >= 0) + { + logDebug("Poller::damageInit", "Called with damage already initialized."); + } + + if (damageExtension_ == 0) + { + logDebug("Poller::damageInit", "Damage disabled. Skip initialization."); + + return; + } + + if (damageExtension_ < 0 && NXShadowOptions.optionDamageExtension == 0) + { + damageExtension_ = 0; + + logUser("Poller::damageInit: Disabling use of DAMAGE extension.\n"); + + return; + } + + damageExtension_ = 0; + + mirrorChanges_ = 0; + + if (XDamageQueryExtension(display_, &damageEventBase, &damageErrorBase) == 0) + { + logUser("Poller::damageInit: DAMAGE not supported.\n"); + + return; + } + #ifdef DEBUG + else + { + fprintf(stderr, "Poller::damageInit: DAMAGE supported. " + "Event base [%d] error base [%d].\n", damageEventBase, damageErrorBase); + } + #endif + + damageEventBase_ = damageEventBase; + + if (XDamageQueryVersion(display_, &damageMajorVersion, &damageMinorVersion) == 0) + { + logWarning("Poller::damageInit", "Error on querying DAMAGE version.\n"); + + damageExtension_ = 0; + + return; + } + #ifdef DEBUG + else + { + fprintf(stderr, "Poller::damageInit: DAMAGE version %d.%d.\n", + damageMajorVersion, damageMinorVersion); + } + #endif + + damage_ = XDamageCreate(display_, DefaultRootWindow(display_), XDamageReportRawRectangles); + + damageExtension_= 1; + + mirror_ = 1; + + return; +} + +void Poller::getEvents(void) +{ + XEvent X; + + if (damageExtension_ == 1) + { + XDamageSubtract(display_, damage_, None, None); + } + + XSync(display_, 0); + + while (XCheckIfEvent(display_, &X, anyEventPredicate, NULL) == 1) + { + if (randrExtension_ == 1 && (X.type == randrEventBase_ + RRScreenChangeNotify || X.type == ConfigureNotify)) + { + XRRUpdateConfiguration(&X); + + handleRRScreenChangeNotify(&X); + + continue; + } + + if (damageExtension_ == 1 && X.type == damageEventBase_ + XDamageNotify) + { + handleDamageNotify(&X); + } + } + + if (damageExtension_ == 1) + { + updateDamagedAreas(); + } + + XFlush(display_); +} + +void Poller::handleRRScreenChangeNotify(XEvent *X) +{ + return; +} + +void Poller::handleDamageNotify(XEvent *X) +{ + XDamageNotifyEvent *e = (XDamageNotifyEvent *) X; + + // + // e->drawable is the window ID of the damaged window + // e->geometry is the geometry of the damaged window + // e->area is the bounding rect for the damaged area + // e->damage is the damage handle returned by XDamageCreate() + // + + #ifdef DEBUG + fprintf(stderr, "handleDamageNotify: drawable [%d] damage [%d] geometry [%d][%d][%d][%d] area [%d][%d][%d][%d].\n", + (int) e -> drawable, (int) e -> damage, e -> geometry.x, e -> geometry.y, + e -> geometry.width, e -> geometry.height, e -> area.x, e -> area.y, + e -> area.width, e -> area.height); + #endif + + XRectangle rectangle = {e -> area.x, e -> area.y, e -> area.width, e -> area.height}; + + XUnionRectWithRegion(&rectangle, lastUpdatedRegion_, lastUpdatedRegion_); + + mirrorChanges_ = 1; + + return; +} + +void Poller::updateDamagedAreas(void) +{ + BOX *boxPtr; + + XRectangle rectangle; + + int i; + int y; + + for (i = 0; i < lastUpdatedRegion_ -> numRects; i++) + { + boxPtr = lastUpdatedRegion_ -> rects + i; + + if (shmExtension_ == 1) + { + image_ -> width = boxPtr -> x2 - boxPtr -> x1; + image_ -> height = boxPtr -> y2 - boxPtr -> y1; + image_ -> bytes_per_line = + ROUNDUP((image_ -> bits_per_pixel * image_ -> width), + image_ -> bitmap_pad); + + if (XShmGetImage(display_, DefaultRootWindow(display_), image_, + boxPtr -> x1, boxPtr -> y1, AllPlanes) == 0) + { + logDebug("Poller::updateDamagedAreas", "XShmGetImage failed!"); + + return; + } + } + else if (shmExtension_ == 0) + { + image_ = XGetImage(display_, DefaultRootWindow(display_), boxPtr -> x1, + boxPtr -> y1, boxPtr -> x2 - boxPtr -> x1, + boxPtr -> y2 - boxPtr -> y1, AllPlanes, + ZPixmap); + + if (image_ == NULL) + { + logDebug("Poller::updateDamagedAreas", "XGetImage failed!"); + + return; + } + + image_ -> width = boxPtr -> x2 - boxPtr -> x1; + image_ -> height = boxPtr -> y2 - boxPtr -> y1; + image_ -> bytes_per_line = + ROUNDUP((image_ -> bits_per_pixel * image_ -> width), + image_ -> bitmap_pad); + } + + rectangle.height = 1; + rectangle.width = image_ -> width; + rectangle.x = boxPtr -> x1; + rectangle.y = boxPtr -> y1; + + for (y = 0; y < image_ -> height; y++) + { + update(image_ -> data + y * image_ -> bytes_per_line, rectangle); + + rectangle.y++; + } + + if (shmExtension_ != 1) + { + XDestroyImage(image_); + + image_ = NULL; + } + } + + return; +} + +void Poller::getScreenSize(int *w, int *h) +{ + *w = WidthOfScreen(DefaultScreenOfDisplay(display_)); + *h = HeightOfScreen(DefaultScreenOfDisplay(display_)); +} + +void Poller::setScreenSize(int *w, int *h) +{ + setRootSize(); + + shmInitTrap = 1; + shmInit(); + shmInitTrap = 0; + + *w = width_; + *h = height_; + + logDebug("Poller::setScreenSize", "New size of screen [%d, %d]", width_, height_); +} + +int anyEventPredicate(Display *display, XEvent *event, XPointer parameter) +{ + return 1; +} + +#endif /* !defined(__CYGWIN32__) && !defined(WIN32) */ diff --git a/nxcompshad/src/X11.h b/nxcompshad/src/X11.h new file mode 100644 index 000000000..87dd31fea --- /dev/null +++ b/nxcompshad/src/X11.h @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMPSHAD, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE which comes in the source */ +/* distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef X11Poller_H +#define X11Poller_H + +#include <nx-X11/Xlib.h> +#include <X11/extensions/XShm.h> +#include "X11/include/Xdamage_nxcompshad.h" +#include "X11/include/Xrandr_nxcompshad.h" + +#include "Core.h" + +class Poller : public CorePoller +{ + public: + + Poller(Input *, Display *display, int = 0); + + ~Poller(); + + int init(); + + void setRootSize(); + + void destroyShmImage(); + + void getEvents(void); + + void getScreenSize(int *width, int *height); + + void setScreenSize(int *width, int *height); + + private: + + Display *display_; + + char *shadowDisplayName_; + + int shadowDisplayUid_; + + char *tmpBuffer_; + + char xtestExtension_; + + char shmExtension_; + + char randrExtension_; + + int randrEventBase_; + + char damageExtension_; + + int damageEventBase_; + + Damage damage_; + + Region repair_; + + char damageChanges_; + + XShmSegmentInfo *shminfo_; + + XImage *image_; + + int updateShadowFrameBuffer(void); + + char *getRect(XRectangle); + + void keymapShadowInit(Display *display); + + void keymapMasterInit(); + + KeySym keymapKeycodeToKeysym(KeyCode keycode, KeySym *keysyms, + int minKey, int per, int col); + + KeyCode keymapKeysymToKeycode(KeySym keysym, KeySym *keysyms, + int minKey, int maxKey, int per, int *col); + + KeyCode translateKeysymToKeycode(KeySym keysym, int *col); + + Bool checkModifierKeys(KeySym keysym, Bool isKeyPress); + + void sendFakeModifierEvents(int pos, Bool skip); + + void cancelFakeModifierEvents(); + + Bool keyIsDown(KeyCode keycode); + + void addKeyPressed(KeyCode received, KeyCode sent); + + KeyCode getKeyPressed(KeyCode received); + + void handleKeyboardEvent(Display *display, XEvent *); + + void handleWebKeyboardEvent(KeySym keysym, Bool isKeyPress); + + void handleMouseEvent(Display *, XEvent *); + + void xtestInit(void); + + void shmInit(void); + + void randrInit(void); + + void damageInit(void); + + void handleRRScreenChangeNotify(XEvent *); + + void handleDamageNotify(XEvent *); + + void updateDamagedAreas(void); +}; + +int anyEventPredicate(Display *display, XEvent *event, XPointer parameter); + +#endif /* X11Poller_H */ diff --git a/nxcompshad/src/X11/include/XTest_nxcompshad.h b/nxcompshad/src/X11/include/XTest_nxcompshad.h new file mode 100644 index 000000000..91a2ba40b --- /dev/null +++ b/nxcompshad/src/X11/include/XTest_nxcompshad.h @@ -0,0 +1,71 @@ +/* + +Copyright 1992, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ + +#ifndef _XTEST_H_ +#define _XTEST_H_ + +#include <nx-X11/Xfuncproto.h> + +_XFUNCPROTOBEGIN + +Bool XTestQueryExtension( + Display* /* dpy */, + int* /* event_basep */, + int* /* error_basep */, + int* /* majorp */, + int* /* minorp */ +); + +extern int XTestFakeKeyEvent( + Display* /* dpy */, + unsigned int /* keycode */, + Bool /* is_press */, + unsigned long /* delay */ +); + +extern int XTestFakeButtonEvent( + Display* /* dpy */, + unsigned int /* button */, + Bool /* is_press */, + unsigned long /* delay */ +); + +extern int XTestFakeMotionEvent( + Display* /* dpy */, + int /* screen */, + int /* x */, + int /* y */, + unsigned long /* delay */ +); + +extern int XTestGrabControl( + Display* /* dpy */, + Bool /* impervious */ +); + +_XFUNCPROTOEND + +#endif diff --git a/nxcompshad/src/X11/include/Xdamage_nxcompshad.h b/nxcompshad/src/X11/include/Xdamage_nxcompshad.h new file mode 100644 index 000000000..cae3e1c67 --- /dev/null +++ b/nxcompshad/src/X11/include/Xdamage_nxcompshad.h @@ -0,0 +1,92 @@ +/* + * Copyright © 2003 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file is a reduced version of the header file of + * <X11/extensions/Xdamaga.h> + * + * This copy of code has been introduced to allow a clear namespace + * separation between <X11/...> and <nx-X11/...> header files. + * + * This version of the Xdamage library header file only contains symbols + * required by nxcompshad and strictly avoids indirectly including + * from an X11 library that is also shipped in nx-X11/lib/. + * + * When using <X11/extensions/Xdamage.h> instead for inclusion in + * nxcompshad, it will attempt pulling in the <X11/extensions/Xfixes.h> + * header which in turn will include <X11/Xlib.h>. However, the headers of + * the same name from <nx-X11/...> should be used instead. + * + * FIXME: Once the nxagent Xserver starts using libX11 from X.Org, this + * hack can be removed. + * + * 2015/06/26, Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + */ + + +#ifndef _XDAMAGE_H_ +#define _XDAMAGE_H_ + +#include <X11/extensions/damagewire.h> +#include <nx-X11/Xfuncproto.h> + +/* from <X11/extensions/Xfixes.h> */ +typedef XID XserverRegion; + +#define XDAMAGE_1_1_INTERFACE + +typedef XID Damage; + +typedef struct { + int type; /* event base */ + unsigned long serial; + Bool send_event; + Display *display; + Drawable drawable; + Damage damage; + int level; + Bool more; /* more events will be delivered immediately */ + Time timestamp; + XRectangle area; + XRectangle geometry; +} XDamageNotifyEvent; + +_XFUNCPROTOBEGIN + +Bool XDamageQueryExtension (Display *dpy, + int *event_base_return, + int *error_base_return); + +Status XDamageQueryVersion (Display *dpy, + int *major_version_return, + int *minor_version_return); + +Damage +XDamageCreate (Display *dpy, Drawable drawable, int level); + +void +XDamageSubtract (Display *dpy, Damage damage, + XserverRegion repair, XserverRegion parts); + +_XFUNCPROTOEND + +#endif /* _XDAMAGE_H_ */ diff --git a/nxcompshad/src/X11/include/Xrandr_nxcompshad.h b/nxcompshad/src/X11/include/Xrandr_nxcompshad.h new file mode 100644 index 000000000..4feb01685 --- /dev/null +++ b/nxcompshad/src/X11/include/Xrandr_nxcompshad.h @@ -0,0 +1,80 @@ +/* + * Copyright © 2000 Compaq Computer Corporation, Inc. + * Copyright © 2002 Hewlett-Packard Company, Inc. + * Copyright © 2006 Intel Corporation + * Copyright © 2008 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + * + * Author: Jim Gettys, HP Labs, Hewlett-Packard, Inc. + * Keith Packard, Intel Corporation + */ + +/* + * This file is a reduced version of the header file of + * <X11/extensions/Xrandr.h> + * + * This copy of code has been introduced to allow a clear namespace + * separation between <X11/...> and <nx-X11/...> header files. + * + * This version of the Xrandr library header file only contains symbols + * required by nxcompshad and strictly avoids indirectly including + * from an X11 library that is also shipped in nx-X11/lib/. + * + * When using <X11/extensions/Xrandr.h> instead for inclusion in + * nxcompshad, it will attempt pulling in the <X11/extensions/Xrender.h> + * header which in turn will include <X11/Xlib.h>. However, the headers of + * the same name from <nx-X11/...> should be used instead. + * + * FIXME: Once the nxagent Xserver starts using libXrender from X.Org, this + * hack can be removed. + * + * 2015/06/26, Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + */ + +#ifndef _XRANDR_H_ +#define _XRANDR_H_ + +/* from <X11/extensions/randr.h> */ +#define RRScreenChangeNotify 0 +#define RRScreenChangeNotifyMask (1L << 0) + +#include <X11/Xfuncproto.h> + +_XFUNCPROTOBEGIN + +/* internal representation is private to the library */ +typedef struct _XRRScreenConfiguration XRRScreenConfiguration; + +Bool XRRQueryExtension (Display *dpy, + int *event_base_return, + int *error_base_return); + +void XRRSelectInput(Display *dpy, Window window, int mask); + + +/* + * intended to take RRScreenChangeNotify, or + * ConfigureNotify (on the root window) + * returns 1 if it is an event type it understands, 0 if not + */ +int XRRUpdateConfiguration(XEvent *event); +_XFUNCPROTOEND + +#endif /* _XRANDR_H_ */ |