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.
n StdPrs
n StdSelect
n V3d
+n Wasm
n WNT
n Xw
n Cocoa
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})
#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>
#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
{
// ================================================================
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;
}
}
- 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);
}
// ================================================================
-// 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();
}
}
{
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
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();
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;
}
// ================================================================
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;
}
// ================================================================
// ================================================================
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;
}
// ================================================================
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;
}
// ================================================================
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);
}
//==============================================================================
//! Application event loop.
void mainloop();
- //! Request view redrawing.
- void updateView();
-
//! Flush events and redraw view.
void redrawView();
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.
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
+++ /dev/null
-// 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
myMouseStopDragOnUnclick (false),
//
myTouchToleranceScale (1.0f),
+ myTouchClickThresholdPx (3.0f),
myTouchRotationThresholdPx (6.0f),
myTouchZRotationThreshold (float(2.0 * M_PI / 180.0)),
myTouchPanThresholdPx (4.0f),
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)
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)
}
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;
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);
}
}
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);
}
{
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;
{
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;
{
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;
{
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;
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;
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;
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;
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;
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;
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;
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;
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;
}
if (aWalk.IsEmpty())
{
+ if (aWalk.IsDefined())
+ {
+ setAskNextFrame();
+ }
return aWalk;
}
else if (myGL.OrbitRotation.ToRotate
//! 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
//! @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.
//! @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.
//! @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
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
{
//! 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]; }
//! 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(); }
AIS_WalkPart myTranslation[3];
AIS_WalkPart myRotation[3];
+ bool myIsDefined;
bool myIsJumping;
bool myIsCrouching;
bool myIsRunning;
+++ /dev/null
-// 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 */
#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>
//! Defines a window.
class Aspect_Window : public Standard_Transient
{
-
+ DEFINE_STANDARD_RTTIEXT(Aspect_Window, Standard_Transient)
public:
//! Modifies the window background.
//! 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; }
//! 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.
protected:
+ Handle(Aspect_DisplayConnection) myDisplay; //!< Display connection
Aspect_Background MyBackground;
Aspect_GradientBackground MyGradientBackground;
Aspect_FillMethod MyBackgroundFillMethod;
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 :
#define _Aspect_WindowInputListener_HeaderFile
#include <Aspect_VKeySet.hxx>
+#include <Aspect_TouchMap.hxx>
#include <Graphic3d_Vec.hxx>
#include <Standard.hxx>
#include <Standard_DefineAlloc.hxx>
//! 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.
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
Aspect_CircularGrid.cxx
Aspect_CircularGrid.hxx
Aspect_ColorSpace.hxx
-Aspect_Convert.hxx
Aspect_Display.hxx
Aspect_DisplayConnection.cxx
Aspect_DisplayConnection.hxx
#include <Cocoa_LocalPool.hxx>
#include <Image_AlienPixMap.hxx>
-#include <Aspect_Convert.hxx>
#include <Aspect_WindowDefinitionError.hxx>
IMPLEMENT_STANDARD_RTTIEXT(Cocoa_Window,Aspect_Window)
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")
#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,
{
#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);
<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()
{
} 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>
#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
}
getDrawWindowList().Append (this);
+#else
+ (void )theParent;
+ (void )theWin;
#endif
init (anXY, aSize);
#elif defined(HAVE_XLIB)
XDrawString (Draw_WindowDisplay, GetDrawable(), myBase->gc, theX, theY, (char* )theText, strlen(theText));
#else
- //
+ (void )theX;
+ (void )theY;
+ (void )theText;
#endif
}
{
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);
}
// 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()
Xw
Image
Media
+Wasm
WNT
Cocoa
Font
#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);
: myCtx (theCtx),
myView (theView),
myToPickPnt (Standard_False),
- myIsTmpContRedraw (Standard_False)
+ myIsTmpContRedraw (Standard_False),
+ myUpdateRequests (0)
{
myViewAnimation = GlobalViewAnimation();
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());
}
//=======================================================================
}
}
+// =======================================================================
+// 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 :
{
if (!myView.IsNull())
{
- myView->Invalidate();
FlushViewEvents (myCtx, myView, true);
}
}
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
&& (!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)
{
return;
}
+ myView->Window()->DoResize();
myView->MustBeResized();
+ myView->Invalidate();
FlushViewEvents (myCtx, myView, true);
}
}
//==============================================================================
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
}
// =======================================================================
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
+}
#include <TCollection_AsciiString.hxx>
class AIS_InteractiveContext;
+class Aspect_Window;
class V3d_View;
DEFINE_STANDARD_HANDLE(ViewerTest_EventManager, Standard_Transient)
//! 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; }
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,
Standard_Boolean myToPickPnt;
Standard_Boolean myIsTmpContRedraw;
+ unsigned int myUpdateRequests; //!< counter for unhandled update requests
+
};
#endif // _ViewerTest_EventManager_HeaderFile
#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
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;
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;
// 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();
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());
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();
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();
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)
{
}
#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 :
#if defined(_WIN32) && !defined(OCCT_UWP)
-#include <Aspect_Convert.hxx>
#include <Aspect_ScrollDelta.hxx>
#include <Aspect_WindowDefinitionError.hxx>
#include <Aspect_WindowError.hxx>
--- /dev/null
+Wasm_Window.cxx
+Wasm_Window.hxx
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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
#include <Xw_Window.hxx>
-#include <Aspect_Convert.hxx>
#include <Aspect_ScrollDelta.hxx>
#include <Aspect_WindowDefinitionError.hxx>
#include <Aspect_WindowInputListener.hxx>
const Standard_Integer thePxWidth,
const Standard_Integer thePxHeight)
: Aspect_Window(),
- myDisplay (theXDisplay),
myXWindow (0),
myFBConfig (NULL),
myXLeft (thePxLeft),
myYBottom (thePxTop + thePxHeight),
myIsOwnWin (Standard_True)
{
+ myDisplay = theXDisplay;
if (thePxWidth <= 0 || thePxHeight <= 0)
{
throw Aspect_WindowDefinitionError("Xw_Window, Coordinate(s) out of range");
const Aspect_Drawable theXWin,
const Aspect_FBConfig theFBConfig)
: Aspect_Window(),
- myDisplay (theXDisplay),
myXWindow (theXWin),
myFBConfig (theFBConfig),
myXLeft (0),
myYBottom (512),
myIsOwnWin (Standard_False)
{
+ myDisplay = theXDisplay;
if (theXWin == 0)
{
throw Aspect_WindowDefinitionError("Xw_Window, given invalid X window");
//! @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
{
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