// Created on: 2013-04-06
// Created by: Kirill Gavrilov
// Copyright (c) 2013-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <Xw_Window.hxx>

#include <Aspect_ScrollDelta.hxx>
#include <Aspect_WindowDefinitionError.hxx>
#include <Aspect_WindowInputListener.hxx>
#include <Message.hxx>

#if defined(HAVE_XLIB)
  #include <X11/Xlib.h>
  #include <X11/Xutil.h>
  //#include <X11/XF86keysym.h>
#endif

#include <Aspect_DisplayConnection.hxx>

IMPLEMENT_STANDARD_RTTIEXT(Xw_Window, Aspect_Window)

// =======================================================================
// function : Xw_Window
// purpose  :
// =======================================================================
Xw_Window::Xw_Window (const Handle(Aspect_DisplayConnection)& theXDisplay,
                      const Standard_CString theTitle,
                      const Standard_Integer thePxLeft,
                      const Standard_Integer thePxTop,
                      const Standard_Integer thePxWidth,
                      const Standard_Integer thePxHeight)
: Aspect_Window(),
  myXWindow  (0),
  myFBConfig (NULL),
  myXLeft    (thePxLeft),
  myYTop     (thePxTop),
  myXRight   (thePxLeft + thePxWidth),
  myYBottom  (thePxTop + thePxHeight),
  myIsOwnWin (Standard_True)
{
  myDisplay = theXDisplay;
  if (thePxWidth <= 0 || thePxHeight <= 0)
  {
    throw Aspect_WindowDefinitionError("Xw_Window, Coordinate(s) out of range");
  }
  else if (theXDisplay.IsNull())
  {
    throw Aspect_WindowDefinitionError("Xw_Window, X Display connection is undefined");
  }

#if defined(HAVE_XLIB)
  myFBConfig = theXDisplay->GetDefaultFBConfig();
  XVisualInfo* aVisInfo = theXDisplay->GetDefaultVisualInfoX();

  Display* aDisp   = myDisplay->GetDisplay();
  int      aScreen = DefaultScreen(aDisp);
  Window   aParent = RootWindow   (aDisp, aScreen);

  unsigned long aMask = 0;
  XSetWindowAttributes aWinAttr;
  memset (&aWinAttr, 0, sizeof(aWinAttr));
  aWinAttr.event_mask = ExposureMask | StructureNotifyMask;
  aMask |= CWEventMask;
  if (aVisInfo != NULL)
  {
    aWinAttr.colormap = XCreateColormap(aDisp, aParent, aVisInfo->visual, AllocNone);
  }
  aWinAttr.border_pixel = 0;
  aWinAttr.override_redirect = False;

  myXWindow = (Window )XCreateWindow (aDisp, aParent,
                            myXLeft, myYTop, thePxWidth, thePxHeight,
                            0, aVisInfo != NULL ? aVisInfo->depth : CopyFromParent,
                            InputOutput,
                            aVisInfo != NULL ? aVisInfo->visual : CopyFromParent,
                            CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &aWinAttr);
  if (myXWindow == 0)
  {
    throw Aspect_WindowDefinitionError("Xw_Window, Unable to create window");
  }

  // if parent - desktop
  XSizeHints aSizeHints;
  aSizeHints.x      = myXLeft;
  aSizeHints.y      = myYTop;
  aSizeHints.flags  = PPosition;
  aSizeHints.width  = thePxWidth;
  aSizeHints.height = thePxHeight;
  aSizeHints.flags |= PSize;
  XSetStandardProperties (aDisp, (Window )myXWindow, theTitle, theTitle, None,
                          NULL, 0, &aSizeHints);

  /*XTextProperty aTitleProperty;
  aTitleProperty.encoding = None;
  char* aTitle = (char* )theTitle;
  Xutf8TextListToTextProperty(aDisp, &aTitle, 1, XUTF8StringStyle, &aTitleProperty);
  XSetWMName      (aDisp, (Window )myXWindow, &aTitleProperty);
  XSetWMProperties(aDisp, (Window )myXWindow, &aTitleProperty, &aTitleProperty, NULL, 0, NULL, NULL, NULL);*/

  XFlush (aDisp);
#else
  (void )theTitle;
  if (myXWindow == 0)
  {
    throw Aspect_WindowDefinitionError ("Xw_Window, Unable to create window - not implemented");
  }
#endif
}

// =======================================================================
// function : Xw_Window
// purpose  :
// =======================================================================
Xw_Window::Xw_Window (const Handle(Aspect_DisplayConnection)& theXDisplay,
                      const Aspect_Drawable theXWin,
                      const Aspect_FBConfig theFBConfig)
: Aspect_Window(),
  myXWindow  (theXWin),
  myFBConfig (theFBConfig),
  myXLeft    (0),
  myYTop     (0),
  myXRight   (512),
  myYBottom  (512),
  myIsOwnWin (Standard_False)
{
  myDisplay = theXDisplay;
  if (theXWin == 0)
  {
    throw Aspect_WindowDefinitionError("Xw_Window, given invalid X window");
  }
  else if (theXDisplay.IsNull())
  {
    throw Aspect_WindowDefinitionError("Xw_Window, X Display connection is undefined");
  }

#if defined(HAVE_XLIB)
  Display* aDisp = myDisplay->GetDisplay();

  XWindowAttributes aWinAttr;
  XGetWindowAttributes (aDisp, (Window )myXWindow, &aWinAttr);
  XVisualInfo aVisInfoTmp;
  aVisInfoTmp.visualid = aWinAttr.visual->visualid;
  aVisInfoTmp.screen   = DefaultScreen (aDisp);
  int aNbItems = 0;
  XVisualInfo* aVisInfo = XGetVisualInfo (aDisp, VisualIDMask | VisualScreenMask, &aVisInfoTmp, &aNbItems);
  if (aVisInfo == NULL)
  {
    throw Aspect_WindowDefinitionError("Xw_Window, Visual is unavailable");
  }
  XFree (aVisInfo);

  DoResize();
#else
  //throw Standard_NotImplemented("Xw_Window, not implemented");
#endif
}

// =======================================================================
// function : ~Xw_Window
// purpose  :
// =======================================================================
Xw_Window::~Xw_Window()
{
  if (myIsOwnWin && myXWindow != 0 && !myDisplay.IsNull())
  {
  #if defined(HAVE_XLIB)
    XDestroyWindow (myDisplay->GetDisplay(), (Window )myXWindow);
  #endif
  }
}

// =======================================================================
// function : IsMapped
// purpose  :
// =======================================================================
Standard_Boolean Xw_Window::IsMapped() const
{
  if (myXWindow == 0)
  {
    return false;
  }
  else if (IsVirtual())
  {
    return Standard_True;
  }

#if defined(HAVE_XLIB)
  XFlush (myDisplay->GetDisplay());
  XWindowAttributes aWinAttr;
  XGetWindowAttributes (myDisplay->GetDisplay(), (Window )myXWindow, &aWinAttr);
  return aWinAttr.map_state == IsUnviewable
      || aWinAttr.map_state == IsViewable;
#else
  return Standard_False;
#endif
}

// =======================================================================
// function : Map
// purpose  :
// =======================================================================
void Xw_Window::Map() const
{
  if (IsVirtual() || myXWindow == 0)
  {
    return;
  }

#if defined(HAVE_XLIB)
  XMapWindow (myDisplay->GetDisplay(), (Window )myXWindow);
  XFlush (myDisplay->GetDisplay());
#endif
}

// =======================================================================
// function : Unmap
// purpose  :
// =======================================================================
void Xw_Window::Unmap() const
{
  if (IsVirtual() || myXWindow == 0)
  {
    return;
  }

#if defined(HAVE_XLIB)
  XIconifyWindow (myDisplay->GetDisplay(), (Window )myXWindow, DefaultScreen(myDisplay->GetDisplay()));
#endif
}

// =======================================================================
// function : DoResize
// purpose  :
// =======================================================================
Aspect_TypeOfResize Xw_Window::DoResize()
{
  if (IsVirtual() || myXWindow == 0)
  {
    return Aspect_TOR_UNKNOWN;
  }

#if defined(HAVE_XLIB)
  XFlush (myDisplay->GetDisplay());
  XWindowAttributes aWinAttr;
  memset (&aWinAttr, 0, sizeof(aWinAttr));
  XGetWindowAttributes (myDisplay->GetDisplay(), (Window )myXWindow, &aWinAttr);
  if (aWinAttr.map_state == IsUnmapped)
  {
    return Aspect_TOR_UNKNOWN;
  }

  Standard_Integer aMask = 0;
  Aspect_TypeOfResize aMode = Aspect_TOR_UNKNOWN;

  if (Abs (aWinAttr.x                     - myXLeft  ) > 2) aMask |= 1;
  if (Abs ((aWinAttr.x + aWinAttr.width)  - myXRight ) > 2) aMask |= 2;
  if (Abs (aWinAttr.y                     - myYTop   ) > 2) aMask |= 4;
  if (Abs ((aWinAttr.y + aWinAttr.height) - myYBottom) > 2) aMask |= 8;
  switch (aMask)
  {
    case 0:  aMode = Aspect_TOR_NO_BORDER;               break;
    case 1:  aMode = Aspect_TOR_LEFT_BORDER;             break;
    case 2:  aMode = Aspect_TOR_RIGHT_BORDER;            break;
    case 4:  aMode = Aspect_TOR_TOP_BORDER;              break;
    case 5:  aMode = Aspect_TOR_LEFT_AND_TOP_BORDER;     break;
    case 6:  aMode = Aspect_TOR_TOP_AND_RIGHT_BORDER;    break;
    case 8:  aMode = Aspect_TOR_BOTTOM_BORDER;           break;
    case 9:  aMode = Aspect_TOR_BOTTOM_AND_LEFT_BORDER;  break;
    case 10: aMode = Aspect_TOR_RIGHT_AND_BOTTOM_BORDER; break;
    default: break;
  }

  myXLeft   = aWinAttr.x;
  myXRight  = aWinAttr.x + aWinAttr.width;
  myYTop    = aWinAttr.y;
  myYBottom = aWinAttr.y + aWinAttr.height;
  return aMode;
#else
  return Aspect_TOR_UNKNOWN;
#endif
}

// =======================================================================
// function : Ratio
// purpose  :
// =======================================================================
Standard_Real Xw_Window::Ratio() const
{
  if (IsVirtual() || myXWindow == 0)
  {
    return Standard_Real(myXRight - myXLeft) / Standard_Real(myYBottom - myYTop);
  }

#if defined(HAVE_XLIB)
  XFlush (myDisplay->GetDisplay());
  XWindowAttributes aWinAttr;
  memset (&aWinAttr, 0, sizeof(aWinAttr));
  XGetWindowAttributes (myDisplay->GetDisplay(), (Window )myXWindow, &aWinAttr);
  return Standard_Real(aWinAttr.width) / Standard_Real(aWinAttr.height);
#else
  return 1.0;
#endif
}

// =======================================================================
// function : Position
// purpose  :
// =======================================================================
void Xw_Window::Position (Standard_Integer& theX1, Standard_Integer& theY1,
                          Standard_Integer& theX2, Standard_Integer& theY2) const
{
  if (IsVirtual() || myXWindow == 0)
  {
    theX1  = myXLeft;
    theX2  = myXRight;
    theY1  = myYTop;
    theY2  = myYBottom;
    return;
  }

#if defined(HAVE_XLIB)
  XFlush (myDisplay->GetDisplay());
  XWindowAttributes anAttributes;
  memset (&anAttributes, 0, sizeof(anAttributes));
  XGetWindowAttributes (myDisplay->GetDisplay(), (Window )myXWindow, &anAttributes);
  Window aChild;
  XTranslateCoordinates (myDisplay->GetDisplay(), anAttributes.root, (Window )myXWindow,
                         0, 0, &anAttributes.x, &anAttributes.y, &aChild);

  theX1 = -anAttributes.x;
  theX2 = theX1 + anAttributes.width;
  theY1 = -anAttributes.y;
  theY2 = theY1 + anAttributes.height;
#endif
}

// =======================================================================
// function : Size
// purpose  :
// =======================================================================
void Xw_Window::Size (Standard_Integer& theWidth,
                      Standard_Integer& theHeight) const
{
  if (IsVirtual() || myXWindow == 0)
  {
    theWidth  = myXRight - myXLeft;
    theHeight = myYBottom - myYTop;
    return;
  }

#if defined(HAVE_XLIB)
  XFlush (myDisplay->GetDisplay());
  XWindowAttributes aWinAttr;
  memset (&aWinAttr, 0, sizeof(aWinAttr));
  XGetWindowAttributes (myDisplay->GetDisplay(), (Window )myXWindow, &aWinAttr);
  theWidth  = aWinAttr.width;
  theHeight = aWinAttr.height;
#endif
}

// =======================================================================
// function : SetTitle
// purpose  :
// =======================================================================
void Xw_Window::SetTitle (const TCollection_AsciiString& theTitle)
{
  if (myXWindow != 0)
  {
  #if defined(HAVE_XLIB)
    XStoreName (myDisplay->GetDisplay(), (Window )myXWindow, theTitle.ToCString());
  #else
    (void )theTitle;
  #endif
  }
}

// =======================================================================
// function : InvalidateContent
// purpose  :
// =======================================================================
void Xw_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& theDisp)
{
  if (myXWindow == 0)
  {
    return;
  }

#if defined(HAVE_XLIB)
  const Handle(Aspect_DisplayConnection)& aDisp = !theDisp.IsNull() ? theDisp : myDisplay;
  Display* aDispX = aDisp->GetDisplay();

  XEvent anEvent;
  memset (&anEvent, 0, sizeof(anEvent));
  anEvent.type = Expose;
  anEvent.xexpose.window = (Window )myXWindow;
  XSendEvent (aDispX, (Window )myXWindow, False, ExposureMask, &anEvent);
  XFlush (aDispX);
#else
  (void )theDisp;
#endif
}

// =======================================================================
// function : VirtualKeyFromNative
// purpose  :
// =======================================================================
Aspect_VKey Xw_Window::VirtualKeyFromNative (unsigned long theKey)
{
#if defined(HAVE_XLIB)
  if (theKey >= XK_0
   && theKey <= XK_9)
  {
    return Aspect_VKey(theKey - XK_0 + Aspect_VKey_0);
  }

  if (theKey >= XK_A
   && theKey <= XK_Z)
  {
    return Aspect_VKey(theKey - XK_A + Aspect_VKey_A);
  }

  if (theKey >= XK_a
   && theKey <= XK_z)
  {
    return Aspect_VKey(theKey - XK_a + Aspect_VKey_A);
  }

  if (theKey >= XK_F1
   && theKey <= XK_F24)
  {
    if (theKey <= XK_F12)
    {
      return Aspect_VKey(theKey - XK_F1 + Aspect_VKey_F1);
    }
    return Aspect_VKey_UNKNOWN;
  }

  switch (theKey)
  {
    case XK_space:
      return Aspect_VKey_Space;
    case XK_apostrophe:
      return Aspect_VKey_Apostrophe;
    case XK_comma:
      return Aspect_VKey_Comma;
    case XK_minus:
      return Aspect_VKey_Minus;
    case XK_period:
      return Aspect_VKey_Period;
    case XK_semicolon:
      return Aspect_VKey_Semicolon;
    case XK_equal:
      return Aspect_VKey_Equal;
    case XK_bracketleft:
      return Aspect_VKey_BracketLeft;
    case XK_backslash:
      return Aspect_VKey_Backslash;
    case XK_bracketright:
      return Aspect_VKey_BracketRight;
    case XK_BackSpace:
      return Aspect_VKey_Backspace;
    case XK_Tab:
      return Aspect_VKey_Tab;
    //case XK_Linefeed:
    case XK_Return:
    case XK_KP_Enter:
      return Aspect_VKey_Enter;
    //case XK_Pause:
    //  return Aspect_VKey_Pause;
    case XK_Escape:
      return Aspect_VKey_Escape;
    case XK_Home:
      return Aspect_VKey_Home;
    case XK_Left:
      return Aspect_VKey_Left;
    case XK_Up:
      return Aspect_VKey_Up;
    case XK_Right:
      return Aspect_VKey_Right;
    case XK_Down:
      return Aspect_VKey_Down;
    case XK_Prior:
      return Aspect_VKey_PageUp;
    case XK_Next:
      return Aspect_VKey_PageDown;
    case XK_End:
      return Aspect_VKey_End;
    //case XK_Insert:
    //  return Aspect_VKey_Insert;
    case XK_Menu:
      return Aspect_VKey_Menu;
    case XK_Num_Lock:
      return Aspect_VKey_Numlock;
    //case XK_KP_Delete:
    //  return Aspect_VKey_NumDelete;
    case XK_KP_Multiply:
      return Aspect_VKey_NumpadMultiply;
    case XK_KP_Add:
      return Aspect_VKey_NumpadAdd;
    //case XK_KP_Separator:
    //  return Aspect_VKey_Separator;
    case XK_KP_Subtract:
      return Aspect_VKey_NumpadSubtract;
    //case XK_KP_Decimal:
    //  return Aspect_VKey_Decimal;
    case XK_KP_Divide:
      return Aspect_VKey_NumpadDivide;
    case XK_Shift_L:
    case XK_Shift_R:
      return Aspect_VKey_Shift;
    case XK_Control_L:
    case XK_Control_R:
      return Aspect_VKey_Control;
    //case XK_Caps_Lock:
    //  return Aspect_VKey_CapsLock;
    case XK_Alt_L:
    case XK_Alt_R:
      return Aspect_VKey_Alt;
    //case XK_Super_L:
    //case XK_Super_R:
    //  return Aspect_VKey_Super;
    case XK_Delete:
      return Aspect_VKey_Delete;

    case 0x1008FF11: // XF86AudioLowerVolume
      return Aspect_VKey_VolumeDown;
    case 0x1008FF12: // XF86AudioMute
      return Aspect_VKey_VolumeMute;
    case 0x1008FF13: // XF86AudioRaiseVolume
      return Aspect_VKey_VolumeUp;

    case 0x1008FF14: // XF86AudioPlay
      return Aspect_VKey_MediaPlayPause;
    case 0x1008FF15: // XF86AudioStop
      return Aspect_VKey_MediaStop;
    case 0x1008FF16: // XF86AudioPrev
      return Aspect_VKey_MediaPreviousTrack;
    case 0x1008FF17: // XF86AudioNext
      return Aspect_VKey_MediaNextTrack;

    case 0x1008FF18: // XF86HomePage
      return Aspect_VKey_BrowserHome;
    case 0x1008FF26: // XF86Back
      return Aspect_VKey_BrowserBack;
    case 0x1008FF27: // XF86Forward
      return Aspect_VKey_BrowserForward;
    case 0x1008FF28: // XF86Stop
      return Aspect_VKey_BrowserStop;
    case 0x1008FF29: // XF86Refresh
      return Aspect_VKey_BrowserRefresh;
  }
#else
  (void )theKey;
#endif
  return Aspect_VKey_UNKNOWN;
}

// =======================================================================
// function : ProcessMessage
// purpose  :
// =======================================================================
bool Xw_Window::ProcessMessage (Aspect_WindowInputListener& theListener,
                                XEvent&
                                #if defined(HAVE_XLIB) // msvc before VS2015 had problems with (void )theMsg
                                        theMsg
                                #endif
                                )
{
#if defined(HAVE_XLIB)
  Display* aDisplay = myDisplay->GetDisplay();

  // Handle event for the chosen display connection
  switch (theMsg.type)
  {
    case ClientMessage:
    {
      if ((Atom)theMsg.xclient.data.l[0] == myDisplay->GetAtom (Aspect_XA_DELETE_WINDOW)
       && theMsg.xclient.window == (Window )myXWindow)
      {
        theListener.ProcessClose();
        return true;
      }
      return false;
    }
    case FocusIn:
    case FocusOut:
    {
      if (theMsg.xfocus.window == (Window )myXWindow)
      {
        theListener.ProcessFocus (theMsg.type == FocusIn);
      }
      return true;
    }
    case Expose:
    {
      if (theMsg.xexpose.window == (Window )myXWindow)
      {
        theListener.ProcessExpose();
      }

      // remove all the ExposureMask and process them at once
      for (int aNbMaxEvents = XPending (aDisplay); aNbMaxEvents > 0; --aNbMaxEvents)
      {
        if (!XCheckWindowEvent (aDisplay, (Window )myXWindow, ExposureMask, &theMsg))
        {
          break;
        }
      }

      return true;
    }
    case ConfigureNotify:
    {
      // remove all the StructureNotifyMask and process them at once
      for (int aNbMaxEvents = XPending (aDisplay); aNbMaxEvents > 0; --aNbMaxEvents)
      {
        if (!XCheckWindowEvent (aDisplay, (Window )myXWindow, StructureNotifyMask, &theMsg))
        {
          break;
        }
      }

      if (theMsg.xconfigure.window == (Window )myXWindow)
      {
        theListener.ProcessConfigure (true);
      }
      return true;
    }
    case KeyPress:
    case KeyRelease:
    {
      XKeyEvent*   aKeyEvent = (XKeyEvent* )&theMsg;
      const KeySym aKeySym = XLookupKeysym (aKeyEvent, 0);
      const Aspect_VKey aVKey = Xw_Window::VirtualKeyFromNative (aKeySym);
      if (aVKey != Aspect_VKey_UNKNOWN)
      {
        const double aTimeStamp = theListener.EventTime();
        if (theMsg.type == KeyPress)
        {
          theListener.KeyDown (aVKey, aTimeStamp);
        }
        else
        {
          theListener.KeyUp (aVKey, aTimeStamp);
        }
        theListener.ProcessInput();
      }
      return true;
    }
    case ButtonPress:
    case ButtonRelease:
    {
      const Graphic3d_Vec2i aPos (theMsg.xbutton.x, theMsg.xbutton.y);
      Aspect_VKeyFlags aFlags  = Aspect_VKeyFlags_NONE;
      Aspect_VKeyMouse aButton = Aspect_VKeyMouse_NONE;
      if (theMsg.xbutton.button == Button1) { aButton = Aspect_VKeyMouse_LeftButton; }
      if (theMsg.xbutton.button == Button2) { aButton = Aspect_VKeyMouse_MiddleButton; }
      if (theMsg.xbutton.button == Button3) { aButton = Aspect_VKeyMouse_RightButton; }

      if ((theMsg.xbutton.state & ControlMask) != 0) { aFlags |= Aspect_VKeyFlags_CTRL; }
      if ((theMsg.xbutton.state & ShiftMask)   != 0) { aFlags |= Aspect_VKeyFlags_SHIFT; }
      if (theListener.Keys().IsKeyDown (Aspect_VKey_Alt))
      {
        aFlags |= Aspect_VKeyFlags_ALT;
      }

      if (theMsg.xbutton.button == Button4
       || theMsg.xbutton.button == Button5)
      {
        if (theMsg.type != ButtonPress)
        {
          return true;
        }

        const double aDeltaF = (theMsg.xbutton.button == Button4 ? 1.0 : -1.0);
        theListener.UpdateMouseScroll (Aspect_ScrollDelta (aPos, aDeltaF, aFlags));
      }
      else if (theMsg.type == ButtonPress)
      {
        theListener.PressMouseButton (aPos, aButton, aFlags, false);
      }
      else
      {
        theListener.ReleaseMouseButton (aPos, aButton, aFlags, false);
      }
      theListener.ProcessInput();
      return true;
    }
    case MotionNotify:
    {
      if (theMsg.xmotion.window != (Window )myXWindow)
      {
        return false;
      }

      // remove all the ButtonMotionMask and process them at once
      for (int aNbMaxEvents = XPending (aDisplay); aNbMaxEvents > 0; --aNbMaxEvents)
      {
        if (!XCheckWindowEvent (aDisplay, (Window )myXWindow, ButtonMotionMask | PointerMotionMask, &theMsg))
        {
          break;
        }
      }

      Graphic3d_Vec2i aPos (theMsg.xmotion.x, theMsg.xmotion.y);
      Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
      Aspect_VKeyFlags aFlags   = Aspect_VKeyFlags_NONE;
      if ((theMsg.xmotion.state & Button1Mask) != 0) { aButtons |= Aspect_VKeyMouse_LeftButton; }
      if ((theMsg.xmotion.state & Button2Mask) != 0) { aButtons |= Aspect_VKeyMouse_MiddleButton; }
      if ((theMsg.xmotion.state & Button3Mask) != 0) { aButtons |= Aspect_VKeyMouse_RightButton; }

      if ((theMsg.xmotion.state & ControlMask) != 0) { aFlags |= Aspect_VKeyFlags_CTRL; }
      if ((theMsg.xmotion.state & ShiftMask)   != 0) { aFlags |= Aspect_VKeyFlags_SHIFT; }
      if (theListener.Keys().IsKeyDown (Aspect_VKey_Alt))
      {
        aFlags |= Aspect_VKeyFlags_ALT;
      }

      theListener.UpdateMousePosition (aPos, aButtons, aFlags, false);
      theListener.ProcessInput();
      return true;
    }
  }
#else
  (void )theListener;
#endif
  return false;
}
