0032433: Visualization, TKService - introduce Wasm_Window implementing Aspect_Window... IR-2021-06-11 WEEK-23
authorkgv <kgv@opencascade.com>
Sun, 25 Apr 2021 14:51:49 +0000 (17:51 +0300)
committerbugmaster <bugmaster@opencascade.com>
Sat, 12 Jun 2021 09:36:24 +0000 (12:36 +0300)
Introduced Wasm_Window implementing Aspect_Window interface.

Aspect_WindowInputListener has been extended by touch input callbacks (moved from AIS_ViewController),
which now implements redirection of single taps to UpdateMouseClick().

AIS_ViewController::FetchNavigationKeys() now requests more frames even if Delta is zero,
but navigation keys are pressed - indicated by a new flag AIS_WalkDelta::IsDefined().

Fixed missing implementation of Xw_Window::DisplayConnection() getter.
The property has been moved to the base class Aspect_Window.

Removed unused Aspect_Convert.hxx.

DRAWEXE targeting Wasm:
- added exposing of FS interface so that it is possible uploading/downloading files to/from emulated file system on JavaScript level;
- added printer redirecting messages to Module.printMessage callback accepting message gravity;
- Run_Appli() now skips std::cin when Module.noExitRuntime is set.

28 files changed:
adm/UDLIST
samples/webgl/CMakeLists.txt
samples/webgl/WasmOcctView.cpp
samples/webgl/WasmOcctView.h
samples/webgl/WasmVKeys.h [deleted file]
src/AIS/AIS_ViewController.cxx
src/AIS/AIS_ViewController.hxx
src/AIS/AIS_WalkDelta.hxx
src/Aspect/Aspect_Convert.hxx [deleted file]
src/Aspect/Aspect_Window.hxx
src/Aspect/Aspect_WindowInputListener.cxx
src/Aspect/Aspect_WindowInputListener.hxx
src/Aspect/FILES
src/Cocoa/Cocoa_Window.mm
src/DRAWEXE/CMakeLists.txt
src/DRAWEXE/DRAWEXE.cxx
src/DRAWEXE/DRAWEXE.html
src/Draw/Draw_Window.cxx
src/TKService/PACKAGES
src/ViewerTest/ViewerTest_EventManager.cxx
src/ViewerTest/ViewerTest_EventManager.hxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
src/WNT/WNT_Window.cxx
src/Wasm/FILES [new file with mode: 0644]
src/Wasm/Wasm_Window.cxx [new file with mode: 0644]
src/Wasm/Wasm_Window.hxx [new file with mode: 0644]
src/Xw/Xw_Window.cxx
src/Xw/Xw_Window.hxx

index 5a18206..f5d2176 100644 (file)
@@ -216,6 +216,7 @@ n SelectMgr
 n StdPrs
 n StdSelect
 n V3d
+n Wasm
 n WNT
 n Xw
 n Cocoa
index 1f7d3af..f5f1e80 100644 (file)
@@ -28,7 +28,6 @@ if (NOT "${SOURCE_MAP_BASE}" STREQUAL "")
 endif()
 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s MODULARIZE=1")
 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='createOccViewerModule'")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap']")
 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --extern-post-js ${CMAKE_CURRENT_SOURCE_DIR}/occt-webgl-viewer.js")
 
 INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
index c1a30ea..057bf08 100644 (file)
 
 #include "WasmOcctView.h"
 
-#include "WasmVKeys.h"
 #include "WasmOcctPixMap.h"
 
 #include <AIS_Shape.hxx>
 #include <AIS_ViewCube.hxx>
 #include <Aspect_Handle.hxx>
 #include <Aspect_DisplayConnection.hxx>
-#include <Aspect_NeutralWindow.hxx>
 #include <Message.hxx>
 #include <Message_Messenger.hxx>
 #include <Graphic3d_CubeMapPacked.hxx>
@@ -36,6 +34,7 @@
 #include <Prs3d_DatumAspect.hxx>
 #include <Prs3d_ToolCylinder.hxx>
 #include <Prs3d_ToolDisk.hxx>
+#include <Wasm_Window.hxx>
 
 #include <BRep_Builder.hxx>
 #include <BRepBndLib.hxx>
 
 namespace
 {
-  EM_JS(int, jsCanvasGetWidth, (), {
-    return Module.canvas.width;
-  });
-
-  EM_JS(int, jsCanvasGetHeight, (), {
-    return Module.canvas.height;
-  });
-
-  EM_JS(float, jsDevicePixelRatio, (), {
-    var aDevicePixelRatio = window.devicePixelRatio || 1;
-    return aDevicePixelRatio;
-  });
-
-  //! Return cavas size in pixels.
-  static Graphic3d_Vec2i jsCanvasSize()
-  {
-    return Graphic3d_Vec2i (jsCanvasGetWidth(), jsCanvasGetHeight());
-  }
-
   //! Auxiliary wrapper for loading model.
   struct ModelAsyncLoader
   {
@@ -199,7 +179,7 @@ void WasmOcctView::run()
 // ================================================================
 void WasmOcctView::initWindow()
 {
-  myDevicePixelRatio = jsDevicePixelRatio();
+  myDevicePixelRatio = emscripten_get_device_pixel_ratio();
   myCanvasId = THE_CANVAS_ID;
   const char* aTargetId = !myCanvasId.IsEmpty() ? myCanvasId.ToCString() : EMSCRIPTEN_EVENT_TARGET_WINDOW;
   const EM_BOOL toUseCapture = EM_TRUE;
@@ -340,13 +320,8 @@ bool WasmOcctView::initViewer()
     }
   }
 
-  Handle(Aspect_NeutralWindow) aWindow = new Aspect_NeutralWindow();
-  Graphic3d_Vec2i aWinSize = jsCanvasSize();
-  if (aWinSize.x() < 10 || aWinSize.y() < 10)
-  {
-    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Warning: invalid canvas size"), Message_Warning);
-  }
-  aWindow->SetSize (aWinSize.x(), aWinSize.y());
+  Handle(Wasm_Window) aWindow = new Wasm_Window (THE_CANVAS_ID);
+  aWindow->Size (myWinSizeOld.x(), myWinSizeOld.y());
 
   myTextStyle = new Prs3d_TextAspect();
   myTextStyle->SetFont (Font_NOF_ASCII_MONO);
@@ -404,30 +379,34 @@ void WasmOcctView::initDemoScene()
 }
 
 // ================================================================
-// Function : UpdateView
+// Function : ProcessInput
 // Purpose  :
 // ================================================================
-void WasmOcctView::UpdateView()
+void WasmOcctView::ProcessInput()
 {
   if (!myView.IsNull())
   {
-    myView->Invalidate();
-    updateView();
+    // Queue onRedrawView()/redrawView callback to redraw canvas after all user input is flushed by browser.
+    // Redrawing viewer on every single message would be a pointless waste of resources,
+    // as user will see only the last drawn frame due to WebGL implementation details.
+    if (++myUpdateRequests == 1)
+    {
+      emscripten_async_call (onRedrawView, this, 0);
+    }
   }
 }
 
 // ================================================================
-// Function : updateView
+// Function : UpdateView
 // Purpose  :
 // ================================================================
-void WasmOcctView::updateView()
+void WasmOcctView::UpdateView()
 {
   if (!myView.IsNull())
   {
-    if (++myUpdateRequests == 1)
-    {
-      emscripten_async_call (onRedrawView, this, 0);
-    }
+    myView->Invalidate();
+    // queue next onRedrawView()/redrawView()
+    ProcessInput();
   }
 }
 
@@ -452,14 +431,6 @@ void WasmOcctView::handleViewRedraw (const Handle(AIS_InteractiveContext)& theCt
 {
   myUpdateRequests = 0;
   AIS_ViewController::handleViewRedraw (theCtx, theView);
-
-  for (NCollection_DataMap<unsigned int, Aspect_VKey>::Iterator aNavKeyIter (myNavKeyMap);
-       !myToAskNextFrame && aNavKeyIter.More(); aNavKeyIter.Next())
-  {
-    const Aspect_VKey aVKey = aNavKeyIter.Key() & ~Aspect_VKeyFlags_ALL;
-    myToAskNextFrame = myKeys.IsKeyDown (aVKey);
-  }
-
   if (myToAskNextFrame)
   {
     // ask more frames
@@ -481,23 +452,21 @@ EM_BOOL WasmOcctView::onResizeEvent (int theEventType, const EmscriptenUiEvent*
     return EM_FALSE;
   }
 
-  Handle(Aspect_NeutralWindow) aWindow = Handle(Aspect_NeutralWindow)::DownCast (myView->Window());
-  Graphic3d_Vec2i aWinSizeOld, aWinSizeNew (jsCanvasSize());
-  if (aWinSizeNew.x() < 10 || aWinSizeNew.y() < 10)
-  {
-    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Warning: invalid canvas size"), Message_Warning);
-  }
-  aWindow->Size (aWinSizeOld.x(), aWinSizeOld.y());
-  const float aPixelRatio = jsDevicePixelRatio();
-  if (aWinSizeNew != aWinSizeOld
+  Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
+  Graphic3d_Vec2i aWinSizeNew;
+  aWindow->DoResize();
+  aWindow->Size (aWinSizeNew.x(), aWinSizeNew.y());
+  const float aPixelRatio = emscripten_get_device_pixel_ratio();
+  if (aWinSizeNew != myWinSizeOld
    || aPixelRatio != myDevicePixelRatio)
   {
+    myWinSizeOld = aWinSizeNew;
     if (myDevicePixelRatio != aPixelRatio)
     {
       myDevicePixelRatio = aPixelRatio;
       initPixelScaleRatio();
     }
-    aWindow->SetSize (aWinSizeNew.x(), aWinSizeNew.y());
+
     myView->MustBeResized();
     myView->Invalidate();
     myView->Redraw();
@@ -517,73 +486,8 @@ EM_BOOL WasmOcctView::onMouseEvent (int theEventType, const EmscriptenMouseEvent
     return EM_FALSE;
   }
 
-  Graphic3d_Vec2i aWinSize;
-  myView->Window()->Size (aWinSize.x(), aWinSize.y());
-  const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (theEvent->targetX, theEvent->targetY));
-  Aspect_VKeyFlags aFlags = 0;
-  if (theEvent->ctrlKey  == EM_TRUE) { aFlags |= Aspect_VKeyFlags_CTRL;  }
-  if (theEvent->shiftKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_SHIFT; }
-  if (theEvent->altKey   == EM_TRUE) { aFlags |= Aspect_VKeyFlags_ALT;   }
-  if (theEvent->metaKey  == EM_TRUE) { aFlags |= Aspect_VKeyFlags_META;  }
-
-  const bool isEmulated = false;
-  const Aspect_VKeyMouse aButtons = WasmVKeys_MouseButtonsFromNative (theEvent->buttons);
-  switch (theEventType)
-  {
-    case EMSCRIPTEN_EVENT_MOUSEMOVE:
-    {
-      if ((aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
-        || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
-        && PressedMouseButtons() == Aspect_VKeyMouse_NONE)
-      {
-        return EM_FALSE;
-      }
-      if (UpdateMousePosition (aNewPos, aButtons, aFlags, isEmulated))
-      {
-        updateView();
-      }
-      break;
-    }
-    case EMSCRIPTEN_EVENT_MOUSEDOWN:
-    case EMSCRIPTEN_EVENT_MOUSEUP:
-    {
-      if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
-       || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
-      {
-        return EM_FALSE;
-      }
-      if (UpdateMouseButtons (aNewPos, aButtons, aFlags, isEmulated))
-      {
-        updateView();
-      }
-      break;
-    }
-    case EMSCRIPTEN_EVENT_CLICK:
-    case EMSCRIPTEN_EVENT_DBLCLICK:
-    {
-      if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
-       || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
-      {
-        return EM_FALSE;
-      }
-      break;
-    }
-    case EMSCRIPTEN_EVENT_MOUSEENTER:
-    {
-      break;
-    }
-    case EMSCRIPTEN_EVENT_MOUSELEAVE:
-    {
-      // there is no SetCapture() support, so that mouse unclick events outside canvas will not arrive,
-      // so we have to forget current state...
-      if (UpdateMouseButtons (aNewPos, Aspect_VKeyMouse_NONE, aFlags, isEmulated))
-      {
-        updateView();
-      }
-      break;
-    }
-  }
-  return EM_TRUE;
+  Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
+  return aWindow->ProcessMouseEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
 }
 
 // ================================================================
@@ -598,40 +502,8 @@ EM_BOOL WasmOcctView::onWheelEvent (int theEventType, const EmscriptenWheelEvent
     return EM_FALSE;
   }
 
-  Graphic3d_Vec2i aWinSize;
-  myView->Window()->Size (aWinSize.x(), aWinSize.y());
-  const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (theEvent->mouse.targetX, theEvent->mouse.targetY));
-  if (aNewPos.x() < 0 || aNewPos.x() > aWinSize.x()
-   || aNewPos.y() < 0 || aNewPos.y() > aWinSize.y())
-  {
-    return EM_FALSE;
-  }
-
-  double aDelta = 0.0;
-  switch (theEvent->deltaMode)
-  {
-    case DOM_DELTA_PIXEL:
-    {
-      aDelta = theEvent->deltaY / (5.0 * myDevicePixelRatio);
-      break;
-    }
-    case DOM_DELTA_LINE:
-    {
-      aDelta = theEvent->deltaY * 8.0;
-      break;
-    }
-    case DOM_DELTA_PAGE:
-    {
-      aDelta = theEvent->deltaY >= 0.0 ? 24.0 : -24.0;
-      break;
-    }
-  }
-
-  if (UpdateZoom (Aspect_ScrollDelta (aNewPos, -aDelta)))
-  {
-    updateView();
-  }
-  return EM_TRUE;
+  Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
+  return aWindow->ProcessWheelEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
 }
 
 // ================================================================
@@ -640,90 +512,13 @@ EM_BOOL WasmOcctView::onWheelEvent (int theEventType, const EmscriptenWheelEvent
 // ================================================================
 EM_BOOL WasmOcctView::onTouchEvent (int theEventType, const EmscriptenTouchEvent* theEvent)
 {
-  const double aClickTolerance = 5.0;
   if (myView.IsNull())
   {
     return EM_FALSE;
   }
 
-  Graphic3d_Vec2i aWinSize;
-  myView->Window()->Size (aWinSize.x(), aWinSize.y());
-  bool hasUpdates = false;
-  for (int aTouchIter = 0; aTouchIter < theEvent->numTouches; ++aTouchIter)
-  {
-    const EmscriptenTouchPoint& aTouch = theEvent->touches[aTouchIter];
-    if (!aTouch.isChanged)
-    {
-      continue;
-    }
-
-    const Standard_Size aTouchId = (Standard_Size )aTouch.identifier;
-    const Graphic3d_Vec2i aNewPos = convertPointToBacking (Graphic3d_Vec2i (aTouch.targetX, aTouch.targetY));
-    switch (theEventType)
-    {
-      case EMSCRIPTEN_EVENT_TOUCHSTART:
-      {
-        if (aNewPos.x() >= 0 && aNewPos.x() < aWinSize.x()
-         && aNewPos.y() >= 0 && aNewPos.y() < aWinSize.y())
-        {
-          hasUpdates = true;
-          AddTouchPoint (aTouchId, Graphic3d_Vec2d (aNewPos));
-          myClickTouch.From.SetValues (-1.0, -1.0);
-          if (myTouchPoints.Extent() == 1)
-          {
-            myClickTouch.From = Graphic3d_Vec2d (aNewPos);
-          }
-        }
-        break;
-      }
-      case EMSCRIPTEN_EVENT_TOUCHMOVE:
-      {
-        const int anOldIndex = myTouchPoints.FindIndex (aTouchId);
-        if (anOldIndex != 0)
-        {
-          hasUpdates = true;
-          UpdateTouchPoint (aTouchId, Graphic3d_Vec2d (aNewPos));
-          if (myTouchPoints.Extent() == 1
-           && (myClickTouch.From - Graphic3d_Vec2d (aNewPos)).cwiseAbs().maxComp() > aClickTolerance)
-          {
-            myClickTouch.From.SetValues (-1.0, -1.0);
-          }
-        }
-        break;
-      }
-      case EMSCRIPTEN_EVENT_TOUCHEND:
-      case EMSCRIPTEN_EVENT_TOUCHCANCEL:
-      {
-        if (RemoveTouchPoint (aTouchId))
-        {
-          if (myTouchPoints.IsEmpty()
-           && myClickTouch.From.minComp() >= 0.0)
-          {
-            if (myDoubleTapTimer.IsStarted()
-             && myDoubleTapTimer.ElapsedTime() <= myMouseDoubleClickInt)
-            {
-              myView->FitAll (0.01, false);
-              myView->Invalidate();
-            }
-            else
-            {
-              myDoubleTapTimer.Stop();
-              myDoubleTapTimer.Reset();
-              myDoubleTapTimer.Start();
-              SelectInViewer (Graphic3d_Vec2i (myClickTouch.From), AIS_SelectionScheme_Replace);
-            }
-          }
-          hasUpdates = true;
-        }
-        break;
-      }
-    }
-  }
-  if (hasUpdates)
-  {
-    updateView();
-  }
-  return hasUpdates || !myTouchPoints.IsEmpty() ? EM_TRUE : EM_FALSE;
+  Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
+  return aWindow->ProcessTouchEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
 }
 
 // ================================================================
@@ -775,35 +570,34 @@ EM_BOOL WasmOcctView::onKeyDownEvent (int theEventType, const EmscriptenKeyboard
     return EM_FALSE;
   }
 
-  const double aTimeStamp = EventTime();
-  const Aspect_VKey aVKey = WasmVKeys_VirtualKeyFromNative (theEvent->keyCode);
-  if (aVKey == Aspect_VKey_UNKNOWN)
-  {
-    return EM_FALSE;
-  }
-  if (theEvent->repeat == EM_TRUE)
-  {
-    return EM_FALSE;
-  }
+  Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
+  return aWindow->ProcessKeyEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
+}
 
+//=======================================================================
+//function : KeyDown
+//purpose  :
+//=======================================================================
+void WasmOcctView::KeyDown (Aspect_VKey theKey,
+                            double theTime,
+                            double thePressure)
+{
   const unsigned int aModifOld = myKeys.Modifiers();
-  AIS_ViewController::KeyDown (aVKey, aTimeStamp);
+  AIS_ViewController::KeyDown (theKey, theTime, thePressure);
 
   const unsigned int aModifNew = myKeys.Modifiers();
   if (aModifNew != aModifOld
-   && navigationKeyModifierSwitch (aModifOld, aModifNew, aTimeStamp))
+   && navigationKeyModifierSwitch (aModifOld, aModifNew, theTime))
   {
     // modifier key just pressed
   }
 
   Aspect_VKey anAction = Aspect_VKey_UNKNOWN;
-  if (myNavKeyMap.Find (aVKey | myKeys.Modifiers(), anAction)
+  if (myNavKeyMap.Find (theKey | myKeys.Modifiers(), anAction)
   &&  anAction != Aspect_VKey_UNKNOWN)
   {
-    AIS_ViewController::KeyDown (anAction, aTimeStamp);
-    UpdateView();
+    AIS_ViewController::KeyDown (anAction, theTime, thePressure);
   }
-  return EM_FALSE;
 }
 
 // ================================================================
@@ -818,32 +612,36 @@ EM_BOOL WasmOcctView::onKeyUpEvent (int theEventType, const EmscriptenKeyboardEv
     return EM_FALSE;
   }
 
-  const double aTimeStamp = EventTime();
-  const Aspect_VKey aVKey = WasmVKeys_VirtualKeyFromNative (theEvent->keyCode);
-  if (aVKey == Aspect_VKey_UNKNOWN)
-  {
-    return EM_FALSE;
-  }
+  Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (myView->Window());
+  return aWindow->ProcessKeyEvent (*this, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
+}
 
+//=======================================================================
+//function : KeyUp
+//purpose  :
+//=======================================================================
+void WasmOcctView::KeyUp (Aspect_VKey theKey,
+                          double theTime)
+{
   const unsigned int aModifOld = myKeys.Modifiers();
-  AIS_ViewController::KeyUp (aVKey, aTimeStamp);
+  AIS_ViewController::KeyUp (theKey, theTime);
 
   Aspect_VKey anAction = Aspect_VKey_UNKNOWN;
-  if (myNavKeyMap.Find (aVKey | myKeys.Modifiers(), anAction)
+  if (myNavKeyMap.Find (theKey | myKeys.Modifiers(), anAction)
   &&  anAction != Aspect_VKey_UNKNOWN)
   {
-    AIS_ViewController::KeyUp (anAction, aTimeStamp);
-    UpdateView();
+    AIS_ViewController::KeyUp (anAction, theTime);
+    processKeyPress (anAction);
   }
 
   const unsigned int aModifNew = myKeys.Modifiers();
   if (aModifNew != aModifOld
-   && navigationKeyModifierSwitch (aModifOld, aModifNew, aTimeStamp))
+   && navigationKeyModifierSwitch (aModifOld, aModifNew, theTime))
   {
     // modifier key released
   }
 
-  return processKeyPress (aVKey | aModifNew) ? EM_TRUE : EM_FALSE;
+  processKeyPress (theKey | aModifNew);
 }
 
 //==============================================================================
index 3acf35e..626ba00 100644 (file)
@@ -136,9 +136,6 @@ private:
   //! Application event loop.
   void mainloop();
 
-  //! Request view redrawing.
-  void updateView();
-
   //! Flush events and redraw view.
   void redrawView();
 
@@ -146,25 +143,24 @@ private:
   virtual void handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
                                  const Handle(V3d_View)& theView) override;
 
+  //! Schedule processing of window input events with the next repaint event.
+  virtual void ProcessInput() override;
+
+  //! Handle key down event.
+  virtual void KeyDown (Aspect_VKey theKey,
+                        double theTime,
+                        double thePressure) override;
+
+  //! Handle key up event.
+  virtual void KeyUp (Aspect_VKey theKey,
+                      double theTime) override;
+
   //! Dump WebGL context information.
   void dumpGlInfo (bool theIsBasic);
 
   //! Initialize pixel scale ratio.
   void initPixelScaleRatio();
 
-  //! Return point from logical units to backing store.
-  Graphic3d_Vec2d convertPointToBacking (const Graphic3d_Vec2d& thePnt) const
-  {
-    return thePnt * myDevicePixelRatio;
-  }
-
-  //! Return point from logical units to backing store.
-  Graphic3d_Vec2i convertPointToBacking (const Graphic3d_Vec2i& thePnt) const
-  {
-    Graphic3d_Vec2d aPnt = Graphic3d_Vec2d (thePnt) * myDevicePixelRatio + Graphic3d_Vec2d (0.5);
-    return Graphic3d_Vec2i (aPnt);
-  }
-
 //! @name Emscripten callbacks
 private:
   //! Window resize event.
@@ -245,8 +241,7 @@ private:
   Handle(Prs3d_TextAspect)       myTextStyle;        //!< text style for OSD elements
   Handle(AIS_ViewCube)           myViewCube;         //!< view cube object
   TCollection_AsciiString        myCanvasId;         //!< canvas element id on HTML page
-  Aspect_Touch                   myClickTouch;       //!< single touch position for handling clicks
-  OSD_Timer                      myDoubleTapTimer;   //!< timer for handling double tap
+  Graphic3d_Vec2i                myWinSizeOld;
   float                          myDevicePixelRatio; //!< device pixel ratio for handling high DPI displays
   unsigned int                   myUpdateRequests;   //!< counter for unhandled update requests
 
diff --git a/samples/webgl/WasmVKeys.h b/samples/webgl/WasmVKeys.h
deleted file mode 100644 (file)
index 3c8bb32..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright (c) 2019 OPEN CASCADE SAS
-//
-// This file is part of the examples of the Open CASCADE Technology software library.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// 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
-// AUTHORS OR COPYRIGHT HOLDERS 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
-
-#ifndef _WasmVKeys_HeaderFile
-#define _WasmVKeys_HeaderFile
-
-#include <Aspect_VKey.hxx>
-
-#include <emscripten/key_codes.h>
-
-//! Convert Emscripten mouse buttons into Aspect_VKeyMouse.
-inline Aspect_VKeyMouse WasmVKeys_MouseButtonsFromNative (unsigned short theButtons)
-{
-  Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
-  if ((theButtons & 0x1) != 0)
-  {
-    aButtons |= Aspect_VKeyMouse_LeftButton;
-  }
-  if ((theButtons & 0x2) != 0)
-  {
-    aButtons |= Aspect_VKeyMouse_RightButton;
-  }
-  if ((theButtons & 0x4) != 0)
-  {
-    aButtons |= Aspect_VKeyMouse_MiddleButton;
-  }
-  return aButtons;
-}
-
-//! Convert DOM virtual key into Aspect_VKey.
-inline Aspect_VKey WasmVKeys_VirtualKeyFromNative (Standard_Integer theKey)
-{
-  if (theKey >= DOM_VK_0
-   && theKey <= DOM_VK_9)
-  {
-    // numpad keys
-    return Aspect_VKey((theKey - DOM_VK_0) + Aspect_VKey_0);
-  }
-  if (theKey >= DOM_VK_A
-   && theKey <= DOM_VK_Z)
-  {
-    // main latin alphabet keys
-    return Aspect_VKey((theKey - DOM_VK_A) + Aspect_VKey_A);
-  }
-  if (theKey >= DOM_VK_F1
-   && theKey <= DOM_VK_F24)
-  {
-    // special keys
-    if (theKey <= DOM_VK_F12)
-    {
-      return Aspect_VKey((theKey - DOM_VK_F1) + Aspect_VKey_F1);
-    }
-    return Aspect_VKey_UNKNOWN;
-  }
-  if (theKey >= DOM_VK_NUMPAD0
-   && theKey <= DOM_VK_NUMPAD9)
-  {
-    // numpad keys
-    return Aspect_VKey((theKey - DOM_VK_NUMPAD0) + Aspect_VKey_Numpad0);
-  }
-
-  switch (theKey)
-  {
-    case DOM_VK_CANCEL:
-    case DOM_VK_HELP:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_BACK_SPACE:
-      return Aspect_VKey_Backspace;
-    case DOM_VK_TAB:
-      return Aspect_VKey_Tab;
-    case DOM_VK_CLEAR:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_RETURN:
-    case DOM_VK_ENTER:
-      return Aspect_VKey_Enter;
-    case DOM_VK_SHIFT:
-      return Aspect_VKey_Shift;
-    case DOM_VK_CONTROL:
-      return Aspect_VKey_Control;
-    case DOM_VK_ALT:
-      return Aspect_VKey_Alt;
-    case DOM_VK_PAUSE:
-    case DOM_VK_CAPS_LOCK:
-    case DOM_VK_KANA:
-    //case DOM_VK_HANGUL:
-    case DOM_VK_EISU:
-    case DOM_VK_JUNJA:
-    case DOM_VK_FINAL:
-    case DOM_VK_HANJA:
-    //case DOM_VK_KANJI:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_ESCAPE:
-      return Aspect_VKey_Escape;
-    case DOM_VK_CONVERT:
-    case DOM_VK_NONCONVERT:
-    case DOM_VK_ACCEPT:
-    case DOM_VK_MODECHANGE:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_SPACE:
-      return Aspect_VKey_Space;
-    case DOM_VK_PAGE_UP:
-      return Aspect_VKey_PageUp;
-    case DOM_VK_PAGE_DOWN:
-      return Aspect_VKey_PageDown;
-    case DOM_VK_END:
-      return Aspect_VKey_End;
-    case DOM_VK_HOME:
-      return Aspect_VKey_Home;
-    case DOM_VK_LEFT:
-      return Aspect_VKey_Left;
-    case DOM_VK_UP:
-      return Aspect_VKey_Up;
-    case DOM_VK_RIGHT:
-      return Aspect_VKey_Right;
-    case DOM_VK_DOWN:
-      return Aspect_VKey_Down;
-    case DOM_VK_SELECT:
-    case DOM_VK_PRINT:
-    case DOM_VK_EXECUTE:
-    case DOM_VK_PRINTSCREEN:
-    case DOM_VK_INSERT:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_DELETE:
-      return Aspect_VKey_Delete;
-    case DOM_VK_COLON:
-      return Aspect_VKey_Comma;
-    case DOM_VK_SEMICOLON:
-      return Aspect_VKey_Semicolon;
-    case DOM_VK_LESS_THAN:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_EQUALS:
-      return Aspect_VKey_Equal;
-    case DOM_VK_GREATER_THAN:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_QUESTION_MARK:
-      return Aspect_VKey_Slash;
-    case DOM_VK_AT: // @ key
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_WIN:
-      return Aspect_VKey_Meta;
-    case DOM_VK_CONTEXT_MENU:
-    case DOM_VK_SLEEP:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_MULTIPLY:
-      return Aspect_VKey_NumpadMultiply;
-    case DOM_VK_ADD:
-      return Aspect_VKey_NumpadAdd;
-    case DOM_VK_SEPARATOR:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_SUBTRACT:
-      return Aspect_VKey_NumpadSubtract;
-    case DOM_VK_DECIMAL:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_DIVIDE:
-      return Aspect_VKey_NumpadDivide;
-    case DOM_VK_NUM_LOCK:
-      return Aspect_VKey_Numlock;
-    case DOM_VK_SCROLL_LOCK:
-      return Aspect_VKey_Scroll;
-    case DOM_VK_WIN_OEM_FJ_JISHO:
-    case DOM_VK_WIN_OEM_FJ_MASSHOU:
-    case DOM_VK_WIN_OEM_FJ_TOUROKU:
-    case DOM_VK_WIN_OEM_FJ_LOYA:
-    case DOM_VK_WIN_OEM_FJ_ROYA:
-    case DOM_VK_CIRCUMFLEX:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_EXCLAMATION:
-    case DOM_VK_DOUBLE_QUOTE:
-    //case DOM_VK_HASH:
-    case DOM_VK_DOLLAR:
-    case DOM_VK_PERCENT:
-    case DOM_VK_AMPERSAND:
-    case DOM_VK_UNDERSCORE:
-    case DOM_VK_OPEN_PAREN:
-    case DOM_VK_CLOSE_PAREN:
-    case DOM_VK_ASTERISK:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_PLUS:
-      return Aspect_VKey_Plus;
-    case DOM_VK_PIPE:
-    case DOM_VK_HYPHEN_MINUS:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_OPEN_CURLY_BRACKET:
-      return Aspect_VKey_BracketLeft;
-    case DOM_VK_CLOSE_CURLY_BRACKET:
-      return Aspect_VKey_BracketRight;
-    case DOM_VK_TILDE:
-      return Aspect_VKey_Tilde;
-    case DOM_VK_VOLUME_MUTE:
-      return Aspect_VKey_VolumeMute;
-    case DOM_VK_VOLUME_DOWN:
-      return Aspect_VKey_VolumeDown;
-    case DOM_VK_VOLUME_UP:
-      return Aspect_VKey_VolumeUp;
-    case DOM_VK_COMMA:
-      return Aspect_VKey_Comma;
-    case DOM_VK_PERIOD:
-      return Aspect_VKey_Period;
-    case DOM_VK_SLASH:
-      return Aspect_VKey_Slash;
-    case DOM_VK_BACK_QUOTE:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_OPEN_BRACKET:
-      return Aspect_VKey_BracketLeft;
-    case DOM_VK_BACK_SLASH:
-      return Aspect_VKey_Backslash;
-    case DOM_VK_CLOSE_BRACKET:
-      return Aspect_VKey_BracketRight;
-    case DOM_VK_QUOTE:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_META:
-      return Aspect_VKey_Meta;
-    case DOM_VK_ALTGR:
-      return Aspect_VKey_Alt;
-    case DOM_VK_WIN_ICO_HELP:
-    case DOM_VK_WIN_ICO_00:
-    case DOM_VK_WIN_ICO_CLEAR:
-    case DOM_VK_WIN_OEM_RESET:
-    case DOM_VK_WIN_OEM_JUMP:
-    case DOM_VK_WIN_OEM_PA1:
-    case DOM_VK_WIN_OEM_PA2:
-    case DOM_VK_WIN_OEM_PA3:
-    case DOM_VK_WIN_OEM_WSCTRL:
-    case DOM_VK_WIN_OEM_CUSEL:
-    case DOM_VK_WIN_OEM_ATTN:
-    case DOM_VK_WIN_OEM_FINISH:
-    case DOM_VK_WIN_OEM_COPY:
-    case DOM_VK_WIN_OEM_AUTO:
-    case DOM_VK_WIN_OEM_ENLW:
-    case DOM_VK_WIN_OEM_BACKTAB:
-    case DOM_VK_ATTN:
-    case DOM_VK_CRSEL:
-    case DOM_VK_EXSEL:
-    case DOM_VK_EREOF:
-      return Aspect_VKey_UNKNOWN;
-    case DOM_VK_PLAY:
-      return Aspect_VKey_MediaPlayPause;
-    case DOM_VK_ZOOM:
-    case DOM_VK_PA1:
-    case DOM_VK_WIN_OEM_CLEAR:
-      return Aspect_VKey_UNKNOWN;
-  }
-  return Aspect_VKey_UNKNOWN;
-}
-
-#endif // _WasmVKeys_HeaderFile
index 2630dca..009d7ce 100644 (file)
@@ -86,6 +86,7 @@ AIS_ViewController::AIS_ViewController()
   myMouseStopDragOnUnclick (false),
   //
   myTouchToleranceScale      (1.0f),
+  myTouchClickThresholdPx    (3.0f),
   myTouchRotationThresholdPx (6.0f),
   myTouchZRotationThreshold  (float(2.0 * M_PI / 180.0)),
   myTouchPanThresholdPx      (4.0f),
@@ -1010,14 +1011,12 @@ void AIS_ViewController::AddTouchPoint (Standard_Size theId,
                                         Standard_Boolean theClearBefore)
 {
   myUI.MoveTo.ToHilight = false;
-  if (theClearBefore)
-  {
-    RemoveTouchPoint ((Standard_Size )-1);
-  }
+  Aspect_WindowInputListener::AddTouchPoint (theId, thePnt, theClearBefore);
 
-  myTouchPoints.Add (theId, Aspect_Touch (thePnt, false));
+  myTouchClick.From = Graphic3d_Vec2d (-1.0);
   if (myTouchPoints.Extent() == 1)
   {
+    myTouchClick.From = thePnt;
     myUpdateStartPointRot = true;
     myStartRotCoord = thePnt;
     if (myToAllowDragging)
@@ -1043,18 +1042,9 @@ void AIS_ViewController::AddTouchPoint (Standard_Size theId,
 bool AIS_ViewController::RemoveTouchPoint (Standard_Size theId,
                                            Standard_Boolean theClearSelectPnts)
 {
-  if (theId == (Standard_Size )-1)
+  if (!Aspect_WindowInputListener::RemoveTouchPoint (theId, theClearSelectPnts))
   {
-    myTouchPoints.Clear (false);
-  }
-  else
-  {
-    const Standard_Integer anOldExtent = myTouchPoints.Extent();
-    myTouchPoints.RemoveKey (theId);
-    if (myTouchPoints.Extent() == anOldExtent)
-    {
-      return false;
-    }
+    return false;
   }
 
   if (myTouchPoints.Extent() == 1)
@@ -1079,6 +1069,30 @@ bool AIS_ViewController::RemoveTouchPoint (Standard_Size theId,
     }
 
     myUI.Dragging.ToStop = true;
+
+    if (theId == (Standard_Size )-1)
+    {
+      // abort clicking
+      myTouchClick.From = Graphic3d_Vec2d (-1);
+    }
+    else if (myTouchClick.From.minComp() >= 0.0)
+    {
+      bool isDoubleClick = false;
+      if (myTouchDoubleTapTimer.IsStarted()
+       && myTouchDoubleTapTimer.ElapsedTime() <= myMouseDoubleClickInt)
+      {
+        isDoubleClick = true;
+      }
+      else
+      {
+        myTouchDoubleTapTimer.Stop();
+        myTouchDoubleTapTimer.Reset();
+        myTouchDoubleTapTimer.Start();
+      }
+
+      // emulate mouse click
+      UpdateMouseClick (Graphic3d_Vec2i (myTouchClick.From), Aspect_VKeyMouse_LeftButton, Aspect_VKeyFlags_NONE, isDoubleClick);
+    }
   }
   myUI.IsNewGesture = true;
   return true;
@@ -1091,13 +1105,13 @@ bool AIS_ViewController::RemoveTouchPoint (Standard_Size theId,
 void AIS_ViewController::UpdateTouchPoint (Standard_Size theId,
                                            const Graphic3d_Vec2d& thePnt)
 {
-  if (Aspect_Touch* aTouch = myTouchPoints.ChangeSeek (theId))
-  {
-    aTouch->To = thePnt;
-  }
-  else
+  Aspect_WindowInputListener::UpdateTouchPoint (theId, thePnt);
+
+  const double aTouchTol = double(myTouchToleranceScale) * double(myTouchClickThresholdPx);
+  if (myTouchPoints.Extent() == 1
+   && (myTouchClick.From - thePnt).cwiseAbs().maxComp() > aTouchTol)
   {
-    AddTouchPoint (theId, thePnt);
+    myTouchClick.From.SetValues (-1.0, -1.0);
   }
 }
 
@@ -1195,12 +1209,14 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavJump, aNewEventTime, aDuration))
   {
     myKeys.KeyUp (Aspect_VKey_NavJump, aNewEventTime);
+    aWalk.SetDefined (true);
     aWalk.SetJumping (true);
   }
   if (!aWalk.IsJumping()
    && theCrouchRatio < 1.0
    && myKeys.HoldDuration (Aspect_VKey_NavCrouch, aNewEventTime, aDuration))
   {
+    aWalk.SetDefined (true);
     aWalk.SetRunning (false);
     aWalk.SetCrouching (true);
   }
@@ -1215,6 +1231,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration));
     aProgress *= aRunRatio;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkTranslation_Forward].Value += aProgress;
     aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
     aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
@@ -1223,6 +1240,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration));
     aProgress *= aRunRatio;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkTranslation_Forward].Value += -aProgress;
     aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
     aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
@@ -1231,6 +1249,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration));
     aProgress *= aRunRatio;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkTranslation_Side].Value = -aProgress;
     aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
     aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
@@ -1239,6 +1258,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration));
     aProgress *= aRunRatio;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkTranslation_Side].Value = aProgress;
     aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
     aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
@@ -1246,6 +1266,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavLookLeft, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkRotation_Yaw].Value = aProgress;
     aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
     aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
@@ -1253,6 +1274,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavLookRight, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkRotation_Yaw].Value = -aProgress;
     aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
     aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
@@ -1260,6 +1282,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavLookUp, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? -aProgress : aProgress;
     aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
     aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
@@ -1267,6 +1290,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavLookDown, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? aProgress : -aProgress;
     aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
     aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
@@ -1274,6 +1298,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavRollCCW, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkRotation_Roll].Value = -aProgress;
     aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
     aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
@@ -1281,6 +1306,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavRollCW, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkRotation_Roll].Value = aProgress;
     aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
     aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
@@ -1288,6 +1314,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavSlideUp, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkTranslation_Up].Value = aProgress;
     aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
     aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
@@ -1295,6 +1322,7 @@ AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRa
   if (myKeys.HoldDuration (Aspect_VKey_NavSlideDown, aNewEventTime, aDuration, aPressure))
   {
     double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aWalk.SetDefined (true);
     aWalk[AIS_WalkTranslation_Up].Value = -aProgress;
     aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
     aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
@@ -1956,6 +1984,10 @@ AIS_WalkDelta AIS_ViewController::handleNavigationKeys (const Handle(AIS_Interac
   }
   if (aWalk.IsEmpty())
   {
+    if (aWalk.IsDefined())
+    {
+      setAskNextFrame();
+    }
     return aWalk;
   }
   else if (myGL.OrbitRotation.ToRotate
index 37d436f..8187275 100644 (file)
@@ -372,9 +372,6 @@ public: //! @name multi-touch input
   //! Set scale factor for adjusting tolerances for starting multi-touch gestures.
   void SetTouchToleranceScale (float theTolerance) { myTouchToleranceScale = theTolerance; }
 
-  //! Return TRUE if touches map is not empty.
-  bool HasTouchPoints() const { return !myTouchPoints.IsEmpty(); }
-
   //! Add touch point with the given ID.
   //! This method is expected to be called from UI thread.
   //! @param theId touch unique identifier
@@ -382,7 +379,7 @@ public: //! @name multi-touch input
   //! @param theClearBefore if TRUE previously registered touches will be removed
   Standard_EXPORT virtual void AddTouchPoint (Standard_Size theId,
                                               const Graphic3d_Vec2d& thePnt,
-                                              Standard_Boolean theClearBefore = false);
+                                              Standard_Boolean theClearBefore = false) Standard_OVERRIDE;
 
   //! Remove touch point with the given ID.
   //! This method is expected to be called from UI thread.
@@ -390,7 +387,7 @@ public: //! @name multi-touch input
   //! @param theClearSelectPnts if TRUE will initiate clearing of selection points
   //! @return TRUE if point has been removed
   Standard_EXPORT virtual bool RemoveTouchPoint (Standard_Size theId,
-                                                 Standard_Boolean theClearSelectPnts = false);
+                                                 Standard_Boolean theClearSelectPnts = false) Standard_OVERRIDE;
 
   //! Update touch point with the given ID.
   //! If point with specified ID was not registered before, it will be added.
@@ -398,7 +395,9 @@ public: //! @name multi-touch input
   //! @param theId touch unique identifier
   //! @param thePnt touch coordinates
   Standard_EXPORT virtual void UpdateTouchPoint (Standard_Size theId,
-                                                 const Graphic3d_Vec2d& thePnt);
+                                                 const Graphic3d_Vec2d& thePnt) Standard_OVERRIDE;
+
+  using Aspect_WindowInputListener::HasTouchPoints;
 
 public: //! @name 3d mouse input
 
@@ -749,13 +748,16 @@ protected: //! @name mouse input variables
 protected: //! @name multi-touch input variables
 
   Standard_ShortReal  myTouchToleranceScale;      //!< tolerance scale factor; 1.0 by default
+  Standard_ShortReal  myTouchClickThresholdPx;    //!< touch click threshold in pixels; 3 by default
   Standard_ShortReal  myTouchRotationThresholdPx; //!< threshold for starting one-touch rotation     gesture in pixels;  6 by default
   Standard_ShortReal  myTouchZRotationThreshold;  //!< threshold for starting two-touch Z-rotation   gesture in radians; 2 degrees by default
   Standard_ShortReal  myTouchPanThresholdPx;      //!< threshold for starting two-touch panning      gesture in pixels;  4 by default
   Standard_ShortReal  myTouchZoomThresholdPx;     //!< threshold for starting two-touch zoom (pitch) gesture in pixels;  6 by default
   Standard_ShortReal  myTouchZoomRatio;           //!< distance ratio for mapping two-touch zoom (pitch) gesture from pixels to zoom; 0.13 by default
 
-  Aspect_TouchMap     myTouchPoints;              //!< map of active touches
+  Aspect_Touch        myTouchClick;               //!< single touch position for handling clicks
+  OSD_Timer           myTouchDoubleTapTimer;      //!< timer for handling double tap
+
   Graphic3d_Vec2d     myStartPanCoord;            //!< touch coordinates at the moment of starting panning  gesture
   Graphic3d_Vec2d     myStartRotCoord;            //!< touch coordinates at the moment of starting rotating gesture
   Standard_Integer    myNbTouchesLast;            //!< number of touches within previous gesture flush to track gesture changes
index 40b0eae..ef68b67 100644 (file)
@@ -51,7 +51,7 @@ struct AIS_WalkDelta
 {
   //! Empty constructor.
   AIS_WalkDelta()
-  : myIsJumping (false), myIsCrouching (false), myIsRunning (false) {}
+  : myIsDefined (false), myIsJumping (false), myIsCrouching (false), myIsRunning (false) {}
 
   //! Return translation component.
   const AIS_WalkPart& operator[] (AIS_WalkTranslation thePart) const { return myTranslation[thePart]; }
@@ -83,6 +83,12 @@ struct AIS_WalkDelta
   //! Set running state.
   void SetRunning (bool theIsRunning) { myIsRunning = theIsRunning; }
 
+  //! Return TRUE if navigation keys are pressed even if delta from the previous frame is empty.
+  bool IsDefined() const { return myIsDefined || !IsEmpty(); }
+
+  //! Set if any navigation key is pressed.
+  void SetDefined (bool theIsDefined) { myIsDefined = theIsDefined; }
+
   //! Return TRUE when both Rotation and Translation deltas are empty.
   bool IsEmpty() const { return !ToMove() && !ToRotate(); }
 
@@ -106,6 +112,7 @@ private:
 
   AIS_WalkPart myTranslation[3];
   AIS_WalkPart myRotation[3];
+  bool myIsDefined;
   bool myIsJumping;
   bool myIsCrouching;
   bool myIsRunning;
diff --git a/src/Aspect/Aspect_Convert.hxx b/src/Aspect/Aspect_Convert.hxx
deleted file mode 100644 (file)
index 048f3be..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 1999-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.
-
-#ifndef _Aspect_Convert_HeaderFile
-#define _Aspect_Convert_HeaderFile
-
-#include <Standard.hxx>
-#include <Standard_Real.hxx>
-
-//! Auxiliary functions for DCU <-> Pixels conversions.
-namespace Aspect_Convert
-{
-
-  inline Standard_Integer Round (Standard_Real theValue)
-  {
-    return Standard_Integer(theValue + (theValue >= 0 ? 0.5 : -0.5 ));
-  }
-
-  inline void ConvertCoordinates (const Standard_Integer theParentPxSizeX, const Standard_Integer theParentPxSizeY,
-                                  const Standard_Real theQCenterX, const Standard_Real theQCenterY,
-                                  const Standard_Real theQSizeX,   const Standard_Real theQSizeY,
-                                  Standard_Integer& thePxLeft,  Standard_Integer& thePxTop,
-                                  Standard_Integer& thePxSizeX, Standard_Integer& thePxSizeY)
-  {
-    Standard_Real theParentSizeMin = Min (theParentPxSizeX, theParentPxSizeY);
-    thePxSizeX = Round (theQSizeX * theParentSizeMin);
-    thePxSizeY = Round (theQSizeY * theParentSizeMin);
-    Standard_Integer thePxCenterX = Round(theQCenterX * Standard_Real (theParentPxSizeX));
-    Standard_Integer thePxCenterY = Round((1.0 - theQCenterY) * Standard_Real (theParentPxSizeY));
-    thePxLeft = thePxCenterX - thePxSizeX / 2;
-    thePxTop  = thePxCenterY - thePxSizeY / 2;
-  }
-
-  inline void ConvertCoordinates (const Standard_Integer theParentPxSizeX, const Standard_Integer theParentPxSizeY,
-                                  const Standard_Integer thePxLeft,  const Standard_Integer thePxTop,
-                                  const Standard_Integer thePxSizeX, const Standard_Integer thePxSizeY,
-                                  Standard_Real& theQCenterX, Standard_Real& theQCenterY,
-                                  Standard_Real& theQSizeX,   Standard_Real& theQSizeY)
-  {
-    Standard_Real theParentSizeMin = Min (theParentPxSizeX, theParentPxSizeY);
-    theQSizeX = Standard_Real(thePxSizeX) / theParentSizeMin;
-    theQSizeY = Standard_Real(thePxSizeY) / theParentSizeMin;
-    Standard_Integer thePxCenterX = thePxLeft + thePxSizeX / 2;
-    Standard_Integer thePxCenterY = thePxTop  + thePxSizeY / 2;
-    theQCenterX =       Standard_Real (thePxCenterX) / Standard_Real (theParentPxSizeX);
-    theQCenterY = 1.0 - Standard_Real (thePxCenterY) / Standard_Real (theParentPxSizeY);
-  }
-
-  inline void FitIn (const Standard_Integer theParentPxSizeX, const Standard_Integer theParentPxSizeY,
-                     Standard_Integer& thePxLeft,  Standard_Integer& thePxTop,
-                     Standard_Integer& thePxSizeX, Standard_Integer& thePxSizeY)
-  {
-    if (thePxLeft < 0)
-    {
-      //thePxSizeX -= 2 * thePxLeft;
-      thePxLeft = 0;
-    }
-    if ((thePxLeft + thePxSizeX) > theParentPxSizeX)
-    {
-      thePxSizeX = theParentPxSizeX - thePxLeft;
-    }
-
-    if (thePxTop < 0)
-    {
-      //thePxSizeY -= 2 * thePxTop;
-      thePxTop = 0;
-    }
-    if ((thePxTop + thePxSizeY) > theParentPxSizeY)
-    {
-      thePxSizeY = theParentPxSizeY - thePxTop;
-    }
-  }
-
-} // namespace Aspect_Convert
-
-#endif /* _Aspect_Convert_HeaderFile */
index 64513cd..c7153f0 100644 (file)
@@ -24,6 +24,7 @@
 #include <Aspect_GradientFillMethod.hxx>
 #include <Aspect_TypeOfResize.hxx>
 #include <Aspect_Drawable.hxx>
+#include <Graphic3d_Vec2.hxx>
 #include <Standard.hxx>
 #include <Standard_Transient.hxx>
 #include <Standard_Type.hxx>
@@ -36,7 +37,7 @@ DEFINE_STANDARD_HANDLE(Aspect_Window, Standard_Transient)
 //! Defines a window.
 class Aspect_Window : public Standard_Transient
 {
-
+  DEFINE_STANDARD_RTTIEXT(Aspect_Window, Standard_Transient)
 public:
 
   //! Modifies the window background.
@@ -102,6 +103,9 @@ public:
   //! Returns native Window FB config (GLXFBConfig on Xlib)
   Standard_EXPORT virtual Aspect_FBConfig NativeFBConfig() const = 0;
 
+  //! Returns connection to Display or NULL.
+  const Handle(Aspect_DisplayConnection)& DisplayConnection() const { return myDisplay; }
+
   //! Sets window title.
   virtual void SetTitle (const TCollection_AsciiString& theTitle) { (void )theTitle; }
 
@@ -114,12 +118,29 @@ public:
   //! on platforms implementing thread-unsafe connections to display.
   //! NULL can be passed instead otherwise.
   virtual void InvalidateContent (const Handle(Aspect_DisplayConnection)& theDisp) { (void )theDisp; }
-  
+
+public:
+
+  //! Return device pixel ratio (logical to backing store scale factor).
+  virtual Standard_Real DevicePixelRatio() const { return 1.0; }
+
+  //! Convert point from logical units into backing store units.
+  virtual Graphic3d_Vec2d ConvertPointToBacking (const Graphic3d_Vec2d& thePnt) const
+  {
+    return thePnt * DevicePixelRatio();
+  }
+
+  //! Convert point from backing store units to logical units.
+  virtual Graphic3d_Vec2d ConvertPointFromBacking (const Graphic3d_Vec2d& thePnt) const
+  {
+    return thePnt / DevicePixelRatio();
+  }
+
+public:
+
   //! Dumps the content of me into the stream
   Standard_EXPORT void DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth = -1) const;
 
-  DEFINE_STANDARD_RTTIEXT(Aspect_Window,Standard_Transient)
-
 protected:
 
   //! Initializes the data of a Window.
@@ -127,6 +148,7 @@ protected:
 
 protected:
 
+  Handle(Aspect_DisplayConnection) myDisplay; //!< Display connection
   Aspect_Background MyBackground;
   Aspect_GradientBackground MyGradientBackground;
   Aspect_FillMethod MyBackgroundFillMethod;
index ed6478b..1fb21eb 100644 (file)
@@ -75,6 +75,70 @@ void Aspect_WindowInputListener::KeyFromAxis (Aspect_VKey theNegative,
   myKeys.KeyFromAxis (theNegative, thePositive, theTime, thePressure);
 }
 
+// =======================================================================
+// function : AddTouchPoint
+// purpose  :
+// =======================================================================
+void Aspect_WindowInputListener::AddTouchPoint (Standard_Size theId,
+                                                const Graphic3d_Vec2d& thePnt,
+                                                Standard_Boolean theClearBefore)
+{
+  if (theClearBefore)
+  {
+    RemoveTouchPoint ((Standard_Size )-1);
+  }
+
+  myTouchPoints.Add (theId, Aspect_Touch (thePnt, false));
+}
+
+// =======================================================================
+// function : RemoveTouchPoint
+// purpose  :
+// =======================================================================
+bool Aspect_WindowInputListener::RemoveTouchPoint (Standard_Size theId,
+                                                   Standard_Boolean theClearSelectPnts)
+{
+  (void )theClearSelectPnts;
+  if (theId == (Standard_Size )-1)
+  {
+    myTouchPoints.Clear (false);
+  }
+  else
+  {
+    const Standard_Integer anOldExtent = myTouchPoints.Extent();
+    myTouchPoints.RemoveKey (theId);
+    if (myTouchPoints.Extent() == anOldExtent)
+    {
+      return false;
+    }
+  }
+
+  if (myTouchPoints.Extent() == 1)
+  {
+    // avoid incorrect transition from pinch to one finger
+    Aspect_Touch& aFirstTouch = myTouchPoints.ChangeFromIndex (1);
+    aFirstTouch.To = aFirstTouch.From;
+  }
+  return true;
+}
+
+// =======================================================================
+// function : UpdateTouchPoint
+// purpose  :
+// =======================================================================
+void Aspect_WindowInputListener::UpdateTouchPoint (Standard_Size theId,
+                                                   const Graphic3d_Vec2d& thePnt)
+{
+  if (Aspect_Touch* aTouch = myTouchPoints.ChangeSeek (theId))
+  {
+    aTouch->To = thePnt;
+  }
+  else
+  {
+    AddTouchPoint (theId, thePnt);
+  }
+}
+
 // =======================================================================
 // function : update3dMouseTranslation
 // purpose  :
index 88e8825..4ca3656 100644 (file)
@@ -15,6 +15,7 @@
 #define _Aspect_WindowInputListener_HeaderFile
 
 #include <Aspect_VKeySet.hxx>
+#include <Aspect_TouchMap.hxx>
 #include <Graphic3d_Vec.hxx>
 #include <Standard.hxx>
 #include <Standard_DefineAlloc.hxx>
@@ -159,6 +160,39 @@ public: //! @name mouse input
   //! Return last mouse position.
   const Graphic3d_Vec2i& LastMousePosition() const { return myMousePositionLast; }
 
+public: //! @name multi-touch input
+
+  //! Return TRUE if touches map is not empty.
+  bool HasTouchPoints() const { return !myTouchPoints.IsEmpty(); }
+
+  //! Return map of active touches.
+  const Aspect_TouchMap& TouchPoints() const { return myTouchPoints; }
+
+  //! Add touch point with the given ID.
+  //! This method is expected to be called from UI thread.
+  //! @param theId touch unique identifier
+  //! @param thePnt touch coordinates
+  //! @param theClearBefore if TRUE previously registered touches will be removed
+  Standard_EXPORT virtual void AddTouchPoint (Standard_Size theId,
+                                              const Graphic3d_Vec2d& thePnt,
+                                              Standard_Boolean theClearBefore = false);
+
+  //! Remove touch point with the given ID.
+  //! This method is expected to be called from UI thread.
+  //! @param theId touch unique identifier
+  //! @param theClearSelectPnts if TRUE will initiate clearing of selection points
+  //! @return TRUE if point has been removed
+  Standard_EXPORT virtual bool RemoveTouchPoint (Standard_Size theId,
+                                                 Standard_Boolean theClearSelectPnts = false);
+
+  //! Update touch point with the given ID.
+  //! If point with specified ID was not registered before, it will be added.
+  //! This method is expected to be called from UI thread.
+  //! @param theId touch unique identifier
+  //! @param thePnt touch coordinates
+  Standard_EXPORT virtual void UpdateTouchPoint (Standard_Size theId,
+                                                 const Graphic3d_Vec2d& thePnt);
+
 public: //! @name 3d mouse input
 
   //! Return acceleration ratio for translation event; 2.0 by default.
@@ -222,6 +256,10 @@ protected: //! @name mouse input variables
   Aspect_VKeyMouse myMousePressed;      //!< active mouse buttons
   Aspect_VKeyFlags myMouseModifiers;    //!< active key modifiers passed with last mouse event
 
+protected:
+
+  Aspect_TouchMap  myTouchPoints;       //!< map of active touches
+
 protected: //! @name 3d mouse input variables
 
   bool                   my3dMouseButtonState[32];//!< cached button state
index e33a7c5..492dcb7 100755 (executable)
@@ -6,7 +6,6 @@ Aspect_Background.hxx
 Aspect_CircularGrid.cxx
 Aspect_CircularGrid.hxx
 Aspect_ColorSpace.hxx
-Aspect_Convert.hxx
 Aspect_Display.hxx
 Aspect_DisplayConnection.cxx
 Aspect_DisplayConnection.hxx
index bd8c2e3..d491b11 100644 (file)
@@ -26,7 +26,6 @@
 #include <Cocoa_LocalPool.hxx>
 
 #include <Image_AlienPixMap.hxx>
-#include <Aspect_Convert.hxx>
 #include <Aspect_WindowDefinitionError.hxx>
 
 IMPLEMENT_STANDARD_RTTIEXT(Cocoa_Window,Aspect_Window)
index 3c2b631..02dd26a 100644 (file)
@@ -49,6 +49,7 @@ if (EMSCRIPTEN)
 
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s MODULARIZE=1")
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='createDRAWEXE'")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s \"EXTRA_EXPORTED_RUNTIME_METHODS=['FS']\"")
 
   # Embed Draw Harness .tcl scripts at recognizable location.
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --preload-file ${CMAKE_CURRENT_SOURCE_DIR}/../DrawResources@/DrawResources")
index 0f4a2f2..f76eefd 100644 (file)
@@ -17,6 +17,7 @@
 #include <DBRep.hxx>
 #include <DrawTrSurf.hxx>
 #include <Message.hxx>
+#include <Message_PrinterOStream.hxx>
 #include <Message_PrinterSystemLog.hxx>
 #include <NCollection_IndexedMap.hxx>
 #include <Standard_ErrorHandler.hxx>
   #include <XDEDRAW.hxx>
 #endif
 
+Standard_IMPORT Standard_Boolean Draw_Interprete (const char* theCommand);
+
+#if defined(__EMSCRIPTEN__)
+#include <emscripten/bind.h>
+#include <emscripten/emscripten.h>
+
+//! Draw Harness interface for JavaScript.
+class DRAWEXE
+{
+public:
+  //! Evaluate Tcl command.
+  static int eval (const std::string& theCommand)
+  {
+    int aRes = 0;
+    try
+    {
+      OCC_CATCH_SIGNALS
+      //aRes = Draw::GetInterpretor().Eval (theCommand.c_str());
+      aRes = Draw_Interprete (theCommand.c_str()) ? 1 : 0;
+    }
+    catch (Standard_Failure& anExcept)
+    {
+      std::cout << "Failed to evaluate command: " << anExcept.GetMessageString() << std::endl;
+    }
+    return aRes;
+  }
+
+  //! Check if Tcl command is complete.
+  static bool isComplete (const std::string& theCommand)
+  {
+    return Draw::GetInterpretor().Complete (theCommand.c_str());
+  }
+};
+
+//! Print message to Module.printMessage callback.
+EM_JS(void, occJSPrintMessage, (const char* theStr, int theGravity), {
+  if (Module.printMessage != undefined && Module.printMessage != null) {
+    Module.printMessage (UTF8ToString(theStr), theGravity);
+  } else if (Module.print != undefined && Module.print != null) {
+    Module.print (UTF8ToString(theStr));
+  } else {
+    //console.info (UTF8ToString(theStr));
+  }
+});
+
+//! Auxiliary printer to a Module.printMessage callback accepting text and gravity.
+class DRAWEXE_WasmModulePrinter : public Message_Printer
+{
+  DEFINE_STANDARD_RTTI_INLINE(DRAWEXE_WasmModulePrinter, Message_Printer)
+public:
+
+  //! Main constructor.
+  DRAWEXE_WasmModulePrinter (const Message_Gravity theTraceLevel = Message_Info)
+  {
+    SetTraceLevel (theTraceLevel);
+  }
+
+  //! Destructor.
+  virtual ~DRAWEXE_WasmModulePrinter() {}
+
+protected:
+
+  //! Puts a message.
+  virtual void send (const TCollection_AsciiString& theString,
+                     const Message_Gravity theGravity) const Standard_OVERRIDE
+  {
+    if (theGravity >= myTraceLevel)
+    {
+      occJSPrintMessage (theString.ToCString(), (int )theGravity);
+    }
+  }
+};
+
+EMSCRIPTEN_BINDINGS(DRAWEXE) {
+  emscripten::function("eval",       &DRAWEXE::eval);
+  emscripten::function("isComplete", &DRAWEXE::isComplete);
+}
+#endif
+
 #ifdef OCCT_NO_PLUGINS
 //! Mimic pload command by loading pre-defined set of statically linked plugins.
 static Standard_Integer Pload (Draw_Interpretor& theDI,
@@ -154,8 +234,13 @@ void Draw_InitAppli (Draw_Interpretor& theDI)
 {
 #if defined(__EMSCRIPTEN__)
   // open JavaScript console within the Browser to see this output
-  Handle(Message_PrinterSystemLog) aJSConsolePrinter = new Message_PrinterSystemLog ("DRAWEXE");
+  Message_Gravity aGravity = Message_Info;
+  Handle(Message_PrinterSystemLog) aJSConsolePrinter = new Message_PrinterSystemLog ("DRAWEXE", aGravity);
   Message::DefaultMessenger()->AddPrinter (aJSConsolePrinter);
+  // replace printer into std::cout by a printer into a custom callback Module.printMessage accepting message gravity
+  Message::DefaultMessenger()->RemovePrinters (STANDARD_TYPE(Message_PrinterOStream));
+  Handle(DRAWEXE_WasmModulePrinter) aJSModulePrinter = new DRAWEXE_WasmModulePrinter (aGravity);
+  Message::DefaultMessenger()->AddPrinter (aJSModulePrinter);
 #endif
 
   Draw::Commands (theDI);
index 4147bf1..fc48527 100644 (file)
 <div>
   <canvas id=occViewerCanvas oncontextmenu=event.preventDefault() tabindex=-1 style="border:0 none;background-color:#000" width="409" height="409"></canvas>
 </div>
-<h4>Output (open JavaScript console):</h4>
+<h4>For output - open JavaScript console in your Browser.</h4>
 <p id="output"></p>
 <script type="text/javascript" src="DRAWEXE.js" charset="utf-8"></script>
 <script>
+/**
+ * Class defining an interface to DRAWEXE and WebAssembly Module.
+ */
+class DrawTerm
+{
+  /**
+   * Class constructor.
+   */
+  constructor()
+  {
+    // define WebGL canvas for WebAssembly viewer
+    this.canvas = document.getElementById ('occViewerCanvas'); // canvas element for OpenGL context
+    this.canvas.tabIndex = -1;
+    this.canvas.onclick = (theEvent) =>
+    {
+      this.canvas.focus()
+    };
+  }
+
+  /**
+   * C++ std::cout callback redirecting to console.
+   */
+  print (theText)
+  {
+    //var anElement = document.getElementById('output');
+    //anElement.innerHTML += theText + "<br>";
+    console.warn (theText);
+  }
+
+  /**
+   * C++ std::cerr callback redirecting to console.
+   */
+  printErr (theText)
+  {
+    //var anElement = document.getElementById('output');
+    //anElement.innerHTML += theText + "<br>";
+    console.warn (theText);
+  }
+
+  /**
+   * C++ Message::Send() callback redirecting to Terminal.
+   */
+  printMessage (theText, theGravity)
+  {
+    switch (theGravity)
+    {
+      case 0: // trace
+        console.debug (theText);
+        return;
+      case 1: // info
+        console.info (theText);
+        return;
+      case 2: // warning
+        console.warn (theText);
+        return;
+      case 3: // alarm
+      case 4: // fail
+        console.error (theText);
+        return;
+    }
+    console.info (theText);
+  }
+
+  onRuntimeInitialized()
+  {
+    //
+  }
+};
+// Define a global DRAWEXE instance (will be initialized asynchronously).
+var DRAWEXE = new DrawTerm();
+
 //! Check browser support.
 function isWasmSupported()
 {
@@ -30,38 +101,22 @@ function isWasmSupported()
   } catch (e) {}
   return false;
 }
+
 if (!isWasmSupported())
 {
   var anElement = document.getElementById('output');
   anElement.innerHTML += "Browser is too old - WebAssembly support is missing!<br>Please check updates or install a modern browser.<br>";
 }
-
-var DRAWEXE =
+else
 {
-  print: (function() {
-    var anElement = document.getElementById('output');
-    //return function(theText) { anElement.innerHTML += theText + "<br>"; };
-    return function(theText) { console.warn(theText); };
-  })(),
-  printErr: function(theText) {
-    //var anElement = document.getElementById('output');
-    //anElement.innerHTML += theText + "<br>";
-    console.warn(theText);
-  },
-  canvas: (function() {
-    var aCanvas = document.getElementById('occViewerCanvas');
-    return aCanvas;
-  })(),
-
-  onRuntimeInitialized: function() {
-    //
-  }
-};
-
-const DRAWEXEInitialized = createDRAWEXE(DRAWEXE);
-DRAWEXEInitialized.then(function(Module) {
-  //DRAWEXE.eval("dversion");
-});
+  // load DRAWEXE.wasm (asynchronously) and wait initialization completion
+  createDRAWEXE(DRAWEXE).then (function(Module) {
+    //DRAWEXE.printMessage ("Hint: use \"pload ALL\" command to load standard commands\r\n", 1);
+    //DRAWEXE.eval ("dversion");
+  }).catch ((theError) => {
+    DRAWEXE.printMessage ("WebAssebly initialization has failed:\r\n" + theError, 4);
+  });
+}
 
 </script>
 </body>
index c30c6bd..5b111ed 100644 (file)
   #include <unistd.h>
 #endif
 
+#if defined(__EMSCRIPTEN__)
+  #include <emscripten/emscripten.h>
+
+  //! Returns Module.noExitRuntime flag.
+  EM_JS(bool, occJSModuleNoExitRuntime, (), {
+    return Module.noExitRuntime === true;
+  });
+#endif
+
 #ifdef HAVE_TK
 #if defined(__APPLE__) && !defined(HAVE_XLIB)
   // use forward declaration for small subset of used Tk functions
@@ -266,6 +275,9 @@ Draw_Window::Draw_Window (const char* theTitle,
   }
 
   getDrawWindowList().Append (this);
+#else
+  (void )theParent;
+  (void )theWin;
 #endif
 
   init (anXY, aSize);
@@ -843,7 +855,9 @@ void Draw_Window::DrawString (Standard_Integer theX, Standard_Integer theY,
 #elif defined(HAVE_XLIB)
   XDrawString (Draw_WindowDisplay, GetDrawable(), myBase->gc, theX, theY, (char* )theText, strlen(theText));
 #else
-  //
+  (void )theX;
+  (void )theY;
+  (void )theText;
 #endif
 }
 
@@ -1200,11 +1214,16 @@ void Run_Appli(Standard_Boolean (*interprete) (const char*))
 {
   Interprete = interprete;
 
+  bool toWaitInput = true;
+#ifdef __EMSCRIPTEN__
+  toWaitInput = !occJSModuleNoExitRuntime();
+#endif
+
   // Commands will come from standard input, so set up an event handler for standard input.
   // If the input device is aEvaluate the .rc file, if one has been specified,
   // set up an event handler for standard input, and print a prompt if the input device is a terminal.
   Tcl_Channel anInChannel = Tcl_GetStdChannel(TCL_STDIN);
-  if (anInChannel)
+  if (anInChannel && toWaitInput)
   {
     Tcl_CreateChannelHandler (anInChannel, TCL_READABLE, StdinProc, (ClientData )anInChannel);
   }
@@ -1237,6 +1256,11 @@ void Run_Appli(Standard_Boolean (*interprete) (const char*))
   // When there are no windows left, Tk_MainLoop returns and we exit.
   Tk_MainLoop();
 #else
+  if (!toWaitInput)
+  {
+    return;
+  }
+
   for (;;)
   {
     Tcl_DoOneEvent (0); // practically the same as Tk_MainLoop()
index 87a69cc..64e8a10 100755 (executable)
@@ -3,6 +3,7 @@ Graphic3d
 Xw
 Image
 Media
+Wasm
 WNT
 Cocoa
 Font
index 2a2ed2f..4e8673b 100644 (file)
 #include <ViewerTest_EventManager.hxx>
 
 #include <AIS_AnimationCamera.hxx>
+#include <Aspect_DisplayConnection.hxx>
 #include <AIS_InteractiveContext.hxx>
 #include <AIS_Shape.hxx>
 #include <Aspect_Grid.hxx>
 #include <Draw.hxx>
+#include <Message.hxx>
 #include <ViewerTest_ContinuousRedrawer.hxx>
 #include <ViewerTest_V3dView.hxx>
+#include <ViewerTest.hxx>
+
+#if defined(_WIN32)
+  //
+#elif defined(HAVE_XLIB)
+  #include <Xw_Window.hxx>
+  #include <X11/Xlib.h>
+  #include <X11/Xutil.h>
+#elif defined(__EMSCRIPTEN__)
+  #include <Wasm_Window.hxx>
+  #include <emscripten.h>
+  #include <emscripten/html5.h>
+
+  //! Callback flushing events and redrawing the WebGL canvas.
+  static void onWasmRedrawView (void* )
+  {
+    Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager();
+    const Handle(V3d_View)& aView = ViewerTest::CurrentView();
+    const Handle(AIS_InteractiveContext)& aCtx = ViewerTest::GetAISContext();
+    if (!aViewCtrl.IsNull() && !aView.IsNull() && !aCtx.IsNull())
+    {
+      aViewCtrl->ProcessExpose();
+    }
+  }
+#endif
 
 Standard_IMPORT Standard_Boolean Draw_Interprete (const char* theCommand);
 
@@ -48,7 +75,8 @@ ViewerTest_EventManager::ViewerTest_EventManager (const Handle(V3d_View)&
 : myCtx  (theCtx),
   myView (theView),
   myToPickPnt (Standard_False),
-  myIsTmpContRedraw (Standard_False)
+  myIsTmpContRedraw (Standard_False),
+  myUpdateRequests (0)
 {
   myViewAnimation = GlobalViewAnimation();
 
@@ -73,6 +101,9 @@ ViewerTest_EventManager::ViewerTest_EventManager (const Handle(V3d_View)&
   addActionHotKeys (Aspect_VKey_NavSlideRight,     Aspect_VKey_Right | Aspect_VKeyFlags_SHIFT);
   addActionHotKeys (Aspect_VKey_NavSlideUp,        Aspect_VKey_Up    | Aspect_VKeyFlags_SHIFT);
   addActionHotKeys (Aspect_VKey_NavSlideDown,      Aspect_VKey_Down  | Aspect_VKeyFlags_SHIFT);
+
+  // window could be actually not yet set to the View
+  //SetupWindowCallbacks (theView->Window());
 }
 
 //=======================================================================
@@ -89,6 +120,23 @@ ViewerTest_EventManager::~ViewerTest_EventManager()
   }
 }
 
+// =======================================================================
+// function : UpdateMouseClick
+// purpose  :
+// =======================================================================
+bool ViewerTest_EventManager::UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
+                                                Aspect_VKeyMouse theButton,
+                                                Aspect_VKeyFlags theModifiers,
+                                                bool theIsDoubleClick)
+{
+  if (theIsDoubleClick && !myView.IsNull() && !myCtx.IsNull())
+  {
+    FitAllAuto (myCtx, myView);
+    return true;
+  }
+  return AIS_ViewController::UpdateMouseClick (thePoint, theButton, theModifiers, theIsDoubleClick);
+}
+
 //=======================================================================
 //function : UpdateMouseButtons
 //purpose  :
@@ -124,7 +172,6 @@ void ViewerTest_EventManager::ProcessExpose()
 {
   if (!myView.IsNull())
   {
-    myView->Invalidate();
     FlushViewEvents (myCtx, myView, true);
   }
 }
@@ -136,6 +183,7 @@ void ViewerTest_EventManager::ProcessExpose()
 void ViewerTest_EventManager::handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
                                                 const Handle(V3d_View)& theView)
 {
+  myUpdateRequests = 0;
   AIS_ViewController::handleViewRedraw (theCtx, theView);
 
   // On non-Windows platforms Aspect_Window::InvalidateContent() from rendering thread does not work as expected
@@ -148,10 +196,16 @@ void ViewerTest_EventManager::handleViewRedraw (const Handle(AIS_InteractiveCont
      && (!aRedrawer.IsStarted() || aRedrawer.IsPaused()))
     {
       myIsTmpContRedraw = true;
-    #ifndef _WIN32
+    #if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
       aRedrawer.Start (theView->Window(), 60.0);
     #endif
     }
+
+    // ask more frames
+    ++myUpdateRequests;
+  #if defined(__EMSCRIPTEN__)
+    emscripten_async_call (onWasmRedrawView, this, 0);
+  #endif
   }
   else if (myIsTmpContRedraw)
   {
@@ -180,7 +234,9 @@ void ViewerTest_EventManager::ProcessConfigure (bool theIsResized)
       return;
     }
 
+    myView->Window()->DoResize();
     myView->MustBeResized();
+    myView->Invalidate();
     FlushViewEvents (myCtx, myView, true);
   }
 }
@@ -191,10 +247,25 @@ void ViewerTest_EventManager::ProcessConfigure (bool theIsResized)
 //==============================================================================
 void ViewerTest_EventManager::ProcessInput()
 {
-  if (!myView.IsNull())
+  if (myView.IsNull())
   {
-    FlushViewEvents (myCtx, myView, true);
+    return;
+  }
+
+#if defined(__EMSCRIPTEN__)
+  // Queue onWasmRedrawView() callback to redraw canvas after all user input is flushed by browser.
+  // Redrawing viewer on every single message would be a pointless waste of resources,
+  // as user will see only the last drawn frame due to WebGL implementation details.
+  if (++myUpdateRequests == 1)
+  {
+  #if defined(__EMSCRIPTEN__)
+    emscripten_async_call (onWasmRedrawView, this, 0);
+  #endif
   }
+#else
+  // handle synchronously
+  ProcessExpose();
+#endif
 }
 
 // =======================================================================
@@ -538,3 +609,145 @@ void ViewerTest_EventManager::ProcessKeyPress (Aspect_VKey theKey)
     Draw_Interprete (aCmd.ToCString());
   }
 }
+
+#if defined(__EMSCRIPTEN__)
+//! Handle browser window resize event.
+static EM_BOOL onResizeCallback (int theEventType, const EmscriptenUiEvent* theEvent, void* )
+{
+  Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager();
+  if (!aViewCtrl.IsNull()
+   && !ViewerTest::CurrentView().IsNull())
+  {
+    Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (ViewerTest::CurrentView()->Window());
+    return aWindow->ProcessUiEvent (*aViewCtrl, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
+  }
+  return EM_FALSE;
+}
+
+//! Handle mouse input event.
+static EM_BOOL onWasmMouseCallback (int theEventType, const EmscriptenMouseEvent* theEvent, void* )
+{
+  Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager();
+  if (!aViewCtrl.IsNull()
+   && !ViewerTest::CurrentView().IsNull())
+  {
+    Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (ViewerTest::CurrentView()->Window());
+    return aWindow->ProcessMouseEvent (*aViewCtrl, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
+  }
+  return EM_FALSE;
+}
+
+//! Handle mouse wheel event.
+static EM_BOOL onWasmWheelCallback (int theEventType, const EmscriptenWheelEvent* theEvent, void* )
+{
+  Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager();
+  if (!aViewCtrl.IsNull()
+   && !ViewerTest::CurrentView().IsNull())
+  {
+    Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (ViewerTest::CurrentView()->Window());
+    return aWindow->ProcessWheelEvent (*aViewCtrl, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
+  }
+  return EM_FALSE;
+}
+
+//! Handle touch input event.
+static EM_BOOL onWasmTouchCallback (int theEventType, const EmscriptenTouchEvent* theEvent, void* )
+{
+  Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager();
+  if (!aViewCtrl.IsNull()
+   && !ViewerTest::CurrentView().IsNull())
+  {
+    Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (ViewerTest::CurrentView()->Window());
+    return aWindow->ProcessTouchEvent (*aViewCtrl, theEventType, theEvent) ? EM_TRUE : EM_FALSE;
+  }
+  return EM_FALSE;
+}
+
+//! Handle keyboard input event.
+static EM_BOOL onWasmKeyCallback (int theEventType, const EmscriptenKeyboardEvent* theEvent, void* )
+{
+  Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager();
+  if (!aViewCtrl.IsNull()
+   && !ViewerTest::CurrentView().IsNull())
+  {
+    Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (ViewerTest::CurrentView()->Window());
+    aWindow->ProcessKeyEvent (*aViewCtrl, theEventType, theEvent);
+    return EM_TRUE;
+  }
+  return EM_FALSE;
+}
+#endif
+
+// ==============================================================================
+// function : SetupWindowCallbacks
+// purpose  :
+// ==============================================================================
+void ViewerTest_EventManager::SetupWindowCallbacks (const Handle(Aspect_Window)& theWin)
+{
+#ifdef _WIN32
+  (void )theWin;
+#elif defined(HAVE_XLIB)
+  // X11
+  Window anXWin = (Window )theWin->NativeHandle();
+  Display* anXDisplay = (Display* )theWin->DisplayConnection()->GetDisplayAspect();
+  XSynchronize (anXDisplay, 1);
+
+  // X11 : For keyboard on SUN
+  XWMHints aWmHints;
+  memset (&aWmHints, 0, sizeof(aWmHints));
+  aWmHints.flags = InputHint;
+  aWmHints.input = 1;
+  XSetWMHints (anXDisplay, anXWin, &aWmHints);
+
+  XSelectInput (anXDisplay, anXWin,
+                ExposureMask | KeyPressMask | KeyReleaseMask
+              | ButtonPressMask | ButtonReleaseMask
+              | StructureNotifyMask
+              | PointerMotionMask
+              | Button1MotionMask | Button2MotionMask
+              | Button3MotionMask | FocusChangeMask);
+  Atom aDeleteWindowAtom = theWin->DisplayConnection()->GetAtom (Aspect_XA_DELETE_WINDOW);
+  XSetWMProtocols (anXDisplay, anXWin, &aDeleteWindowAtom, 1);
+
+  XSynchronize (anXDisplay, 0);
+#elif defined(__EMSCRIPTEN__)
+  Handle(Wasm_Window) aWindow = Handle(Wasm_Window)::DownCast (theWin);
+  if (aWindow->CanvasId().IsEmpty()
+   || aWindow->CanvasId() == "#")
+  {
+    Message::SendFail ("Error: unable registering callbacks to Module.canvas");
+    return;
+  }
+
+  const char* aTargetId = aWindow->CanvasId().ToCString();
+  const EM_BOOL toUseCapture = EM_TRUE;
+  void* anOpaque = NULL; //this; // unused
+
+  // make sure to clear previously set listeners (e.g. created by another ViewerTest_EventManager instance)
+  emscripten_html5_remove_all_event_listeners();
+
+  // resize event implemented only for a window by browsers,
+  // so that if web application changes canvas size by other means it should use another way to tell OCCT about resize
+  emscripten_set_resize_callback     (EMSCRIPTEN_EVENT_TARGET_WINDOW, anOpaque, toUseCapture, onResizeCallback);
+
+  emscripten_set_mousedown_callback  (aTargetId, anOpaque, toUseCapture, onWasmMouseCallback);
+  emscripten_set_mouseup_callback    (aTargetId, anOpaque, toUseCapture, onWasmMouseCallback);
+  emscripten_set_mousemove_callback  (aTargetId, anOpaque, toUseCapture, onWasmMouseCallback);
+  emscripten_set_dblclick_callback   (aTargetId, anOpaque, toUseCapture, onWasmMouseCallback);
+  emscripten_set_click_callback      (aTargetId, anOpaque, toUseCapture, onWasmMouseCallback);
+  emscripten_set_mouseenter_callback (aTargetId, anOpaque, toUseCapture, onWasmMouseCallback);
+  emscripten_set_mouseleave_callback (aTargetId, anOpaque, toUseCapture, onWasmMouseCallback);
+  emscripten_set_wheel_callback      (aTargetId, anOpaque, toUseCapture, onWasmWheelCallback);
+
+  emscripten_set_touchstart_callback (aTargetId, anOpaque, toUseCapture, onWasmTouchCallback);
+  emscripten_set_touchend_callback   (aTargetId, anOpaque, toUseCapture, onWasmTouchCallback);
+  emscripten_set_touchmove_callback  (aTargetId, anOpaque, toUseCapture, onWasmTouchCallback);
+  emscripten_set_touchcancel_callback(aTargetId, anOpaque, toUseCapture, onWasmTouchCallback);
+
+  // keyboard input requires a focusable element or EMSCRIPTEN_EVENT_TARGET_WINDOW
+  emscripten_set_keydown_callback    (aTargetId, anOpaque, toUseCapture, onWasmKeyCallback);
+  emscripten_set_keyup_callback      (aTargetId, anOpaque, toUseCapture, onWasmKeyCallback);
+#else
+  (void )theWin;
+#endif
+}
index 4b54598..23dfe7d 100644 (file)
@@ -22,6 +22,7 @@
 #include <TCollection_AsciiString.hxx>
 
 class AIS_InteractiveContext;
+class Aspect_Window;
 class V3d_View;
 
 DEFINE_STANDARD_HANDLE(ViewerTest_EventManager, Standard_Transient)
@@ -58,6 +59,9 @@ public:
   //! Destructor.
   Standard_EXPORT virtual ~ViewerTest_EventManager();
 
+  //! Setup or adjust window callbacks.
+  Standard_EXPORT static void SetupWindowCallbacks (const Handle(Aspect_Window)& theWin);
+
   //! Return interactive context.
   const Handle(AIS_InteractiveContext)& Context() const { return myCtx; }
 
@@ -75,6 +79,12 @@ public:
     myPickPntArgVec[2] = theArgZ;
   }
 
+  //! Handle mouse button click event.
+  Standard_EXPORT virtual bool UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
+                                                  Aspect_VKeyMouse theButton,
+                                                  Aspect_VKeyFlags theModifiers,
+                                                  bool theIsDoubleClick) Standard_OVERRIDE;
+
   //! Handle mouse button press/release event.
   Standard_EXPORT virtual bool UpdateMouseButtons (const Graphic3d_Vec2i& thePoint,
                                                    Aspect_VKeyMouse theButtons,
@@ -138,6 +148,8 @@ private:
   Standard_Boolean myToPickPnt;
   Standard_Boolean myIsTmpContRedraw;
 
+  unsigned int     myUpdateRequests; //!< counter for unhandled update requests
+
 };
 
 #endif // _ViewerTest_EventManager_HeaderFile
index 0a37eb4..ac31f4e 100644 (file)
   #include <X11/Xutil.h>
 #elif defined(__APPLE__)
   #include <Cocoa_Window.hxx>
+#elif defined(__EMSCRIPTEN__)
+  #include <Wasm_Window.hxx>
+  #include <emscripten/emscripten.h>
 #else
   #include <Aspect_NeutralWindow.hxx>
 #endif
@@ -135,10 +138,32 @@ static void VProcessEvents(ClientData,int);
 typedef Cocoa_Window ViewerTest_Window;
 extern void ViewerTest_SetCocoaEventManagerView (const Handle(Cocoa_Window)& theWindow);
 extern void GetCocoaScreenResolution (Standard_Integer& theWidth, Standard_Integer& theHeight);
+#elif defined(__EMSCRIPTEN__)
+typedef Wasm_Window ViewerTest_Window;
 #else
 typedef Aspect_NeutralWindow ViewerTest_Window;
 #endif
 
+#if defined(__EMSCRIPTEN__)
+//! Return DOM id of default WebGL canvas from Module.canvas.
+EM_JS(char*, occJSModuleCanvasId, (), {
+  const aCanvasId = Module.canvas.id;
+  const aNbBytes  = lengthBytesUTF8 (aCanvasId) + 1;
+  const aStrPtr   = Module._malloc (aNbBytes);
+  stringToUTF8 (aCanvasId, aStrPtr, aNbBytes);
+  return aStrPtr;
+});
+
+//! Return DOM id of default WebGL canvas from Module.canvas.
+static TCollection_AsciiString getModuleCanvasId()
+{
+  char* aRawId = occJSModuleCanvasId();
+  TCollection_AsciiString anId (aRawId != NULL ? aRawId : "");
+  free (aRawId);
+  return anId;
+}
+#endif
+
 static Handle(ViewerTest_Window)& VT_GetWindow()
 {
   static Handle(ViewerTest_Window) aWindow;
@@ -160,8 +185,6 @@ NCollection_DoubleMap <TCollection_AsciiString, Handle(V3d_View)> ViewerTest_myV
 static NCollection_DoubleMap <TCollection_AsciiString, Handle(AIS_InteractiveContext)>  ViewerTest_myContexts;
 static NCollection_DoubleMap <TCollection_AsciiString, Handle(Graphic3d_GraphicDriver)> ViewerTest_myDrivers;
 
-static void OSWindowSetup();
-
 static struct
 {
   Quantity_Color FlatColor;
@@ -1663,15 +1686,18 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
   // window fit in the small screens (actual for remote desktops, see #23003).
   // The position corresponds to the window's client area, thus some
   // gap is added for window frame to be visible.
-  Standard_Integer aPxLeft   = 20;
-  Standard_Integer aPxTop    = 40;
-  Standard_Integer aPxWidth  = 409;
-  Standard_Integer aPxHeight = 409;
+  Standard_Integer aPxLeft  = 20,  aPxTop    = 40;
+  Standard_Integer aPxWidth = 409, aPxHeight = 409;
+  Standard_Boolean isDefViewSize = Standard_True;
   Standard_Boolean toCreateViewer = Standard_False;
   const Standard_Boolean isVirtual = Draw_VirtualWindows || theIsVirtual;
   if (!theViewToClone.IsNull())
   {
     theViewToClone->Window()->Size (aPxWidth, aPxHeight);
+    isDefViewSize = Standard_False;
+  #if !defined(__EMSCRIPTEN__)
+    (void )isDefViewSize;
+  #endif
   }
 
   Handle(Graphic3d_GraphicDriverFactory) aFactory = Graphic3d_GraphicDriverFactory::DefaultDriverFactory();
@@ -1692,17 +1718,29 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
 
   Handle(Graphic3d_GraphicDriver) aGraphicDriver;
   ViewerTest_Names aViewNames(theViewName);
-  if (ViewerTest_myViews.IsBound1 (aViewNames.GetViewName ()))
+  if (ViewerTest_myViews.IsBound1 (aViewNames.GetViewName()))
+  {
     aViewNames.SetViewName (aViewNames.GetViewerName() + "/" + CreateName<Handle(V3d_View)>(ViewerTest_myViews, "View"));
+  }
 
   if (thePxLeft != 0)
+  {
     aPxLeft = thePxLeft;
+  }
   if (thePxTop != 0)
+  {
     aPxTop = thePxTop;
+  }
   if (thePxWidth != 0)
+  {
+    isDefViewSize = Standard_False;
     aPxWidth = thePxWidth;
+  }
   if (thePxHeight != 0)
+  {
+    isDefViewSize = Standard_False;
     aPxHeight = thePxHeight;
+  }
 
   // Get graphic driver (create it or get from another view)
   const bool isNewDriver = !ViewerTest_myDrivers.IsBound1 (aViewNames.GetDriverName());
@@ -1862,6 +1900,25 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
                                      aPxLeft, aPxTop,
                                      aPxWidth, aPxHeight);
   ViewerTest_SetCocoaEventManagerView (VT_GetWindow());
+#elif defined(__EMSCRIPTEN__)
+  // current EGL implementation in Emscripten supports only one global WebGL canvas returned by Module.canvas property;
+  // the code should be revised for handling multiple canvas elements (which is technically also possible)
+  TCollection_AsciiString aCanvasId = getModuleCanvasId();
+  if (!aCanvasId.IsEmpty())
+  {
+    aCanvasId = TCollection_AsciiString("#") + aCanvasId;
+  }
+
+  VT_GetWindow() = new Wasm_Window (aCanvasId);
+  Graphic3d_Vec2i aRealSize;
+  VT_GetWindow()->Size (aRealSize.x(), aRealSize.y());
+  if (!isDefViewSize || (aRealSize.x() <= 0 && aRealSize.y() <= 0))
+  {
+    // Wasm_Window wraps an existing HTML element without creating a new one.
+    // Keep size defined on a web page instead of defaulting to 409x409 (as in case of other platform),
+    // but resize canvas if vinit has been called with explicitly specified dimensions.
+    VT_GetWindow()->SetSizeLogical (Graphic3d_Vec2d (aPxWidth, aPxHeight));
+  }
 #else
   // not implemented
   VT_GetWindow() = new Aspect_NeutralWindow();
@@ -1887,7 +1944,8 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
   ViewerTest_myViews.Bind (aViewNames.GetViewName(), aView);
 
   // Setup for X11 or NT
-  OSWindowSetup();
+  SetDisplayConnection (ViewerTest::CurrentView()->Viewer()->Driver()->GetDisplayConnection());
+  ViewerTest_EventManager::SetupWindowCallbacks (VT_GetWindow());
 
   // Set parameters for V3d_View and V3d_Viewer
   const Handle (V3d_View) aV3dView = ViewerTest::CurrentView();
@@ -2540,15 +2598,7 @@ void ActivateView (const TCollection_AsciiString& theViewName,
     ViewerTest::CurrentView (aView);
     ViewerTest::SetAISContext (anAISContext);
     aView->Window()->SetTitle (TCollection_AsciiString("3D View - ") + theViewName + "(*)");
-#if defined(_WIN32)
-    VT_GetWindow() = Handle(WNT_Window)::DownCast(ViewerTest::CurrentView()->Window());
-#elif defined(HAVE_XLIB)
-    VT_GetWindow() = Handle(Xw_Window)::DownCast(ViewerTest::CurrentView()->Window());
-#elif defined(__APPLE__)
-    VT_GetWindow() = Handle(Cocoa_Window)::DownCast(ViewerTest::CurrentView()->Window());
-#else
-    VT_GetWindow() = Handle(Aspect_NeutralWindow)::DownCast(ViewerTest::CurrentView()->Window());
-#endif
+    VT_GetWindow() = Handle(ViewerTest_Window)::DownCast(ViewerTest::CurrentView()->Window());
     SetDisplayConnection(ViewerTest::CurrentView()->Viewer()->Driver()->GetDisplayConnection());
     if (theToUpdate)
     {
@@ -3473,45 +3523,6 @@ int ViewerMainLoop (Standard_Integer , const char** )
 }
 #endif
 
-//==============================================================================
-//function : OSWindowSetup
-//purpose  : Setup for the X11 window to be able to catch the event
-//==============================================================================
-static void OSWindowSetup()
-{
-#ifdef _WIN32
-  //
-
-#elif defined(HAVE_XLIB)
-  // X11
-  Window anXWin = VT_GetWindow()->XWindow();
-  SetDisplayConnection (ViewerTest::CurrentView()->Viewer()->Driver()->GetDisplayConnection());
-  Display* aDisplay = (Display* )GetDisplayConnection()->GetDisplayAspect();
-  XSynchronize (aDisplay, 1);
-
-  // X11 : For keyboard on SUN
-  XWMHints aWmHints;
-  memset (&aWmHints, 0, sizeof(aWmHints));
-  aWmHints.flags = InputHint;
-  aWmHints.input = 1;
-  XSetWMHints (aDisplay, anXWin, &aWmHints);
-
-  XSelectInput (aDisplay, anXWin,
-                ExposureMask | KeyPressMask | KeyReleaseMask
-              | ButtonPressMask | ButtonReleaseMask
-              | StructureNotifyMask
-              | PointerMotionMask
-              | Button1MotionMask | Button2MotionMask
-              | Button3MotionMask | FocusChangeMask);
-  Atom aDeleteWindowAtom = GetDisplayConnection()->GetAtom (Aspect_XA_DELETE_WINDOW);
-  XSetWMProtocols (aDisplay, anXWin, &aDeleteWindowAtom, 1);
-
-  XSynchronize (aDisplay, 0);
-#else
-  //
-#endif
-}
-
 //==============================================================================
 //function : VFit
 //purpose  :
index 8f9ed39..d48a9d8 100644 (file)
@@ -21,7 +21,6 @@
 
 #if defined(_WIN32) && !defined(OCCT_UWP)
 
-#include <Aspect_Convert.hxx>
 #include <Aspect_ScrollDelta.hxx>
 #include <Aspect_WindowDefinitionError.hxx>
 #include <Aspect_WindowError.hxx>
diff --git a/src/Wasm/FILES b/src/Wasm/FILES
new file mode 100644 (file)
index 0000000..56fc2c6
--- /dev/null
@@ -0,0 +1,2 @@
+Wasm_Window.cxx
+Wasm_Window.hxx
diff --git a/src/Wasm/Wasm_Window.cxx b/src/Wasm/Wasm_Window.cxx
new file mode 100644 (file)
index 0000000..b2a81e3
--- /dev/null
@@ -0,0 +1,779 @@
+// Created by: Kirill Gavrilov
+// Copyright (c) 2021 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 <Wasm_Window.hxx>
+
+#include <Aspect_ScrollDelta.hxx>
+#include <Aspect_WindowInputListener.hxx>
+
+#if defined(__EMSCRIPTEN__)
+  #include <emscripten.h>
+  #include <emscripten/html5.h>
+  #include <emscripten/key_codes.h>
+#endif
+
+IMPLEMENT_STANDARD_RTTIEXT(Wasm_Window, Aspect_Window)
+
+// =======================================================================
+// function : Wasm_Window
+// purpose  :
+// =======================================================================
+Wasm_Window::Wasm_Window (const TCollection_AsciiString& theCanvasId,
+                          const bool theToScaleBacking)
+: myCanvasId (theCanvasId),
+  mySize (0),
+  myDevicePixelRatio (1.0),
+  myToScaleBacking (theToScaleBacking),
+  myIsMapped (true)
+{
+#if defined(__EMSCRIPTEN__)
+  myDevicePixelRatio = emscripten_get_device_pixel_ratio();
+  emscripten_get_canvas_element_size (myCanvasId.ToCString(), &mySize.x(), &mySize.y());
+  if (myToScaleBacking)
+  {
+    myDevicePixelRatio = emscripten_get_device_pixel_ratio();
+    Graphic3d_Vec2d aCssSize;
+    emscripten_get_element_css_size (myCanvasId.ToCString(), &aCssSize.x(), &aCssSize.y());
+    Graphic3d_Vec2i aCanvasSize = Graphic3d_Vec2i (aCssSize * myDevicePixelRatio);
+    if (aCanvasSize != mySize)
+    {
+      mySize = aCanvasSize;
+      emscripten_set_canvas_element_size (myCanvasId.ToCString(), aCanvasSize.x(), aCanvasSize.y());
+      emscripten_set_element_css_size    (myCanvasId.ToCString(), aCssSize.x(),    aCssSize.y());
+    }
+  }
+#endif
+}
+
+// =======================================================================
+// function : ~Wasm_Window
+// purpose  :
+// =======================================================================
+Wasm_Window::~Wasm_Window()
+{
+  //
+}
+
+// =======================================================================
+// function : DoResize
+// purpose  :
+// =======================================================================
+Aspect_TypeOfResize Wasm_Window::DoResize()
+{
+  if (IsVirtual())
+  {
+    return Aspect_TOR_UNKNOWN;
+  }
+
+#if defined(__EMSCRIPTEN__)
+  emscripten_get_canvas_element_size (myCanvasId.ToCString(), &mySize.x(), &mySize.y());
+  if (myToScaleBacking)
+  {
+    myDevicePixelRatio = emscripten_get_device_pixel_ratio();
+    Graphic3d_Vec2d aCssSize;
+    emscripten_get_element_css_size (myCanvasId.ToCString(), &aCssSize.x(), &aCssSize.y());
+    Graphic3d_Vec2i aCanvasSize = Graphic3d_Vec2i (aCssSize * myDevicePixelRatio);
+    if (aCanvasSize != mySize)
+    {
+      mySize = aCanvasSize;
+      emscripten_set_canvas_element_size (myCanvasId.ToCString(), aCanvasSize.x(), aCanvasSize.y());
+      emscripten_set_element_css_size    (myCanvasId.ToCString(), aCssSize.x(),    aCssSize.y());
+    }
+  }
+#endif
+  return Aspect_TOR_UNKNOWN;
+}
+
+// =======================================================================
+// function : Ratio
+// purpose  :
+// =======================================================================
+Standard_Real Wasm_Window::Ratio() const
+{
+  Graphic3d_Vec2i aCanvasSize = mySize;
+  if (!IsVirtual())
+  {
+  #if defined(__EMSCRIPTEN__)
+    emscripten_get_canvas_element_size (myCanvasId.ToCString(), &aCanvasSize.x(), &aCanvasSize.y());
+  #endif
+  }
+
+  return (aCanvasSize.x() != 0 && aCanvasSize.y() != 0)
+        ? Standard_Real(aCanvasSize.x()) / Standard_Real(aCanvasSize.y())
+        : 1.0;
+}
+
+// =======================================================================
+// function : Position
+// purpose  :
+// =======================================================================
+void Wasm_Window::Position (Standard_Integer& theX1, Standard_Integer& theY1,
+                            Standard_Integer& theX2, Standard_Integer& theY2) const
+{
+  theX1 = 0;
+  theY1 = 0;
+  if (IsVirtual())
+  {
+    theX2 = mySize.x();
+    theY2 = mySize.y();
+    return;
+  }
+
+#if defined(__EMSCRIPTEN__)
+  emscripten_get_canvas_element_size (myCanvasId.ToCString(), &theX2, &theY2);
+#endif
+}
+
+// =======================================================================
+// function : Size
+// purpose  :
+// =======================================================================
+void Wasm_Window::Size (Standard_Integer& theWidth,
+                        Standard_Integer& theHeight) const
+{
+  if (IsVirtual())
+  {
+    theWidth  = mySize.x();
+    theHeight = mySize.y();
+    return;
+  }
+
+#if defined(__EMSCRIPTEN__)
+  emscripten_get_canvas_element_size (myCanvasId.ToCString(), &theWidth, &theHeight);
+#endif
+}
+
+// =======================================================================
+// function : SetSizeLogical
+// purpose  :
+// =======================================================================
+void Wasm_Window::SetSizeLogical (const Graphic3d_Vec2d& theSize)
+{
+  mySize = Graphic3d_Vec2i (theSize * myDevicePixelRatio);
+  if (IsVirtual())
+  {
+    return;
+  }
+
+#if defined(__EMSCRIPTEN__)
+  emscripten_set_canvas_element_size (myCanvasId.ToCString(), mySize.x(),  mySize.y());
+  emscripten_set_element_css_size    (myCanvasId.ToCString(), theSize.x(), theSize.y());
+#endif
+}
+
+// =======================================================================
+// function : SetSizeBacking
+// purpose  :
+// =======================================================================
+void Wasm_Window::SetSizeBacking (const Graphic3d_Vec2i& theSize)
+{
+  mySize = theSize;
+  if (IsVirtual())
+  {
+    return;
+  }
+
+#if defined(__EMSCRIPTEN__)
+  Graphic3d_Vec2i aCanvasSize = mySize;
+  Graphic3d_Vec2d aCssSize = Graphic3d_Vec2d (mySize) / myDevicePixelRatio;
+  emscripten_set_canvas_element_size (myCanvasId.ToCString(), aCanvasSize.x(), aCanvasSize.y());
+  emscripten_set_element_css_size    (myCanvasId.ToCString(), aCssSize.x(),    aCssSize.y());
+#endif
+}
+
+// =======================================================================
+// function : InvalidateContent
+// purpose  :
+// =======================================================================
+void Wasm_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
+{
+  //
+}
+
+// =======================================================================
+// function : ProcessMessage
+// purpose  :
+// =======================================================================
+bool Wasm_Window::ProcessMessage (Aspect_WindowInputListener& theListener,
+                                  int theEventType, const void* theEvent)
+{
+#if defined(__EMSCRIPTEN__)
+  switch (theEventType)
+  {
+    case EMSCRIPTEN_EVENT_MOUSEMOVE:
+    case EMSCRIPTEN_EVENT_MOUSEDOWN:
+    case EMSCRIPTEN_EVENT_MOUSEUP:
+    case EMSCRIPTEN_EVENT_CLICK:
+    case EMSCRIPTEN_EVENT_DBLCLICK:
+    case EMSCRIPTEN_EVENT_MOUSEENTER:
+    case EMSCRIPTEN_EVENT_MOUSELEAVE:
+    {
+      return ProcessMouseEvent (theListener, theEventType, (const EmscriptenMouseEvent* )theEvent);
+    }
+    case EMSCRIPTEN_EVENT_TOUCHSTART:
+    case EMSCRIPTEN_EVENT_TOUCHMOVE:
+    case EMSCRIPTEN_EVENT_TOUCHEND:
+    case EMSCRIPTEN_EVENT_TOUCHCANCEL:
+    {
+      return ProcessTouchEvent (theListener, theEventType, (const EmscriptenTouchEvent* )theEvent);
+    }
+    case EMSCRIPTEN_EVENT_WHEEL:
+    {
+      return ProcessWheelEvent (theListener, theEventType, (const EmscriptenWheelEvent* )theEvent);
+    }
+    case EMSCRIPTEN_EVENT_KEYDOWN:
+    case EMSCRIPTEN_EVENT_KEYUP:
+    case EMSCRIPTEN_EVENT_KEYPRESS:
+    {
+      return ProcessKeyEvent (theListener, theEventType, (const EmscriptenKeyboardEvent* )theEvent);
+    }
+    case EMSCRIPTEN_EVENT_RESIZE:
+    case EMSCRIPTEN_EVENT_CANVASRESIZED:
+    {
+      return ProcessUiEvent (theListener, theEventType, (const EmscriptenUiEvent* )theEvent);
+    }
+  }
+  return false;
+#else
+  (void )theListener;
+  (void )theEventType;
+  (void )theEvent;
+  return false;
+#endif
+}
+
+// =======================================================================
+// function : ProcessMouseEvent
+// purpose  :
+// =======================================================================
+bool Wasm_Window::ProcessMouseEvent (Aspect_WindowInputListener& theListener,
+                                     int theEventType, const EmscriptenMouseEvent* theEvent)
+{
+#if defined(__EMSCRIPTEN__)
+  const Graphic3d_Vec2d aNewPos2d = ConvertPointToBacking (Graphic3d_Vec2d (theEvent->targetX, theEvent->targetY));
+  const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i (aNewPos2d + Graphic3d_Vec2d (0.5));
+  Aspect_VKeyFlags aFlags = 0;
+  if (theEvent->ctrlKey  == EM_TRUE) { aFlags |= Aspect_VKeyFlags_CTRL;  }
+  if (theEvent->shiftKey == EM_TRUE) { aFlags |= Aspect_VKeyFlags_SHIFT; }
+  if (theEvent->altKey   == EM_TRUE) { aFlags |= Aspect_VKeyFlags_ALT;   }
+  if (theEvent->metaKey  == EM_TRUE) { aFlags |= Aspect_VKeyFlags_META;  }
+
+  const bool isEmulated = false;
+  const Aspect_VKeyMouse aButtons = Wasm_Window::MouseButtonsFromNative (theEvent->buttons);
+  switch (theEventType)
+  {
+    case EMSCRIPTEN_EVENT_MOUSEMOVE:
+    {
+      if ((aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
+        || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
+        && theListener.PressedMouseButtons() == Aspect_VKeyMouse_NONE)
+      {
+        return false;
+      }
+      if (theListener.UpdateMousePosition (aNewPos2i, aButtons, aFlags, isEmulated))
+      {
+        theListener.ProcessInput();
+      }
+      break;
+    }
+    case EMSCRIPTEN_EVENT_MOUSEDOWN:
+    case EMSCRIPTEN_EVENT_MOUSEUP:
+    {
+      if (aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
+       || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
+      {
+        return false;
+      }
+      if (theListener.UpdateMouseButtons (aNewPos2i, aButtons, aFlags, isEmulated))
+      {
+        theListener.ProcessInput();
+      }
+      break;
+    }
+    case EMSCRIPTEN_EVENT_CLICK:
+    case EMSCRIPTEN_EVENT_DBLCLICK:
+    {
+      if (aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
+       || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
+      {
+        return false;
+      }
+      break;
+    }
+    case EMSCRIPTEN_EVENT_MOUSEENTER:
+    {
+      break;
+    }
+    case EMSCRIPTEN_EVENT_MOUSELEAVE:
+    {
+      // there is no SetCapture() support, so that mouse unclick events outside canvas will not arrive,
+      // so we have to forget current state...
+      if (theListener.UpdateMouseButtons (aNewPos2i, Aspect_VKeyMouse_NONE, aFlags, isEmulated))
+      {
+        theListener.ProcessInput();
+      }
+      break;
+    }
+  }
+  return true;
+#else
+  (void )theListener;
+  (void )theEventType;
+  (void )theEvent;
+  return false;
+#endif
+}
+
+// =======================================================================
+// function : ProcessWheelEvent
+// purpose  :
+// =======================================================================
+bool Wasm_Window::ProcessWheelEvent (Aspect_WindowInputListener& theListener,
+                                     int theEventType, const EmscriptenWheelEvent* theEvent)
+{
+#if defined(__EMSCRIPTEN__)
+  if (theEventType != EMSCRIPTEN_EVENT_WHEEL)
+  {
+    return false;
+  }
+
+  const Graphic3d_Vec2d aNewPos2d = ConvertPointToBacking (Graphic3d_Vec2d (theEvent->mouse.targetX, theEvent->mouse.targetY));
+  const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i (aNewPos2d + Graphic3d_Vec2d (0.5));
+  if (aNewPos2i.x() < 0 || aNewPos2i.x() > mySize.x()
+   || aNewPos2i.y() < 0 || aNewPos2i.y() > mySize.y())
+  {
+    return false;
+  }
+
+  double aDelta = 0.0;
+  switch (theEvent->deltaMode)
+  {
+    case DOM_DELTA_PIXEL:
+    {
+      aDelta = theEvent->deltaY / (5.0 * DevicePixelRatio());
+      break;
+    }
+    case DOM_DELTA_LINE:
+    {
+      aDelta = theEvent->deltaY * 8.0;
+      break;
+    }
+    case DOM_DELTA_PAGE:
+    {
+      aDelta = theEvent->deltaY >= 0.0 ? 24.0 : -24.0;
+      break;
+    }
+  }
+  aDelta /= 15.0;
+
+  if (theListener.UpdateMouseScroll (Aspect_ScrollDelta (aNewPos2i, -aDelta)))
+  {
+    theListener.ProcessInput();
+  }
+  return true;
+#else
+  (void )theListener;
+  (void )theEventType;
+  (void )theEvent;
+  return false;
+#endif
+}
+
+// =======================================================================
+// function : ProcessTouchEvent
+// purpose  :
+// =======================================================================
+bool Wasm_Window::ProcessTouchEvent (Aspect_WindowInputListener& theListener,
+                                     int theEventType, const EmscriptenTouchEvent* theEvent)
+{
+  bool hasUpdates = false;
+#if defined(__EMSCRIPTEN__)
+  if (theEventType != EMSCRIPTEN_EVENT_TOUCHSTART
+   && theEventType != EMSCRIPTEN_EVENT_TOUCHMOVE
+   && theEventType != EMSCRIPTEN_EVENT_TOUCHEND
+   && theEventType != EMSCRIPTEN_EVENT_TOUCHCANCEL)
+  {
+    return false;
+  }
+
+  for (int aTouchIter = 0; aTouchIter < theEvent->numTouches; ++aTouchIter)
+  {
+    const EmscriptenTouchPoint& aTouch = theEvent->touches[aTouchIter];
+    if (!aTouch.isChanged)
+    {
+      continue;
+    }
+
+    const Standard_Size aTouchId = (Standard_Size )aTouch.identifier;
+
+    const Graphic3d_Vec2d aNewPos2d = ConvertPointToBacking (Graphic3d_Vec2d (aTouch.targetX, aTouch.targetY));
+    const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i (aNewPos2d + Graphic3d_Vec2d (0.5));
+    switch (theEventType)
+    {
+      case EMSCRIPTEN_EVENT_TOUCHSTART:
+      {
+        if (aNewPos2i.x() >= 0 && aNewPos2i.x() < mySize.x()
+         && aNewPos2i.y() >= 0 && aNewPos2i.y() < mySize.y())
+        {
+          hasUpdates = true;
+          theListener.AddTouchPoint (aTouchId, aNewPos2d);
+        }
+        break;
+      }
+      case EMSCRIPTEN_EVENT_TOUCHMOVE:
+      {
+        const int anOldIndex = theListener.TouchPoints().FindIndex (aTouchId);
+        if (anOldIndex != 0)
+        {
+          hasUpdates = true;
+          theListener.UpdateTouchPoint (aTouchId, aNewPos2d);
+        }
+        break;
+      }
+      case EMSCRIPTEN_EVENT_TOUCHEND:
+      case EMSCRIPTEN_EVENT_TOUCHCANCEL:
+      {
+        if (theListener.RemoveTouchPoint (aTouchId))
+        {
+          hasUpdates = true;
+        }
+        break;
+      }
+    }
+  }
+  if (hasUpdates)
+  {
+    theListener.ProcessInput();
+  }
+#else
+  (void )theEventType;
+  (void )theEvent;
+#endif
+  return hasUpdates || theListener.HasTouchPoints();
+}
+
+// =======================================================================
+// function : ProcessKeyEvent
+// purpose  :
+// =======================================================================
+bool Wasm_Window::ProcessKeyEvent (Aspect_WindowInputListener& theListener,
+                                   int theEventType, const EmscriptenKeyboardEvent* theEvent)
+{
+#if defined(__EMSCRIPTEN__)
+  if (theEventType != EMSCRIPTEN_EVENT_KEYDOWN
+   && theEventType != EMSCRIPTEN_EVENT_KEYUP
+   && theEventType != EMSCRIPTEN_EVENT_KEYPRESS)
+  {
+    return false;
+  }
+
+  const double aTimeStamp = theListener.EventTime();
+  const Aspect_VKey aVKey = Wasm_Window::VirtualKeyFromNative (theEvent->keyCode);
+  if (aVKey == Aspect_VKey_UNKNOWN)
+  {
+    return false;
+  }
+
+  switch (theEventType)
+  {
+    case EMSCRIPTEN_EVENT_KEYDOWN:
+    {
+      if (theEvent->repeat == EM_TRUE)
+      {
+        return false;
+      }
+
+      theListener.KeyDown (aVKey, aTimeStamp);
+      theListener.ProcessInput();
+      return false;
+    }
+    case EMSCRIPTEN_EVENT_KEYUP:
+    {
+      theListener.KeyUp (aVKey, aTimeStamp);
+      theListener.ProcessInput();
+      return false;
+    }
+  }
+#else
+  (void )theListener;
+  (void )theEventType;
+  (void )theEvent;
+#endif
+  return false;
+}
+
+// =======================================================================
+// function : ProcessUiEvent
+// purpose  :
+// =======================================================================
+bool Wasm_Window::ProcessUiEvent (Aspect_WindowInputListener& theListener,
+                                  int theEventType, const EmscriptenUiEvent* )
+{
+#if defined(__EMSCRIPTEN__)
+  if (theEventType != EMSCRIPTEN_EVENT_RESIZE
+   && theEventType != EMSCRIPTEN_EVENT_CANVASRESIZED)
+  {
+    return false;
+  }
+#else
+  (void )theEventType;
+#endif
+  theListener.ProcessConfigure (true);
+  return true;
+}
+
+// =======================================================================
+// function : MouseButtonsFromNative
+// purpose  :
+// =======================================================================
+Aspect_VKeyMouse Wasm_Window::MouseButtonsFromNative (unsigned short theButtons)
+{
+  Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
+  if ((theButtons & 0x1) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_LeftButton;
+  }
+  if ((theButtons & 0x2) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_RightButton;
+  }
+  if ((theButtons & 0x4) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_MiddleButton;
+  }
+  return aButtons;
+}
+
+// =======================================================================
+// function : VirtualKeyFromNative
+// purpose  :
+// =======================================================================
+Aspect_VKey Wasm_Window::VirtualKeyFromNative (Standard_Integer theKey)
+{
+#if defined(__EMSCRIPTEN__)
+  if (theKey >= DOM_VK_0
+   && theKey <= DOM_VK_9)
+  {
+    // numpad keys
+    return Aspect_VKey((theKey - DOM_VK_0) + Aspect_VKey_0);
+  }
+  if (theKey >= DOM_VK_A
+   && theKey <= DOM_VK_Z)
+  {
+    // main latin alphabet keys
+    return Aspect_VKey((theKey - DOM_VK_A) + Aspect_VKey_A);
+  }
+  if (theKey >= DOM_VK_F1
+   && theKey <= DOM_VK_F24)
+  {
+    // special keys
+    if (theKey <= DOM_VK_F12)
+    {
+      return Aspect_VKey((theKey - DOM_VK_F1) + Aspect_VKey_F1);
+    }
+    return Aspect_VKey_UNKNOWN;
+  }
+  if (theKey >= DOM_VK_NUMPAD0
+   && theKey <= DOM_VK_NUMPAD9)
+  {
+    // numpad keys
+    return Aspect_VKey((theKey - DOM_VK_NUMPAD0) + Aspect_VKey_Numpad0);
+  }
+
+  switch (theKey)
+  {
+    case DOM_VK_CANCEL:
+    case DOM_VK_HELP:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_BACK_SPACE:
+      return Aspect_VKey_Backspace;
+    case DOM_VK_TAB:
+      return Aspect_VKey_Tab;
+    case DOM_VK_CLEAR:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_RETURN:
+    case DOM_VK_ENTER:
+      return Aspect_VKey_Enter;
+    case DOM_VK_SHIFT:
+      return Aspect_VKey_Shift;
+    case DOM_VK_CONTROL:
+      return Aspect_VKey_Control;
+    case DOM_VK_ALT:
+      return Aspect_VKey_Alt;
+    case DOM_VK_PAUSE:
+    case DOM_VK_CAPS_LOCK:
+    case DOM_VK_KANA:
+    //case DOM_VK_HANGUL:
+    case DOM_VK_EISU:
+    case DOM_VK_JUNJA:
+    case DOM_VK_FINAL:
+    case DOM_VK_HANJA:
+    //case DOM_VK_KANJI:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_ESCAPE:
+      return Aspect_VKey_Escape;
+    case DOM_VK_CONVERT:
+    case DOM_VK_NONCONVERT:
+    case DOM_VK_ACCEPT:
+    case DOM_VK_MODECHANGE:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_SPACE:
+      return Aspect_VKey_Space;
+    case DOM_VK_PAGE_UP:
+      return Aspect_VKey_PageUp;
+    case DOM_VK_PAGE_DOWN:
+      return Aspect_VKey_PageDown;
+    case DOM_VK_END:
+      return Aspect_VKey_End;
+    case DOM_VK_HOME:
+      return Aspect_VKey_Home;
+    case DOM_VK_LEFT:
+      return Aspect_VKey_Left;
+    case DOM_VK_UP:
+      return Aspect_VKey_Up;
+    case DOM_VK_RIGHT:
+      return Aspect_VKey_Right;
+    case DOM_VK_DOWN:
+      return Aspect_VKey_Down;
+    case DOM_VK_SELECT:
+    case DOM_VK_PRINT:
+    case DOM_VK_EXECUTE:
+    case DOM_VK_PRINTSCREEN:
+    case DOM_VK_INSERT:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_DELETE:
+      return Aspect_VKey_Delete;
+    case DOM_VK_COLON:
+      return Aspect_VKey_Comma;
+    case DOM_VK_SEMICOLON:
+      return Aspect_VKey_Semicolon;
+    case DOM_VK_LESS_THAN:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_EQUALS:
+      return Aspect_VKey_Equal;
+    case DOM_VK_GREATER_THAN:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_QUESTION_MARK:
+      return Aspect_VKey_Slash;
+    case DOM_VK_AT: // @ key
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_WIN:
+      return Aspect_VKey_Meta;
+    case DOM_VK_CONTEXT_MENU:
+    case DOM_VK_SLEEP:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_MULTIPLY:
+      return Aspect_VKey_NumpadMultiply;
+    case DOM_VK_ADD:
+      return Aspect_VKey_NumpadAdd;
+    case DOM_VK_SEPARATOR:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_SUBTRACT:
+      return Aspect_VKey_NumpadSubtract;
+    case DOM_VK_DECIMAL:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_DIVIDE:
+      return Aspect_VKey_NumpadDivide;
+    case DOM_VK_NUM_LOCK:
+      return Aspect_VKey_Numlock;
+    case DOM_VK_SCROLL_LOCK:
+      return Aspect_VKey_Scroll;
+    case DOM_VK_WIN_OEM_FJ_JISHO:
+    case DOM_VK_WIN_OEM_FJ_MASSHOU:
+    case DOM_VK_WIN_OEM_FJ_TOUROKU:
+    case DOM_VK_WIN_OEM_FJ_LOYA:
+    case DOM_VK_WIN_OEM_FJ_ROYA:
+    case DOM_VK_CIRCUMFLEX:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_EXCLAMATION:
+    case DOM_VK_DOUBLE_QUOTE:
+    //case DOM_VK_HASH:
+    case DOM_VK_DOLLAR:
+    case DOM_VK_PERCENT:
+    case DOM_VK_AMPERSAND:
+    case DOM_VK_UNDERSCORE:
+    case DOM_VK_OPEN_PAREN:
+    case DOM_VK_CLOSE_PAREN:
+    case DOM_VK_ASTERISK:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_PLUS:
+      return Aspect_VKey_Plus;
+    case DOM_VK_PIPE:
+    case DOM_VK_HYPHEN_MINUS:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_OPEN_CURLY_BRACKET:
+      return Aspect_VKey_BracketLeft;
+    case DOM_VK_CLOSE_CURLY_BRACKET:
+      return Aspect_VKey_BracketRight;
+    case DOM_VK_TILDE:
+      return Aspect_VKey_Tilde;
+    case DOM_VK_VOLUME_MUTE:
+      return Aspect_VKey_VolumeMute;
+    case DOM_VK_VOLUME_DOWN:
+      return Aspect_VKey_VolumeDown;
+    case DOM_VK_VOLUME_UP:
+      return Aspect_VKey_VolumeUp;
+    case DOM_VK_COMMA:
+      return Aspect_VKey_Comma;
+    case DOM_VK_PERIOD:
+      return Aspect_VKey_Period;
+    case DOM_VK_SLASH:
+      return Aspect_VKey_Slash;
+    case DOM_VK_BACK_QUOTE:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_OPEN_BRACKET:
+      return Aspect_VKey_BracketLeft;
+    case DOM_VK_BACK_SLASH:
+      return Aspect_VKey_Backslash;
+    case DOM_VK_CLOSE_BRACKET:
+      return Aspect_VKey_BracketRight;
+    case DOM_VK_QUOTE:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_META:
+      return Aspect_VKey_Meta;
+    case DOM_VK_ALTGR:
+      return Aspect_VKey_Alt;
+    case DOM_VK_WIN_ICO_HELP:
+    case DOM_VK_WIN_ICO_00:
+    case DOM_VK_WIN_ICO_CLEAR:
+    case DOM_VK_WIN_OEM_RESET:
+    case DOM_VK_WIN_OEM_JUMP:
+    case DOM_VK_WIN_OEM_PA1:
+    case DOM_VK_WIN_OEM_PA2:
+    case DOM_VK_WIN_OEM_PA3:
+    case DOM_VK_WIN_OEM_WSCTRL:
+    case DOM_VK_WIN_OEM_CUSEL:
+    case DOM_VK_WIN_OEM_ATTN:
+    case DOM_VK_WIN_OEM_FINISH:
+    case DOM_VK_WIN_OEM_COPY:
+    case DOM_VK_WIN_OEM_AUTO:
+    case DOM_VK_WIN_OEM_ENLW:
+    case DOM_VK_WIN_OEM_BACKTAB:
+    case DOM_VK_ATTN:
+    case DOM_VK_CRSEL:
+    case DOM_VK_EXSEL:
+    case DOM_VK_EREOF:
+      return Aspect_VKey_UNKNOWN;
+    case DOM_VK_PLAY:
+      return Aspect_VKey_MediaPlayPause;
+    case DOM_VK_ZOOM:
+    case DOM_VK_PA1:
+    case DOM_VK_WIN_OEM_CLEAR:
+      return Aspect_VKey_UNKNOWN;
+  }
+#else
+  (void )theKey;
+#endif
+  return Aspect_VKey_UNKNOWN;
+}
diff --git a/src/Wasm/Wasm_Window.hxx b/src/Wasm/Wasm_Window.hxx
new file mode 100644 (file)
index 0000000..5485d25
--- /dev/null
@@ -0,0 +1,185 @@
+// Created by: Kirill Gavrilov
+// Copyright (c) 2021 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.
+
+#ifndef _Wasm_Window_HeaderFile
+#define _Wasm_Window_HeaderFile
+
+#include <Aspect_Window.hxx>
+
+#include <Aspect_VKey.hxx>
+#include <Aspect_Handle.hxx>
+#include <Graphic3d_Vec2.hxx>
+
+class Aspect_WindowInputListener;
+
+struct EmscriptenMouseEvent;
+struct EmscriptenWheelEvent;
+struct EmscriptenTouchEvent;
+struct EmscriptenKeyboardEvent;
+struct EmscriptenUiEvent;
+
+//! This class defines WebAssembly window (HTML5 canvas) intended for creation of OpenGL (WebGL) context.
+//!
+//! Note that canvas may define an independent dimensions for backing store (WebGL buffer to render)
+//! and for CSS (logical units to present buffer onto screen).
+//! These dimensions differ when browser is dragged into a high pixel density screen (HiDPI),
+//! or when user scales page in the browser (in both cases window.devicePixelRatio JavaScript property becomes not equal to 1.0).
+//!
+//! By default, Wasm_Window::DoResize() will scale backing store of a canvas basing on DevicePixelRatio() scale factor
+//! to ensure canvas content being rendered with the native resolution and not stretched by browser.
+//! This, however, might have side effects:
+//! - a slow GPU might experience performance issues on drawing into larger buffer (e.g. HiDPI);
+//! - user interface displayed in 3D Viewer (e.g. AIS presentations) should be scaled proportionally to be accessible,
+//!   which might require extra processing at application level.
+//! Consider changing ToScaleBacking flag passed to Wasm_Window constructor in case of issues.
+class Wasm_Window : public Aspect_Window
+{
+  DEFINE_STANDARD_RTTIEXT(Wasm_Window, Aspect_Window)
+public:
+
+  //! Convert Emscripten mouse buttons into Aspect_VKeyMouse.
+  Standard_EXPORT static Aspect_VKeyMouse MouseButtonsFromNative (unsigned short theButtons);
+
+  //! Convert DOM virtual key into Aspect_VKey.
+  Standard_EXPORT static Aspect_VKey VirtualKeyFromNative (Standard_Integer theKey);
+
+public:
+
+  //! Wraps existing HTML5 canvas into window.
+  //! @param[in] theCanvasId target HTML element id defined in a querySelector() syntax
+  //! @param[in] theToScaleBacking when TRUE, window will automatically scale backing store of canvas
+  //!                              basing on DevicePixelRatio() scale factor within DoResize()
+  Standard_EXPORT Wasm_Window (const TCollection_AsciiString& theCanvasId,
+                               const bool theToScaleBacking = true);
+
+  //! Destroys the window.
+  Standard_EXPORT virtual ~Wasm_Window();
+
+  //! Return true if window is not hidden.
+  virtual Standard_Boolean IsMapped() const Standard_OVERRIDE { return myIsMapped; }
+
+  //! Change window mapped flag to TRUE.
+  virtual void Map()   const Standard_OVERRIDE { myIsMapped = Standard_True; }
+
+  //! Change window mapped flag to FALSE.
+  virtual void Unmap() const Standard_OVERRIDE { myIsMapped = Standard_False; }
+
+  //! Resize window.
+  //! In case of ToScaleBacking flag, this method will resize the backing store of canvas
+  //! basing on DevicePixelRatio() scale factor and CSS canvas size.
+  Standard_EXPORT virtual Aspect_TypeOfResize DoResize() Standard_OVERRIDE;
+
+  //! Apply the mapping change to the window.
+  virtual Standard_Boolean DoMapping() const Standard_OVERRIDE { return Standard_True; }
+
+  //! Returns window ratio equal to the physical width/height dimensions.
+  Standard_EXPORT virtual Standard_Real Ratio() const Standard_OVERRIDE;
+
+  //! Returns The Window POSITION in PIXEL
+  Standard_EXPORT virtual void Position (Standard_Integer& theX1,
+                                         Standard_Integer& theY1,
+                                         Standard_Integer& theX2,
+                                         Standard_Integer& theY2) const Standard_OVERRIDE;
+
+  //! Return the window size in pixels.
+  Standard_EXPORT virtual void Size (Standard_Integer& theWidth,
+                                     Standard_Integer& theHeight) const Standard_OVERRIDE;
+
+  //! Set new window size in logical (density-independent units).
+  //! Backing store will be resized basing on DevicePixelRatio().
+  Standard_EXPORT void SetSizeLogical (const Graphic3d_Vec2d& theSize);
+
+  //! Set new window size in pixels.
+  //! Logical size of the element will be resized basing on DevicePixelRatio().
+  Standard_EXPORT void SetSizeBacking (const Graphic3d_Vec2i& theSize);
+
+  //! Returns canvas id.
+  const TCollection_AsciiString& CanvasId() const { return myCanvasId; }
+
+  //! Current EGL implementation in Emscripten accepts only 0 for native window id.
+  virtual Aspect_Drawable NativeHandle() const Standard_OVERRIDE { return 0; }
+
+  //! Always returns 0 for this class.
+  virtual Aspect_Drawable NativeParentHandle() const Standard_OVERRIDE { return 0; }
+
+  //! Always returns 0 for this class.
+  virtual Aspect_FBConfig NativeFBConfig() const Standard_OVERRIDE { return 0; }
+
+  //! Return device pixel ratio (logical to backing store scale factor).
+  virtual Standard_Real DevicePixelRatio() const Standard_OVERRIDE { return myDevicePixelRatio; }
+
+  //! Invalidate entire window content through generation of Expose event.
+  Standard_EXPORT virtual void InvalidateContent (const Handle(Aspect_DisplayConnection)& theDisp) Standard_OVERRIDE;
+
+public:
+
+  //! Process a single window message.
+  //! @param[in,out] theListener listener to redirect message
+  //! @param[in] theEventType message type to process
+  //! @param[in] theEvent message to process
+  //! @return TRUE if message has been processed
+  Standard_EXPORT virtual bool ProcessMessage (Aspect_WindowInputListener& theListener,
+                                               int theEventType, const void* theEvent);
+
+  //! Process a mouse input message.
+  //! @param[in,out] theListener listener to redirect message
+  //! @param[in] theEventType message type to process
+  //! @param[in] theEvent message to process
+  //! @return TRUE if message has been processed
+  Standard_EXPORT virtual bool ProcessMouseEvent (Aspect_WindowInputListener& theListener,
+                                                  int theEventType, const EmscriptenMouseEvent* theEvent);
+
+  //! Process a (mouse) wheel input message.
+  //! @param[in,out] theListener listener to redirect message
+  //! @param[in] theEventType message type to process
+  //! @param[in] theEvent message to process
+  //! @return TRUE if message has been processed
+  Standard_EXPORT virtual bool ProcessWheelEvent (Aspect_WindowInputListener& theListener,
+                                                  int theEventType, const EmscriptenWheelEvent* theEvent);
+
+  //! Process a mouse input message.
+  //! @param[in,out] theListener listener to redirect message
+  //! @param[in] theEventType message type to process
+  //! @param[in] theEvent message to process
+  //! @return TRUE if message has been processed
+  Standard_EXPORT virtual bool ProcessTouchEvent (Aspect_WindowInputListener& theListener,
+                                                  int theEventType, const EmscriptenTouchEvent* theEvent);
+
+  //! Process a keyboard input message.
+  //! @param[in,out] theListener listener to redirect message
+  //! @param[in] theEventType message type to process
+  //! @param[in] theEvent message to process
+  //! @return TRUE if message has been processed
+  Standard_EXPORT virtual bool ProcessKeyEvent (Aspect_WindowInputListener& theListener,
+                                               int theEventType, const EmscriptenKeyboardEvent* theEvent);
+
+  //! Process a UI input message (like window resize).
+  //! @param[in,out] theListener listener to redirect message
+  //! @param[in] theEventType message type to process
+  //! @param[in] theEvent message to process
+  //! @return TRUE if message has been processed
+  Standard_EXPORT virtual bool ProcessUiEvent (Aspect_WindowInputListener& theListener,
+                                               int theEventType, const EmscriptenUiEvent* theEvent);
+
+protected:
+
+  TCollection_AsciiString  myCanvasId;
+  Graphic3d_Vec2i          mySize;
+  Standard_Real            myDevicePixelRatio;
+  Standard_Boolean         myToScaleBacking;
+  mutable Standard_Boolean myIsMapped;
+
+};
+
+#endif // _Wasm_Window_HeaderFile
index b3e538d..8ffa134 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <Xw_Window.hxx>
 
-#include <Aspect_Convert.hxx>
 #include <Aspect_ScrollDelta.hxx>
 #include <Aspect_WindowDefinitionError.hxx>
 #include <Aspect_WindowInputListener.hxx>
@@ -44,7 +43,6 @@ Xw_Window::Xw_Window (const Handle(Aspect_DisplayConnection)& theXDisplay,
                       const Standard_Integer thePxWidth,
                       const Standard_Integer thePxHeight)
 : Aspect_Window(),
-  myDisplay  (theXDisplay),
   myXWindow  (0),
   myFBConfig (NULL),
   myXLeft    (thePxLeft),
@@ -53,6 +51,7 @@ Xw_Window::Xw_Window (const Handle(Aspect_DisplayConnection)& theXDisplay,
   myYBottom  (thePxTop + thePxHeight),
   myIsOwnWin (Standard_True)
 {
+  myDisplay = theXDisplay;
   if (thePxWidth <= 0 || thePxHeight <= 0)
   {
     throw Aspect_WindowDefinitionError("Xw_Window, Coordinate(s) out of range");
@@ -129,7 +128,6 @@ Xw_Window::Xw_Window (const Handle(Aspect_DisplayConnection)& theXDisplay,
                       const Aspect_Drawable theXWin,
                       const Aspect_FBConfig theFBConfig)
 : Aspect_Window(),
-  myDisplay  (theXDisplay),
   myXWindow  (theXWin),
   myFBConfig (theFBConfig),
   myXLeft    (0),
@@ -138,6 +136,7 @@ Xw_Window::Xw_Window (const Handle(Aspect_DisplayConnection)& theXDisplay,
   myYBottom  (512),
   myIsOwnWin (Standard_False)
 {
+  myDisplay = theXDisplay;
   if (theXWin == 0)
   {
     throw Aspect_WindowDefinitionError("Xw_Window, given invalid X window");
index 13cd559..b7d4c4f 100644 (file)
@@ -93,9 +93,6 @@ public:
   //! @return native Window handle
   Aspect_Drawable XWindow() const { return myXWindow; }
 
-  //! @return connection to X Display
-  Standard_EXPORT const Handle(Aspect_DisplayConnection)& DisplayConnection() const;
-
   //! @return native Window handle
   virtual Aspect_Drawable NativeHandle() const Standard_OVERRIDE
   {
@@ -133,7 +130,6 @@ public:
 
 protected:
 
-  Handle(Aspect_DisplayConnection) myDisplay; //!< X Display connection
   Aspect_Drawable  myXWindow;  //!< XLib window handle
   Aspect_FBConfig  myFBConfig; //!< GLXFBConfig
   Standard_Integer myXLeft;    //!< left   position in pixels