/**************************************************************************/
/*                                                                        */
/* 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.                                          */
/*                                                                        */
/**************************************************************************/

#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) */
